Improve the animation to filmstrip.

1. Remove the snap-in-center check at the end of scale animation.
2. Don't go back to full-screen when animating from camera preview
   to filmstrip.

  bug:10806298
  bug:10864100

Change-Id: I751040a22e2758709c22e7230a5418a871ef85f9
diff --git a/src/com/android/camera/data/CameraPreviewData.java b/src/com/android/camera/data/CameraPreviewData.java
index f6127e4..d13465e 100644
--- a/src/com/android/camera/data/CameraPreviewData.java
+++ b/src/com/android/camera/data/CameraPreviewData.java
@@ -41,7 +41,7 @@
 
     @Override
     public int getViewType() {
-        return ImageData.TYPE_STICKY_VIEW;
+        return ImageData.VIEW_TYPE_STICKY;
     }
 
     @Override
diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java
index 17ddbca..1f31f5f 100644
--- a/src/com/android/camera/data/LocalMediaData.java
+++ b/src/com/android/camera/data/LocalMediaData.java
@@ -402,7 +402,7 @@
 
         @Override
         public int getViewType() {
-            return TYPE_REMOVABLE_VIEW;
+            return VIEW_TYPE_REMOVABLE;
         }
 
         @Override
@@ -638,7 +638,7 @@
 
         @Override
         public int getViewType() {
-            return TYPE_REMOVABLE_VIEW;
+            return VIEW_TYPE_REMOVABLE;
         }
 
         @Override
diff --git a/src/com/android/camera/data/SimpleViewData.java b/src/com/android/camera/data/SimpleViewData.java
index a73a52e..c440296 100644
--- a/src/com/android/camera/data/SimpleViewData.java
+++ b/src/com/android/camera/data/SimpleViewData.java
@@ -81,7 +81,7 @@
 
     @Override
     public int getViewType() {
-        return FilmStripView.ImageData.TYPE_REMOVABLE_VIEW;
+        return FilmStripView.ImageData.VIEW_TYPE_REMOVABLE;
     }
 
     @Override
diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java
index cbbc54e..f28637c 100644
--- a/src/com/android/camera/ui/FilmStripView.java
+++ b/src/com/android/camera/ui/FilmStripView.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.net.Uri;
+import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -118,9 +119,9 @@
         }
 
         // View types.
-        public static final int TYPE_NONE = 0;
-        public static final int TYPE_STICKY_VIEW = 1;
-        public static final int TYPE_REMOVABLE_VIEW = 2;
+        public static final int VIEW_TYPE_NONE = 0;
+        public static final int VIEW_TYPE_STICKY = 1;
+        public static final int VIEW_TYPE_REMOVABLE = 2;
 
         // Actions allowed to be performed on the image data.
         // The actions are defined bit-wise so we can use bit operations like
@@ -705,7 +706,7 @@
     private int getCurrentViewType() {
         ViewItem curr = mViewItem[mCurrentItem];
         if (curr == null) {
-            return ImageData.TYPE_NONE;
+            return ImageData.VIEW_TYPE_NONE;
         }
         return mDataAdapter.getImageData(curr.getId()).getViewType();
     }
@@ -1048,7 +1049,7 @@
                 /  mDrawArea.width());
         mController.scrollToPosition(currentViewCenter,
                 snapInTime, false);
