OSDN Git Service

Merge "Import translations. DO NOT MERGE" into ub-camera-glacier
authorBaligh Uddin <baligh@google.com>
Fri, 19 Sep 2014 22:33:35 +0000 (22:33 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Fri, 19 Sep 2014 22:33:36 +0000 (22:33 +0000)
25 files changed:
res/drawable-hdpi/ic_discard_disabled.png [deleted file]
res/drawable-hdpi/ic_discard_normal.png [deleted file]
res/drawable-hdpi/ic_menu_trash_holo_light.png [deleted file]
res/drawable-mdpi/ic_discard_disabled.png [deleted file]
res/drawable-mdpi/ic_discard_normal.png [deleted file]
res/drawable-mdpi/ic_menu_trash_holo_light.png [deleted file]
res/drawable-xhdpi/ic_discard_disabled.png [deleted file]
res/drawable-xhdpi/ic_discard_normal.png [deleted file]
res/drawable-xxhdpi/ic_discard_disabled.png [deleted file]
res/drawable-xxhdpi/ic_discard_normal.png [deleted file]
res/drawable/ic_menu_trash.xml
src/com/android/camera/MultiToggleImageButton.java
src/com/android/camera/PhotoModule.java
src/com/android/camera/VideoModule.java
src/com/android/camera/app/CameraAppUI.java
src/com/android/camera/one/OneCamera.java
src/com/android/camera/one/Settings3A.java
src/com/android/camera/one/v2/AutoFocusHelper.java
src/com/android/camera/one/v2/OneCameraImpl.java
src/com/android/camera/one/v2/OneCameraZslImpl.java
src/com/android/camera/settings/CameraSettingsActivity.java
src/com/android/camera/ui/ModeIconView.java
src/com/android/camera/ui/ModeListView.java
src/com/android/camera/ui/ModeSelectorItem.java
src/com/android/camera/util/CameraUtil.java

diff --git a/res/drawable-hdpi/ic_discard_disabled.png b/res/drawable-hdpi/ic_discard_disabled.png
deleted file mode 100644 (file)
index d6395d2..0000000
Binary files a/res/drawable-hdpi/ic_discard_disabled.png and /dev/null differ
diff --git a/res/drawable-hdpi/ic_discard_normal.png b/res/drawable-hdpi/ic_discard_normal.png
deleted file mode 100644 (file)
index 05ca6a7..0000000
Binary files a/res/drawable-hdpi/ic_discard_normal.png and /dev/null differ
diff --git a/res/drawable-hdpi/ic_menu_trash_holo_light.png b/res/drawable-hdpi/ic_menu_trash_holo_light.png
deleted file mode 100644 (file)
index 721ee5c..0000000
Binary files a/res/drawable-hdpi/ic_menu_trash_holo_light.png and /dev/null differ
diff --git a/res/drawable-mdpi/ic_discard_disabled.png b/res/drawable-mdpi/ic_discard_disabled.png
deleted file mode 100644 (file)
index 8138c5d..0000000
Binary files a/res/drawable-mdpi/ic_discard_disabled.png and /dev/null differ
diff --git a/res/drawable-mdpi/ic_discard_normal.png b/res/drawable-mdpi/ic_discard_normal.png
deleted file mode 100644 (file)
index dcbf991..0000000
Binary files a/res/drawable-mdpi/ic_discard_normal.png and /dev/null differ
diff --git a/res/drawable-mdpi/ic_menu_trash_holo_light.png b/res/drawable-mdpi/ic_menu_trash_holo_light.png
deleted file mode 100644 (file)
index f45540b..0000000
Binary files a/res/drawable-mdpi/ic_menu_trash_holo_light.png and /dev/null differ
diff --git a/res/drawable-xhdpi/ic_discard_disabled.png b/res/drawable-xhdpi/ic_discard_disabled.png
deleted file mode 100644 (file)
index 37e2bae..0000000
Binary files a/res/drawable-xhdpi/ic_discard_disabled.png and /dev/null differ
diff --git a/res/drawable-xhdpi/ic_discard_normal.png b/res/drawable-xhdpi/ic_discard_normal.png
deleted file mode 100644 (file)
index 27e64f4..0000000
Binary files a/res/drawable-xhdpi/ic_discard_normal.png and /dev/null differ
diff --git a/res/drawable-xxhdpi/ic_discard_disabled.png b/res/drawable-xxhdpi/ic_discard_disabled.png
deleted file mode 100644 (file)
index ccb90e5..0000000
Binary files a/res/drawable-xxhdpi/ic_discard_disabled.png and /dev/null differ
diff --git a/res/drawable-xxhdpi/ic_discard_normal.png b/res/drawable-xxhdpi/ic_discard_normal.png
deleted file mode 100644 (file)
index 4e0d89d..0000000
Binary files a/res/drawable-xxhdpi/ic_discard_normal.png and /dev/null differ
index 318ad41..cb94342 100644 (file)
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-          android:drawable="@drawable/ic_discard_disabled" />
+          android:drawable="@drawable/ic_trash_disabled" />
     <item android:state_enabled="true"
-          android:drawable="@drawable/ic_discard_normal" />
+          android:drawable="@drawable/ic_trash_normal" />
 </selector>
index b36cb3c..8e08a56 100644 (file)
@@ -18,6 +18,7 @@ package com.android.camera;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -25,6 +26,7 @@ import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageButton;
@@ -58,7 +60,7 @@ public class MultiToggleImageButton extends ImageButton {
     public static final int ANIM_DIRECTION_VERTICAL = 0;
     public static final int ANIM_DIRECTION_HORIZONTAL = 1;
 
-    private static final int AINM_DURATION_MS = 250;
+    private static final int ANIM_DURATION_MS = 250;
     private static final int UNSET = -1;
 
     private OnStateChangeListener mOnStateChangeListener;
@@ -69,6 +71,8 @@ public class MultiToggleImageButton extends ImageButton {
     private boolean mClickEnabled = true;
     private int mParentSize;
     private int mAnimDirection;
+    private Matrix mMatrix = new Matrix();
+    private ValueAnimator mAnimator;
 
     public MultiToggleImageButton(Context context) {
         super(context);
@@ -123,8 +127,7 @@ public class MultiToggleImageButton extends ImageButton {
      * @param callListener should the state change listener be called?
      */
     public void setState(final int state, final boolean callListener) {
-        // TODO: animate button transitions, b/17414652
-        setStateInternal(state, callListener);
+        setStateAnimatedInternal(state, callListener);
     }
 
     /**
@@ -143,60 +146,47 @@ public class MultiToggleImageButton extends ImageButton {
             return;
         }
 
-        Bitmap bitmap = combine(mState, state);
-        if (bitmap == null) {
-            setStateInternal(state, callListener);
-            return;
-        }
-
-        setImageBitmap(bitmap);
-        final Matrix matrix = new Matrix();
-
-        int offset;
-        if (mAnimDirection == ANIM_DIRECTION_VERTICAL) {
-            offset = (mParentSize+getHeight())/2;
-        } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) {
-            offset = (mParentSize+getWidth())/2;
-        } else {
-            return;
-        }
-
-        ValueAnimator animator = ValueAnimator.ofFloat(-offset, 0.0f);
-        animator.setDuration(AINM_DURATION_MS);
-        animator.setInterpolator(Gusterpolator.INSTANCE);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                matrix.reset();
-                if (mAnimDirection == ANIM_DIRECTION_VERTICAL) {
-                    matrix.setTranslate(0.0f, (Float) animation.getAnimatedValue());
-                } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) {
-                    matrix.setTranslate((Float) animation.getAnimatedValue(), 0.0f);
-                }
-
-                setImageMatrix(matrix);
-                invalidate();
-            }
-        });
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                setClickEnabled(false);
-            }
-
+        new AsyncTask<Integer, Void, Bitmap>() {
             @Override
-            public void onAnimationEnd(Animator animation) {
-                setStateInternal(state, callListener);
-                setClickEnabled(true);
+            protected Bitmap doInBackground(Integer... params) {
+                return combine(params[0], params[1]);
             }
 
             @Override
-            public void onAnimationCancel(Animator animation) {
-                setStateInternal(state, callListener);
-                setClickEnabled(true);
+            protected void onPostExecute(Bitmap bitmap) {
+                if (bitmap == null) {
+                    setStateInternal(state, callListener);
+                } else {
+                    setImageBitmap(bitmap);
+
+                    int offset;
+                    if (mAnimDirection == ANIM_DIRECTION_VERTICAL) {
+                        offset = (mParentSize+getHeight())/2;
+                    } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) {
+                        offset = (mParentSize+getWidth())/2;
+                    } else {
+                        return;
+                    }
+
+                    mAnimator.setFloatValues(-offset, 0.0f);
+                    AnimatorSet s = new AnimatorSet();
+                    s.play(mAnimator);
+                    s.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            setClickEnabled(false);
+                        }
+
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            setStateInternal(state, callListener);
+                            setClickEnabled(true);
+                        }
+                    });
+                    s.start();
+                }
             }
-        });
-        animator.start();
+        }.execute(mState, state);
     }
 
     /**
@@ -251,6 +241,24 @@ public class MultiToggleImageButton extends ImageButton {
             }
         });
         setScaleType(ImageView.ScaleType.MATRIX);
+
+        mAnimator = ValueAnimator.ofFloat(0.0f, 0.0f);
+        mAnimator.setDuration(ANIM_DURATION_MS);
+        mAnimator.setInterpolator(Gusterpolator.INSTANCE);
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mMatrix.reset();
+                if (mAnimDirection == ANIM_DIRECTION_VERTICAL) {
+                    mMatrix.setTranslate(0.0f, (Float) animation.getAnimatedValue());
+                } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) {
+                    mMatrix.setTranslate((Float) animation.getAnimatedValue(), 0.0f);
+                }
+
+                setImageMatrix(mMatrix);
+                invalidate();
+            }
+        });
     }
 
     private void parseAttributes(Context context, AttributeSet attrs) {
index 8865a80..ab6afdb 100644 (file)
@@ -75,6 +75,7 @@ import com.android.camera.util.SessionStatsCollector;
 import com.android.camera.util.UsageStatistics;
 import com.android.camera.widget.AspectRatioSelector;
 import com.android.camera2.R;
+import com.android.ex.camera2.portability.CameraAgent;
 import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
 import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
 import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
@@ -1991,13 +1992,26 @@ public class PhotoModule
             mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
 
             Log.i(TAG, "startPreview");
-            mCameraDevice.startPreview();
-
-            mFocusManager.onPreviewStarted();
-            onPreviewStarted();
-            SessionStatsCollector.instance().previewActive(true);
-            if (mSnapshotOnIdle) {
-                mHandler.post(mDoSnapRunnable);
+            // If we're using API2 in portability layers, don't use startPreviewWithCallback()
+            // b/17576554
+            CameraAgent.CameraStartPreviewCallback startPreviewCallback =
+                new CameraAgent.CameraStartPreviewCallback() {
+                    @Override
+                    public void onPreviewStarted() {
+                        mFocusManager.onPreviewStarted();
+                        PhotoModule.this.onPreviewStarted();
+                        SessionStatsCollector.instance().previewActive(true);
+                        if (mSnapshotOnIdle) {
+                            mHandler.post(mDoSnapRunnable);
+                        }
+                    }
+                };
+            if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
+                mCameraDevice.startPreview();
+                startPreviewCallback.onPreviewStarted();
+            } else {
+                mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
+                        startPreviewCallback);
             }
         } finally {
             mStartPreviewLock = false;
@@ -2096,6 +2110,12 @@ public class PhotoModule
     }
 
     private void updateCameraParametersPreference() {
+        // some monkey tests can get here when shutting the app down
+        // make sure mCameraDevice is still valid, b/17580046
+        if (mCameraDevice == null) {
+            return;
+        }
+
         setAutoExposureLockIfSupported();
         setAutoWhiteBalanceLockIfSupported();
         setFocusAreasIfSupported();
index b71b30f..d4dcdad 100644 (file)
@@ -37,6 +37,7 @@ import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
@@ -917,9 +918,14 @@ public class VideoModule extends CameraModule
         }
         try {
             mCameraDevice.setPreviewTexture(surfaceTexture);
-            mCameraDevice.startPreview();
+            mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
+                    new CameraAgent.CameraStartPreviewCallback() {
+                @Override
+                public void onPreviewStarted() {
+                    VideoModule.this.onPreviewStarted();
+                }
+            });
             mPreviewing = true;
-            onPreviewStarted();
         } catch (Throwable ex) {
             closeCamera();
             throw new RuntimeException("startPreview failed", ex);
index 30b654c..04d6105 100644 (file)
@@ -945,7 +945,6 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
                     mController.getQuickSwitchToModuleId(currentModuleIndex);
             if (currentModuleIndex != moduleToTransitionTo) {
                 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
-
                 int shadeColorId = R.color.mode_cover_default_color;
                 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
                         mController.getAndroidContext());
@@ -966,11 +965,6 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
                         }
                     }
                 };
-                if (mSwipeState == SWIPE_UP) {
-                    mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener);
-                } else {
-                    mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener);
-                }
             }
         } else if (swipeState == SWIPE_LEFT) {
             // Pass the touch sequence to filmstrip layout.
@@ -1355,7 +1349,6 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
         Log.v(TAG, "onNewPreviewFrame");
         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
         hideModeCover();
-        mModeCoverState = COVER_HIDDEN;
     }
 
     /**
@@ -1429,13 +1422,6 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
     }
 
     private void updateModeSpecificUIColors() {
-        // set up UI colors to match the current mode
-        /*
-        int colorId = CameraUtil.getCameraThemeColorId(mController.getCurrentModuleIndex(),
-                mController.getAndroidContext());
-        int pressedColor = mController.getAndroidContext().getResources().getColor(colorId);
-        setBottomBarPressedColor(pressedColor);
-        */
         setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
     }
 
@@ -1582,14 +1568,13 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
     @Override
     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
         mSurface = surface;
+        if (mPreviewStatusListener != null) {
+            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
+        }
         if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
             Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
             CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
             hideModeCover();
-            mModeCoverState = COVER_HIDDEN;
-        }
-        if (mPreviewStatusListener != null) {
-            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
         }
     }
 
index 4e95f7f..a22c889 100644 (file)
@@ -265,11 +265,11 @@ public interface OneCamera {
     /**
      * Meters and triggers auto focus scan with ROI around tap point.
      * <p/>
-     * Normalized coordinates are referenced to portrait preview window with 0,0
-     * top left and 1,1 bottom right. Rotation has no effect.
+     * Normalized coordinates are referenced to portrait preview window with
+     * (0, 0) top left and (1, 1) bottom right. Rotation has no effect.
      *
      * @param nx normalized x coordinate.
-     * @param nx normalized y coordinate.
+     * @param ny normalized y coordinate.
      */
     public void triggerFocusAndMeterAtPoint(float nx, float ny);
 
index 24edd28..a8abf69 100644 (file)
@@ -23,24 +23,82 @@ package com.android.camera.one;
 public class Settings3A {
 
     /**
-     * Width of touch AF region relative to shortest edge at 1.0 zoom.
-     * Was 0.125 * longest edge prior to L release.
+     * Width of touch AF region in [0,1] relative to shorter edge of the current
+     * crop region. Multiply this number by the number of pixels along the
+     * shorter edge of the current crop region's width to get a value in pixels.
+     *
+     * <p>
+     * This value has been tested on Nexus 5 and Shamu, but will need to be
+     * tuned per device depending on how its ISP interprets the metering box and weight.
+     * </p>
+     *
+     * <p>
+     * Values prior to L release:
+     * Normal mode: 0.125 * longest edge
+     * Gcam: Fixed at 300px x 300px.
+     * </p>
      */
     private static final float AF_REGION_BOX = 0.2f;
 
     /**
-     * Width of touch metering region relative to shortest edge at 1.0 zoom.
-     * Larger than {@link #AF_REGION_BOX} because exposure is sensitive and it is
-     * easy to over- or underexposure if area is too small.
+     * Width of touch metering region in [0,1] relative to shorter edge of the
+     * current crop region. Multiply this number by the number of pixels along
+     * shorter edge of the current crop region's width to get a value in pixels.
+     *
+     * <p>
+     * This value has been tested on Nexus 5 and Shamu, but will need to be
+     * tuned per device depending on how its ISP interprets the metering box and weight.
+     * </p>
+     *
+     * <p>
+     * Values prior to L release:
+     * Normal mode: 0.1875 * longest edge
+     * Gcam: Fixed at 300px x 300px.
+     * </p>
      */
     private static final float AE_REGION_BOX = 0.3f;
 
-    /** Metering region weight between 0 and 1. */
-    private static final float REGION_WEIGHT = 0.25f;
+    /** Metering region weight between 0 and 1.
+     *
+     * <p>
+     * This value has been tested on Nexus 5 and Shamu, but will need to be
+     * tuned per device depending on how its ISP interprets the metering box and weight.
+     * </p>
+     */
+    private static final float REGION_WEIGHT = 0.022f;
 
     /** Duration to hold after manual tap to focus. */
     private static final int FOCUS_HOLD_MILLIS = 3000;
 
+    /**
+     * Width of touch metering region in [0,1] relative to shorter edge of the
+     * current crop region. Multiply this number by the number of pixels along
+     * shorter edge of the current crop region's width to get a value in pixels.
+     *
+     * <p>
+     * This value has been tested on Nexus 5 and Shamu, but will need to be
+     * tuned per device depending on how its ISP interprets the metering box and weight.
+     * </p>
+     *
+     * <p>
+     * Was fixed at 300px x 300px prior to L release.
+     * </p>
+     */
+    private static final float GCAM_METERING_REGION_FRACTION = 0.1225f;
+
+    /**
+     * Weight of a touch metering region, in [0, \inf).
+     *
+     * <p>
+     * This value has been tested on Nexus 5 and Shamu, but will need to be
+     * tuned per device.
+     * </p>
+     *
+     * <p>
+     * Was fixed at 15.0f prior to L release.
+     * </p>
+     */
+    private static final float GCAM_METERING_REGION_WEIGHT = 22.0f;
 
     public static float getAutoFocusRegionWidth() {
         return AF_REGION_BOX;
@@ -54,6 +112,14 @@ public class Settings3A {
         return REGION_WEIGHT;
     }
 
+    public static float getGcamMeteringRegionFraction() {
+        return GCAM_METERING_REGION_FRACTION;
+    }
+
+    public static float getGcamMeteringRegionWeight() {
+        return GCAM_METERING_REGION_WEIGHT;
+    }
+
     public static int getFocusHoldMillis() {
         return FOCUS_HOLD_MILLIS;
     }
index f0e7f85..8cfab68 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.camera.one.v2;
 
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureResult;
@@ -24,6 +25,7 @@ import android.hardware.camera2.params.MeteringRectangle;
 import com.android.camera.debug.Log;
 import com.android.camera.one.OneCamera;
 import com.android.camera.one.Settings3A;
+import com.android.camera.util.CameraUtil;
 
 /**
  * Helper class to implement auto focus and 3A in camera2-based
@@ -34,8 +36,8 @@ public class AutoFocusHelper {
 
     /** camera2 API metering region weight. */
     private static final int CAMERA2_REGION_WEIGHT = (int)
-            (((1 - Settings3A.getMeteringRegionWeight()) * MeteringRectangle.METERING_WEIGHT_MIN +
-                    Settings3A.getMeteringRegionWeight() * MeteringRectangle.METERING_WEIGHT_MAX));
+        (CameraUtil.lerp(MeteringRectangle.METERING_WEIGHT_MIN, MeteringRectangle.METERING_WEIGHT_MAX,
+                        Settings3A.getMeteringRegionWeight()));
 
     /** Zero weight 3A region, to reset regions per API. */
     private static final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = new MeteringRectangle[]{
@@ -110,35 +112,79 @@ public class AutoFocusHelper {
         ));
     }
 
-    /** Compute 3A regions for a sensor-referenced touch coordinate. */
-    private static MeteringRectangle[] regionsForSensorCoord(int xc, int yc, float width,
-                                                             Rect cropRegion) {
-        float minCropEdge = (float) Math.min(cropRegion.width(), cropRegion.height());
-        int delta = (int) (0.5 * width * minCropEdge);
-        Rect region = new Rect(xc - delta, yc - delta, xc + delta, yc + delta);
-        // Make sure region is inside the sensor area.
-        if (!region.intersect(cropRegion)) {
-            region = cropRegion;
-        }
-        return new MeteringRectangle[]{new MeteringRectangle(region, CAMERA2_REGION_WEIGHT)};
+    /** Compute 3A regions for a sensor-referenced touch coordinate.
+     * Returns a MeteringRectangle[] with length 1.
+     *
+     * @param nx x coordinate of the touch point, in normalized portrait coordinates.
+     * @param ny y coordinate of the touch point, in normalized portrait coordinates.
+     * @param fraction Fraction in [0,1]. Multiplied by min(cropRegion.width(), cropRegion.height())
+     *             to determine the side length of the square MeteringRectangle.
+     * @param cropRegion Crop region of the image.
+     * @param sensorOrientation sensor orientation as defined by
+     *             CameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION).
+     *
+     * */
+    private static MeteringRectangle[] regionsForNormalizedCoord(float nx, float ny, float fraction,
+        final Rect cropRegion, int sensorOrientation) {
+        // Compute half side length in pixels.
+        int minCropEdge = Math.min(cropRegion.width(), cropRegion.height());
+        int halfSideLength = (int) (0.5f * fraction * minCropEdge);
+
+        // Compute the output MeteringRectangle in sensor space.
+        // nx, ny is normalized to the screen.
+        // Crop region itself is specified in sensor coordinates.
+
+        // Normalized coordinates, now rotated into sensor space.
+        PointF nsc = CameraUtil.normalizedSensorCoordsForNormalizedDisplayCoords(
+            nx, ny, sensorOrientation);
+
+        int xCenterSensor = (int)(cropRegion.left + nsc.x * cropRegion.width());
+        int yCenterSensor = (int)(cropRegion.top + nsc.y * cropRegion.height());
+
+        Rect meteringRegion = new Rect(xCenterSensor - halfSideLength,
+            yCenterSensor - halfSideLength,
+            xCenterSensor + halfSideLength,
+            yCenterSensor + halfSideLength);
+
+        // Clamp meteringRegion to cropRegion.
+        meteringRegion.left = CameraUtil.clamp(meteringRegion.left, cropRegion.left, cropRegion.right);
+        meteringRegion.top = CameraUtil.clamp(meteringRegion.top, cropRegion.top, cropRegion.bottom);
+        meteringRegion.right = CameraUtil.clamp(meteringRegion.right, cropRegion.left, cropRegion.right);
+        meteringRegion.bottom = CameraUtil.clamp(meteringRegion.bottom, cropRegion.top, cropRegion.bottom);
+
+        return new MeteringRectangle[]{new MeteringRectangle(meteringRegion, CAMERA2_REGION_WEIGHT)};
     }
 
     /**
      * Return AF region(s) for a sensor-referenced touch coordinate.
      *
+     * <p>
+     * Normalized coordinates are referenced to portrait preview window with
+     * (0, 0) top left and (1, 1) bottom right. Rotation has no effect.
+     * </p>
+     *
      * @return AF region(s).
      */
-    public static MeteringRectangle[] afRegionsForSensorCoord(int xc, int yc, Rect cropRegion) {
-        return regionsForSensorCoord(xc, yc, Settings3A.getAutoFocusRegionWidth(), cropRegion);
+    public static MeteringRectangle[] afRegionsForNormalizedCoord(float nx,
+        float ny, final Rect cropRegion, int sensorOrientation) {
+        return regionsForNormalizedCoord(nx, ny, Settings3A.getAutoFocusRegionWidth(),
+            cropRegion, sensorOrientation);
     }
 
     /**
      * Return AE region(s) for a sensor-referenced touch coordinate.
      *
+     * <p>
+     * Normalized coordinates are referenced to portrait preview window with
+     * (0, 0) top left and (1, 1) bottom right. Rotation has no effect.
+     * </p>
+     *
      * @return AE region(s).
      */
-    public static MeteringRectangle[] aeRegionsForSensorCoord(int xc, int yc, Rect cropRegion) {
-        return regionsForSensorCoord(xc, yc, Settings3A.getMeteringRegionWidth(), cropRegion);
+    public static MeteringRectangle[] aeRegionsForNormalizedCoord(float nx,
+        float ny, final Rect cropRegion, int sensorOrientation) {
+        return regionsForNormalizedCoord(nx, ny, Settings3A.getMeteringRegionWidth(),
+            cropRegion, sensorOrientation);
     }
 
     /**
index 258eadd..8f7dc44 100644 (file)
@@ -622,12 +622,10 @@ public class OneCameraImpl extends AbstractOneCamera {
 
     @Override
     public void triggerFocusAndMeterAtPoint(float nx, float ny) {
-        // xc, yc is center of tap point in sensor coordinate system.
-        int xc = mCropRegion.left + (int) (mCropRegion.width() * ny);
-        int yc = mCropRegion.top + (int) (mCropRegion.height() * (1f - nx));
-
-        mAERegions = AutoFocusHelper.aeRegionsForSensorCoord(xc, yc, mCropRegion);
-        mAFRegions = AutoFocusHelper.afRegionsForSensorCoord(xc, yc, mCropRegion);
+        int sensorOrientation = mCharacteristics.get(
+            CameraCharacteristics.SENSOR_ORIENTATION);
+        mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
+        mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
 
         sendAutoFocusTriggerCaptureRequest(RequestTag.TAP_TO_FOCUS);
     }
index 4d6f632..aa03b88 100644 (file)
@@ -999,12 +999,10 @@ public class OneCameraZslImpl extends AbstractOneCamera {
      */
     @Override
     public void triggerFocusAndMeterAtPoint(float nx, float ny) {
-        // xc, yc is center of tap point in sensor coordinate system.
-        int xc = mCropRegion.left + (int) (mCropRegion.width() * ny);
-        int yc = mCropRegion.top + (int) (mCropRegion.height() * (1f - nx));
-
-        mAERegions = AutoFocusHelper.aeRegionsForSensorCoord(xc, yc, mCropRegion);
-        mAFRegions = AutoFocusHelper.afRegionsForSensorCoord(xc, yc, mCropRegion);
+        int sensorOrientation = mCharacteristics.get(
+            CameraCharacteristics.SENSOR_ORIENTATION);
+        mAERegions = AutoFocusHelper.aeRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
+        mAFRegions = AutoFocusHelper.afRegionsForNormalizedCoord(nx, ny, mCropRegion, sensorOrientation);
 
         startAFCycle();
     }
index 3692a75..f09f33b 100644 (file)
@@ -86,6 +86,7 @@ public class CameraSettingsActivity extends FragmentActivity {
         private String[] mCamcorderProfileNames;
         private CameraDeviceInfo mInfos;
         private final String mPrefKey;
+        private boolean mGetSubPrefAsRoot = true;
 
         // Selected resolutions for the different cameras and sizes.
         private SelectedPictureSizes mOldPictureSizesBack;
@@ -104,11 +105,13 @@ public class CameraSettingsActivity extends FragmentActivity {
             super.onCreate(savedInstanceState);
             Context context = this.getActivity().getApplicationContext();
             addPreferencesFromResource(R.xml.camera_preferences);
-            // Only add the additional preferences when in the main settings
-            // view, and not in the sub-preferences screens.
-            if (mPrefKey == null) {
-                CameraSettingsActivityHelper.addAdditionalPreferences(this, context);
-            }
+
+            // Allow the Helper to edit the full preference hierarchy, not the sub
+            // tree we may show as root. See {@link #getPreferenceScreen()}.
+            mGetSubPrefAsRoot = false;
+            CameraSettingsActivityHelper.addAdditionalPreferences(this, context);
+            mGetSubPrefAsRoot = true;
+
             mCamcorderProfileNames = getResources().getStringArray(R.array.camcorder_profile_names);
             mInfos = CameraAgentFactory
                     .getAndroidCameraAgent(context, CameraAgentFactory.CameraApi.API_1)
@@ -170,7 +173,7 @@ public class CameraSettingsActivity extends FragmentActivity {
         @Override
         public PreferenceScreen getPreferenceScreen() {
             PreferenceScreen root = super.getPreferenceScreen();
-            if (mPrefKey == null || root == null) {
+            if (!mGetSubPrefAsRoot || mPrefKey == null || root == null) {
                 return root;
             } else {
                 PreferenceScreen match = findByKey(root, mPrefKey);
index e7e2701..a978bc7 100644 (file)
@@ -34,16 +34,13 @@ import com.android.camera2.R;
  * whereas a state list drawable would require a different drawable for each state.
  */
 public class ModeIconView extends View {
-    private boolean mHighlightIsOn = false;
     private final GradientDrawable mBackground;
-    private final GradientDrawable mHoverDrawable;
 
     private final int mIconBackgroundSize;
     private int mHighlightColor;
     private final int mBackgroundDefaultColor;
     private final int mIconDrawableSize;
     private Drawable mIconDrawable = null;
-    private boolean mSelected = false;
 
     public ModeIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -53,13 +50,8 @@ public class ModeIconView extends View {
         mBackground = (GradientDrawable) getResources()
                 .getDrawable(R.drawable.mode_icon_background).mutate();
         mBackground.setBounds(0, 0, mIconBackgroundSize, mIconBackgroundSize);
-        mHoverDrawable = (GradientDrawable) getResources()
-                .getDrawable(R.drawable.mode_icon_background).mutate();
-        mHoverDrawable.setBounds(0, 0, mIconBackgroundSize, mIconBackgroundSize);
         mIconDrawableSize = getResources().getDimensionPixelSize(
                 R.dimen.mode_selector_icon_drawable_size);
-
-        mHoverDrawable.setColor(getResources().getColor(R.color.mode_icon_hover_highlight));
     }
 
     /**
@@ -83,11 +75,7 @@ public class ModeIconView extends View {
     @Override
     public void draw(Canvas canvas) {
         super.draw(canvas);
-        if (mHighlightIsOn && !mSelected) {
-            mHoverDrawable.draw(canvas);
-        } else {
-            mBackground.draw(canvas);
-        }
+        mBackground.draw(canvas);
         if (mIconDrawable != null) {
             mIconDrawable.draw(canvas);
         }
@@ -117,24 +105,10 @@ public class ModeIconView extends View {
     public void setSelected(boolean selected) {
         if (selected) {
             mBackground.setColor(mHighlightColor);
-            mHighlightIsOn = false;
         } else {
             mBackground.setColor(mBackgroundDefaultColor);
         }
 
-        mSelected = selected;
-        invalidate();
-    }
-
-    /**
-     * This gets called when the highlighted state is changed. When highlighted,
-     * a ring shaped drawable of a solid pre-defined color will be drawn on top
-     * of the background drawable to indicate highlight state.
-     *
-     * @param highlighted true when highlighted, false otherwise.
-     */
-    public void setHighlighted(boolean highlighted) {
-        mHighlightIsOn = highlighted;
         invalidate();
     }
 
index 78ab986..41bd3d6 100644 (file)
@@ -826,12 +826,11 @@ public class ModeListView extends FrameLayout
      * be revealed through a pinhole animation. After all the animations finish,
      * mode list will transition into fully hidden state.
      */
-    private class SelectedState extends  ModeListState {
+    private class SelectedState extends ModeListState {
         public SelectedState(ModeSelectorItem selectedItem) {
             final int modeId = selectedItem.getModeId();
             // Un-highlight all the modes.
             for (int i = 0; i < mModeSelectorItems.length; i++) {
-                mModeSelectorItems[i].setHighlighted(false);
                 mModeSelectorItems[i].setSelected(false);
             }
 
@@ -1501,7 +1500,6 @@ public class ModeListView extends FrameLayout
             if (mModeSelectorItems != null) {
                 // When becoming invisible/gone after initializing mode selector items.
                 for (int i = 0; i < mModeSelectorItems.length; i++) {
-                    mModeSelectorItems[i].setHighlighted(false);
                     mModeSelectorItems[i].setSelected(false);
                 }
             }
@@ -1906,6 +1904,8 @@ public class ModeListView extends FrameLayout
         private int mPeepHoleCenterY = UNSET;
         private float mRadius = 0f;
         private ValueAnimator mPeepHoleAnimator;
+        private ValueAnimator mFadeOutAlphaAnimator;
+        private ValueAnimator mRevealAlphaAnimator;
         private Bitmap mBackground;
         private Bitmap mBackgroundOverlay;
 
@@ -1923,6 +1923,107 @@ public class ModeListView extends FrameLayout
 
             mCoverPaint.setColor(0);
             mCoverPaint.setAlpha(0);
+
+            setupAnimators();
+        }
+
+        private void setupAnimators() {
+            mFadeOutAlphaAnimator = ValueAnimator.ofInt(0, 255);
+            mFadeOutAlphaAnimator.setDuration(100);
+            mFadeOutAlphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
+            mFadeOutAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    mCoverPaint.setAlpha((Integer) animation.getAnimatedValue());
+                    invalidate();
+                }
+            });
+            mFadeOutAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    // Sets a HW layer on the view for the animation.
+                    setLayerType(LAYER_TYPE_HARDWARE, null);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    // Sets the layer type back to NONE as a workaround for b/12594617.
+                    setLayerType(LAYER_TYPE_NONE, null);
+                }
+            });
+
+            /////////////////
+
+            mRevealAlphaAnimator = ValueAnimator.ofInt(255, 0);
+            mRevealAlphaAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
+            mRevealAlphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
+            mRevealAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    int alpha = (Integer) animation.getAnimatedValue();
+                    mCirclePaint.setAlpha(alpha);
+                    mCoverPaint.setAlpha(alpha);
+                }
+            });
+            mRevealAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    // Sets a HW layer on the view for the animation.
+                    setLayerType(LAYER_TYPE_HARDWARE, null);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    // Sets the layer type back to NONE as a workaround for b/12594617.
+                    setLayerType(LAYER_TYPE_NONE, null);
+                }
+            });
+
+            ////////////////
+
+            int horizontalDistanceToFarEdge = Math.max(mPeepHoleCenterX, mWidth - mPeepHoleCenterX);
+            int verticalDistanceToFarEdge = Math.max(mPeepHoleCenterY, mHeight - mPeepHoleCenterY);
+            int endRadius = (int) (Math.sqrt(horizontalDistanceToFarEdge * horizontalDistanceToFarEdge
+                    + verticalDistanceToFarEdge * verticalDistanceToFarEdge));
+            int startRadius = getResources().getDimensionPixelSize(
+                    R.dimen.mode_selector_icon_block_width) / 2;
+
+            mPeepHoleAnimator = ValueAnimator.ofFloat(startRadius, endRadius);
+            mPeepHoleAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
+            mPeepHoleAnimator.setInterpolator(Gusterpolator.INSTANCE);
+            mPeepHoleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    // Modify mask by enlarging the hole
+                    mRadius = (Float) mPeepHoleAnimator.getAnimatedValue();
+                    invalidate();
+                }
+            });
+            mPeepHoleAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    // Sets a HW layer on the view for the animation.
+                    setLayerType(LAYER_TYPE_HARDWARE, null);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    // Sets the layer type back to NONE as a workaround for b/12594617.
+                    setLayerType(LAYER_TYPE_NONE, null);
+                }
+            });
+
+            ////////////////
+            int size = getContext().getResources()
+                    .getDimensionPixelSize(R.dimen.mode_selector_icon_block_width);
+            mCircleDrawable = new TouchCircleDrawable(getContext().getResources());
+            mCircleDrawable.setSize(size, size);
+            mCircleDrawable.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    invalidate();
+                }
+            });
         }
 
         @Override
