Merge "Fix display of preview thumbnail after recording video."
diff --git a/jni/feature_mos/src/mosaic/Blend.cpp b/jni/feature_mos/src/mosaic/Blend.cpp
index 470fba0..e8b30f3 100644
--- a/jni/feature_mos/src/mosaic/Blend.cpp
+++ b/jni/feature_mos/src/mosaic/Blend.cpp
@@ -148,6 +148,12 @@
     double xLeftCorners[2] = {2e30, 2e30};
     double xRightCorners[2] = {-2e30, -2e30};
 
+    // Corners of the top-most and bottom-most frames respectively in the
+    // mosaic coordinate system.
+    double yTopCorners[2] = {2e30, 2e30};
+    double yBottomCorners[2] = {-2e30, -2e30};
+
+
     // Determine the extents of the final mosaic
     CSite *csite = m_AllSites ;
     for(int mfit = 0; mfit < frames_size; mfit++)
@@ -177,6 +183,19 @@
             xRightCorners[1] = x2;
         }
 
+        if(y0 < yTopCorners[0] || y3 < yTopCorners[1])    // If either of the top corners is lower
+        {
+            yTopCorners[0] = y0;
+            yTopCorners[1] = y3;
+        }
+
+        if(y1 > yBottomCorners[0] || y2 > yBottomCorners[1])    // If either of the bottom corners is higher
+        {
+            yBottomCorners[0] = y1;
+            yBottomCorners[1] = y2;
+        }
+
+
         // Compute the centroid of the warped region
         FindQuadCentroid(x0, y0, x1, y1, x2, y2, x3, y3, csite->getVCenter().x, csite->getVCenter().y);
 
@@ -198,11 +217,15 @@
     Mheight = (unsigned short) (fullRect.bottom - fullRect.top + 1);
 
     int xLeftMost, xRightMost;
+    int yTopMost, yBottomMost;
 
     // Rounding up, so that we don't include the gray border.
     xLeftMost = max(0, max(xLeftCorners[0], xLeftCorners[1]) - fullRect.left + 1);
     xRightMost = min(Mwidth - 1, min(xRightCorners[0], xRightCorners[1]) - fullRect.left - 1);
 
+    yTopMost = max(0, max(yTopCorners[0], yTopCorners[1]) - fullRect.top + 1);
+    yBottomMost = min(Mheight - 1, min(yBottomCorners[0], yBottomCorners[1]) - fullRect.top - 1);
+
     // Make sure image width is multiple of 4
     Mwidth = (unsigned short) ((Mwidth + 3) & ~3);
     Mheight = (unsigned short) ((Mheight + 3) & ~3);    // Round up.
@@ -234,8 +257,16 @@
     // cropped out of the computed mosaic to get rid of the gray borders.
     MosaicRect cropping_rect;
 