-        if (getCurrentViewType() == ImageData.TYPE_STICKY_VIEW
+        if (getCurrentViewType() == ImageData.VIEW_TYPE_STICKY
                 && !mController.isScaling()
                 && mScale != FULL_SCREEN_SCALE) {
             // Now going to full screen camera
@@ -1332,7 +1333,7 @@
     // Keeps the view in the view hierarchy if it's camera preview.
     // Remove from the hierarchy otherwise.
     private void checkForRemoval(ImageData data, View v) {
-        if (data.getViewType() != ImageData.TYPE_STICKY_VIEW) {
+        if (data.getViewType() != ImageData.VIEW_TYPE_STICKY) {
             removeView(v);
             data.recycle();
         } else {
@@ -1615,7 +1616,7 @@
 
     public boolean inCameraFullscreen() {
         return isDataAtCenter(0) && inFullScreen()
-                && (getCurrentViewType() == ImageData.TYPE_STICKY_VIEW);
+                && (getCurrentViewType() == ImageData.VIEW_TYPE_STICKY);
     }
 
     @Override
@@ -1816,10 +1817,7 @@
      * MyController controls all the geometry animations. It passively tells the
      * geometry information on demand.
      */
-    private class MyController implements
-            Controller,
-            ValueAnimator.AnimatorUpdateListener,
-            Animator.AnimatorListener {
+    private class MyController implements Controller {
 
         private final ValueAnimator mScaleAnimator;
         private ValueAnimator mZoomAnimator;
@@ -1827,50 +1825,50 @@
         private final Scroller mScroller;
         private boolean mCanStopScroll;
         private final DecelerateInterpolator mDecelerateInterpolator;
-        private final TimeInterpolator mDecelerateAccelerateInterpolator =
-                new TimeInterpolator() {
-                    private final TimeInterpolator interpolator =
-                            new AccelerateInterpolator();
+
+        private final MyScroller.Listener mScrollerListener =
+                new MyScroller.Listener() {
+                    @Override
+                    public void onScrollUpdate(int currX, int currY) {
+                        mCenterX = currX;
+                        clampCenterX();
+                        invalidate();
+                    }
 
                     @Override
-                    public float getInterpolation(float v) {
-                        float v2 = v * 2f;
-                        return ((v2 < 1f) ?
-                                mDecelerateInterpolator.getInterpolation(v2) / 2f :
-                                interpolator.getInterpolation(v2 - 1f) / 2f + 0.5f);
-                    }
-                };
-
-        private final Runnable mScrollUpdater = new Runnable() {
-            @Override
-            public void run() {
-                boolean newPosition = !mScroller.isFinished();
-                if (!newPosition) {
-                    mCanStopScroll = true;
-                    if (mViewItem[mCurrentItem] != null) {
+                    public void onScrollEnd() {
+                        mCanStopScroll = true;
+                        if (mViewItem[mCurrentItem] == null) {
+                            return;
+                        }
                         snapInCenter();
                         if (mCenterX == mViewItem[mCurrentItem].getCenterX()
-                            && getCurrentViewType() == ImageData.TYPE_STICKY_VIEW) {
+                                && getCurrentViewType() == ImageData.VIEW_TYPE_STICKY) {
+                            // Special case for the scrolling end on the camera preview.
                             goToFullScreen();
                         }
                     }
-                    return;
-                }
-                mScroller.computeScrollOffset();
-                mCenterX = mScroller.getCurrX();
-                clampCenterX();
-                invalidate();
-                FilmStripView.this.post(this);
-            }
-        };
+                };
+
+        private ValueAnimator.AnimatorUpdateListener mScaleAnimatorUpdateListener =
+                new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        if (mViewItem[mCurrentItem] == null) {
+                            return;
+                        }
+                        mScale = (Float) animation.getAnimatedValue();
+                        invalidate();
+                    }
+                };
 
         MyController(Context context) {
-            mScroller = new Scroller(context);
+            mScroller = new MyScroller(mActivity,
+                    new Handler(mActivity.getMainLooper()), mScrollerListener);
             mCanStopScroll = true;
 
             mScaleAnimator = new ValueAnimator();
-            mScaleAnimator.addUpdateListener(MyController.this);
-            mScaleAnimator.addListener(MyController.this);
+            mScaleAnimator.addUpdateListener(mScaleAnimatorUpdateListener);
             mDecelerateInterpolator = new DecelerateInterpolator(1.5f);
         }
 
@@ -1983,7 +1981,7 @@
             }
 
             float scaledVelocityX = velocityX / mScale;
-            if (inFullScreen() && getCurrentViewType() == ImageData.TYPE_STICKY_VIEW
+            if (inFullScreen() && getCurrentViewType() == ImageData.VIEW_TYPE_STICKY
                     && scaledVelocityX < 0) {
                 // Swipe left in camera preview.
                 goToFilmStrip();
@@ -1998,7 +1996,6 @@
             // the possible maximum too.
             int maxX = estimateMaxX(item.getId(), item.getLeftPosition(), w);
             mScroller.fling(mCenterX, 0, (int) -velocityX, 0, minX, maxX, 0, 0);
-            FilmStripView.this.post(mScrollUpdater);
         }
 
         @Override
@@ -2024,21 +2021,21 @@
             mCanStopScroll = interruptible;
             mScroller.startScroll(mCenterX, 0, position - mCenterX,
                     0, duration);
-            FilmStripView.this.post(mScrollUpdater);
         }
 
         @Override
         public boolean goToNextItem() {
-            ViewItem nextItem = mViewItem[mCurrentItem + 1];
+            final ViewItem nextItem = mViewItem[mCurrentItem + 1];
             if (nextItem == null) {
                 return false;
             }
-            stopScale();
+            stopScrolling(true);
             scrollToPosition(nextItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS * 2, false);
-            mScaleAnimator.setFloatValues(FULL_SCREEN_SCALE, FILM_STRIP_SCALE, FULL_SCREEN_SCALE);
-            mScaleAnimator.setInterpolator(mDecelerateAccelerateInterpolator);
-            mScaleAnimator.setDuration(GEOMETRY_ADJUST_TIME_MS * 2);
-            mScaleAnimator.start();
+
+            if (getCurrentViewType() == ImageData.VIEW_TYPE_STICKY) {
+                // Special case when moving from camera preview.
+                scaleTo(FILM_STRIP_SCALE, GEOMETRY_ADJUST_TIME_MS);
+            }
             return true;
         }
 
@@ -2059,7 +2056,7 @@
 
             final ViewItem nextItem = mViewItem[mCurrentItem + 1];
             if (mViewItem[mCurrentItem].getId() == 0 &&
-                    getCurrentViewType() == ImageData.TYPE_STICKY_VIEW &&
+                    getCurrentViewType() == ImageData.VIEW_TYPE_STICKY &&
                     nextItem != null) {
                 // Deal with the special case of swiping in camera preview.
                 scrollToPosition(nextItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS, false);
@@ -2160,40 +2157,6 @@
             reload();
         }
 
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            if (mViewItem[mCurrentItem] == null) {
-                return;
-            }
-            mScale = (Float) animation.getAnimatedValue();
-            invalidate();
-        }
-
-        @Override
-        public void onAnimationStart(Animator anim) {
-        }
-
-        @Override
-        public void onAnimationEnd(Animator anim) {
-            ViewItem item = mViewItem[mCurrentItem];
-            if (item == null) {
-                return;
-            }
-            if (mCenterX != item.getCenterX()) {
-                if (inFilmStrip()) {
-                    snapInCenter();
-                }
-            }
-        }
-
-        @Override
-        public void onAnimationCancel(Animator anim) {
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator anim) {
-        }
-
         public boolean isZoomStarted() {
             return mScale > FULL_SCREEN_SCALE;
         }
@@ -2203,6 +2166,65 @@
         }
     }
 
+    private static class MyScroller extends Scroller {
+        public interface Listener {
+            public void onScrollUpdate(int currX, int currY);
+            public void onScrollEnd();
+        }
+
+        private Handler mHandler;
+        private Listener mListener;
+        private Runnable mScrollChecker = new Runnable() {
+            @Override
+            public void run() {
+                boolean newPosition = computeScrollOffset();
+                if (!newPosition) {
+                    mListener.onScrollEnd();
+                    return;
+                }
+                mListener.onScrollUpdate(getCurrX(), getCurrY());
+                mHandler.removeCallbacks(this);
+                mHandler.post(this);
+            }
+        };
+
+        public MyScroller(Context ctx, Handler handler, Listener listener) {
+            super(ctx);
+            mHandler = handler;
+            mListener = listener;
+        }
+
+        @Override
+        public void fling(
+                int startX, int startY,
+                int velocityX, int velocityY,
+                int minX, int maxX,
+                int minY, int maxY) {
+            super.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
+            runChecker();
+        }
+
+        @Override
+        public void startScroll(int startX, int startY, int dx, int dy) {
+            super.startScroll(startX, startY, dx, dy);
+            runChecker();
+        }
+
+        @Override
+        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
+            super.startScroll(startX, startY, dx, dy, duration);
+            runChecker();
+        }
+
+        private void runChecker() {
+            if (mHandler == null || mListener == null) {
+                return;
+            }
+            mHandler.removeCallbacks(mScrollChecker);
+            mHandler.post(mScrollChecker);
+        }
+    }
+
     private class MyGestureReceiver implements FilmStripGestureRecognizer.Listener {
         // Indicating the current trend of scaling is up (>1) or down (<1).
         private float mScaleTrend;
@@ -2305,7 +2327,7 @@
 
             if (mCenterX > currItem.getCenterX() + CAMERA_PREVIEW_SWIPE_THRESHOLD
                     && currItem.getId() == 0
-                    && getCurrentViewType() == ImageData.TYPE_STICKY_VIEW
+                    && getCurrentViewType() == ImageData.VIEW_TYPE_STICKY
                     && mDataIdOnUserScrolling == 0) {
                 mController.goToFilmStrip();
                 // Special case to go from camera preview to the next photo.
@@ -2318,7 +2340,7 @@
                     snapInCenter();
                 }
             } if (mCenterX == currItem.getCenterX() && currItem.getId() == 0
-                    && getCurrentViewType() == ImageData.TYPE_STICKY_VIEW) {
+                    && getCurrentViewType() == ImageData.VIEW_TYPE_STICKY) {
                 mController.goToFullScreen();
             } else {
                 if (mDataIdOnUserScrolling  == 0 && currItem.getId() != 0) {
@@ -2436,7 +2458,7 @@
                         }
                         mController.scrollToPosition(
                                 nextItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS, true);
-                        if (getCurrentViewType() == ImageData.TYPE_STICKY_VIEW) {
+                        if (getCurrentViewType() == ImageData.VIEW_TYPE_STICKY) {
                             mController.goToFilmStrip();
                         }
                     }
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index bb72ba7..fc80c5c 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -830,14 +830,20 @@
         }
     }
 
+    /**
+     * Dumps the stack trace.
+     *
+     * @param level How many levels of the stack are dumped. 0 means all.
+     * @return A {@link java.lang.String} of all the output with newline
+     * between each.
+     */
     public static String dumpStackTrace(int level) {
         StackTraceElement[] elems = Thread.currentThread().getStackTrace();
         // Ignore the first 3 elements.
-        level += 3;
-        level = (level == 0 ? elems.length : Math.min(level, elems.length));
+        level = (level == 0 ? elems.length : Math.min(level + 3, elems.length));
         String ret = new String();
         for (int i = 3; i < level; i++) {
-            ret = ret + elems[i].toString() + '\n';
+            ret = ret + "\t" + elems[i].toString() + '\n';
         }
         return ret;
     }