From ee148d0bc0c37c2efd2d3a23b4a50109c4846061 Mon Sep 17 00:00:00 2001 From: Josh Tsuji Date: Fri, 12 Jun 2020 16:20:02 -0400 Subject: [PATCH] Avoid bad animation states. This ignores bubble touches during animations (they are quick, this does not feel restricting) and makes sure to cancel inflight animations if we do start another animation. Fixes: 158722494 Fixes: 158731143 Test: manual, mash those bubbles Change-Id: I4b38e2cd335ded1f2b873ad694f5e369be545714 --- .../systemui/bubbles/BubbleStackView.java | 100 +++++++++++++----- 1 file changed, 71 insertions(+), 29 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 015079ff900a5..e06ae6992165d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -49,6 +49,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; +import android.os.Handler; import android.provider.Settings; import android.util.Log; import android.view.Choreographer; @@ -159,6 +160,12 @@ public class BubbleStackView extends FrameLayout new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY); + /** + * Handler to use for all delayed animations - this way, we can easily cancel them before + * starting a new animation. + */ + private final Handler mDelayedAnimationHandler = new Handler(); + /** * Interface to synchronize {@link View} state and the screen. * @@ -293,6 +300,7 @@ public class BubbleStackView extends FrameLayout private boolean mViewUpdatedRequested = false; private boolean mIsExpansionAnimating = false; + private boolean mIsBubbleSwitchAnimating = false; private boolean mShowingDismiss = false; /** The view to desaturate/darken when magneted to the dismiss target. */ @@ -470,6 +478,13 @@ public class BubbleStackView extends FrameLayout private OnClickListener mBubbleClickListener = new OnClickListener() { @Override public void onClick(View view) { + // Bubble clicks either trigger expansion/collapse or a bubble switch, both of which we + // shouldn't interrupt. These are quick transitions, so it's not worth trying to adjust + // the animations inflight. + if (mIsExpansionAnimating || mIsBubbleSwitchAnimating) { + return; + } + final Bubble clickedBubble = mBubbleData.getBubbleWithView(view); // If the bubble has since left us, ignore the click. @@ -1730,6 +1745,8 @@ public class BubbleStackView extends FrameLayout } private void animateExpansion() { + cancelDelayedExpandCollapseSwitchAnimations(); + mIsExpanded = true; hideStackUserEducation(true /* fromExpansion */); beforeExpandedViewAnimation(); @@ -1778,37 +1795,43 @@ public class BubbleStackView extends FrameLayout mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); } - postDelayed(() -> PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) - .spring(AnimatableScaleMatrix.SCALE_X, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .spring(AnimatableScaleMatrix.SCALE_Y, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .addUpdateListener((target, values) -> { - if (mExpandedBubble.getIconView() == null) { - return; - } - mExpandedViewContainerMatrix.postTranslate( - mExpandedBubble.getIconView().getTranslationX() - - bubbleWillBeAtX, - 0); - mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); - }) - .withEndActions(() -> { - if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); - } - }) - .start(), startDelay); - + mDelayedAnimationHandler.postDelayed(() -> + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .addUpdateListener((target, values) -> { + if (mExpandedBubble.getIconView() == null) { + return; + } + mExpandedViewContainerMatrix.postTranslate( + mExpandedBubble.getIconView().getTranslationX() + - bubbleWillBeAtX, + 0); + mExpandedViewContainer.setAnimationMatrix( + mExpandedViewContainerMatrix); + }) + .withEndActions(() -> { + if (mExpandedBubble != null + && mExpandedBubble.getExpandedView() != null) { + mExpandedBubble.getExpandedView() + .setSurfaceZOrderedOnTop(false); + } + }) + .start(), startDelay); } private void animateCollapse() { + cancelDelayedExpandCollapseSwitchAnimations(); + // Hide the menu if it's visible. showManageMenu(false); mIsExpanded = false; + mIsExpansionAnimating = true; mBubbleContainer.cancelAllAnimations(); @@ -1824,12 +1847,10 @@ public class BubbleStackView extends FrameLayout final long startDelay = (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f); - postDelayed(() -> mExpandedAnimationController.collapseBackToStack( + mDelayedAnimationHandler.postDelayed(() -> mExpandedAnimationController.collapseBackToStack( mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() /* collapseTo */, - () -> { - mBubbleContainer.setActiveController(mStackAnimationController); - }), startDelay); + () -> mBubbleContainer.setActiveController(mStackAnimationController)), startDelay); // We want to visually collapse into this bubble during the animation. final View expandingFromBubble = mExpandedBubble.getIconView(); @@ -1884,6 +1905,13 @@ public class BubbleStackView extends FrameLayout } private void animateSwitchBubbles() { + // If we're no longer expanded, this is meaningless. + if (!mIsExpanded) { + return; + } + + mIsBubbleSwitchAnimating = true; + // The surface contains a screenshot of the animating out bubble, so we just need to animate // it out (and then release the GraphicBuffer). PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); @@ -1909,8 +1937,9 @@ public class BubbleStackView extends FrameLayout 0f, 0f, expandingFromBubbleDestinationX + mBubbleSize / 2f, getExpandedViewY()); mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); - mExpandedViewContainer.postDelayed(() -> { + mDelayedAnimationHandler.postDelayed(() -> { if (!mIsExpanded) { + mIsBubbleSwitchAnimating = false; return; } @@ -1928,11 +1957,24 @@ public class BubbleStackView extends FrameLayout if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); } + + mIsBubbleSwitchAnimating = false; }) .start(); }, 25); } + /** + * Cancels any delayed steps for expand/collapse and bubble switch animations, and resets the is + * animating flags for those animations. + */ + private void cancelDelayedExpandCollapseSwitchAnimations() { + mDelayedAnimationHandler.removeCallbacksAndMessages(null); + + mIsExpansionAnimating = false; + mIsBubbleSwitchAnimating = false; + } + private void notifyExpansionChanged(BubbleViewProvider bubble, boolean expanded) { if (mExpandListener != null && bubble != null) { mExpandListener.onBubbleExpandChanged(expanded, bubble.getKey());