Implement overshoot and squish motion in all apps open

Bug: 187475924
Bug: 183062683
Test: manual

Change-Id: I33b6c647c45ff467c6d49cf3796f92ca366ab3f1
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3267a5d..65c4c80 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -84,7 +84,7 @@
     <dimen name="fastscroll_end_margin">-26dp</dimen>
 
     <!-- All Apps -->
-    <dimen name="all_apps_open_vertical_translate">300dp</dimen>
+    <dimen name="all_apps_open_vertical_translate">320dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
     <dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5ecdca6..7b67807 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -905,7 +905,7 @@
         } else {
             mOverlayManager.onActivityStopped(this);
         }
-
+        hideKeyboard();
         logStopAndResume(false /* isResume */);
         mAppWidgetHost.setActivityStarted(false);
         NotificationListener.removeNotificationsChangedListener();
@@ -1459,7 +1459,7 @@
                 && AbstractFloatingView.getTopOpenView(this) == null;
         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
         boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
-
+        hideKeyboard();
         if (isActionMain) {
             if (!internalStateHandled) {
                 // In all these cases, only animate if we're already on home
@@ -1481,9 +1481,6 @@
                 }
             }
 
-            // Handle HOME_INTENT
-            hideKeyboard();
-
             if (mLauncherCallbacks != null) {
                 mLauncherCallbacks.onHomeIntent(internalStateHandled);
             }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 47236b6..d9c8c96 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -45,6 +45,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.ColorUtils;
 import androidx.core.os.BuildCompat;
 import androidx.recyclerview.widget.DefaultItemAnimator;
@@ -79,10 +80,11 @@
         Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener,
         ScrimView.ScrimDrawingController {
 
-    private static final float FLING_VELOCITY_MULTIPLIER = 1000f;
+    public static final float PULL_MULTIPLIER = .02f;
+    public static final float FLING_VELOCITY_MULTIPLIER = 2000f;
 
     // Starts the springs after at least 25% of the animation has passed.
-    private static final float FLING_ANIMATION_THRESHOLD = 0.25f;
+    public static final float FLING_ANIMATION_THRESHOLD = 0.25f;
 
     private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
@@ -100,6 +102,7 @@
     private AllAppsPagedView mViewPager;
 
     protected FloatingHeaderView mHeader;
+    private float mHeaderTop;
     private WorkModeSwitch mWorkModeSwitch;
 
 
@@ -530,7 +533,7 @@
         return view.getGlobalVisibleRect(new Rect());
     }
 
-    // Used by tests only
+    @VisibleForTesting
     public boolean isPersonalTabVisible() {
         return isDescendantViewVisible(R.id.tab_personal);
     }
@@ -582,6 +585,7 @@
             mAH[i].padding.top = padding;
             mAH[i].applyPadding();
         }
+        mHeaderTop = mHeader.getTop();
     }
 
     public void setLastSearchQuery(String query) {
@@ -636,14 +640,42 @@
             public void onAnimationUpdate(ValueAnimator valueAnimator) {
                 if (shouldSpring
                         && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
-                    absorbSwipeUpVelocity(Math.abs(
-                            Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
+                    absorbSwipeUpVelocity(Math.max(100, Math.abs(
+                            Math.round(velocity * FLING_VELOCITY_MULTIPLIER))));
+                    // calculate the velocity of using the not user controlled interpolator
+                    // of when the container reach the end.
                     shouldSpring = false;
                 }
             }
         });
     }
 
+    public void onPull(float deltaDistance, float displacement) {
+        absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance,
+                PULL_MULTIPLIER * displacement);
+        // ideally, this should be done using EdgeEffect.onPush to create squish effect.
+        // However, until such method is available, launcher to simulate the onPush method.
+        mHeader.setTranslationY(-.5f * mHeaderTop * deltaDistance);
+        getRecyclerViewContainer().setTranslationY(-mHeaderTop * deltaDistance);
+    }
+
+    public void onRelease() {
+        ValueAnimator anim1 = ValueAnimator.ofFloat(1f, 0f);
+        final float floatingHeaderHeight = getFloatingHeaderView().getTranslationY();
+        final float recyclerViewHeight = getRecyclerViewContainer().getTranslationY();
+        anim1.setDuration(200);
+        anim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                getFloatingHeaderView().setTranslationY(
+                        ((float) valueAnimator.getAnimatedValue()) * floatingHeaderHeight);
+                getRecyclerViewContainer().setTranslationY(
+                        ((float) valueAnimator.getAnimatedValue()) * recyclerViewHeight);
+            }
+        });
+        anim1.start();
+        super.onRelease();
+    }
     @Override
     public void getDrawingRect(Rect outRect) {
         super.getDrawingRect(outRect);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 90f2d7c..75f3149 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -17,10 +17,7 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -158,9 +155,8 @@
             return;
         }
 
-        Interpolator interpolator = toState.equals(ALL_APPS)
-                ? (config.userControlled ? ACCEL_2 : ACCEL_0_75) :
-                (config.userControlled ? DEACCEL_2 : DEACCEL);
+        // need to decide depending on the release velocity
+        Interpolator interpolator = (config.userControlled ? LINEAR : DEACCEL_1_7);
 
         Animator anim = createSpringAnimation(mProgress, targetProgress);
         anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index d092f11..a086635 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -56,9 +56,8 @@
 
     protected final AnimatorListener mClearStateOnCancelListener =
             newCancelListener(this::clearState);
+    private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
 
-    private boolean mNoIntercept;
-    private boolean mIsLogContainerSet;
     protected int mStartContainerType;
 
     protected LauncherState mStartState;
@@ -67,12 +66,14 @@
     protected AnimatorPlaybackController mCurrentAnimation;
     protected boolean mGoingBetweenStates = true;
 
+    private boolean mNoIntercept;
+    private boolean mIsLogContainerSet;
     private float mStartProgress;
     // Ratio of transition process [0, 1] to drag displacement (px)
     private float mProgressMultiplier;
     private float mDisplacementShift;
     private boolean mCanBlockFling;
-    private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
+    private boolean mAllAppsOvershootStarted;
 
     public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
         mLauncher = l;
@@ -216,8 +217,15 @@
                     mFlingBlockCheck.blockFling();
                 }
             }
+            if (mToState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+                mAllAppsOvershootStarted = true;
+                // 1f, value when all apps container hit the top
+                mLauncher.getAppsView().onPull(progress - 1f, progress - 1f);
+            }
+
         } else {
             mFlingBlockCheck.onEvent();
+
         }
 
         return true;
@@ -325,8 +333,15 @@
         anim.setFloatValues(startProgress, endProgress);
         updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStart();
-        if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
-            mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+        if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+            if (mAllAppsOvershootStarted) {
+
+                mLauncher.getAppsView().onRelease();
+                mAllAppsOvershootStarted = false;
+
+            } else {
+                mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+            }
         }
         anim.start();
     }
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 9701389..8342d3e 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -32,6 +32,7 @@
  */
 public class SpringRelativeLayout extends RelativeLayout {
 
+    // fixed edge at the time force is applied
     private final EdgeEffect mEdgeGlowTop;
     private final EdgeEffect mEdgeGlowBottom;
 
@@ -87,6 +88,15 @@
         invalidate();
     }
 
+    protected void absorbPullDeltaDistance(float deltaDistance, float displacement) {
+        mEdgeGlowBottom.onPull(deltaDistance, displacement);
+        invalidate();
+    }
+
+    protected void onRelease() {
+        mEdgeGlowBottom.onRelease();
+    }
+
     public EdgeEffectFactory createEdgeEffectFactory() {
         return new ProxyEdgeEffectFactory();
     }