@@ -1936,6 +2037,16 @@ public class ModeListView extends FrameLayout
             return true;
         }
 
+        @Override
+        public void drawForeground(Canvas canvas) {
+            // Draw the circle in clear mode
+            if (mPeepHoleAnimator != null) {
+                // Draw a transparent circle using clear mode
+                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
+                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mCirclePaint);
+            }
+        }
+
         public void setAnimationStartingPosition(int x, int y) {
             mPeepHoleCenterX = x;
             mPeepHoleCenterY = y;
@@ -1978,16 +2089,6 @@ public class ModeListView extends FrameLayout
         }
 
         @Override
-        public void drawForeground(Canvas canvas) {
-            // Draw the circle in clear mode
-            if (mPeepHoleAnimator != null) {
-                // Draw a transparent circle using clear mode
-                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
-                canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mCirclePaint);
-            }
-        }
-
-        @Override
         public boolean shouldDrawSuper() {
             // No need to draw super when mBackgroundOverlay is being drawn, as
             // background overlay already contains what's drawn in super.
@@ -2000,36 +2101,11 @@ public class ModeListView extends FrameLayout
             mCoverPaint.setColor(0);
             mCoverPaint.setAlpha(0);
 
-            ValueAnimator alphaAnimator = ValueAnimator.ofInt(0, 255);
-            alphaAnimator.setDuration(100);
-            alphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
-            alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    mCoverPaint.setAlpha((Integer) animation.getAnimatedValue());
-                    invalidate();
-                }
-            });
-            if (listener != null) {
-                alphaAnimator.addListener(listener);
-            }
-
-            int size = getContext().getResources()
-                    .getDimensionPixelSize(R.dimen.mode_selector_icon_block_width);
-            mCircleDrawable = new TouchCircleDrawable(getContext().getResources());
             mCircleDrawable.setIconDrawable(
                     selectedItem.getIcon().getIconDrawableClone(),
                     selectedItem.getIcon().getIconDrawableSize());
