merge in ics-mr0-release history after reset to ics-mr0
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index d9340f5..d832adb 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -122,8 +122,8 @@
     <string name="effect_background" msgid="6909716214852487679">"Arrière-plan"</string>
     <string name="accessibility_shutter_button" msgid="2664037763232556307">"Bouton de l\'obturateur"</string>
     <string name="accessibility_review_thumbnail" msgid="8961275263537513017">"Photo la plus récente"</string>
-    <string name="accessibility_camera_picker" msgid="8807945470215734566">"Interrupteur des appareils photo avant et arrière"</string>
-    <string name="accessibility_mode_picker" msgid="3264968460835265505">"Sélecteur du mode Appareil photo, Vidéo ou Panorama"</string>
+    <string name="accessibility_camera_picker" msgid="8807945470215734566">"Interrupteur des caméras avant et arrière"</string>
+    <string name="accessibility_mode_picker" msgid="3264968460835265505">"Sélecteur du mode Appareil photo, Vidéo ou Panoramique"</string>
     <string name="accessibility_second_level_indicators" msgid="3855951632917627620">"Plus de paramètres"</string>
     <string name="accessibility_back_to_first_level" msgid="5234411571109877131">"Fermer les paramètres"</string>
     <string name="accessibility_zoom_control" msgid="1339909363226825709">"Contrôle du zoom"</string>
diff --git a/src/com/android/camera/ActivityBase.java b/src/com/android/camera/ActivityBase.java
index ec878ee..b2ef481 100644
--- a/src/com/android/camera/ActivityBase.java
+++ b/src/com/android/camera/ActivityBase.java
@@ -19,17 +19,25 @@
 import com.android.camera.ui.PopupManager;
 
 import android.app.Activity;
+import android.app.KeyguardManager;
 import android.view.KeyEvent;
+import android.content.Context;
 import android.content.Intent;
+import android.hardware.Camera;
 import android.media.AudioManager;
 import android.os.Bundle;
+import android.util.Log;
 
 /**
  * Superclass of Camera and VideoCamera activities.
  */
-public class ActivityBase extends Activity {
+abstract public class ActivityBase extends Activity {
+    private static final String TAG = "ActivityBase";
+    private static boolean LOGV = false;
     private int mResultCodeForTesting;
+    private boolean mOnResumePending;
     private Intent mResultDataForTesting;
+    protected Camera mCameraDevice;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -38,6 +46,42 @@
     }
 
     @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        if (LOGV) Log.v(TAG, "onWindowFocusChanged.hasFocus=" + hasFocus
+                + ".mOnResumePending=" + mOnResumePending);
+        if (hasFocus && mOnResumePending) {
+            doOnResume();
+            mOnResumePending = false;
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        // Don't grab the camera if in use by lockscreen. For example, face
+        // unlock may be using the camera. Camera may be already opened in
+        // onCreate. doOnResume should continue if mCameraDevice != null.
+        if (mCameraDevice == null && !hasWindowFocus() && isKeyguardLocked()) {
+            if (LOGV) Log.v(TAG, "onRsume. mOnResumePending=true");
+            mOnResumePending = true;
+        } else {
+            if (LOGV) Log.v(TAG, "onRsume. mOnResumePending=false");
+            doOnResume();
+            mOnResumePending = false;
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        if (LOGV) Log.v(TAG, "onPause");
+        super.onPause();
+        mOnResumePending = false;
+    }
+
+    // Put the code of onResume in this method.
+    abstract protected void doOnResume();
+
+    @Override
     public boolean onSearchRequested() {
         return false;
     }
@@ -77,4 +121,10 @@
         PopupManager.removeInstance(this);
         super.onDestroy();
     }
+
+    private boolean isKeyguardLocked() {
+        KeyguardManager kgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+        // isKeyguardSecure excludes the slide lock case.
+        return (kgm != null) && kgm.isKeyguardLocked() && kgm.isKeyguardSecure();
+    }
 }
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index 70f70c8..931095e 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -135,7 +135,6 @@
 
     private static final String sTempCropFilename = "crop-temp";
 
