Fix b/5624131: Panning enlarged photo switches images by mistake.

Avoid swiping images when panning to view vertically enlarged photo.

Change-Id: Ieb72cb8f38595fbedc927e8a0380fe31532ee434
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index aa84d53..9d2d459 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -404,35 +404,40 @@
         }
     }
 
-    private boolean swipeImages(float velocity) {
+    private boolean swipeImages(float velocityX, float velocityY) {
         if (mTransitionMode != TRANS_NONE
                 && mTransitionMode != TRANS_SWITCH_NEXT
                 && mTransitionMode != TRANS_SWITCH_PREVIOUS) return false;
 
-        ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
-        ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
-
-        int width = getWidth();
+        // Avoid swiping images if we're possibly flinging to view the
+        // zoomed in picture vertically.
+        PositionController controller = mPositionController;
+        boolean isMinimal = controller.isAtMinimalScale();
+        int edges = controller.getImageAtEdges();
+        if (!isMinimal && Math.abs(velocityY) > Math.abs(velocityX))
+            if ((edges & PositionController.IMAGE_AT_TOP_EDGE) == 0
+                    || (edges & PositionController.IMAGE_AT_BOTTOM_EDGE) == 0)
+                return false;
 
         // If we are at the edge of the current photo and the sweeping velocity
         // exceeds the threshold, switch to next / previous image.
-        PositionController controller = mPositionController;
-        boolean isMinimal = controller.isAtMinimalScale();
-
-        if (velocity < -SWIPE_THRESHOLD &&
-                (isMinimal || controller.isAtRightEdge())) {
+        int halfWidth = getWidth() / 2;
+        if (velocityX < -SWIPE_THRESHOLD && (isMinimal
+                || (edges & PositionController.IMAGE_AT_RIGHT_EDGE) != 0)) {
             stopCurrentSwipingIfNeeded();
+            ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
             if (next.isEnabled()) {
                 mTransitionMode = TRANS_SWITCH_NEXT;
-                controller.startHorizontalSlide(next.mOffsetX - width / 2);
+                controller.startHorizontalSlide(next.mOffsetX - halfWidth);
                 return true;
             }
-        } else if (velocity > SWIPE_THRESHOLD &&
-                (isMinimal || controller.isAtLeftEdge())) {
+        } else if (velocityX > SWIPE_THRESHOLD && (isMinimal
+                || (edges & PositionController.IMAGE_AT_LEFT_EDGE) != 0)) {
             stopCurrentSwipingIfNeeded();
+            ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
             if (prev.isEnabled()) {
                 mTransitionMode = TRANS_SWITCH_PREVIOUS;
-                controller.startHorizontalSlide(prev.mOffsetX - width / 2);
+                controller.startHorizontalSlide(prev.mOffsetX - halfWidth);
                 return true;
             }
         }
@@ -440,26 +445,24 @@
         return false;
     }
 
-    public boolean snapToNeighborImage() {
+    private boolean snapToNeighborImage() {
         if (mTransitionMode != TRANS_NONE) return false;
 
-        ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
-        ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
-
-        int width = getWidth();
         PositionController controller = mPositionController;
-
         RectF bounds = controller.getImageBounds();
         int left = Math.round(bounds.left);
         int right = Math.round(bounds.right);
+        int width = getWidth();
         int threshold = SWITCH_THRESHOLD + gapToSide(right - left, width);
 
         // If we have moved the picture a lot, switching.
+        ScreenNailEntry next = mScreenNails[ENTRY_NEXT];
         if (next.isEnabled() && threshold < width - right) {
             mTransitionMode = TRANS_SWITCH_NEXT;
             controller.startHorizontalSlide(next.mOffsetX - width / 2);
             return true;
         }
+        ScreenNailEntry prev = mScreenNails[ENTRY_PREVIOUS];
         if (prev.isEnabled() && threshold < left) {
             mTransitionMode = TRANS_SWITCH_PREVIOUS;
             controller.startHorizontalSlide(prev.mOffsetX - width / 2);
@@ -497,7 +500,7 @@
         @Override
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                 float velocityY) {
-            if (swipeImages(velocityX)) {
+            if (swipeImages(velocityX, velocityY)) {
                 mIgnoreUpEvent = true;
             } else if (mTransitionMode != TRANS_NONE) {
                 // do nothing
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index 734c571..236c8aa 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -16,27 +16,21 @@
 
 package com.android.gallery3d.ui;
 
-import com.android.gallery3d.R;
-import com.android.gallery3d.app.GalleryActivity;
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.Path;
 import com.android.gallery3d.ui.PositionRepository.Position;
 import com.android.gallery3d.util.GalleryUtils;
 
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.RectF;
-import android.os.Message;
 import android.os.SystemClock;
 import android.util.FloatMath;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.widget.Scroller;
 
 class PositionController {
-    private static final String TAG = "PositionController";
+    public static final int IMAGE_AT_LEFT_EDGE = 1;
+    public static final int IMAGE_AT_RIGHT_EDGE = 2;
+    public static final int IMAGE_AT_TOP_EDGE = 4;
+    public static final int IMAGE_AT_BOTTOM_EDGE = 8;
+
     private long mAnimationStartTime = NO_ANIMATION;
     private static final long NO_ANIMATION = -1;
     private static final long LAST_ANIMATION = -2;
@@ -373,12 +367,26 @@
 
     public boolean fling(float velocityX, float velocityY) {
         // We only want to do fling when the picture is zoomed-in.
-        if (mImageW * mCurrentScale <= mViewW &&
-            mImageH * mCurrentScale <= mViewH) {
+        if (viewWiderThanScaledImage(mCurrentScale) &&
+            viewHigherThanScaledImage(mCurrentScale)) {
             return false;
         }
 
-        calculateStableBound(mCurrentScale);
+        // We only allow flinging in the directions where it won't go over the
+        // picture.
+        int edges = getImageAtEdges();
+        if ((velocityX > 0 && (edges & IMAGE_AT_LEFT_EDGE) != 0) ||
+            (velocityX < 0 && (edges & IMAGE_AT_RIGHT_EDGE) != 0)) {
+            velocityX = 0;
+        }
+        if ((velocityY > 0 && (edges & IMAGE_AT_TOP_EDGE) != 0) ||
+            (velocityY < 0 && (edges & IMAGE_AT_BOTTOM_EDGE) != 0)) {
+            velocityY = 0;
+        }
+        if (isAlmostEquals(velocityX, 0) && isAlmostEquals(velocityY, 0)) {
+            return false;
+        }
+
         mScroller.fling(mCurrentX, mCurrentY,
                 Math.round(-velocityX / mCurrentScale),
                 Math.round(-velocityY / mCurrentScale),
@@ -407,7 +415,7 @@
         // force it to be in the center.
         // (We do for height only, not width, because the user may
         // want to scroll to the previous/next image.)
-        if (FloatMath.floor(mImageH * mToScale) <= mViewH) {
+        if (viewHigherThanScaledImage(mToScale)) {
             mToY = mImageH / 2;
         }
 
@@ -590,16 +598,24 @@
 
         // If the scaled height is smaller than the view height,
         // force it to be in the center.
-        if (FloatMath.floor(mImageH * scale) <= mViewH) {
+        if (viewHigherThanScaledImage(scale)) {
             mBoundTop = mBoundBottom = mImageH / 2;
         }
 
         // Same for width
-        if (FloatMath.floor(mImageW * scale) <= mViewW) {
+        if (viewWiderThanScaledImage(scale)) {
             mBoundLeft = mBoundRight = mImageW / 2;
         }
     }
 
+    private boolean viewHigherThanScaledImage(float scale) {
+        return FloatMath.floor(mImageH * scale) <= mViewH;
+    }
+
+    private boolean viewWiderThanScaledImage(float scale) {
+        return FloatMath.floor(mImageW * scale) <= mViewW;
+    }
+
     private boolean useCurrentValueAsTarget() {
         return mAnimationStartTime == NO_ANIMATION ||
                 mAnimationKind == ANIM_KIND_SNAPBACK ||
@@ -658,13 +674,21 @@
         return mImageH;
     }
 
-    public boolean isAtLeftEdge() {
+    public int getImageAtEdges() {
         calculateStableBound(mCurrentScale);
-        return mCurrentX <= mBoundLeft;
-    }
-
-    public boolean isAtRightEdge() {
-        calculateStableBound(mCurrentScale);
-        return mCurrentX >= mBoundRight;
+        int edges = 0;
+        if (mCurrentX <= mBoundLeft) {
+            edges |= IMAGE_AT_LEFT_EDGE;
+        }
+        if (mCurrentX >= mBoundRight) {
+            edges |= IMAGE_AT_RIGHT_EDGE;
+        }
+        if (mCurrentY <= mBoundTop) {
+            edges |= IMAGE_AT_TOP_EDGE;
+        }
+        if (mCurrentY >= mBoundBottom) {
+            edges |= IMAGE_AT_BOTTOM_EDGE;
+        }
+        return edges;
     }
 }