-            mCircleDrawable.setSize(size, size);
             mCircleDrawable.setCenter(new Point(x, y));
             mCircleDrawable.setColor(selectedItem.getHighlightColor());
-            mCircleDrawable.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    invalidate();
-                }
-            });
-
             mCircleDrawable.setAnimatorListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -2046,8 +2122,16 @@ public class ModeListView extends FrameLayout
                     });
                 }
             });
+
+            // add fade out animator to a set, so we can freely add
+            // the listener without having to worry about listener dupes
+            AnimatorSet s = new AnimatorSet();
+            s.play(mFadeOutAlphaAnimator);
+            if (listener != null) {
+                s.addListener(listener);
+            }
             mCircleDrawable.animate();
-            alphaAnimator.start();
+            s.start();
         }
 
         @Override
@@ -2060,73 +2144,18 @@ public class ModeListView extends FrameLayout
                 mPeepHoleCenterY = mHeight / 2;
             }
 
-            int horizontalDistanceToFarEdge = Math.max(mPeepHoleCenterX, mWidth - mPeepHoleCenterX);
-            int verticalDistanceToFarEdge = Math.max(mPeepHoleCenterY, mHeight - mPeepHoleCenterY);
-            int endRadius = (int) (Math.sqrt(horizontalDistanceToFarEdge * horizontalDistanceToFarEdge
-                    + verticalDistanceToFarEdge * verticalDistanceToFarEdge));
-            int startRadius = getResources().getDimensionPixelSize(
-                    R.dimen.mode_selector_icon_block_width) / 2;
-
-            mPeepHoleAnimator = ValueAnimator.ofFloat(startRadius, endRadius);
-            mPeepHoleAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
-            mPeepHoleAnimator.setInterpolator(Gusterpolator.INSTANCE);
-            mPeepHoleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    // Modify mask by enlarging the hole
-                    mRadius = (Float) mPeepHoleAnimator.getAnimatedValue();
-                    invalidate();
-                }
-            });
-
-            if (listener != null) {
-                mPeepHoleAnimator.addListener(listener);
-            }
-
-            mPeepHoleAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    // Sets a HW layer on the view for the animation.
-                    setLayerType(LAYER_TYPE_HARDWARE, null);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    // Sets the layer type back to NONE as a workaround for b/12594617.
-                    setLayerType(LAYER_TYPE_NONE, null);
-                }
-            });
-
             mCirclePaint.setAlpha(255);
             mCoverPaint.setAlpha(255);
