Loading libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt +36 −0 Original line number Diff line number Diff line Loading @@ -306,6 +306,42 @@ class BubbleBarAnimationHelperTest { assertThat(afterCalled).isTrue() } @Test fun animateExpansion_withPendingAnimation() { val bubble = createBubble("key").initialize(container) val bbev = bubble.bubbleBarExpandedView!! val semaphore = Semaphore(0) var afterCalled = false val after = Runnable { afterCalled = true semaphore.release() } var expandedViewWithPendingAnimationBefore: BubbleBarExpandedView? = null var expandedViewWithPendingAnimationAfter: BubbleBarExpandedView? = null activityScenario.onActivity { bbev.onTaskCreated() // Make the TaskView invisible so that the animation waits on TaskView visibility. bbev.bubbleTaskView!!.listener .onTaskVisibilityChanged(0, false /* visible */) animationHelper.animateExpansion(bubble, after) expandedViewWithPendingAnimationBefore = animationHelper.expandedViewWithPendingAnimation animationHelper.cancelAnimations() expandedViewWithPendingAnimationAfter = animationHelper.expandedViewWithPendingAnimation } getInstrumentation().waitForIdleSync() assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() assertThat(afterCalled).isTrue() assertThat(expandedViewWithPendingAnimationBefore).isEqualTo(bbev) assertThat(expandedViewWithPendingAnimationAfter).isNull() } @Test fun onImeTopChanged_noOverlap() { val bubble = createBubble(key = "b1").initialize(container) Loading libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +66 −3 Original line number Diff line number Diff line Loading @@ -159,10 +159,18 @@ class BubbleBarExpandedViewTest { @Test fun animateExpansion_waitsUntilTaskCreated() { var animated = false bubbleExpandedView.animateExpansionWhenTaskViewVisible { animated = true } var endRunnableRun = false bubbleExpandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isFalse() assertThat(endRunnableRun).isFalse() bubbleExpandedView.onTaskCreated() assertThat(animated).isTrue() // The end runnable should not be run unless the animation is canceled by // cancelPendingAnimation. assertThat(endRunnableRun).isFalse() } @Test Loading @@ -189,8 +197,15 @@ class BubbleBarExpandedViewTest { assertThat(taskView.taskView.parent).isEqualTo(expandedView) var animated = false expandedView.animateExpansionWhenTaskViewVisible { animated = true } var endRunnableRun = false expandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isTrue() // The end runnable should not be run unless the animation is canceled by // cancelPendingAnimation. assertThat(endRunnableRun).isFalse() } @Test Loading Loading @@ -219,13 +234,61 @@ class BubbleBarExpandedViewTest { assertThat(taskView.taskView.parent).isEqualTo(expandedView) var animated = false expandedView.animateExpansionWhenTaskViewVisible { animated = true } var endRunnableRun = false expandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isFalse() assertThat(endRunnableRun).isFalse() // send a visible signal to simulate a new surface getting created expandedView.onContentVisibilityChanged(true) assertThat(animated).isTrue() assertThat(endRunnableRun).isFalse() } @Test fun animateExpansion_taskViewAttachedAndInvisibleThenAnimationCanceled() { val inflater = LayoutInflater.from(context) val expandedView = inflater.inflate( R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ ) as BubbleBarExpandedView val taskView = bubbleTaskViewFactory.create() val taskViewParent = FrameLayout(context) taskViewParent.addView(taskView.taskView) taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) assertThat(taskView.isVisible).isTrue() taskView.listener.onTaskVisibilityChanged(666, false) assertThat(taskView.isVisible).isFalse() expandedView.initialize( expandedViewManager, positioner, false /* isOverflow */, bubble, taskView, ) // the task view should be added to the expanded view assertThat(taskView.taskView.parent).isEqualTo(expandedView) var animated = false var endRunnableRun = false expandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isFalse() assertThat(endRunnableRun).isFalse() // Cancel the pending animation. expandedView.cancelPendingAnimation() assertThat(animated).isFalse() assertThat(endRunnableRun).isTrue() } private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView { Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +58 −25 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.view.SurfaceControl; import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.app.animation.Interpolators; import com.android.internal.protolog.ProtoLog; Loading Loading @@ -110,6 +111,7 @@ public class BubbleBarAnimationHelper { // TODO(b/381936992): remove expanded bubble state from this helper class private BubbleViewProvider mExpandedBubble; private BubbleBarExpandedView mExpandedViewWithPendingAnimation; public BubbleBarAnimationHelper(Context context, BubblePositioner positioner) { mPositioner = positioner; Loading Loading @@ -142,8 +144,6 @@ public class BubbleBarAnimationHelper { bbev.setAnimationMatrix(mExpandedViewContainerMatrix); bbev.animateExpansionWhenTaskViewVisible(() -> { bbev.getHandleView().setAlpha(1); ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ true); alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION); alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); Loading @@ -153,6 +153,8 @@ public class BubbleBarAnimationHelper { bbev.setAnimating(false); } }); Runnable animationRunnable = () -> { bbev.getHandleView().setAlpha(1); startNewAnimator(alphaAnim); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); Loading @@ -178,7 +180,8 @@ public class BubbleBarAnimationHelper { } }) .start(); }); }; animateExpansionWhenTaskViewVisible(bbev, animationRunnable, endRunnable); } private void prepareForAnimateIn(BubbleBarExpandedView bbev) { Loading Loading @@ -297,7 +300,6 @@ public class BubbleBarAnimationHelper { toBbev.getHandleView().setAlpha(0f); toBbev.getHandleView().setHandleInitialColor(fromBbev.getHandleView().getHandleColor()); toBbev.animateExpansionWhenTaskViewVisible(() -> { AnimatorSet switchAnim = new AnimatorSet(); switchAnim.playTogether( switchOutAnimator(fromBbev), switchInAnimator(toBbev, startTx, endTx)); Loading @@ -311,8 +313,22 @@ public class BubbleBarAnimationHelper { } } }); startNewAnimator(switchAnim); }); Runnable animationRunnable = () -> startNewAnimator(switchAnim); animateExpansionWhenTaskViewVisible(toBbev, animationRunnable, endRunnable); } private void animateExpansionWhenTaskViewVisible(@NonNull BubbleBarExpandedView bbev, @NonNull Runnable animationRunnable, @Nullable Runnable endRunnable) { // When a new animation is requested, the old pending animation should be canceled, // regardless of whether the new animation is run immediately or not. cancelPendingAnimation(); boolean pending = bbev.animateExpansionWhenTaskViewVisible(() -> { mExpandedViewWithPendingAnimation = null; animationRunnable.run(); }, endRunnable); if (pending) { mExpandedViewWithPendingAnimation = bbev; } } private float getSwitchAnimationInitialTx(float endTx) { Loading Loading @@ -680,6 +696,7 @@ public class BubbleBarAnimationHelper { } mRunningAnimator = null; } cancelPendingAnimation(); } /** Handles IME position changes. */ Loading @@ -692,6 +709,17 @@ public class BubbleBarAnimationHelper { bbev.onImeTopChanged(imeTop); } /** * Cancels the pending animations. This also executes the endRunnable for the animation. * Canceling pending animations prevents out-of-order animation execution. */ private void cancelPendingAnimation() { if (mExpandedViewWithPendingAnimation != null) { mExpandedViewWithPendingAnimation.cancelPendingAnimation(); mExpandedViewWithPendingAnimation = null; } } private @Nullable BubbleBarExpandedView getExpandedView() { BubbleViewProvider bubble = mExpandedBubble; if (bubble != null) { Loading @@ -700,6 +728,11 @@ public class BubbleBarAnimationHelper { return null; } @VisibleForTesting BubbleBarExpandedView getExpandedViewWithPendingAnimation() { return mExpandedViewWithPendingAnimation; } private void updateExpandedView(BubbleBarExpandedView bbev) { if (bbev == null) { Log.w(TAG, "Trying to update the expanded view without a bubble"); Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +44 −1 Original line number Diff line number Diff line Loading @@ -148,6 +148,14 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Nullable private Runnable mAnimateExpansion = null; /** * A runnable to be executed if the expansion animation is canceled before the task view is made * visible. If the animation is run, the end runnable is executed with the animation, so only * need to run the end runnable if the animation is dropped before it is ever started. */ @Nullable private Runnable mAnimateExpansionEndRunnable = null; /** * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha Loading Loading @@ -411,11 +419,37 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mListener.onBackPressed(); } void animateExpansionWhenTaskViewVisible(Runnable animateExpansion) { /** * Returns {@code true} if the animation is scheduled instead of being run immediately. * * @param animateExpansion the {@link Runnable} to be run when the TaskView is visible. * @param endRunnable the {@link Runnable} to be run only when the animation is canceled by * {@link #cancelPendingAnimation()}. */ boolean animateExpansionWhenTaskViewVisible( @NonNull Runnable animateExpansion, @Nullable Runnable endRunnable) { if ((mBubbleTaskView != null && mBubbleTaskView.isVisible()) || mIsOverflow) { animateExpansion.run(); return false; } else { mAnimateExpansion = animateExpansion; mAnimateExpansionEndRunnable = endRunnable; return true; } } /** * Cancels the pending animation that is waiting on TaskView becoming visible. This also * executes the end {@code Runnable}. */ void cancelPendingAnimation() { if (mAnimateExpansion != null) { mAnimateExpansion = null; // The end runnable must be executed here, because it is not invoked for non-running // animators. if (mAnimateExpansionEndRunnable != null) { mAnimateExpansionEndRunnable.run(); } } } Loading @@ -424,6 +458,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView if (mAnimateExpansion != null) { mAnimateExpansion.run(); mAnimateExpansion = null; // No need to execute the end runnable if the animation is played. It will be run in the // animation end callback. mAnimateExpansionEndRunnable = null; } } Loading Loading @@ -557,6 +594,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView return mTaskView != null && mTaskView.isZOrderedOnTop(); } @VisibleForTesting @Nullable BubbleTaskView getBubbleTaskView() { return mBubbleTaskView; } /** * Sets whether the view is animating, in this case we won't change the content visibility * until the animation is done. Loading Loading
libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt +36 −0 Original line number Diff line number Diff line Loading @@ -306,6 +306,42 @@ class BubbleBarAnimationHelperTest { assertThat(afterCalled).isTrue() } @Test fun animateExpansion_withPendingAnimation() { val bubble = createBubble("key").initialize(container) val bbev = bubble.bubbleBarExpandedView!! val semaphore = Semaphore(0) var afterCalled = false val after = Runnable { afterCalled = true semaphore.release() } var expandedViewWithPendingAnimationBefore: BubbleBarExpandedView? = null var expandedViewWithPendingAnimationAfter: BubbleBarExpandedView? = null activityScenario.onActivity { bbev.onTaskCreated() // Make the TaskView invisible so that the animation waits on TaskView visibility. bbev.bubbleTaskView!!.listener .onTaskVisibilityChanged(0, false /* visible */) animationHelper.animateExpansion(bubble, after) expandedViewWithPendingAnimationBefore = animationHelper.expandedViewWithPendingAnimation animationHelper.cancelAnimations() expandedViewWithPendingAnimationAfter = animationHelper.expandedViewWithPendingAnimation } getInstrumentation().waitForIdleSync() assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() assertThat(afterCalled).isTrue() assertThat(expandedViewWithPendingAnimationBefore).isEqualTo(bbev) assertThat(expandedViewWithPendingAnimationAfter).isNull() } @Test fun onImeTopChanged_noOverlap() { val bubble = createBubble(key = "b1").initialize(container) Loading
libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +66 −3 Original line number Diff line number Diff line Loading @@ -159,10 +159,18 @@ class BubbleBarExpandedViewTest { @Test fun animateExpansion_waitsUntilTaskCreated() { var animated = false bubbleExpandedView.animateExpansionWhenTaskViewVisible { animated = true } var endRunnableRun = false bubbleExpandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isFalse() assertThat(endRunnableRun).isFalse() bubbleExpandedView.onTaskCreated() assertThat(animated).isTrue() // The end runnable should not be run unless the animation is canceled by // cancelPendingAnimation. assertThat(endRunnableRun).isFalse() } @Test Loading @@ -189,8 +197,15 @@ class BubbleBarExpandedViewTest { assertThat(taskView.taskView.parent).isEqualTo(expandedView) var animated = false expandedView.animateExpansionWhenTaskViewVisible { animated = true } var endRunnableRun = false expandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isTrue() // The end runnable should not be run unless the animation is canceled by // cancelPendingAnimation. assertThat(endRunnableRun).isFalse() } @Test Loading Loading @@ -219,13 +234,61 @@ class BubbleBarExpandedViewTest { assertThat(taskView.taskView.parent).isEqualTo(expandedView) var animated = false expandedView.animateExpansionWhenTaskViewVisible { animated = true } var endRunnableRun = false expandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isFalse() assertThat(endRunnableRun).isFalse() // send a visible signal to simulate a new surface getting created expandedView.onContentVisibilityChanged(true) assertThat(animated).isTrue() assertThat(endRunnableRun).isFalse() } @Test fun animateExpansion_taskViewAttachedAndInvisibleThenAnimationCanceled() { val inflater = LayoutInflater.from(context) val expandedView = inflater.inflate( R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ ) as BubbleBarExpandedView val taskView = bubbleTaskViewFactory.create() val taskViewParent = FrameLayout(context) taskViewParent.addView(taskView.taskView) taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) assertThat(taskView.isVisible).isTrue() taskView.listener.onTaskVisibilityChanged(666, false) assertThat(taskView.isVisible).isFalse() expandedView.initialize( expandedViewManager, positioner, false /* isOverflow */, bubble, taskView, ) // the task view should be added to the expanded view assertThat(taskView.taskView.parent).isEqualTo(expandedView) var animated = false var endRunnableRun = false expandedView.animateExpansionWhenTaskViewVisible( { animated = true }, { endRunnableRun = true } ) assertThat(animated).isFalse() assertThat(endRunnableRun).isFalse() // Cancel the pending animation. expandedView.cancelPendingAnimation() assertThat(animated).isFalse() assertThat(endRunnableRun).isTrue() } private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +58 −25 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.view.SurfaceControl; import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.app.animation.Interpolators; import com.android.internal.protolog.ProtoLog; Loading Loading @@ -110,6 +111,7 @@ public class BubbleBarAnimationHelper { // TODO(b/381936992): remove expanded bubble state from this helper class private BubbleViewProvider mExpandedBubble; private BubbleBarExpandedView mExpandedViewWithPendingAnimation; public BubbleBarAnimationHelper(Context context, BubblePositioner positioner) { mPositioner = positioner; Loading Loading @@ -142,8 +144,6 @@ public class BubbleBarAnimationHelper { bbev.setAnimationMatrix(mExpandedViewContainerMatrix); bbev.animateExpansionWhenTaskViewVisible(() -> { bbev.getHandleView().setAlpha(1); ObjectAnimator alphaAnim = createAlphaAnimator(bbev, /* visible= */ true); alphaAnim.setDuration(EXPANDED_VIEW_EXPAND_ALPHA_DURATION); alphaAnim.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED); Loading @@ -153,6 +153,8 @@ public class BubbleBarAnimationHelper { bbev.setAnimating(false); } }); Runnable animationRunnable = () -> { bbev.getHandleView().setAlpha(1); startNewAnimator(alphaAnim); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); Loading @@ -178,7 +180,8 @@ public class BubbleBarAnimationHelper { } }) .start(); }); }; animateExpansionWhenTaskViewVisible(bbev, animationRunnable, endRunnable); } private void prepareForAnimateIn(BubbleBarExpandedView bbev) { Loading Loading @@ -297,7 +300,6 @@ public class BubbleBarAnimationHelper { toBbev.getHandleView().setAlpha(0f); toBbev.getHandleView().setHandleInitialColor(fromBbev.getHandleView().getHandleColor()); toBbev.animateExpansionWhenTaskViewVisible(() -> { AnimatorSet switchAnim = new AnimatorSet(); switchAnim.playTogether( switchOutAnimator(fromBbev), switchInAnimator(toBbev, startTx, endTx)); Loading @@ -311,8 +313,22 @@ public class BubbleBarAnimationHelper { } } }); startNewAnimator(switchAnim); }); Runnable animationRunnable = () -> startNewAnimator(switchAnim); animateExpansionWhenTaskViewVisible(toBbev, animationRunnable, endRunnable); } private void animateExpansionWhenTaskViewVisible(@NonNull BubbleBarExpandedView bbev, @NonNull Runnable animationRunnable, @Nullable Runnable endRunnable) { // When a new animation is requested, the old pending animation should be canceled, // regardless of whether the new animation is run immediately or not. cancelPendingAnimation(); boolean pending = bbev.animateExpansionWhenTaskViewVisible(() -> { mExpandedViewWithPendingAnimation = null; animationRunnable.run(); }, endRunnable); if (pending) { mExpandedViewWithPendingAnimation = bbev; } } private float getSwitchAnimationInitialTx(float endTx) { Loading Loading @@ -680,6 +696,7 @@ public class BubbleBarAnimationHelper { } mRunningAnimator = null; } cancelPendingAnimation(); } /** Handles IME position changes. */ Loading @@ -692,6 +709,17 @@ public class BubbleBarAnimationHelper { bbev.onImeTopChanged(imeTop); } /** * Cancels the pending animations. This also executes the endRunnable for the animation. * Canceling pending animations prevents out-of-order animation execution. */ private void cancelPendingAnimation() { if (mExpandedViewWithPendingAnimation != null) { mExpandedViewWithPendingAnimation.cancelPendingAnimation(); mExpandedViewWithPendingAnimation = null; } } private @Nullable BubbleBarExpandedView getExpandedView() { BubbleViewProvider bubble = mExpandedBubble; if (bubble != null) { Loading @@ -700,6 +728,11 @@ public class BubbleBarAnimationHelper { return null; } @VisibleForTesting BubbleBarExpandedView getExpandedViewWithPendingAnimation() { return mExpandedViewWithPendingAnimation; } private void updateExpandedView(BubbleBarExpandedView bbev) { if (bbev == null) { Log.w(TAG, "Trying to update the expanded view without a bubble"); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +44 −1 Original line number Diff line number Diff line Loading @@ -148,6 +148,14 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Nullable private Runnable mAnimateExpansion = null; /** * A runnable to be executed if the expansion animation is canceled before the task view is made * visible. If the animation is run, the end runnable is executed with the animation, so only * need to run the end runnable if the animation is dropped before it is ever started. */ @Nullable private Runnable mAnimateExpansionEndRunnable = null; /** * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha Loading Loading @@ -411,11 +419,37 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mListener.onBackPressed(); } void animateExpansionWhenTaskViewVisible(Runnable animateExpansion) { /** * Returns {@code true} if the animation is scheduled instead of being run immediately. * * @param animateExpansion the {@link Runnable} to be run when the TaskView is visible. * @param endRunnable the {@link Runnable} to be run only when the animation is canceled by * {@link #cancelPendingAnimation()}. */ boolean animateExpansionWhenTaskViewVisible( @NonNull Runnable animateExpansion, @Nullable Runnable endRunnable) { if ((mBubbleTaskView != null && mBubbleTaskView.isVisible()) || mIsOverflow) { animateExpansion.run(); return false; } else { mAnimateExpansion = animateExpansion; mAnimateExpansionEndRunnable = endRunnable; return true; } } /** * Cancels the pending animation that is waiting on TaskView becoming visible. This also * executes the end {@code Runnable}. */ void cancelPendingAnimation() { if (mAnimateExpansion != null) { mAnimateExpansion = null; // The end runnable must be executed here, because it is not invoked for non-running // animators. if (mAnimateExpansionEndRunnable != null) { mAnimateExpansionEndRunnable.run(); } } } Loading @@ -424,6 +458,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView if (mAnimateExpansion != null) { mAnimateExpansion.run(); mAnimateExpansion = null; // No need to execute the end runnable if the animation is played. It will be run in the // animation end callback. mAnimateExpansionEndRunnable = null; } } Loading Loading @@ -557,6 +594,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView return mTaskView != null && mTaskView.isZOrderedOnTop(); } @VisibleForTesting @Nullable BubbleTaskView getBubbleTaskView() { return mBubbleTaskView; } /** * Sets whether the view is animating, in this case we won't change the content visibility * until the animation is done. Loading