Change interpolation logic to all apps transitioning on fling
b/30486958

Cleaning up/refactoring this CL will be done in polish branch.

Change-Id: Ib51633a1e1cb79f58b505f835053ef238ae1bdec
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index b6e2e4c..2b1ebde 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -174,7 +174,7 @@
             animType = PULLUP;
         }
         // Only animate the search bar if animating from spring loaded mode back to all apps
-        mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
+        startAnimationToOverlay(fromWorkspaceState,
                 Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb);
     }
 
@@ -185,7 +185,7 @@
             final boolean animated) {
         final WidgetsContainerView toView = mLauncher.getWidgetsView();
         final View buttonView = mLauncher.getWidgetsButton();
-        mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
+        startAnimationToOverlay(fromWorkspaceState,
                 Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL,
                 new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
                     @Override
@@ -229,7 +229,7 @@
      * Creates and starts a new animation to a particular overlay view.
      */
     @SuppressLint("NewApi")
-    private AnimatorSet startAnimationToOverlay(
+    private void startAnimationToOverlay(
             final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
             final View buttonView, final BaseContainerView toView,
             final boolean animated, int animType, final PrivateTransitionCallbacks pCb) {
@@ -277,8 +277,7 @@
             dispatchOnLauncherTransitionStart(toView, animated, false);
             dispatchOnLauncherTransitionEnd(toView, animated, false);
             pCb.onTransitionComplete();
-
-            return null;
+            return;
         }
         if (animType == CIRCULAR_REVEAL) {
             // Setup the reveal view animation
@@ -414,30 +413,29 @@
             toView.bringToFront();
             toView.setVisibility(View.VISIBLE);
             toView.post(startAnimRunnable);
-
-            return animation;
+            mCurrentAnimation = animation;
         } else if (animType == PULLUP) {
             // We are animating the content view alpha, so ensure we have a layer for it
             layerViews.put(contentView, BUILD_AND_SET_LAYER);
 
             animation.addListener(new AnimatorListenerAdapter() {
-                  @Override
-                  public void onAnimationEnd(Animator animation) {
-                      dispatchOnLauncherTransitionEnd(fromView, animated, false);
-                      dispatchOnLauncherTransitionEnd(toView, animated, false);
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
+                    dispatchOnLauncherTransitionEnd(toView, animated, false);
 
-                      // Disable all necessary layers
-                      for (View v : layerViews.keySet()) {
-                          if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
-                              v.setLayerType(View.LAYER_TYPE_NONE, null);
-                          }
-                      }
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
 
-                      cleanupAnimation();
-                      pCb.onTransitionComplete();
-                  }
+                    cleanupAnimation();
+                    pCb.onTransitionComplete();
+                }
             });
-            mAllAppsController.animateToAllApps(animation, revealDurationSlide);
+            boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide);
 
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
@@ -467,10 +465,13 @@
                     stateAnimation.start();
                 }
             };
-            toView.post(startAnimRunnable);
-            return animation;
+            mCurrentAnimation = animation;
+            if (shouldPost) {
+                toView.post(startAnimRunnable);
+            } else {
+                startAnimRunnable.run();
+            }
         }
-        return null;
     }
 
     /**
@@ -552,7 +553,7 @@
             }
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
-        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
+        startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
                 mLauncher.getStartViewForAllAppsRevealAnimation(), appsView,
                 animated, type, onCompleteRunnable, cb);
     }
@@ -581,7 +582,7 @@
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
             }
         };
-        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
+        startAnimationToWorkspaceFromOverlay(
                 fromWorkspaceState, toWorkspaceState,
                 mLauncher.getWidgetsButton(), widgetsView,
                 animated, CIRCULAR_REVEAL, onCompleteRunnable, cb);
@@ -673,7 +674,7 @@
     /**
      * Creates and starts a new animation to the workspace.
      */
-    private AnimatorSet startAnimationToWorkspaceFromOverlay(
+    private void startAnimationToWorkspaceFromOverlay(
             final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
             final View buttonView, final BaseContainerView fromView,
             final boolean animated, int animType, final Runnable onCompleteRunnable,
@@ -720,7 +721,7 @@
             if (onCompleteRunnable != null) {
                 onCompleteRunnable.run();
             }
-            return null;
+            return;
         }
         if (animType == CIRCULAR_REVEAL) {
             // hideAppsCustomizeHelper is called in some cases when it is already hidden
@@ -885,9 +886,8 @@
                     stateAnimation.start();
                 }
             };
+            mCurrentAnimation = animation;
             fromView.post(startAnimRunnable);
-
-            return animation;
         } else if (animType == PULLUP) {
             // We are animating the content view alpha, so ensure we have a layer for it
             layerViews.put(contentView, BUILD_AND_SET_LAYER);
@@ -921,7 +921,7 @@
                 }
 
             });
-            mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
+            boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
 
             // Dispatch the prepare transition signal
             dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
