Improve image size change handling.

- When the camera relative frame is set, we delay the change for camera
rectange until the next setImageSize call. This avoids the unwanted
intermediate state if we change them separately.

- Don't draw other screennails when the camera is in full screen. This
avoids showing other screennails when the image size changes.

- When the aspect ratio of the image changes, we assume the view angle
of the longer side doesn't change (so the aspect ratio change is because
the view angle of the shorter side changes). This matches what camera
preview does.

Bug: 6566612

Change-Id: I7603416f31c96ba77c96cdc2a3d0b79f8921c491
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 4490569..568dcf2 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -157,7 +157,7 @@
     private boolean mFilmMode = false;
     private int mDisplayRotation = 0;
     private int mCompensation = 0;
-    private boolean mFullScreen = true;
+    private boolean mFullScreenCamera;
     private Rect mCameraRelativeFrame = new Rect();
     private Rect mCameraRect = new Rect();
 
@@ -305,19 +305,21 @@
             for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) {
                 Picture p = mPictures.get(i);
                 if (p.isCamera()) {
-                    p.updateSize(true);
+                    p.forceSize();
                 }
             }
         }
 
-        updateConstrainedFrame();
+        updateCameraRect();
+        mPositionController.setConstrainedFrame(mCameraRect);
         if (changeSize) {
             mPositionController.setViewSize(getWidth(), getHeight());
         }
     }
 
