diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index a654a561e4..30f2073117 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -208,51 +208,83 @@ import java.util.function.Supplier; private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) { AnimatorSet animatorSet = new AnimatorSet(); - if (hasAnyFlag(changedFlags, FLAG_RESUMED)) { + + // Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can + // determine whether goingToUnstashedLauncherStateChanged. + boolean wasGoingToUnstashedLauncherState = goingToUnstashedLauncherState(); + if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) { + boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING); + playStateTransitionAnim(animatorSet, duration, committed); + + if (committed && mLauncherState == LauncherState.QUICK_SWITCH) { + // We're about to be paused, set immediately to ensure seamless handoff. + updateStateForFlag(FLAG_RESUMED, false); + applyState(0 /* duration */); + } + } + boolean goingToUnstashedLauncherStateChanged = wasGoingToUnstashedLauncherState + != goingToUnstashedLauncherState(); + + boolean launcherStateChangedDuringAnimToResumeAlignment = + mIconAlignmentForResumedState.isAnimating() && goingToUnstashedLauncherStateChanged; + if (hasAnyFlag(changedFlags, FLAG_RESUMED) + || launcherStateChangedDuringAnimToResumeAlignment) { boolean isResumed = isResumed(); - ObjectAnimator anim = mIconAlignmentForResumedState - .animateToValue(isResumed && goingToUnstashedLauncherState() - ? 1 : 0) - .setDuration(duration); + float toAlignmentForResumedState = isResumed && goingToUnstashedLauncherState() ? 1 : 0; + // If we're already animating to the value, just leave it be instead of restarting it. + if (!mIconAlignmentForResumedState.isAnimatingToValue(toAlignmentForResumedState)) { + ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState + .animateToValue(toAlignmentForResumedState) + .setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mIsAnimatingToLauncherViaResume = false; - } + resumeAlignAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimatingToLauncherViaResume = false; + } - @Override - public void onAnimationStart(Animator animation) { - mIsAnimatingToLauncherViaResume = isResumed; + @Override + public void onAnimationStart(Animator animation) { + mIsAnimatingToLauncherViaResume = isResumed; - TaskbarStashController stashController = mControllers.taskbarStashController; - stashController.updateStateForFlag(FLAG_IN_APP, !isResumed); - stashController.applyState(duration); - } - }); - animatorSet.play(anim); + TaskbarStashController stashController = + mControllers.taskbarStashController; + stashController.updateStateForFlag(FLAG_IN_APP, !isResumed); + stashController.applyState(duration); + } + }); + animatorSet.play(resumeAlignAnim); + } } - if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)) { - boolean isRecentsAnimationRunning = isRecentsAnimationRunning(); - Animator animator = mIconAlignmentForGestureState - .animateToValue(isRecentsAnimationRunning && goingToUnstashedLauncherState() - ? 1 : 0); - if (isRecentsAnimationRunning) { - animator.setDuration(duration); - } - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mIsAnimatingToLauncherViaGesture = false; - } - @Override - public void onAnimationStart(Animator animation) { - mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning(); + boolean launcherStateChangedDuringAnimToGestureAlignment = + mIconAlignmentForGestureState.isAnimating() && goingToUnstashedLauncherStateChanged; + if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING) + || launcherStateChangedDuringAnimToGestureAlignment) { + boolean isRecentsAnimationRunning = isRecentsAnimationRunning(); + float toAlignmentForGestureState = isRecentsAnimationRunning + && goingToUnstashedLauncherState() ? 1 : 0; + // If we're already animating to the value, just leave it be instead of restarting it. + if (!mIconAlignmentForGestureState.isAnimatingToValue(toAlignmentForGestureState)) { + Animator gestureAlignAnim = mIconAlignmentForGestureState + .animateToValue(toAlignmentForGestureState); + if (isRecentsAnimationRunning) { + gestureAlignAnim.setDuration(duration); } - }); - animatorSet.play(animator); + gestureAlignAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimatingToLauncherViaGesture = false; + } + + @Override + public void onAnimationStart(Animator animation) { + mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning(); + } + }); + animatorSet.play(gestureAlignAnim); + } } if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) { @@ -265,17 +297,6 @@ import java.util.function.Supplier; .setDuration(duration)); } - if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) { - boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING); - playStateTransitionAnim(animatorSet, duration, committed); - - if (committed && mLauncherState == LauncherState.QUICK_SWITCH) { - // We're about to be paused, set immediately to ensure seamless handoff. - updateStateForFlag(FLAG_RESUMED, false); - applyState(0 /* duration */); - } - } - if (start) { animatorSet.start(); } @@ -315,8 +336,11 @@ import java.util.function.Supplier; animatorSet.play(stashAnimator); } - animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment) - .setDuration(duration)); + // If we're already animating to the value, just leave it be instead of restarting it. + if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) { + animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment) + .setDuration(duration)); + } } private boolean isResumed() { diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java index 95c871099b..6a7d066cd8 100644 --- a/quickstep/src/com/android/quickstep/AnimatedFloat.java +++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java @@ -42,6 +42,8 @@ public class AnimatedFloat { private final Runnable mUpdateCallback; private ObjectAnimator mValueAnimator; + // Only non-null when an animation is playing to this value. + private Float mEndValue; public float value; @@ -67,10 +69,18 @@ public class AnimatedFloat { cancelAnimation(); mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end); mValueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + if (mValueAnimator == animator) { + mEndValue = end; + } + } + @Override public void onAnimationEnd(Animator animator) { if (mValueAnimator == animator) { mValueAnimator = null; + mEndValue = null; } } }); @@ -103,4 +113,15 @@ public class AnimatedFloat { public ObjectAnimator getCurrentAnimation() { return mValueAnimator; } + + public boolean isAnimating() { + return mValueAnimator != null; + } + + /** + * Returns whether we are currently animating, and the animation's end value matches the given. + */ + public boolean isAnimatingToValue(float endValue) { + return isAnimating() && mEndValue != null && mEndValue == endValue; + } }