@@ -953,10 +953,14 @@
                     stateAnimation.start();
                 }
             };
-            fromView.post(startAnimRunnable);
-            return animation;
+            mCurrentAnimation = animation;
+            if (shouldPost) {
+                fromView.post(startAnimRunnable);
+            } else {
+                startAnimRunnable.run();
+            }
         }
-        return null;
+        return;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index eb6c926..e29e4d7 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -18,7 +18,6 @@
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
@@ -44,11 +43,12 @@
 
     private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f);
     private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
-    private final Interpolator mScrollInterpolator = new PagedView.ScrollInterpolator();
+    private final ScrollInterpolator mScrollInterpolator = new ScrollInterpolator();
 
     private static final float ANIMATION_DURATION = 1200;
-
     private static final float PARALLAX_COEFFICIENT = .125f;
+    private static final float FAST_FLING_PX_MS = 10;
+    private static final int SINGLE_FRAME_MS = 16;
 
     private AllAppsContainerView mAppsView;
     private int mAllAppsBackgroundColor;
@@ -78,6 +78,7 @@
     private float mShiftRange;      // changes depending on the orientation
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
 
+    // Velocity of the container. Unit is in px/ms.
     private float mContainerVelocity;
 
     private static final float DEFAULT_SHIFT_RANGE = 10;
@@ -357,7 +358,7 @@
 
     private void calculateDuration(float velocity, float disp) {
         // TODO: make these values constants after tuning.
-        float velocityDivisor = Math.max(1.5f, Math.abs(0.5f * velocity));
+        float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
         float travelDistance = Math.max(0.2f, disp / mShiftRange);
         mAnimationDuration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
         if (DBG) {
@@ -365,22 +366,29 @@
         }
     }
 
-    public void animateToAllApps(AnimatorSet animationOut, long duration) {
-        Interpolator interpolator;
+    public boolean animateToAllApps(AnimatorSet animationOut, long duration) {
+        boolean shouldPost = true;
         if (animationOut == null) {
-            return;
+            return shouldPost;
         }
+        Interpolator interpolator;
         if (mDetector.isIdleState()) {
             preparePull(true);
             mAnimationDuration = duration;
             mShiftStart = mAppsView.getTranslationY();
             interpolator = mFastOutSlowInInterpolator;
         } else {
+            mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity));
             interpolator = mScrollInterpolator;
+            float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange;
+            if (nextFrameProgress >= 0f) {
+                mProgress = nextFrameProgress;
+            }
+            shouldPost = false;
         }
-        final float fromAllAppsTop = mAppsView.getTranslationY();
+
         ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
-                fromAllAppsTop / mShiftRange, 0f);
+                mProgress, 0f);
         driftAndAlpha.setDuration(mAnimationDuration);
         driftAndAlpha.setInterpolator(interpolator);
         animationOut.play(driftAndAlpha);
@@ -405,6 +413,7 @@
             }
         });
         mCurrentAnimation = animationOut;
+        return shouldPost;
     }
 
     public void showDiscoveryBounce() {
@@ -437,9 +446,10 @@
         });
     }
 
-    public void animateToWorkspace(AnimatorSet animationOut, long duration) {
+    public boolean animateToWorkspace(AnimatorSet animationOut, long duration) {
+        boolean shouldPost = true;
         if (animationOut == null) {
-            return;
+            return shouldPost;
         }
         Interpolator interpolator;
         if (mDetector.isIdleState()) {
@@ -448,12 +458,17 @@
             mShiftStart = mAppsView.getTranslationY();
             interpolator = mFastOutSlowInInterpolator;
         } else {
+            mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity));
             interpolator = mScrollInterpolator;
+            float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange;
+            if (nextFrameProgress <= 1f) {
+                mProgress = nextFrameProgress;
+            }
+            shouldPost = false;
         }
-        final float fromAllAppsTop = mAppsView.getTranslationY();
 
         ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
-                fromAllAppsTop / mShiftRange, 1f);
+                mProgress, 1f);
         driftAndAlpha.setDuration(mAnimationDuration);
         driftAndAlpha.setInterpolator(interpolator);
         animationOut.play(driftAndAlpha);
@@ -478,6 +493,7 @@
             }
         });
         mCurrentAnimation = animationOut;
+        return shouldPost;
     }
 
     public void finishPullUp() {
@@ -575,4 +591,22 @@
         }
         setProgress(mProgress);
     }
+
+    static class ScrollInterpolator implements Interpolator {
+
+        boolean mSteeper;
+
+        public void setVelocityAtZero(float velocity) {
+            mSteeper = velocity > FAST_FLING_PX_MS;
+        }
+
+        public float getInterpolation(float t) {
+            t -= 1.0f;
+            float output = t * t * t;
+            if (mSteeper) {
+                output *= t * t; // Make interpolation initial slope steeper
+            }
+            return output + 1;
+        }
+    }
 }