diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java index ea0e55239e..cd56bd21c9 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.AnimatorSetBuilder.ANIM_VERTICAL_PROGRE 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 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr if (mCurrentAnimation != null) { if (mFinishFastOnSecondTouch) { // TODO: Animate to finish instead. - mCurrentAnimation.getAnimationPlayer().end(); + mCurrentAnimation.skipToEnd(); } AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); @@ -241,7 +242,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr 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 62f59e4026..cf070c510a 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -80,6 +80,9 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat } private void dispatchOnStartRecursively(Animator animator) { - for (AnimatorListener l : nonNullList(animator.getListeners())) { + List listeners = animator instanceof SpringObjectAnimator + ? nonNullList(((SpringObjectAnimator) animator).getSuperListeners()) + : nonNullList(animator.getListeners()); + + for (AnimatorListener l : listeners) { l.onAnimationStart(animator); } @@ -280,6 +287,17 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat */ 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 4ece909c09..e4aec107d2 100644 --- a/src/com/android/launcher3/anim/SpringObjectAnimator.java +++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java @@ -73,7 +73,9 @@ public class SpringObjectAnimator extends ValueAnim 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 @@ public class SpringObjectAnimator extends ValueAnim for (AnimatorListener l : mListeners) { l.onAnimationCancel(animation); } - mSpring.animateToFinalPosition(mObject.getProgress()); + mSpring.cancel(); } }); @@ -145,6 +147,10 @@ public class SpringObjectAnimator extends ValueAnim mListeners.add(listener); } + public ArrayList getSuperListeners() { + return super.getListeners(); + } + @Override public ArrayList getListeners() { return mListeners; @@ -167,8 +173,8 @@ public class SpringObjectAnimator extends ValueAnim @Override public void cancel() { - mSpring.animateToFinalPosition(mObject.getProgress()); mObjectAnimator.cancel(); + mSpring.cancel(); } @Override