Fix the mapping between preview UI and camera sensor

Bug: 11409659
Change-Id: I46ae3d7797590d59befbdc394d5304f14a212a80
diff --git a/src/com/android/camera/FocusOverlayManager.java b/src/com/android/camera/FocusOverlayManager.java
index c223aea..edc2db7 100644
--- a/src/com/android/camera/FocusOverlayManager.java
+++ b/src/com/android/camera/FocusOverlayManager.java
@@ -76,8 +76,6 @@
     private boolean mAeAwbLock;
     private Matrix mMatrix;
 
-    private int mPreviewWidth; // The width of the preview frame layout.
-    private int mPreviewHeight; // The height of the preview frame layout.
     private boolean mMirror; // true if the camera is front-facing.
     private int mDisplayOrientation;
     private List<Object> mFocusArea; // focus area in driver format
@@ -93,6 +91,7 @@
     private boolean mFocusDefault;
 
     private FocusUI mUI;
+    private final Rect mPreviewRect = new Rect(0, 0, 0, 0);
 
     public  interface FocusUI {
         public boolean hasFaces();
@@ -159,13 +158,25 @@
     }
 
     public void setPreviewSize(int previewWidth, int previewHeight) {
-        if (mPreviewWidth != previewWidth || mPreviewHeight != previewHeight) {
-            mPreviewWidth = previewWidth;
-            mPreviewHeight = previewHeight;
+        if (mPreviewRect.width() != previewWidth || mPreviewRect.height() != previewHeight) {
+            setPreviewRect(new Rect(0, 0, previewWidth, previewHeight));
+        }
+    }
+
+    /** This setter should be the only way to mutate mPreviewRect. */
+    public void setPreviewRect(Rect previewRect) {
+        if (!mPreviewRect.equals(previewRect)) {
+            mPreviewRect.set(previewRect);
             setMatrix();
         }
     }
 
+    /** Returns a copy of mPreviewRect so that outside class cannot modify preview
+     *  rect except deliberately doing so through the setter. */
+    public Rect getPreviewRect() {
+        return new Rect(mPreviewRect);
+    }
+
     public void setMirror(boolean mirror) {
         mMirror = mirror;
         setMatrix();
@@ -177,10 +188,9 @@
     }
 
     private void setMatrix() {
-        if (mPreviewWidth != 0 && mPreviewHeight != 0) {
+        if (mPreviewRect.width() != 0 && mPreviewRect.height() != 0) {
             Matrix matrix = new Matrix();
-            CameraUtil.prepareMatrix(matrix, mMirror, mDisplayOrientation,
-                    mPreviewWidth, mPreviewHeight);
+            CameraUtil.prepareMatrix(matrix, mMirror, mDisplayOrientation, getPreviewRect());
             // 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.
@@ -352,7 +362,7 @@
                     mState == STATE_SUCCESS || mState == STATE_FAIL)) {
             cancelAutoFocus();
         }
-        if (mPreviewWidth == 0 || mPreviewHeight == 0) return;
+        if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0) return;
         mFocusDefault = false;
         // Initialize mFocusArea.
         if (mFocusAreaSupported) {
@@ -507,7 +517,7 @@
         mUI.clearFocus();
         // Initialize mFocusArea.
         if (mFocusAreaSupported) {
-            initializeFocusAreas(mPreviewWidth / 2, mPreviewHeight / 2);
+            initializeFocusAreas(mPreviewRect.centerX(), mPreviewRect.centerY());
         }
         // Reset metering area when no specific region is selected.
         if (mMeteringAreaSupported) {
@@ -518,8 +528,10 @@
 
     private void calculateTapArea(int x, int y, float areaMultiple, Rect rect) {
         int areaSize = (int) (getAreaSize() * areaMultiple);
-        int left = CameraUtil.clamp(x - areaSize / 2, 0, mPreviewWidth - areaSize);
-        int top = CameraUtil.clamp(y - areaSize / 2, 0, mPreviewHeight - areaSize);
+        int left = CameraUtil.clamp(x - areaSize / 2, mPreviewRect.left,
+                mPreviewRect.right - areaSize);
+        int top = CameraUtil.clamp(y - areaSize / 2, mPreviewRect.top,
+                mPreviewRect.bottom - areaSize);
 
         RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
         mMatrix.mapRect(rectF);
@@ -529,7 +541,7 @@
     private int getAreaSize() {
         // Recommended focus area size from the manufacture is 1/8 of the image
         // width (i.e. longer edge of the image)
-        return Math.max(mPreviewWidth, mPreviewHeight) / 8;
+        return Math.max(mPreviewRect.width(), mPreviewRect.height()) / 8;
     }
 
     /* package */ int getFocusState() {
diff --git a/src/com/android/camera/PhotoController.java b/src/com/android/camera/PhotoController.java
index 291b5df..833c825 100644
--- a/src/com/android/camera/PhotoController.java
+++ b/src/com/android/camera/PhotoController.java
@@ -16,6 +16,7 @@
 
 package com.android.camera;
 
+import android.graphics.Rect;
 import android.view.View;
 
 import com.android.camera.ShutterButton.OnShutterButtonListener;
@@ -54,7 +55,7 @@
 
     public void onCountDownFinished();
 
-    public void onScreenSizeChanged(int width, int height);
+    public void onPreviewRectChanged(Rect previewRect);
 
     public void updateCameraOrientation();
 
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index aeca3dd..46d7138 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -25,6 +25,7 @@
 import android.content.SharedPreferences.Editor;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Parameters;
@@ -505,8 +506,8 @@
     }
 
     @Override
-    public void onScreenSizeChanged(int width, int height) {
-        if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
+    public void onPreviewRectChanged(Rect previewRect) {
+        if (mFocusManager != null) mFocusManager.setPreviewRect(previewRect);
     }
 
     private void resetExposureCompensation() {
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index cd3b9a5..3909445 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -22,6 +22,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Matrix;
+import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
 import android.graphics.drawable.ColorDrawable;
 import android.hardware.Camera;
@@ -131,8 +132,6 @@
                 mPreviewWidth = width;
                 mPreviewHeight = height;
                 setTransformMatrix(width, height);
-                mController.onScreenSizeChanged((int) mSurfaceTextureUncroppedWidth,
-                        (int) mSurfaceTextureUncroppedHeight);
             }
         }
     };
@@ -253,6 +252,11 @@
         scaleY = scaledTextureHeight / height;
         mMatrix.setScale(scaleX, scaleY, (float) width / 2, (float) height / 2);
         mTextureView.setTransform(mMatrix);
+
+        // Calculate the new preview rectangle.
+        RectF previewRect = new RectF(0, 0, width, height);
+        mMatrix.mapRect(previewRect);
+        mController.onPreviewRectChanged(CameraUtil.rectFToRect(previewRect));
     }
 
     @Override
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index eff0c40..cbc9ebe 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -678,6 +678,16 @@
         rect.bottom = Math.round(rectF.bottom);
     }
 
+    public static Rect rectFToRect(RectF rectF) {
+        Rect rect = new Rect();
+        rectFToRect(rectF, rect);
+        return rect;
+    }
+
+    public static RectF rectToRectF(Rect r) {
+        return new RectF(r.left, r.top, r.right, r.bottom);
+    }
+
     public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
             int viewWidth, int viewHeight) {
         // Need mirror for front camera.
@@ -690,6 +700,21 @@
         matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
     }
 
+    public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
+                                     Rect previewRect) {
+        // Need mirror for front camera.
+        matrix.setScale(mirror ? -1 : 1, 1);
+        // This is the value for android.hardware.Camera.setDisplayOrientation.
+        matrix.postRotate(displayOrientation);
+
+        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
+        // We need to map camera driver coordinates to preview rect coordinates
+        Matrix mapping = new Matrix();
+        mapping.setRectToRect(new RectF(-1000, -1000, 1000, 1000), rectToRectF(previewRect),
+                Matrix.ScaleToFit.FILL);
+        matrix.setConcat(mapping, matrix);
+    }
+
     public static String createJpegName(long dateTaken) {
         synchronized (sImageFileNamer) {
             return sImageFileNamer.generateName(dateTaken);