-    cropping_rect.left = xLeftMost;
-    cropping_rect.right = xRightMost;
+    if (m_wb.horizontal)
+    {
+        cropping_rect.left = xLeftMost;
+        cropping_rect.right = xRightMost;
+    }
+    else
+    {
+        cropping_rect.top = yTopMost;
+        cropping_rect.bottom = yBottomMost;
+    }
 
     // Do merging and blending :
     ret = DoMergeAndBlend(frames, numCenters, width, height, *imgMos, fullRect,
@@ -378,47 +409,95 @@
     // between the images on either side of each seam:
     if (m_wb.stripType == STRIP_TYPE_WIDE)
     {
-        // Set the number of pixels around the seam to cross-fade between
-        // the two component images,
-        int tw = STRIP_CROSS_FADE_WIDTH * width;
-
-        for(int y = 0; y < imgMos.Y.height; y++)
+        if(m_wb.horizontal)
         {
-            for(int x = tw; x < imgMos.Y.width - tw + 1; )
+            // Set the number of pixels around the seam to cross-fade between
+            // the two component images,
+            int tw = STRIP_CROSS_FADE_WIDTH * width;
+
+            for(int y = 0; y < imgMos.Y.height; y++)
             {
-                // Determine where the seam is...
-                if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y][x+1] &&
-                        imgMos.Y.ptr[y][x] != 255 &&
-                        imgMos.Y.ptr[y][x+1] != 255)
+                for(int x = tw; x < imgMos.Y.width - tw + 1; )
                 {
-                    // Find the image indices on both sides of the seam
-                    unsigned char idx1 = imgMos.Y.ptr[y][x];
-                    unsigned char idx2 = imgMos.Y.ptr[y][x+1];
-
-                    for (int o = tw; o >= 0; o--)
+                    // Determine where the seam is...
+                    if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y][x+1] &&
+                            imgMos.Y.ptr[y][x] != 255 &&
+                            imgMos.Y.ptr[y][x+1] != 255)
                     {
-                        // Set the image index to use for cross-fading
-                        imgMos.V.ptr[y][x - o] = idx2;
-                        // Set the intensity weights to use for cross-fading
-                        imgMos.U.ptr[y][x - o] = 50 + (99 - 50) * o / tw;
-                    }
+                        // Find the image indices on both sides of the seam
+                        unsigned char idx1 = imgMos.Y.ptr[y][x];
+                        unsigned char idx2 = imgMos.Y.ptr[y][x+1];
 
-                    for (int o = 1; o <= tw; o++)
+                        for (int o = tw; o >= 0; o--)
+                        {
+                            // Set the image index to use for cross-fading
+                            imgMos.V.ptr[y][x - o] = idx2;
+                            // Set the intensity weights to use for cross-fading
+                            imgMos.U.ptr[y][x - o] = 50 + (99 - 50) * o / tw;
+                        }
+
+                        for (int o = 1; o <= tw; o++)
+                        {
+                            // Set the image index to use for cross-fading
+                            imgMos.V.ptr[y][x + o] = idx1;
+                            // Set the intensity weights to use for cross-fading
+                            imgMos.U.ptr[y][x + o] = imgMos.U.ptr[y][x - o];
+                        }
+
+                        x += (tw + 1);
+                    }
+                    else
                     {
-                        // Set the image index to use for cross-fading
-                        imgMos.V.ptr[y][x + o] = idx1;
-                        // Set the intensity weights to use for cross-fading
-                        imgMos.U.ptr[y][x + o] = imgMos.U.ptr[y][x - o];
+                        x++;
                     }
-
-                    x += (tw + 1);
-                }
-                else
-                {
-                    x++;
                 }
             }
         }
+        else
+        {
+            // Set the number of pixels around the seam to cross-fade between
+            // the two component images,
+            int tw = STRIP_CROSS_FADE_WIDTH * height;
+
+            for(int x = 0; x < imgMos.Y.width; x++)
+            {
+                for(int y = tw; y < imgMos.Y.height - tw + 1; )
+                {
+                    // Determine where the seam is...
+                    if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y+1][x] &&
+                            imgMos.Y.ptr[y][x] != 255 &&
+                            imgMos.Y.ptr[y+1][x] != 255)
+                    {
+                        // Find the image indices on both sides of the seam
+                        unsigned char idx1 = imgMos.Y.ptr[y][x];
+                        unsigned char idx2 = imgMos.Y.ptr[y+1][x];
+
+                        for (int o = tw; o >= 0; o--)
+                        {
+                            // Set the image index to use for cross-fading
+                            imgMos.V.ptr[y - o][x] = idx2;
+                            // Set the intensity weights to use for cross-fading
+                            imgMos.U.ptr[y - o][x] = 50 + (99 - 50) * o / tw;
+                        }
+
+                        for (int o = 1; o <= tw; o++)
+                        {
+                            // Set the image index to use for cross-fading
+                            imgMos.V.ptr[y + o][x] = idx1;
+                            // Set the intensity weights to use for cross-fading
+                            imgMos.U.ptr[y + o][x] = imgMos.U.ptr[y - o][x];
+                        }
+
+                        y += (tw + 1);
+                    }
+                    else
+                    {
+                        y++;
+                    }
+                }
+            }
+        }
+
     }
 
     // Now perform the actual blending using the frame assignment determined above