-    // Update the constrained frame due to layout change.
-    private void updateConstrainedFrame() {
+    // Update the camera rectangle due to layout change or camera relative frame
+    // change.
+    private void updateCameraRect() {
         // Get the width and height in framework orientation because the given
         // mCameraRelativeFrame is in that coordinates.
         int w = getWidth();
@@ -343,12 +345,15 @@
         Log.d(TAG, "compensation = " + mCompensation
                 + ", CameraRelativeFrame = " + mCameraRelativeFrame
                 + ", mCameraRect = " + mCameraRect);
-        mPositionController.setConstrainedFrame(mCameraRect);
     }
 
     public void setCameraRelativeFrame(Rect frame) {
         mCameraRelativeFrame.set(frame);
-        updateConstrainedFrame();
+        updateCameraRect();
+        // Originally we do
+        //     mPositionController.setConstrainedFrame(mCameraRect);
+        // here, but it is moved to a parameter of the setImageSize() call, so
+        // it can be updated atomically with the CameraScreenNail's size change.
     }
 
     // Returns the rotation we need to do to the camera texture before drawing
@@ -371,7 +376,7 @@
         void draw(GLCanvas canvas, Rect r);
         void setScreenNail(ScreenNail s);
         boolean isCamera();  // whether the picture is a camera preview
-        void updateSize(boolean force);  // called when mCompensation changes
+        void forceSize();  // called when mCompensation changes
     };
 
     class FullPicture implements Picture {
@@ -381,6 +386,7 @@
         private boolean mIsVideo;
         private int mLoadingState = Model.LOADING_INIT;
         private boolean mWasCameraCenter;
+        private int mWidth, mHeight;
 
         public void FullPicture(TileImageView tileView) {
             mTileView = tileView;
@@ -396,11 +402,22 @@
             mIsVideo = mModel.isVideo(0);
             mLoadingState = mModel.getLoadingState(0);
             setScreenNail(mModel.getScreenNail(0));
-            updateSize(false);
+            setSize();
+        }
+
+        private void setSize() {
+            updateSize();
+            mPositionController.setImageSize(0, mWidth, mHeight,
+                    mIsCamera ? mCameraRect : null);
         }
 
         @Override
-        public void updateSize(boolean force) {
+        public void forceSize() {
+            updateSize();
+            mPositionController.forceImageSize(0, mWidth, mHeight);
+        }
+
+        private void updateSize() {
             if (mIsPanorama) {
                 mRotation = getPanoramaRotation();
             } else if (mIsCamera) {
@@ -411,26 +428,14 @@
 
             int w = mTileView.mImageWidth;
             int h = mTileView.mImageHeight;
-            mPositionController.setImageSize(0,
-                    getRotated(mRotation, w, h),
-                    getRotated(mRotation, h, w),
-                    force);
+            mWidth = getRotated(mRotation, w, h);
+            mHeight = getRotated(mRotation, h, w);
         }
 
         @Override
         public void draw(GLCanvas canvas, Rect r) {
             drawTileView(canvas, r);
 
-            boolean isCenter = mPositionController.isCenter();
-            if (mIsCamera) {
-                boolean full = !mFilmMode && isCenter
-                        && mPositionController.isAtMinimalScale();
-                if (full != mFullScreen) {
-                    mFullScreen = full;
-                    mListener.onFullScreenChanged(full);
-                }
-            }
-
             // We want to have the following transitions:
             // (1) Move camera preview out of its place: switch to film mode
             // (2) Move camera preview into its place: switch to page mode
@@ -440,6 +445,7 @@
             // Holdings except touch-down prevent the transitions.
             if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return;
 
+            boolean isCenter = mPositionController.isCenter();
             boolean isCameraCenter = mIsCamera && isCenter;
 
             if (mWasCameraCenter && mIsCamera && !isCenter && !mFilmMode) {
@@ -449,7 +455,7 @@
                 setFilmMode(false);
             }
 
-            if (isCenter && !mFilmMode && mIsCamera) {
+            if (isCameraCenter && !mFilmMode) {
                 // Move into camera in page mode, lock
                 mListener.lockOrientation();
             }
@@ -565,6 +571,7 @@
         private boolean mIsPanorama;
         private boolean mIsVideo;
         private int mLoadingState = Model.LOADING_INIT;
+        private int mWidth, mHeight;
 
         public ScreenNailPicture(int index) {
             mIndex = index;
@@ -595,11 +602,6 @@
                 return;
             }
 
-            if (mIsCamera && mFullScreen != false) {
-                mFullScreen = false;
-                mListener.onFullScreenChanged(false);
-            }
-
             float filmRatio = mPositionController.getFilmRatio();
             boolean wantsCardEffect = CARD_EFFECT && mIndex > 0
                     && filmRatio != 1f && !mPictures.get(0).isCamera();
@@ -646,11 +648,21 @@
         public void setScreenNail(ScreenNail s) {
             if (mScreenNail == s) return;
             mScreenNail = s;
-            updateSize(false);
+            setSize();
+        }
+
+        private void setSize() {
+            updateSize();
+            mPositionController.setImageSize(mIndex, mWidth, mHeight, null);
         }
 
         @Override
-        public void updateSize(boolean force) {
+        public void forceSize() {
+            updateSize();
+            mPositionController.forceImageSize(mIndex, mWidth, mHeight);
+        }
+
+        private void updateSize() {
             if (mIsPanorama) {
                 mRotation = getPanoramaRotation();
             } else if (mIsCamera) {
@@ -671,12 +683,8 @@
                 h = mSize.height;
             }
 
-            if (w != 0 && h != 0)  {
-                mPositionController.setImageSize(mIndex,
-                        getRotated(mRotation, w, h),
-                        getRotated(mRotation, h, w),
-                        force);
-            }
+            mWidth = getRotated(mRotation, w, h);
+            mHeight = getRotated(mRotation, h, w);
         }
 
         @Override
@@ -956,12 +964,32 @@
 
     @Override
     protected void render(GLCanvas canvas) {
-        // In page mode, we draw only one previous/next photo. But if we are
-        // doing capture animation, we want to draw all photos.
-        boolean inPageMode = (mPositionController.getFilmRatio() == 0f);
-        boolean inCaptureAnimation = ((mHolding & HOLD_CAPTURE_ANIMATION) != 0);
-        boolean drawOneNeighborOnly = inPageMode && !inCaptureAnimation;
-        int neighbors = drawOneNeighborOnly ? 1 : SCREEN_NAIL_MAX;
+        // Check if the camera preview occupies the full screen.
+        boolean full = !mFilmMode && mPictures.get(0).isCamera()
+                && mPositionController.isCenter()
+                && mPositionController.isAtMinimalScale();
+        if (full != mFullScreenCamera) {
+            mFullScreenCamera = full;
+            mListener.onFullScreenChanged(full);
+        }
+
+        // Determine how many photos we need to draw in addition to the center
+        // one.
+        int neighbors;
+        if (mFullScreenCamera) {
+            neighbors = 0;
+        } else {
+            // In page mode, we draw only one previous/next photo. But if we are
+            // doing capture animation, we want to draw all photos.
+            boolean inPageMode = (mPositionController.getFilmRatio() == 0f);
+            boolean inCaptureAnimation =
+                    ((mHolding & HOLD_CAPTURE_ANIMATION) != 0);
+            if (inPageMode && !inCaptureAnimation) {
+                neighbors = 1;
+            } else {
+                neighbors = SCREEN_NAIL_MAX;
+            }
+        }
 
         // Draw photos from back to front
         for (int i = neighbors; i >= -neighbors; i--) {
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index db77b99..cf41d3a 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -218,28 +218,34 @@
         }
     }
 
-    public void setConstrainedFrame(Rect f) {
-        if (mConstrainedFrame.equals(f)) return;
-        mConstrainedFrame.set(f);
+    public void setConstrainedFrame(Rect cFrame) {
+        if (mConstrainedFrame.equals(cFrame)) return;
+        mConstrainedFrame.set(cFrame);
         mPlatform.updateDefaultXY();
         updateScaleAndGapLimit();
         snapAndRedraw();
     }
 
-    public void setImageSize(int index, int width, int height, boolean force) {
-        if (force) {
-            Box b = mBoxes.get(index);
-            b.mImageW = width;
-            b.mImageH = height;
-            return;
-        }
+    public void forceImageSize(int index, int width, int height) {
+        if (width == 0 || height == 0) return;
+        Box b = mBoxes.get(index);
+        b.mImageW = width;
+        b.mImageH = height;
+        return;
+    }
 
-        if (width == 0 || height == 0) {
-            initBox(index);
-        } else if (!setBoxSize(index, width, height, false)) {
-            return;
-        }
+    public void setImageSize(int index, int width, int height, Rect cFrame) {
+        if (width == 0 || height == 0) return;
 
+        boolean needUpdate = false;
+        if (cFrame != null && !mConstrainedFrame.equals(cFrame)) {
+            mConstrainedFrame.set(cFrame);
+            mPlatform.updateDefaultXY();
+            needUpdate = true;
+        }
+        needUpdate |= setBoxSize(index, width, height, false);
+
+        if (!needUpdate) return;
         updateScaleAndGapLimit();
         snapAndRedraw();
     }
@@ -259,8 +265,15 @@
         }
 
         // The ratio of the old size and the new size.
-        float ratio = Math.min(
-                (float) b.mImageW / width, (float) b.mImageH / height);
+        //
+        // If the aspect ratio changes, we don't know if it is because one side
+        // grows or the other side shrinks. Currently we just assume the view
+        // angle of the longer side doesn't change (so the aspect ratio change
+        // is because the view angle of the shorter side changes). This matches
+        // what camera preview does.
+        float ratio = (width > height)
+                ? (float) b.mImageW / width
+                : (float) b.mImageH / height;
 
         b.mImageW = width;
         b.mImageH = height;