Switch from stepwise index-based zoom to a ratio-based scale

This brings the app up to date with the removal of the deprecated zoom methods
in the portability layer's CameraSettings class.

Bug: 17016658
Change-Id: Icb9f773188f7481d9af991d71feaff86379f5525
diff --git a/src/com/android/camera/CaptureModuleUI.java b/src/com/android/camera/CaptureModuleUI.java
index d00e7f7..bbb7c60 100644
--- a/src/com/android/camera/CaptureModuleUI.java
+++ b/src/com/android/camera/CaptureModuleUI.java
@@ -70,9 +70,8 @@
     /** Set up listener to receive zoom changes from View and send to module. */
     private final OnZoomChangedListener mZoomChancedListener  = new OnZoomChangedListener() {
         @Override
-        public void onZoomValueChanged(int index) {
-            float zoomValue = ((float) PreviewOverlay.ZOOM_MIN_FACTOR + (float) index) / 100;
-            mModule.setZoom(zoomValue);
+        public void onZoomValueChanged(float ratio) {
+            mModule.setZoom(ratio);
         }
 
         @Override
diff --git a/src/com/android/camera/PhotoController.java b/src/com/android/camera/PhotoController.java
index d1e045a..d95a0fc 100644
--- a/src/com/android/camera/PhotoController.java
+++ b/src/com/android/camera/PhotoController.java
@@ -32,8 +32,7 @@
     // Switching between cameras.
     public static final int SWITCHING_CAMERA = 4;
 
-    // returns the actual set zoom value
-    public int onZoomChanged(int requestedZoom);
+    public void onZoomChanged(float requestedZoom);
 
     public boolean isImageCaptureIntent();
 
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 45002f2..f59173c 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -151,7 +151,7 @@
     // needed to be updated in mUpdateSet.
     private int mUpdateSet;
 
-    private int mZoomValue; // The current zoom value.
+    private float mZoomValue; // The current zoom ratio.
     private int mTimerDuration;
     /** Set when a volume button is clicked to take photo */
     private boolean mVolumeButtonClickedFlag = false;
@@ -1084,15 +1084,10 @@
 
             int orientation = Exif.getOrientation(exif);
 
-            float zoomValue = 0f;
+            float zoomValue = 1.0f;
             if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
-                int zoomIndex = mCameraSettings.getCurrentZoomIndex();
-                List<Integer> zoomRatios = mCameraCapabilities.getZoomRatioList();
-                if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
-                    zoomValue = 0.01f * zoomRatios.get(zoomIndex);
-                }
+                zoomValue = mCameraSettings.getCurrentZoomRatio();
             }
-
             boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
             String flashSetting =
                     mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
@@ -1360,7 +1355,7 @@
         initializeCapabilities();
 
         // Reset zoom value index.
-        mZoomValue = 0;
+        mZoomValue = 1.0f;
         if (mFocusManager == null) {
             initializeFocusManager();
         }
@@ -1583,7 +1578,7 @@
         requestCameraOpen();
 
         mJpegPictureCallbackTime = 0;
-        mZoomValue = 0;
+        mZoomValue = 1.0f;
 
         mOnResumeTime = SystemClock.uptimeMillis();
         checkDisplayRotation();
@@ -2009,7 +2004,7 @@
     private void updateCameraParametersZoom() {
         // Set zoom.
         if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
-            mCameraSettings.setZoomIndex(mZoomValue);
+            mCameraSettings.setZoomRatio(mZoomValue);
         }
     }
 
@@ -2299,25 +2294,19 @@
                 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
     }
 
-    // TODO: Use zoomRatio device API rather than deprecated zoomIndex
     @Override
-    public int onZoomChanged(int index) {
+    public void onZoomChanged(float ratio) {
         // Not useful to change zoom value when the activity is paused.
         if (mPaused) {
-            return index;
+            return;
         }
-        mZoomValue = index;
+        mZoomValue = ratio;
         if (mCameraSettings == null || mCameraDevice == null) {
-            return index;
+            return;
         }
         // Set zoom parameters asynchronously
-        mCameraSettings.setZoomIndex(mZoomValue);
+        mCameraSettings.setZoomRatio(mZoomValue);
         mCameraDevice.applySettings(mCameraSettings);
-        CameraSettings settings = mCameraDevice.getSettings();
-        if (settings != null) {
-            return settings.getCurrentZoomIndex();
-        }
-        return index;
     }
 
     @Override
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index e7be5be..c40a418 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -69,8 +69,7 @@
     private final FaceView mFaceView = null;
     private DecodeImageForReview mDecodeTaskForReview = null;
 
-    private int mZoomMax;
-    private List<Integer> mZoomRatios;
+    private float mZoomMax;
 
     private int mPreviewWidth = 0;
     private int mPreviewHeight = 0;
@@ -450,12 +449,11 @@
                 !capabilities.supports(CameraCapabilities.Feature.ZOOM)) {
             return;
         }
