Merge "Fix issue with IconAppChipView animated states" into main
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.java b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
index d2b0540..0a261ef 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.java
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
@@ -86,6 +86,7 @@
     private final int mMinIconBackgroundHeight;
     private final int mMaxIconBackgroundCornerRadius;
     private final float mMinIconBackgroundCornerRadius;
+    private AnimatorSet mAnimator;
 
     private int mMaxWidth = Integer.MAX_VALUE;
 
@@ -316,11 +317,13 @@
     }
 
     protected void revealAnim(boolean isRevealing) {
+        cancelInProgressAnimations();
+
         if (isRevealing) {
             boolean isRtl = isLayoutRtl();
             bringToFront();
             ((AnimatedVectorDrawable) mIconArrowView.getDrawable()).start();
-            AnimatorSet anim = new AnimatorSet();
+            mAnimator = new AnimatorSet();
             float backgroundScaleY = mMaxIconBackgroundHeight / (float) mMinIconBackgroundHeight;
             float maxCornerSize = Math.min(mMaxIconBackgroundHeight / 2f,
                     mMaxIconBackgroundCornerRadius);
@@ -341,7 +344,7 @@
                             mIconTextMaxWidth + maxCornerSize);
                 }
             });
-            anim.playTogether(
+            mAnimator.playTogether(
                     expandedTextRevealAnim,
                     ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_Y,
                             backgroundScaleY),
@@ -367,9 +370,9 @@
                     ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 1),
                     ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X,
                             isRtl ? -arrowTranslationX : arrowTranslationX));
-            anim.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
-            anim.setInterpolator(EMPHASIZED);
-            anim.start();
+            mAnimator.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
+            mAnimator.setInterpolator(EMPHASIZED);
+            mAnimator.start();
         } else {
             ((AnimatedVectorDrawable) mIconArrowView.getDrawable()).reverse();
             float maxCornerSize = Math.min(mMaxIconBackgroundHeight / 2f,
@@ -386,8 +389,8 @@
                             mIconTextExpandedView.getHeight() / 2f, 0);
                 }
             });
-            AnimatorSet anim = new AnimatorSet();
-            anim.playTogether(
+            mAnimator = new AnimatorSet();
+            mAnimator.playTogether(
                     expandedTextClipAnim,
                     ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_X, 1),
                     ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_Y, 1),
@@ -403,9 +406,9 @@
                     ObjectAnimator.ofFloat(mIconTextCollapsedView, ALPHA, 1),
                     ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 0),
                     ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X, 0));
-            anim.setDuration(MENU_BACKGROUND_HIDE_DURATION);
-            anim.setInterpolator(EMPHASIZED);
-            anim.start();
+            mAnimator.setDuration(MENU_BACKGROUND_HIDE_DURATION);
+            mAnimator.setInterpolator(EMPHASIZED);
+            mAnimator.start();
         }
     }
 
@@ -425,6 +428,16 @@
         mIconTextExpandedView.setAlpha(0);
         mIconArrowView.setTranslationX(0);
         ((AnimatedVectorDrawable) mIconArrowView.getDrawable()).reset();
+        mAnimator = null;
+    }
+
+    private void cancelInProgressAnimations() {
+        // We null the `AnimatorSet` because it holds references to the `Animators` which aren't
+        // expecting to be mutable and will cause a crash if they are re-used.
+        if (mAnimator != null && mAnimator.isStarted()) {
+            mAnimator.cancel();
+            mAnimator = null;
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index d779276..cf50835 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -24,6 +24,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Outline;
 import android.graphics.Rect;
@@ -69,6 +70,8 @@
     private TextView mTaskName;
     @Nullable
     private AnimatorSet mOpenCloseAnimator;
+    @Nullable
+    private ValueAnimator mRevealAnimator;
     @Nullable private Runnable mOnClosingStartCallback;
     private TaskView mTaskView;
     private TaskIdAttributeContainer mTaskContainer;
@@ -290,13 +293,18 @@
 
     private void animateOpenOrClosed(boolean closing) {
         if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
-            mOpenCloseAnimator.end();
+            mOpenCloseAnimator.cancel();
         }
         mOpenCloseAnimator = new AnimatorSet();
-
-        final Animator revealAnimator = createOpenCloseOutlineProvider()
-                .createRevealAnimator(this, closing);
-        revealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
+        // If we're opening, we just start from the beginning as a new `TaskMenuView` is created
+        // each time we do the open animation so there will never be a partial value here.
+        float revealAnimationStartProgress = 0f;
+        if (closing && mRevealAnimator != null) {
+            revealAnimationStartProgress = 1f - mRevealAnimator.getAnimatedFraction();
+        }
+        mRevealAnimator = createOpenCloseOutlineProvider()
+                .createRevealAnimator(this, closing, revealAnimationStartProgress);
+        mRevealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
                 : Interpolators.DECELERATE);
 
         if (enableOverviewIconMenu()) {
@@ -349,7 +357,7 @@
             mOpenCloseAnimator.playTogether(translationXAnim, menuTranslationXAnim);
         }
 
-        mOpenCloseAnimator.playTogether(revealAnimator,
+        mOpenCloseAnimator.playTogether(mRevealAnimator,
                 ObjectAnimator.ofFloat(
                         mTaskContainer.getThumbnailView(), DIM_ALPHA,
                         closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
@@ -378,6 +386,7 @@
         mIsOpen = false;
         resetOverviewIconMenu();
         mActivity.getDragLayer().removeView(this);
+        mRevealAnimator = null;
     }
 
     private void resetOverviewIconMenu() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 11e721e..b971f67 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1137,9 +1137,8 @@
         DeviceProfile dp = mActivity.getDeviceProfile();
         if (enableOverviewIconMenu() && iconView instanceof IconAppChipView) {
             ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ true);
-            return TaskMenuView.showForTask(menuContainer, () -> {
-                ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false);
-            });
+            return TaskMenuView.showForTask(menuContainer,
+                    () -> ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false));
         } else if (dp.isTablet) {
             int alignedOptionIndex = 0;
             if (getRecentsView().isOnGridBottomRow(menuContainer.getTaskView()) && dp.isLandscape) {