-    private android.hardware.Camera mCameraDevice;
     private ContentProviderClient mMediaProviderClient;
     private SurfaceHolder mSurfaceHolder = null;
     private ShutterButton mShutterButton;
@@ -378,7 +377,10 @@
         mPreviewFrame = findViewById(R.id.camera_preview);
         mPreviewFrame.setOnTouchListener(this);
         mFocusIndicator = (RotateLayout) findViewById(R.id.focus_indicator_rotate_layout);
-        mFocusManager.initialize(mFocusIndicator, mPreviewFrame, mFaceView, this);
+        CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
+        boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
+        mFocusManager.initialize(mFocusIndicator, mPreviewFrame, mFaceView, this,
+                mirror, mDisplayOrientation);
         mFocusManager.initializeSoundPlayer(getResources().openRawResourceFd(R.raw.camera_focus));
         mImageSaver = new ImageSaver();
         Util.initializeScreenBrightness(getWindow(), getContentResolver());
@@ -1441,11 +1443,11 @@
     }
 
     @Override
-    protected void onResume() {
-        super.onResume();
-        mPausing = false;
+    protected void doOnResume() {
         if (mOpenCameraFail || mCameraDisabled) return;
 
+        mPausing = false;
+
         mJpegPictureCallbackTime = 0;
         mZoomValue = 0;
 
diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java
index 7c81bf0..d3b277d 100644
--- a/src/com/android/camera/EffectsRecorder.java
+++ b/src/com/android/camera/EffectsRecorder.java
@@ -112,9 +112,10 @@
 
     private static final int STATE_CONFIGURE              = 0;
     private static final int STATE_WAITING_FOR_SURFACE    = 1;
-    private static final int STATE_PREVIEW                = 2;
-    private static final int STATE_RECORD                 = 3;
-    private static final int STATE_RELEASED               = 4;
+    private static final int STATE_STARTING_PREVIEW       = 2;
+    private static final int STATE_PREVIEW                = 3;
+    private static final int STATE_RECORD                 = 4;
+    private static final int STATE_RELEASED               = 5;
     private int mState = STATE_CONFIGURE;
 
     private boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
@@ -287,6 +288,7 @@
             case STATE_WAITING_FOR_SURFACE:
                 startPreview();
                 break;
+            case STATE_STARTING_PREVIEW:
             case STATE_PREVIEW:
                 initializeEffect(true);
                 break;
@@ -309,7 +311,8 @@
         mEffect = effect;
         mEffectParameter = effectParameter;
 
-        if (mState == STATE_PREVIEW) {
+        if (mState == STATE_PREVIEW ||
+                mState == STATE_STARTING_PREVIEW) {
             initializeEffect(false);
         }
     }
@@ -334,7 +337,8 @@
     }
 
     private void setRecordingOrientation() {
-        if ( mState <= STATE_PREVIEW && mRunner != null ) {
+        if ( (mState == STATE_CONFIGURE || mState == STATE_WAITING_FOR_SURFACE)
+                && mRunner != null ) {
             Point bl = new Point(0, 0);
             Point br = new Point(1, 0);
             Point tl = new Point(0, 1);
@@ -443,7 +447,8 @@
                     "previewWidth", mPreviewWidth,
                     "previewHeight", mPreviewHeight,
                     "orientation", mOrientationHint);
-            if (mState == STATE_PREVIEW) {
+            if (mState == STATE_PREVIEW ||
+                    mState == STATE_STARTING_PREVIEW) {
                 // Switching effects while running. Inform video camera.
                 sendMessage(mCurrentEffect, EFFECT_MSG_SWITCHING_EFFECT);
             }
@@ -468,7 +473,8 @@
                 Log.v(TAG, "New runner: " + mRunner
                       + ". Old runner: " + mOldRunner);
             }
-            if (mState == STATE_PREVIEW) {
+            if (mState == STATE_PREVIEW ||
+                    mState == STATE_STARTING_PREVIEW) {
                 // Switching effects while running. Stop existing runner.
                 // The stop callback will take care of starting new runner.
                 mCameraDevice.stopPreview();
@@ -505,6 +511,7 @@
         if (mLogVerbose) Log.v(TAG, "Starting preview (" + this + ")");
 
         switch (mState) {
+            case STATE_STARTING_PREVIEW:
             case STATE_PREVIEW:
                 // Already running preview
                 Log.w(TAG, "startPreview called when already running preview");
@@ -543,6 +550,7 @@
 
         if (mLogVerbose) Log.v(TAG, "Starting filter graph");
 
+        mState = STATE_STARTING_PREVIEW;
         mRunner.run();
         // Rest of preview startup handled in mSourceReadyCallback
     }
@@ -554,12 +562,26 @@
             synchronized(EffectsRecorder.this) {
                 mTextureSource = source;
 
-                // When shutting down a graph, we receive a null SurfaceTexture to
-                // indicate that. Don't want to connect up the camera in that case.
-                if (source == null) return;
-
                 if (mState == STATE_RELEASED) return;
 
+                if (source == null) {
+                    if (mState == STATE_PREVIEW ||
+                            mState == STATE_STARTING_PREVIEW ||
+                            mState == STATE_RECORD) {
+                        // A null source here means the graph is shutting down
+                        // unexpectedly, so we need to turn off preview before
+                        // the surface texture goes away.
+                        mCameraDevice.stopPreview();
+                        try {
+                            mCameraDevice.setPreviewTexture(null);
+                        } catch(IOException e) {
+                            throw new RuntimeException("Unable to disconnect " +
+                                    "camera from effect input", e);
+                        }
+                    }
+                    return;
+                }
+
                 // Lock AE/AWB to reduce transition flicker
                 tryEnable3ALocks(true);
 
@@ -667,6 +689,7 @@
 
         switch (mState) {
             case STATE_CONFIGURE:
+            case STATE_STARTING_PREVIEW:
             case STATE_PREVIEW:
                 Log.w(TAG, "StopRecording called when recording not active!");
                 return;
@@ -780,7 +803,8 @@
                     }
                     mOldRunner = null;
                 }
-                if (mState == STATE_PREVIEW) {
+                if (mState == STATE_PREVIEW ||
+                        mState == STATE_STARTING_PREVIEW) {
                     // Switching effects, start up the new runner
                     if (mLogVerbose) Log.v(TAG, "Previous effect halted, starting new effect.");
                     tryEnable3ALocks(false);
@@ -803,6 +827,7 @@
 
         switch (mState) {
             case STATE_RECORD:
+            case STATE_STARTING_PREVIEW:
             case STATE_PREVIEW:
                 stopPreview();
                 // Fall-through
diff --git a/src/com/android/camera/FocusManager.java b/src/com/android/camera/FocusManager.java
index 228e2d1..86b92c2 100644
--- a/src/com/android/camera/FocusManager.java
+++ b/src/com/android/camera/FocusManager.java
@@ -21,7 +21,9 @@
 import com.android.camera.ui.FocusIndicatorView;
 
 import android.content.res.AssetFileDescriptor;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.hardware.Camera.Area;
 import android.hardware.Camera.Parameters;
 import android.os.Handler;
@@ -57,6 +59,7 @@
     private boolean mInLongPress;
     private boolean mLockAeAwbNeeded;
     private boolean mAeAwbLock;
+    private Matrix mMatrix;
     private SoundPlayer mSoundPlayer;
     private View mFocusIndicatorRotateLayout;
     private FocusIndicatorView mFocusIndicator;
@@ -98,6 +101,7 @@
         mPreferences = preferences;
         mDefaultFocusMode = defaultFocusMode;
         mHandler = new MainHandler();
+        mMatrix = new Matrix();
     }
 
     // This has to be initialized before initialize().
@@ -111,13 +115,22 @@
     }
 
     public void initialize(View focusIndicatorRotate, View previewFrame,
-            FaceView faceView, Listener listener) {
+            FaceView faceView, Listener listener, boolean mirror, int displayOrientation) {
         mFocusIndicatorRotateLayout = focusIndicatorRotate;
         mFocusIndicator = (FocusIndicatorView) focusIndicatorRotate.findViewById(
                 R.id.focus_indicator);
         mPreviewFrame = previewFrame;
         mFaceView = faceView;
         mListener = listener;
+
+        Matrix matrix = new Matrix();
+        Util.prepareMatrix(matrix, mirror, displayOrientation,
+                previewFrame.getWidth(), previewFrame.getHeight());
+        // In face detection, the matrix converts the driver coordinates to UI
+        // coordinates. In tap focus, the inverted matrix converts the UI
+        // coordinates to driver coordinates.
+        matrix.invert(mMatrix);
+
         if (mParameters != null) {
             mInitialized = true;
         } else {
@@ -270,9 +283,9 @@
         // Convert the coordinates to driver format.
         // AE area is bigger because exposure is sensitive and
         // easy to over- or underexposure if area is too small.
-        calculateTapArea(focusWidth, focusHeight, 1, x, y, previewWidth, previewHeight,
+        calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight,
                 mFocusArea.get(0).rect);
-        calculateTapArea(focusWidth, focusHeight, 1.5, x, y, previewWidth, previewHeight,
+        calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight,
                 mMeteringArea.get(0).rect);
 
         // Use margin to set the focus indicator to the touched area.
@@ -451,23 +464,16 @@
         mMeteringArea = null;
     }
 
-    public void calculateTapArea(int focusWidth, int focusHeight, double areaMultiple,
+    public void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple,
             int x, int y, int previewWidth, int previewHeight, Rect rect) {
         int areaWidth = (int)(focusWidth * areaMultiple);
         int areaHeight = (int)(focusHeight * areaMultiple);
-        int areaLeft = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth);
-        int areaTop = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight);
-        convertToFocusArea(areaLeft, areaTop, areaWidth, areaHeight, previewWidth, previewHeight,
-                rect);
-    }
+        int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth);
+        int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight);
 
-    // Convert the touch point to the focus area in driver format.
-    public static void convertToFocusArea(int left, int top, int focusWidth, int focusHeight,
-            int previewWidth, int previewHeight, Rect rect) {
-        rect.left = Math.round((float) left / previewWidth * 2000 - 1000);
-        rect.top = Math.round((float) top / previewHeight * 2000 - 1000);
-        rect.right = Math.round((float) (left + focusWidth) / previewWidth * 2000 - 1000);
-        rect.bottom = Math.round((float) (top + focusHeight) / previewHeight * 2000 - 1000);
+        RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight);
+        mMatrix.mapRect(rectF);
+        Util.rectFToRect(rectF, rect);
     }
 
     public boolean isFocusCompleted() {
diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java
index 31a54d5..4c28e56 100644
--- a/src/com/android/camera/Util.java
+++ b/src/com/android/camera/Util.java
@@ -27,6 +27,8 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Parameters;
@@ -522,6 +524,18 @@
         }
     }
 
+    public static void dumpRect(RectF rect, String msg) {
+        Log.v(TAG, msg + "=(" + rect.left + "," + rect.top
+                + "," + rect.right + "," + rect.bottom + ")");
+    }
+
+    public static void rectFToRect(RectF rectF, Rect rect) {
+        rect.left = Math.round(rectF.left);
+        rect.top = Math.round(rectF.top);
+        rect.right = Math.round(rectF.right);
+        rect.bottom = Math.round(rectF.bottom);
+    }
+
     public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
             int viewWidth, int viewHeight) {
         // Need mirror for front camera.
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index 4694c90..5ad9205 100755
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -134,7 +134,6 @@
 
     private static final String EFFECT_BG_FROM_GALLERY = "gallery";
 
-    private android.hardware.Camera mCameraDevice;
     private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
 
     private ComboPreferences mPreferences;
@@ -785,10 +784,10 @@
     }
 
     @Override