-            ValueAnimator alphaAnimator = ValueAnimator.ofInt(255, 0);
-            alphaAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
-            alphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
-            alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    int alpha = (Integer) animation.getAnimatedValue();
-                    mCirclePaint.setAlpha(alpha);
-                    mCoverPaint.setAlpha(alpha);
-                }
-            });
-            alphaAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    // Sets a HW layer on the view for the animation.
-                    setLayerType(LAYER_TYPE_HARDWARE, null);
-                }
 
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    // Sets the layer type back to NONE as a workaround for b/12594617.
-                    setLayerType(LAYER_TYPE_NONE, null);
-                }
-            });
-
-
-            mPeepHoleAnimator.start();
-            alphaAnimator.start();
+            // add peephole and reveal animators to a set, so we can
+            // freely add the listener without having to worry about
+            // listener dupes
+            AnimatorSet s = new AnimatorSet();
+            s.play(mPeepHoleAnimator).with(mRevealAlphaAnimator);
+            if (listener != null) {
+                s.addListener(listener);
+            }
+            s.start();
         }
 
         @Override
index 8b535b4..6ce70fc 100644 (file)
@@ -42,11 +42,6 @@ import com.android.camera2.R;
  * we display the view partially.
  */
 class ModeSelectorItem extends FrameLayout {
-    // Drawing modes that defines how the TextView should be drawn when there
-    // is not enough space to draw the whole TextView.
-    public static final int FLY_IN = 1;
-    public static final int FLY_OUT = 2;
-
     private TextView mText;
     private ModeIconView mIcon;
     private int mVisibleWidth = 0;
@@ -99,10 +94,6 @@ class ModeSelectorItem extends FrameLayout {
         mListener = listener;
     }
 
-    public void setHighlighted(boolean highlighted) {
-        mIcon.setHighlighted(highlighted);
-    }
-
     @Override
     public void setSelected(boolean selected) {
         mIcon.setSelected(selected);
@@ -121,13 +112,6 @@ class ModeSelectorItem extends FrameLayout {
         return false;
     }
 
-    @Override
-    public void setPressed(boolean pressed) {
-        super.setPressed(pressed);
-        // When pressed state changes, highlight the icon.
-        mIcon.setHighlighted(pressed);
-    }
-
     /**
      * When swiping in, we truncate the end of the item if the visible width
      * is not enough to show the whole item. When swiping out, we truncate the
index 627b6bf..3a7e355 100644 (file)
@@ -30,6 +30,7 @@ import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.camera2.CameraCharacteristics;
@@ -322,6 +323,7 @@ public class CameraUtil {
     }
 
     public static int nextPowerOf2(int n) {
+        // TODO: what happens if n is negative or already a power of 2?
         n -= 1;
         n |= n >>> 16;
         n |= n >>> 8;
@@ -337,6 +339,10 @@ public class CameraUtil {
         return (float) Math.sqrt(dx * dx + dy * dy);
     }
 
+    /**
+     * Clamps x to between min and max (inclusive on both ends, x = min --> min,
+     * x = max --> max).
+     */
     public static int clamp(int x, int min, int max) {
         if (x > max) {
             return max;
@@ -347,6 +353,10 @@ public class CameraUtil {
         return x;
     }
 
+    /**
+     * Clamps x to between min and max (inclusive on both ends, x = min --> min,
+     * x = max --> max).
+     */
     public static float clamp(float x, float min, float max) {
         if (x > max) {
             return max;
@@ -366,6 +376,31 @@ public class CameraUtil {
     }
 
     /**
+     * Given (nx, ny) \in [0, 1]^2, in the display's portrait coordinate system,
+     * returns normalized sensor coordinates \in [0, 1]^2 depending on how
+     * the sensor's orientation \in {0, 90, 180, 270}.
+     *
+     * <p>
+     * Returns null if sensorOrientation is not one of the above.
+     * </p>
+     */
+    public static PointF normalizedSensorCoordsForNormalizedDisplayCoords(
+        float nx, float ny, int sensorOrientation) {
+        switch (sensorOrientation) {
+        case 0:
+            return new PointF(nx, ny);
+        case 90:
+            return new PointF(ny, 1.0f - nx);
+        case 180:
+            return new PointF(1.0f - nx, 1.0f - ny);
+        case 270:
+            return new PointF(1.0f - ny, nx);
+        default:
+            return null;
+        }
+    }
+
+    /**
      * Given a size, return the largest size with the given aspectRatio that
      * maximally fits into the bounding rectangle of the original Size.
      *