Merge "Import revised translations.  DO NOT MERGE" into ics-mr1
diff --git a/jni/feature_mos/src/mosaic/Blend.cpp b/jni/feature_mos/src/mosaic/Blend.cpp
index 18972ee..cce89ff 100644
--- a/jni/feature_mos/src/mosaic/Blend.cpp
+++ b/jni/feature_mos/src/mosaic/Blend.cpp
@@ -226,14 +226,25 @@
     yTopMost = max(0, max(yTopCorners[0], yTopCorners[1]) - fullRect.top + 1);
     yBottomMost = min(Mheight - 1, min(yBottomCorners[0], yBottomCorners[1]) - fullRect.top - 1);
 
+    if (xRightMost <= xLeftMost || yBottomMost <= yTopMost)
+    {
+        LOGE("RunBlend: aborting -consistency check failed,"
+             "(xLeftMost, xRightMost, yTopMost, yBottomMost): (%d, %d, %d, %d)",
+             xLeftMost, xRightMost, yTopMost, yBottomMost);
+        return BLEND_RET_ERROR;
+    }
+
     // Make sure image width is multiple of 4
     Mwidth = (unsigned short) ((Mwidth + 3) & ~3);
     Mheight = (unsigned short) ((Mheight + 3) & ~3);    // Round up.
 
-    if (Mwidth < width || Mheight < height || xRightMost <= xLeftMost)
+    ret = MosaicSizeCheck(LIMIT_SIZE_MULTIPLIER, LIMIT_HEIGHT_MULTIPLIER);
+    if (ret != BLEND_RET_OK)
     {
-        LOGE("RunBlend: aborting - consistency check failed, w=%d, h=%d, xLeftMost=%d, xRightMost=%d", Mwidth, Mheight, xLeftMost, xRightMost);
-        return BLEND_RET_ERROR;
+       LOGE("RunBlend: aborting - mosaic size check failed, "
+            "(frame_width, frame_height) vs (mosaic_width, mosaic_height): "
+            "(%d, %d) vs (%d, %d)", width, height, Mwidth, Mheight);
+       return ret;
     }
 
     LOGI("Allocate mosaic image for blending - size: %d x %d", Mwidth, Mheight);
@@ -296,6 +307,26 @@
     return ret;
 }
 
+int Blend::MosaicSizeCheck(float sizeMultiplier, float heightMultiplier) {
+   if (Mwidth < width || Mheight < height) {
+        return BLEND_RET_ERROR;
+    }
+
+   if ((Mwidth * Mheight) > (width * height * sizeMultiplier)) {
+         return BLEND_RET_ERROR;
+   }
+
+   // We won't do blending for the cases where users swing the device too much
+   // in the secondary direction. We use a short side to determine the
+   // secondary direction because users may hold the device in landsape
+   // or portrait.
+   int shortSide = min(Mwidth, Mheight);
+   if (shortSide > height * heightMultiplier) {
+       return BLEND_RET_ERROR;
+   }
+
+   return BLEND_RET_OK;
+}
 
 int Blend::FillFramePyramid(MosaicFrame *mb)
 {
diff --git a/jni/feature_mos/src/mosaic/Blend.h b/jni/feature_mos/src/mosaic/Blend.h
index 80bb577..ebb3bdc 100644
--- a/jni/feature_mos/src/mosaic/Blend.h
+++ b/jni/feature_mos/src/mosaic/Blend.h
@@ -113,6 +113,11 @@
 
   int  PerformFinalBlending(YUVinfo &imgMos, MosaicRect &cropping_rect);
   void CropFinalMosaic(YUVinfo &imgMos, MosaicRect &cropping_rect);
+
+private:
+   static const float LIMIT_SIZE_MULTIPLIER = 5.0f * 2.0f;
+   static const float LIMIT_HEIGHT_MULTIPLIER = 2.5f;
+   int MosaicSizeCheck(float sizeMultiplier, float heightMultiplier);
 };
 
 #endif