@@ -581,39 +660,80 @@
         }
     }
 
-    //Scan through each row and increment top if the row contains any gray
-    for (j = 0; j < imgMos.Y.height; j++)
+    if(m_wb.horizontal)
     {
-        for (i = cropping_rect.left; i < cropping_rect.right; i++)
+        //Scan through each row and increment top if the row contains any gray
+        for (j = 0; j < imgMos.Y.height; j++)
         {
-            if (b[j][i])
+            for (i = cropping_rect.left; i < cropping_rect.right; i++)
             {
-                break; // to next row
+                if (b[j][i])
+                {
+                    break; // to next row
+                }
+            }
+
+            if (i == cropping_rect.right)   //no gray pixel in this row!
+            {
+                cropping_rect.top = j;
+                break;
             }
         }
 
-        if (i == cropping_rect.right)   //no gray pixel in this row!
+        //Scan through each row and decrement bottom if the row contains any gray
+        for (j = imgMos.Y.height-1; j >= 0; j--)
         {
-            cropping_rect.top = j;
-            break;
+            for (i = cropping_rect.left; i < cropping_rect.right; i++)
+            {
+                if (b[j][i])
+                {
+                    break; // to next row
+                }
+            }
+
+            if (i == cropping_rect.right)   //no gray pixel in this row!
+            {
+                cropping_rect.bottom = j;
+                break;
+            }
         }
     }
-
-    //Scan through each row and decrement bottom if the row contains any gray
-    for (j = imgMos.Y.height-1; j >= 0; j--)
+    else // Vertical Mosaic
     {
-        for (i = cropping_rect.left; i < cropping_rect.right; i++)
+        //Scan through each column and increment left if the column contains any gray
+        for (i = 0; i < imgMos.Y.width; i++)
         {
-            if (b[j][i])
+            for (j = cropping_rect.top; j < cropping_rect.bottom; j++)
             {
-                break; // to next row
+                if (b[j][i])
+                {
+                    break; // to next column
+                }
+            }
+
+            if (j == cropping_rect.bottom)   //no gray pixel in this column!
+            {
+                cropping_rect.left = i;
+                break;
             }
         }
 
-        if (i == cropping_rect.right)   //no gray pixel in this row!
+        //Scan through each column and decrement right if the column contains any gray
+        for (i = imgMos.Y.width-1; i >= 0; i--)
         {
-            cropping_rect.bottom = j;
-            break;
+            for (j = cropping_rect.top; j < cropping_rect.bottom; j++)
+            {
+                if (b[j][i])
+                {
+                    break; // to next column
+                }
+            }
+
+            if (j == cropping_rect.bottom)   //no gray pixel in this column!
+            {
+                cropping_rect.right = i;
+                break;
+            }
         }
     }
 
@@ -1046,7 +1166,8 @@
         double deltaY = currY - prevY;
         double center2centerDist = sqrt(deltaY * deltaY + deltaX * deltaX);
 
-        if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD * last->width)
+        if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD * last->width ||
+                fabs(deltaY) > STRIP_SEPARATION_THRESHOLD * last->height)
         {
             relevant_frames[relevant_frames_size] = mb;
             relevant_frames_size++;
diff --git a/res/raw/backdropper.graph b/res/raw/backdropper.graph
index e3fe877..25c86e6 100644
--- a/res/raw/backdropper.graph
+++ b/res/raw/backdropper.graph
@@ -35,6 +35,8 @@
 @external previewWidth;
 @external previewHeight;
 
+@external orientation;
+
 @external learningDoneListener;
 
 // Filters ---------------------------------------------------
@@ -52,6 +54,7 @@
   sourceUrl = "no_file_specified";
   waitForNewFrame = false;
   sourceIsUrl = true;
+  orientation = $orientation;
 }
 
 // Background replacer