-        mZoomMax = capabilities.getMaxZoomIndex();
-        mZoomRatios = capabilities.getZoomRatioList();
+        mZoomMax = capabilities.getMaxZoomRatio();
         // Currently we use immediate zoom for fast zooming to get better UX and
         // there is no plan to take advantage of the smooth zoom.
         // TODO: Need to setup a path to AppUI to do this
-        mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomIndex(), mZoomRatios,
+        mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(),
                 new ZoomChangeListener());
     }
 
@@ -501,8 +499,8 @@
 
     private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
         @Override
-        public void onZoomValueChanged(int index) {
-            mController.onZoomChanged(index);
+        public void onZoomValueChanged(float ratio) {
+            mController.onZoomChanged(ratio);
         }
 
         @Override
diff --git a/src/com/android/camera/VideoController.java b/src/com/android/camera/VideoController.java
index 5e17f49..b754eae 100644
--- a/src/com/android/camera/VideoController.java
+++ b/src/com/android/camera/VideoController.java
@@ -28,7 +28,7 @@
 
     public boolean isVideoCaptureIntent();
     public boolean isInReviewMode();
-    public int onZoomChanged(int index);
+    public void onZoomChanged(float ratio);
 
     public void onSingleTapUp(View view, int x, int y);
 
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 6fe83d2..8b2c4fe 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -182,7 +182,7 @@
     // The degrees of the device rotated clockwise from its natural orientation.
     private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
 
-    private int mZoomValue;  // The current zoom value.
+    private float mZoomValue;  // The current zoom ratio.
 
     private final MediaSaver.OnMediaSavedListener mOnVideoSavedListener =
             new MediaSaver.OnMediaSavedListener() {
@@ -876,35 +876,22 @@
      * Returns current Zoom value, with 1.0 as the value for no zoom.
      */
     private float currentZoomValue() {
-        float zoomValue = 1.0f;
-        if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
-            int zoomIndex = mCameraSettings.getCurrentZoomIndex();
-            List<Integer> zoomRatios = mCameraCapabilities.getZoomRatioList();
-            if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
-                zoomValue = 0.01f * zoomRatios.get(zoomIndex);
-            }
-        }
-        return zoomValue;
+        return mCameraSettings.getCurrentZoomRatio();
     }
 
     @Override
-    public int onZoomChanged(int index) {
+    public void onZoomChanged(float ratio) {
         // Not useful to change zoom value when the activity is paused.
         if (mPaused) {
-            return index;
+            return;
         }
-        mZoomValue = index;
+        mZoomValue = ratio;
         if (mCameraSettings == null || mCameraDevice == null) {
-            return index;
+            return;
         }
         // Set zoom parameters asynchronously
-        mCameraSettings.setZoomIndex(mZoomValue);
+        mCameraSettings.setZoomRatio(mZoomValue);
         mCameraDevice.applySettings(mCameraSettings);
-        CameraSettings settings = mCameraDevice.getSettings();
-        if (settings != null) {
-            return settings.getCurrentZoomIndex();
-        }
-        return index;
     }
 
     private void startPreview() {
@@ -1585,7 +1572,7 @@
 
         // Set zoom.
         if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
-            mCameraSettings.setZoomIndex(mZoomValue);
+            mCameraSettings.setZoomRatio(mZoomValue);
         }
         updateFocusParameters();
 
@@ -1660,7 +1647,7 @@
         mPaused = false;
         installIntentFilter();
         mAppController.setShutterEnabled(false);
-        mZoomValue = 0;
+        mZoomValue = 1.0f;
 
         showVideoSnapshotUI(false);
 
@@ -1760,7 +1747,7 @@
         }
 
         // From onResume
-        mZoomValue = 0;
+        mZoomValue = 1.0f;
         mUI.setOrientationIndicator(0, false);
 
         // Start switch camera animation. Post a message because
diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java
index a91f038..112a8b1 100644
--- a/src/com/android/camera/VideoUI.java
+++ b/src/com/android/camera/VideoUI.java
@@ -57,8 +57,7 @@
     private RotateLayout mRecordingTimeRect;
     private boolean mRecordingStarted = false;
     private final VideoController mController;