-    protected void onResume() {
-        super.onResume();
-        mPausing = false;
+    protected void doOnResume() {
         if (mOpenCameraFail || mCameraDisabled) return;
+
+        mPausing = false;
         mZoomValue = 0;
 
         showVideoSnapshotUI(false);
diff --git a/src/com/android/camera/panorama/MosaicFrameProcessor.java b/src/com/android/camera/panorama/MosaicFrameProcessor.java
index 300e4e3..6c70b19 100644
--- a/src/com/android/camera/panorama/MosaicFrameProcessor.java
+++ b/src/com/android/camera/panorama/MosaicFrameProcessor.java
@@ -91,8 +91,10 @@
     }
 
     public void clear() {
-        mIsMosaicMemoryAllocated = false;
-        mMosaicer.freeMosaicMemory();
+        if (mIsMosaicMemoryAllocated) {
+            mIsMosaicMemoryAllocated = false;
+            mMosaicer.freeMosaicMemory();
+        }
     }
 
     public void setStripType(int type) {
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index 0e055e5..43d33a8 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -144,7 +144,6 @@
 
     private int mPreviewWidth;
     private int mPreviewHeight;
-    private Camera mCameraDevice;
     private int mCameraState;
     private int mCaptureState;
     private SensorManager mSensorManager;
@@ -916,9 +915,7 @@
     }
 
     @Override
-    protected void onResume() {
-        super.onResume();
-
+    protected void doOnResume() {
         mPausing = false;
         mOrientationEventListener.enable();
 
diff --git a/src/com/android/camera/ui/FaceView.java b/src/com/android/camera/ui/FaceView.java
index 167d8b9..9018886 100644
--- a/src/com/android/camera/ui/FaceView.java
+++ b/src/com/android/camera/ui/FaceView.java
@@ -117,17 +117,11 @@
         mPause = false;
     }
 
-    private void dumpRect(RectF rect, String msg) {
-        Log.v(TAG, msg + "=(" + rect.left + "," + rect.top
-                + "," + rect.right + "," + rect.bottom + ")");
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         if (mFaces != null && mFaces.length > 0) {
             // Prepare the matrix.
-            Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, getWidth(),
-                    getHeight());
+            Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, getWidth(), getHeight());
 
             // Focus indicator is directional. Rotate the matrix and the canvas
             // so it looks correctly in all orientations.
@@ -137,9 +131,9 @@
             for (int i = 0; i < mFaces.length; i++) {
                 // Transform the coordinates.
                 mRect.set(mFaces[i].rect);
-                if (LOGV) dumpRect(mRect, "Original rect");
+                if (LOGV) Util.dumpRect(mRect, "Original rect");
                 mMatrix.mapRect(mRect);
-                if (LOGV) dumpRect(mRect, "Transformed rect");
+                if (LOGV) Util.dumpRect(mRect, "Transformed rect");
 
                 mFaceIndicator.setBounds(Math.round(mRect.left), Math.round(mRect.top),
                         Math.round(mRect.right), Math.round(mRect.bottom));
diff --git a/tests/src/com/android/camera/unittest/CameraTest.java b/tests/src/com/android/camera/unittest/CameraTest.java
index ad0eed5..0e1242c 100644
--- a/tests/src/com/android/camera/unittest/CameraTest.java
+++ b/tests/src/com/android/camera/unittest/CameraTest.java
@@ -73,22 +73,6 @@
         assertEquals(180, Util.roundOrientation(180, 270));
     }
 
-    public void testConvertToFocusArea() {
-        Rect rect = new Rect();
-        FocusManager.convertToFocusArea(0, 0, 100, 100, 800, 480, rect);
-        assertEquals(new Rect(-1000, -1000, -750, -583), rect);
-        FocusManager.convertToFocusArea(0, 0, 400, 240, 800, 480, rect);
-        assertEquals(new Rect(-1000, -1000, 0, 0), rect);
-        FocusManager.convertToFocusArea(400, 240, 400, 240, 800, 480, rect);
-        assertEquals(new Rect(0, 0, 1000, 1000), rect);
-        FocusManager.convertToFocusArea(200, 120, 400, 240, 800, 480, rect);
-        assertEquals(new Rect(-500, -500, 500, 500), rect);
-        FocusManager.convertToFocusArea(0, 0, 800, 480, 800, 480, rect);
-        assertEquals(new Rect(-1000, -1000, 1000, 1000), rect);
-        FocusManager.convertToFocusArea(860, 620, 100, 100, 960, 720, rect);
-        assertEquals(new Rect(792, 722, 1000, 1000), rect);
-    }
-
     public void testPrepareMatrix() {
         Matrix matrix = new Matrix();
         float[] points;