diff --git a/res/raw/goofy_face.graph b/res/raw/goofy_face.graph
index e346322..430c811 100644
--- a/res/raw/goofy_face.graph
+++ b/res/raw/goofy_face.graph
@@ -37,6 +37,10 @@
 @external previewWidth;
 @external previewHeight;
 
+// Not used by this graph, but simplifies higher-level
+// graph initialization code.
+@external orientation;
+
 // Filters ---------------------------------------------------
 
 // Camera input
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 07863e5..10f0b80 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -119,9 +119,6 @@
     private Parameters mInitialParams;
     private boolean mFocusAreaSupported;
     private boolean mMeteringAreaSupported;
-    private boolean mAwbLockSupported;
-    private boolean mAeLockSupported;
-    private boolean mAeAwbLock;
 
     private MyOrientationEventListener mOrientationListener;
     // The degrees of the device rotated clockwise from its natural orientation.
@@ -1420,9 +1417,6 @@
         // Do not do focus if there is not enough storage.
         if (pressed && !canTakePicture()) return;
 
-        // Lock AE and AWB so users can half-press shutter and recompose.
-        mAeAwbLock = pressed;
-        setCameraParameters(UPDATE_PARAM_PREFERENCE);
         mFocusManager.doFocus(pressed);
     }
 
@@ -1527,7 +1521,6 @@
         if (mFaceView != null) {
             mFaceView.setDisplayOrientation(mDisplayOrientation);
         }
-        mAeAwbLock = false; // Always unlock AE and AWB before start.
         setCameraParameters(UPDATE_PARAM_ALL);
         // If the focus mode is continuous autofocus, call cancelAutoFocus to
         // resume it because it may have been paused by autoFocus call.
@@ -1582,14 +1575,6 @@
     }
 
     private void updateCameraParametersPreference() {
-        if (mAwbLockSupported) {
-            mParameters.setAutoWhiteBalanceLock(mAeAwbLock);
-        }
-
-        if (mAeLockSupported) {
-            mParameters.setAutoExposureLock(mAeAwbLock);
-        }
-
         if (mFocusAreaSupported) {
             mParameters.setFocusAreas(mFocusManager.getFocusAreas());
         }
@@ -1985,7 +1970,5 @@
                 && isSupported(Parameters.FOCUS_MODE_AUTO,
                         mInitialParams.getSupportedFocusModes()));
         mMeteringAreaSupported = (mInitialParams.getMaxNumMeteringAreas() > 0);
-        mAwbLockSupported = mInitialParams.isAutoWhiteBalanceLockSupported();
-        mAeLockSupported = mInitialParams.isAutoExposureLockSupported();
     }
 }
diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java
index 301f2a2..f177387 100644
--- a/src/com/android/camera/EffectsRecorder.java
+++ b/src/com/android/camera/EffectsRecorder.java
@@ -300,7 +300,8 @@
             mGraphEnv.addReferences(
                     "previewSurface", mPreviewSurfaceHolder.getSurface(),
                     "previewWidth", mPreviewWidth,
-                    "previewHeight", mPreviewHeight);
+                    "previewHeight", mPreviewHeight,
+                    "orientation", mOrientationHint);
             if (mState == STATE_PREVIEW) {
                 // Switching effects while running. Inform video camera.
                 sendMessage(mCurrentEffect, EFFECT_MSG_SWITCHING_EFFECT);
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index a1605de..09df1e0 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -38,6 +38,7 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.ProgressDialog;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
@@ -71,6 +72,7 @@
 import android.widget.TextView;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.util.List;
 
 /**
@@ -147,9 +149,6 @@
     private Thumbnail mThumbnail;
     private SharePopup mSharePopup;
 
-    private AnimatorSet mThumbnailViewAndModePickerOut;
-    private AnimatorSet mThumbnailViewAndModePickerIn;
-
     private int mPreviewWidth;
     private int mPreviewHeight;
     private Camera mCameraDevice;
@@ -271,9 +270,8 @@
                         onBackgroundThreadFinished();
                         // Set the thumbnail bitmap here because mThumbnailView must be accessed
                         // from the UI thread.
-                        if (mThumbnail != null) {
-                            mThumbnailView.setBitmap(mThumbnail.getBitmap());
-                        }
+                        updateThumbnailButton();
+
                         // Share popup may still have the reference to the old thumbnail. Clear it.
                         mSharePopup = null;
                         resetToPreview();
@@ -309,6 +307,12 @@
                 });
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        updateThumbnailButton();
+    }
+
     private void setupCamera() {
         openCamera();
         Parameters parameters = mCameraDevice.getParameters();
@@ -526,30 +530,6 @@
         mCaptureIndicator.setVisibility(View.VISIBLE);
         showDirectionIndicators(PanoProgressBar.DIRECTION_NONE);
 
-        // XML-style animations can not be used here. The Y position has to be calculated runtime.
-        float ystart = mThumbnailView.getY();
-        ValueAnimator va1 = ObjectAnimator.ofFloat(
-                mThumbnailView, "y", ystart, -mThumbnailView.getHeight());
-        ValueAnimator va1Reverse = ObjectAnimator.ofFloat(
-                mThumbnailView, "y", -mThumbnailView.getHeight(), ystart);
-        ystart = mModePicker.getY();
-        float height = mCaptureLayout.getHeight();
-        ValueAnimator va2 = ObjectAnimator.ofFloat(
-                mModePicker, "y", ystart, height + 1);
-        ValueAnimator va2Reverse = ObjectAnimator.ofFloat(
-                mModePicker, "y", height + 1, ystart);
-        LinearInterpolator li = new LinearInterpolator();
-        mThumbnailViewAndModePickerOut = new AnimatorSet();
-        mThumbnailViewAndModePickerOut.play(va1).with(va2);
-        mThumbnailViewAndModePickerOut.setDuration(500);
-        mThumbnailViewAndModePickerOut.setInterpolator(li);
-        mThumbnailViewAndModePickerIn = new AnimatorSet();
-        mThumbnailViewAndModePickerIn.play(va1Reverse).with(va2Reverse);
-        mThumbnailViewAndModePickerIn.setDuration(500);
-        mThumbnailViewAndModePickerIn.setInterpolator(li);
-
-        mThumbnailViewAndModePickerOut.start();
-
         mCompassValueXStart = mCompassValueXStartBuffer;
         mCompassValueYStart = mCompassValueYStartBuffer;
         mMinAngleX = 0;
@@ -571,6 +551,8 @@
             }
         });
 
+        if (mModePicker != null) mModePicker.setEnabled(false);
+
         mPanoProgressBar.reset();
         // TODO: calculate the indicator width according to different devices to reflect the actual
         // angle of view of the camera device.
@@ -609,7 +591,8 @@
                 }
             });
         }
-        mThumbnailViewAndModePickerIn.start();
+        // do we have to wait for the thread to complete before enabling this?
+        if (mModePicker != null) mModePicker.setEnabled(true);
     }
 
     private void showTooFastIndication() {
@@ -749,6 +732,19 @@
         t.start();
     }
 
+    private void updateThumbnailButton() {
+        // Update last image if URI is invalid and the storage is ready.
+        ContentResolver contentResolver = getContentResolver();
+        if ((mThumbnail == null || !Util.isUriValid(mThumbnail.getUri(), contentResolver))) {
+            mThumbnail = Thumbnail.getLastThumbnail(contentResolver);
+        }
+        if (mThumbnail != null) {
+            mThumbnailView.setBitmap(mThumbnail.getBitmap());
+        } else {
+            mThumbnailView.setBitmap(null);
+        }
+    }
+
     public void saveHighResMosaic() {
         runBackgroundThread(new Thread() {
             @Override
@@ -884,6 +880,7 @@
             stopCapture(true);
             reset();
         }
+        if (mSharePopup != null) mSharePopup.dismiss();
         releaseCamera();
         mMosaicView.onPause();
         clearMosaicFrameProcessorIfNeeded();