-    private int mZoomMax;
-    private List<Integer> mZoomRatios;
+    private float mZoomMax;
 
     private float mAspectRatio = UNSET;
     private final AnimationManager mAnimationManager;
@@ -213,12 +212,11 @@
     }
 
     public void initializeZoom(CameraSettings settings, CameraCapabilities capabilities) {
-        mZoomMax = capabilities.getMaxZoomIndex();
-        mZoomRatios = capabilities.getZoomRatioList();
+        mZoomMax = capabilities.getMaxZoomRatio();
         // Currently we use immediate zoom for fast zooming to get better UX and
         // there is no plan to take advantage of the smooth zoom.
         // TODO: setup zoom through App UI.
-        mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomIndex(), mZoomRatios,
+        mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(),
                 new ZoomChangeListener());
     }
 
@@ -277,8 +275,8 @@
 
     private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
         @Override
-        public void onZoomValueChanged(int index) {
-            mController.onZoomChanged(index);
+        public void onZoomValueChanged(float ratio) {
+            mController.onZoomChanged(ratio);
         }
 
         @Override
diff --git a/src/com/android/camera/ui/PreviewOverlay.java b/src/com/android/camera/ui/PreviewOverlay.java
index 2a29a64..ba0e697 100644
--- a/src/com/android/camera/ui/PreviewOverlay.java
+++ b/src/com/android/camera/ui/PreviewOverlay.java
@@ -22,6 +22,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.RectF;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
@@ -48,10 +49,15 @@
 public class PreviewOverlay extends View
     implements PreviewStatusListener.PreviewAreaChangedListener {
 
+    public static final float ZOOM_MIN_RATIO = 1.0f;
+
     private static final Log.Tag TAG = new Log.Tag("PreviewOverlay");
 
-    public static final int ZOOM_MIN_FACTOR = 100;
+    /** Minimum time between calls to zoom listener. */
+    private static final long ZOOM_MINIMUM_WAIT_MILLIS = 33;
 
+    /** Next time zoom change should be sent to listener. */
+    private long mDelayZoomCallUntilMillis = 0;
     private final ZoomGestureDetector mScaleDetector;
     private final ZoomProcessor mZoomProcessor = new ZoomProcessor();
     private GestureDetector mGestureDetector = null;
@@ -73,9 +79,9 @@
         /**
          * This gets called when scale gesture changes the zoom value.
          *
-         * @param index index of the list of supported zoom ratios
+         * @param ratio zoom ratio, [1.0f,maximum]
          */
-        void onZoomValueChanged(int index);  // only for immediate zoom
+        void onZoomValueChanged(float ratio);  // only for immediate zoom
     }
 
     public interface OnPreviewTouchedListener {
@@ -91,31 +97,17 @@
     }
 
     /**
-     * This sets up the zoom listener and zoom related parameters.
-     *
-     * @param zoomMax max zoom index
-     * @param zoom current zoom index
-     * @param zoomRatios a list of zoom ratios
-     * @param zoomChangeListener a listener that receives callbacks when zoom changes
-     */
-    public void setupZoom(int zoomMax, int zoom, List<Integer> zoomRatios,
-                          OnZoomChangedListener zoomChangeListener) {
-        mZoomListener = zoomChangeListener;
-        mZoomProcessor.setupZoom(zoomMax, zoom, zoomRatios);
-    }
-
-    /**
      * This sets up the zoom listener and zoom related parameters when
      * the range of zoom ratios is continuous.
      *
-     * @param zoomMaxRatio max zoom ratio
-     * @param zoom current zoom index
+     * @param zoomMaxRatio max zoom ratio, [1.0f,+Inf)
+     * @param zoom current zoom ratio, [1.0f,zoomMaxRatio]
      * @param zoomChangeListener a listener that receives callbacks when zoom changes
      */
-    public void setupZoom(float zoomMaxRatio, int zoom, OnZoomChangedListener zoomChangeListener) {
+    public void setupZoom(float zoomMaxRatio, float zoom,
+                          OnZoomChangedListener zoomChangeListener) {
         mZoomListener = zoomChangeListener;
-        int zoomMax = ((int) zoomMaxRatio * 100) - ZOOM_MIN_FACTOR;
-        mZoomProcessor.setupZoom(zoomMax, zoom, null);
+        mZoomProcessor.setupZoom(zoomMaxRatio, zoom);
     }
 
     @Override
@@ -240,12 +232,10 @@
         // Diameter of Zoom UI donut hole as fraction of Zoom UI diameter.
         private static final float ZOOM_UI_DONUT = 0.25f;
 
-        final private int mMinIndex = 0;
-        private int mMaxIndex;
-        // Discrete Zoom level [mMinIndex,mMaxIndex].
-        private int mCurrentIndex;
+        private final float mMinRatio = 1.0f;
+        private float mMaxRatio;
         // Continuous Zoom level [0,1].
-        private float mCurrentFraction;
+        private float mCurrentRatio;
         private double mFingerAngle;  // in radians.
         private final Paint mPaint;
         private int mCenterX;
@@ -267,19 +257,14 @@
             mPaint.setStrokeCap(Paint.Cap.ROUND);
         }
 
-        // Set maximum Zoom Index from Module.
-        public void setZoomMax(int zoomMaxIndex) {
-            mMaxIndex = zoomMaxIndex;
+        // Set maximum zoom ratio from Module.
+        public void setZoomMax(float zoomMaxRatio) {
+            mMaxRatio = zoomMaxRatio;
         }
 
-        // Set current Zoom Index from Module.
-        public void setZoom(int index) {
-            mCurrentIndex = index;
-            mCurrentFraction = (float) index / (mMaxIndex - mMinIndex);
-        }
-
-        public void setZoomValue(int value) {
-            // Do nothing because we are not display text value in current UI.
+        // Set current zoom ratio from Module.
+        public void setZoom(float ratio) {
+            mCurrentRatio = ratio;
         }
 
         public void layout(int l, int t, int r, int b) {
@@ -308,7 +293,8 @@
                     mCenterY + mOuterRadius * (float) Math.sin(mFingerAngle), mPaint);
             // Draw Zoom progress.
             mPaint.setAlpha(255);
-            float zoomRadius = mInnerRadius + mCurrentFraction * (mOuterRadius - mInnerRadius);
+            float fillRatio = (mCurrentRatio - mMinRatio) / (mMaxRatio - mMinRatio);
+            float zoomRadius = mInnerRadius + fillRatio * (mOuterRadius - mInnerRadius);
             canvas.drawLine(mCenterX + mInnerRadius * (float) Math.cos(mFingerAngle),
                     mCenterY - mInnerRadius * (float) Math.sin(mFingerAngle),
                     mCenterX + zoomRadius * (float) Math.cos(mFingerAngle),
@@ -322,13 +308,21 @@
         @Override
         public boolean onScale(ScaleGestureDetector detector) {
             final float sf = detector.getScaleFactor();
-            mCurrentFraction = (0.33f + mCurrentFraction) * sf * sf - 0.33f;
-            if (mCurrentFraction < 0.0f) mCurrentFraction = 0.0f;
-            if (mCurrentFraction > 1.0f) mCurrentFraction = 1.0f;
-            int newIndex = mMinIndex + (int) (mCurrentFraction * (mMaxIndex - mMinIndex));
-            if (mZoomListener != null && newIndex != mCurrentIndex) {
-                mZoomListener.onZoomValueChanged(newIndex);
-                mCurrentIndex = newIndex;
+            mCurrentRatio = (0.33f + mCurrentRatio) * sf * sf - 0.33f;
+            if (mCurrentRatio < mMinRatio) mCurrentRatio = mMinRatio;
+            if (mCurrentRatio > mMaxRatio) mCurrentRatio = mMaxRatio;
+
+            // Only call the listener with a certain frequency. This is
+            // necessary because these listeners will make repeated
+            // applySettings() calls into the portability layer, and doing this
+            // too often can back up its handler and result in visible lag in
+            // updating the zoom level and other controls.
+            long now = SystemClock.uptimeMillis();
+            if (now > mDelayZoomCallUntilMillis) {
+                if (mZoomListener != null) {
+                    mZoomListener.onZoomValueChanged(mCurrentRatio);
+                }
+                mDelayZoomCallUntilMillis = now + ZOOM_MINIMUM_WAIT_MILLIS;
             }
             mFingerAngle = mScaleDetector.getAngle();
             invalidate();
@@ -379,8 +373,7 @@
             invalidate();
         }
 
-        private void setupZoom(int zoomMax, int zoom, List<Integer> zoomRatios) {
-            mZoomRatios = zoomRatios;
+        private void setupZoom(float zoomMax, float zoom) {
             setZoomMax(zoomMax);
             setZoom(zoom);
         }