diff --git a/res/layout/indicator_bar.xml b/res/layout/indicator_bar.xml
index 33a8c7a..b7348b4 100644
--- a/res/layout/indicator_bar.xml
+++ b/res/layout/indicator_bar.xml
@@ -63,7 +63,7 @@
                     android:layout_width="0dp"
                     android:background="@color/mode_selection_border"
                     android:visibility="gone" />
-            <com.android.camera.ui.ColorFilterImageView
+            <com.android.camera.ui.TwoStateImageView
                     android:id="@+id/back_to_first_level"
                     android:layout_height="match_parent"
                     android:layout_width="wrap_content"
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 9c7bbfe..1fad8a8 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -143,6 +143,7 @@
     private GestureDetector mPopupGestureDetector;
     private boolean mOpenCameraFail = false;
     private boolean mCameraDisabled = false;
+    private boolean mFaceDetectionStarted = false;
 
     private View mPreviewPanel;  // The container of PreviewFrameLayout.
     private PreviewFrameLayout mPreviewFrameLayout;
@@ -520,7 +521,9 @@
 
     @Override
     public void startFaceDetection() {
+        if (mFaceDetectionStarted || mCameraState != IDLE) return;
         if (mParameters.getMaxNumDetectedFaces() > 0) {
+            mFaceDetectionStarted = true;
             mFaceView = (FaceView) findViewById(R.id.face_view);
             mFaceView.clear();
             mFaceView.setVisibility(View.VISIBLE);
@@ -535,7 +538,9 @@
 
     @Override
     public void stopFaceDetection() {
+        if (!mFaceDetectionStarted) return;
         if (mParameters.getMaxNumDetectedFaces() > 0) {
+            mFaceDetectionStarted = false;
             mCameraDevice.setFaceDetectionListener(null);
             mCameraDevice.stopFaceDetection();
             if (mFaceView != null) mFaceView.clear();
@@ -982,6 +987,7 @@
 
         mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,
                 mPostViewPictureCallback, new JpegPictureCallback(loc));
+        mFaceDetectionStarted = false;
         setCameraState(SNAPSHOT_IN_PROGRESS);
         return true;
     }
@@ -1461,7 +1467,7 @@
                 initializeCapabilities();
                 resetExposureCompensation();
                 startPreview();
-                if (mFirstTimeInitialized) startFaceDetection();
+                startFaceDetection();
             } catch (CameraHardwareException e) {
                 Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
                 return;
@@ -1683,7 +1689,7 @@
         // display rotation in onCreate may not be what we want.
         if (mCameraState == PREVIEW_STOPPED) {
             startPreview();
-            if (mFirstTimeInitialized) startFaceDetection();
+            startFaceDetection();
         } else {
             if (Util.getDisplayRotation(this) != mDisplayRotation) {
                 setDisplayOrientation();
@@ -1717,6 +1723,7 @@
     private void closeCamera() {
         if (mCameraDevice != null) {
             CameraHolder.instance().release();
+            mFaceDetectionStarted = false;
             mCameraDevice.setZoomChangeListener(null);
             mCameraDevice.setFaceDetectionListener(null);
             mCameraDevice.setErrorCallback(null);
@@ -1795,6 +1802,7 @@
             Log.v(TAG, "stopPreview");
             mCameraDevice.cancelAutoFocus(); // Reset the focus.
             mCameraDevice.stopPreview();
+            mFaceDetectionStarted = false;
         }
         setCameraState(PREVIEW_STOPPED);
         mFocusManager.onPreviewStopped();
diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java
index 07caeb2..576e9c4 100644
--- a/src/com/android/camera/MenuHelper.java
+++ b/src/com/android/camera/MenuHelper.java
@@ -47,26 +47,6 @@
     private static final String PANORAMA_CLASS = "com.android.camera.panorama.PanoramaActivity";
     private static final String VIDEO_CAMERA_CLASS = "com.android.camera.VideoCamera";
 
-    public static void confirmAction(Context context, String title,
-            String message, final Runnable action) {
-        OnClickListener listener = new OnClickListener() {
-            public void onClick(DialogInterface dialog, int which) {
-                switch (which) {
-                    case DialogInterface.BUTTON_POSITIVE:
-                        if (action != null) action.run();
-                }
-            }
-        };
-        new AlertDialog.Builder(context)
-            .setIconAttribute(android.R.attr.alertDialogIcon)
-            .setTitle(title)
-            .setMessage(message)
-            .setPositiveButton(android.R.string.ok, listener)
-            .setNegativeButton(android.R.string.cancel, listener)
-            .create()
-            .show();
-    }
-
     public static void addSwitchModeMenuItem(Menu menu, int mode,
             final Runnable r) {
         int labelId, iconId;
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index 0b946c4..75cba44 100755
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -151,6 +151,7 @@
     private int mSurfaceWidth;
     private int mSurfaceHeight;
     private View mReviewControl;
+    private RotateDialogController mRotateDialog;
 
     private Toast mNoShareToast;
     // An review image having same size as preview. It is displayed when
@@ -418,6 +419,8 @@
             mModePicker.setOnModeChangeListener(this);
         }
 
+        mRotateDialog = new RotateDialogController(this, R.layout.rotate_dialog);
+
         mPreviewPanel = findViewById(R.id.frame_layout);
         mPreviewFrameLayout = (PreviewFrameLayout) findViewById(R.id.frame);
         mReviewImage = (ImageView) findViewById(R.id.review_image);
@@ -569,7 +572,7 @@
     private void setOrientationIndicator(int orientation) {
         Rotatable[] indicators = {mThumbnailView, mModePicker, mSharePopup,
                 mBgLearningMessageRotater, mIndicatorControlContainer,
-                mReviewDoneButton, mReviewPlayButton, mReviewCancelButton};
+                mReviewDoneButton, mReviewPlayButton, mReviewCancelButton, mRotateDialog};
         for (Rotatable indicator : indicators) {
             if (indicator != null) indicator.setOrientation(orientation);
         }
@@ -2048,10 +2051,11 @@
                 restorePreferences();
             }
         };
-        MenuHelper.confirmAction(this,
+        mRotateDialog.showAlertDialog(
                 getString(R.string.confirm_restore_title),
                 getString(R.string.confirm_restore_message),
-                runnable);
+                getString(android.R.string.ok), runnable,
+                getString(android.R.string.cancel), null);
     }
 
     private void restorePreferences() {
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index 8b9c65a..a65d263 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -989,9 +989,20 @@
         keepScreenOnAwhile();
     }
 
+    /**
+     * Generate the final mosaic image.
+     *
+     * @param highRes flag to indicate whether we want to get a high-res version.
+     * @return a MosaicJpeg with its isValid flag set to true if successful; null if the generation
+     *         process is cancelled; and a MosaicJpeg with its isValid flag set to false if there
+     *         is an error in generating the final mosaic.
+     */
     public MosaicJpeg generateFinalMosaic(boolean highRes) {
-        if (mMosaicFrameProcessor.createMosaic(highRes) == Mosaic.MOSAIC_RET_CANCELLED) {
+        int mosaicReturnCode = mMosaicFrameProcessor.createMosaic(highRes);
+        if (mosaicReturnCode == Mosaic.MOSAIC_RET_CANCELLED) {
             return null;
+        } else if (mosaicReturnCode == Mosaic.MOSAIC_RET_ERROR) {
+            return new MosaicJpeg();
         }
 
         byte[] imageData = mMosaicFrameProcessor.getFinalMosaicNV21();
diff --git a/src/com/android/camera/ui/IndicatorControlWheel.java b/src/com/android/camera/ui/IndicatorControlWheel.java
index 3b8bea4..92f658a 100644
--- a/src/com/android/camera/ui/IndicatorControlWheel.java
+++ b/src/com/android/camera/ui/IndicatorControlWheel.java
@@ -187,7 +187,7 @@
         if (rotatable) {
             view = new RotateImageView(context);
         } else {
-            view = new ColorFilterImageView(context);
+            view = new TwoStateImageView(context);
         }
         view.setImageResource(resourceId);
         view.setOnClickListener(this);
diff --git a/src/com/android/camera/ui/RotateImageView.java b/src/com/android/camera/ui/RotateImageView.java
index f47a26b..39c4df2 100644
--- a/src/com/android/camera/ui/RotateImageView.java
+++ b/src/com/android/camera/ui/RotateImageView.java
@@ -32,7 +32,7 @@
 /**
  * A @{code ImageView} which can rotate it's content.
  */
-public class RotateImageView extends ColorFilterImageView implements Rotatable {
+public class RotateImageView extends TwoStateImageView implements Rotatable {
 
     @SuppressWarnings("unused")
     private static final String TAG = "RotateImageView";
diff --git a/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java b/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java
index 1fb9a80..fbf1585 100644
--- a/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java
+++ b/src/com/android/camera/ui/SecondLevelIndicatorControlBar.java
@@ -89,6 +89,12 @@
         return (mNonIndicatorButtonCount + ((baselineX - x) / buttonRange));
     }
 
+    private void dispatchRelativeTouchEvent(View view, MotionEvent event) {
+        event.offsetLocation(-view.getLeft(), -view.getTop());
+        view.dispatchTouchEvent(event);
+        event.offsetLocation(view.getLeft(), view.getTop());
+    }
+
     @Override
     public boolean dispatchTouchEvent(MotionEvent event) {
         if (!onFilterTouchEventForSecurity(event)) return false;
@@ -106,19 +112,19 @@
         int index = getTouchViewIndex((int) x, width);
         if (index == -1) return true;
         View b = getChildAt(index);
-        b.dispatchTouchEvent(event);
+        dispatchRelativeTouchEvent(b, event);
         if ((mSelectedIndex != -1) && (index != mSelectedIndex)) {
             View v = getChildAt(mSelectedIndex);
             if (v instanceof AbstractIndicatorButton) {
                 AbstractIndicatorButton c = (AbstractIndicatorButton) v;
                 event.setAction(MotionEvent.ACTION_CANCEL);
-                c.dispatchTouchEvent(event);
+                dispatchRelativeTouchEvent(c, event);
                 c.dismissPopup();
             }
 
             if (action == MotionEvent.ACTION_MOVE) {
                 event.setAction(MotionEvent.ACTION_DOWN);
-                b.dispatchTouchEvent(event);
+                dispatchRelativeTouchEvent(b, event);
             }
         }
         mSelectedIndex = index;
diff --git a/src/com/android/camera/ui/ColorFilterImageView.java b/src/com/android/camera/ui/TwoStateImageView.java
similarity index 73%
rename from src/com/android/camera/ui/ColorFilterImageView.java
rename to src/com/android/camera/ui/TwoStateImageView.java
index 56bef82..beb6bf5 100644
--- a/src/com/android/camera/ui/ColorFilterImageView.java
+++ b/src/com/android/camera/ui/TwoStateImageView.java
@@ -23,18 +23,17 @@
 import android.widget.ImageView;
 
 /**
- * A @{code ImageView} which grey out the icon if disabled.
+ * A @{code ImageView} which change the opacity of the icon if disabled.
  */
-public class ColorFilterImageView extends ImageView {
-    private final int DISABLED_COLOR;
+public class TwoStateImageView extends ImageView {
+    private final float DISABLED_ALPHA = 0.4f;
     private boolean mFilterEnabled = true;
 
-    public ColorFilterImageView(Context context, AttributeSet attrs) {
+    public TwoStateImageView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        DISABLED_COLOR = context.getResources().getColor(R.color.icon_disabled_color);
     }
 
-    public ColorFilterImageView(Context context) {
+    public TwoStateImageView(Context context) {
         this(context, null);
     }
 
@@ -43,9 +42,9 @@
         super.setEnabled(enabled);
         if (mFilterEnabled) {
             if (enabled) {
-                clearColorFilter();
+                setAlpha(1.0f);
             } else {
-                setColorFilter(DISABLED_COLOR);
+                setAlpha(DISABLED_ALPHA);
             }
         }
     }