Fix bugs with quickstep springs.

* Listeners weren't getting called properly. We add one listener to the
SpringbjectAnimator and then use that to dispatch to the other listeners.

* We fast finish on both double swipe cases to prevent the shelf from
  ending in an invalid state. This causes a visual jump but this can be
  addressed in follow up CL.

Bug: 111698021
Change-Id: Ifeb55da9dd253d062122a8e1577f94044f688641
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index ea0e552..cd56bd2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -82,7 +83,7 @@
         if (mCurrentAnimation != null) {
             if (mFinishFastOnSecondTouch) {
                 // TODO: Animate to finish instead.
-                mCurrentAnimation.getAnimationPlayer().end();
+                mCurrentAnimation.skipToEnd();
             }
 
             AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
@@ -241,7 +242,10 @@
     private void handleFirstSwipeToOverview(final ValueAnimator animator,
             final long expectedDuration, final LauncherState targetState, final float velocity,
             final boolean isFling) {
-        if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
+        if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+                && targetState == OVERVIEW) {
+            mFinishFastOnSecondTouch = true;
+        } else  if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
             mFinishFastOnSecondTouch = true;
             if (isFling && expectedDuration != 0) {
                 // Update all apps interpolator to add a bit of overshoot starting from currFraction
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 62f59e4..cf070c5 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -80,6 +80,9 @@
 
     private OnAnimationEndDispatcher mEndListener;
     private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
+    // We need this variable to ensure the end listener is called immediately, otherwise we run into
+    // issues where the callback interferes with the states of the swipe detector.
+    private boolean mSkipToEnd = false;
 
     protected AnimatorPlaybackController(AnimatorSet anim, long duration,
             Runnable onCancelRunnable) {
@@ -232,7 +235,11 @@
     }
 
     private void dispatchOnStartRecursively(Animator animator) {
-        for (AnimatorListener l : nonNullList(animator.getListeners())) {
+        List<AnimatorListener> listeners = animator instanceof SpringObjectAnimator
+                ? nonNullList(((SpringObjectAnimator) animator).getSuperListeners())
+                : nonNullList(animator.getListeners());
+
+        for (AnimatorListener l : listeners) {
             l.onAnimationStart(animator);
         }
 
@@ -280,6 +287,17 @@
         return mOnCancelRunnable;
     }
 
+    public void skipToEnd() {
+        mSkipToEnd = true;
+        for (SpringAnimation spring : mSprings) {
+            if (spring.canSkipToEnd()) {
+                spring.skipToEnd();
+            }
+        }
+        mAnimationPlayer.end();
+        mSkipToEnd = false;
+    }
+
     public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
 
         private final ValueAnimator[] mChildAnimations;
@@ -343,19 +361,34 @@
      */
     private class OnAnimationEndDispatcher extends AnimationSuccessListener {
 
+        boolean mAnimatorDone = false;
+        boolean mSpringsDone = false;
+        boolean mDispatched = false;
+
         @Override
         public void onAnimationStart(Animator animation) {
             mCancelled = false;
+            mDispatched = false;
         }
 
         @Override
         public void onAnimationSuccess(Animator animator) {
+            if (mSprings.isEmpty()) {
+                mSpringsDone = mAnimatorDone = true;
+            }
+            if (isAnySpringRunning()) {
+                mAnimatorDone = true;
+            } else {
+                mSpringsDone = true;
+            }
+
             // We wait for the spring (if any) to finish running before completing the end callback.
-            if (mSprings.isEmpty() || !isAnySpringRunning()) {
+            if (!mDispatched && (mSkipToEnd || (mAnimatorDone && mSpringsDone))) {
                 dispatchOnEndRecursively(mAnim);
                 if (mEndAction != null) {
                     mEndAction.run();
                 }
+                mDispatched = true;
             }
         }
 
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index 4ece909..e4aec10 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -73,7 +73,9 @@
         mListeners = new ArrayList<>();
         setFloatValues(values);
 
-        mObjectAnimator.addListener(new AnimatorListenerAdapter() {
+        // We use this listener and track mListeners so that we can sync the animator and spring
+        // listeners.
+        super.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mAnimatorEnded = false;
@@ -94,7 +96,7 @@
                 for (AnimatorListener l : mListeners) {
                     l.onAnimationCancel(animation);
                 }
-                mSpring.animateToFinalPosition(mObject.getProgress());
+                mSpring.cancel();
             }
         });
 
@@ -145,6 +147,10 @@
         mListeners.add(listener);
     }
 
+    public ArrayList<AnimatorListener> getSuperListeners() {
+        return super.getListeners();
+    }
+
     @Override
     public ArrayList<AnimatorListener> getListeners() {
         return mListeners;
@@ -167,8 +173,8 @@
 
     @Override
     public void cancel() {
-        mSpring.animateToFinalPosition(mObject.getProgress());
         mObjectAnimator.cancel();
+        mSpring.cancel();
     }
 
     @Override