From 05eaeb29aa73ed15e63be1e9acc65052117fb651 Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Tue, 19 Nov 2024 13:33:17 -0500 Subject: [PATCH] Interrupt bubble animation when IME is visible When the IME becomes visible, interrupt the bubble animation if it's running. This ensures that the flyout doesn't show over the IME and is more consistent with the behavior of bubble notification when the IME is visible. Flag: com.android.wm.shell.enable_bubble_bar Fixes: 378404363 Test: atest BubbleBarViewAnimatorTest Test: manual - launch app - send bubble - while bubble is animating launch IME - observe flyout not visible over IME Change-Id: I28390982bb41d8cc9b494395418728ff5fb373b6 --- .../taskbar/bubbles/BubbleBarController.java | 18 +++----- .../bubbles/BubbleBarViewController.java | 7 +++ .../taskbar/bubbles/BubbleControllers.java | 1 - .../animation/BubbleBarViewAnimator.kt | 15 +++++++ .../animation/BubbleBarViewAnimatorTest.kt | 45 +++++++++++++++++++ 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index e0814d3393..987937e976 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -115,7 +115,6 @@ public class BubbleBarController extends IBubblesListener.Stub { private BubbleBarItem mSelectedBubble; private TaskbarSharedState mSharedState; - private ImeVisibilityChecker mImeVisibilityChecker; private BubbleBarViewController mBubbleBarViewController; private BubbleStashController mBubbleStashController; private Optional mBubbleStashedHandleViewController; @@ -126,6 +125,8 @@ public class BubbleBarController extends IBubblesListener.Stub { // Cache last sent top coordinate to avoid sending duplicate updates to shell private int mLastSentBubbleBarTop; + private boolean mIsImeVisible = false; + /** * Similar to {@link BubbleBarUpdate} but rather than {@link BubbleInfo}s it uses * {@link BubbleBarBubble}s so that it can be used to update the views. @@ -192,10 +193,8 @@ public class BubbleBarController extends IBubblesListener.Stub { /** Initializes controllers. */ public void init(BubbleControllers bubbleControllers, BubbleBarLocationListener bubbleBarLocationListener, - ImeVisibilityChecker imeVisibilityChecker, TaskbarSharedState sharedState) { mSharedState = sharedState; - mImeVisibilityChecker = imeVisibilityChecker; mBubbleBarViewController = bubbleControllers.bubbleBarViewController; mBubbleStashController = bubbleControllers.bubbleStashController; mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController; @@ -234,6 +233,10 @@ public class BubbleBarController extends IBubblesListener.Stub { boolean sysuiLocked = (flags & MASK_SYSUI_LOCKED) != 0; mBubbleStashController.setSysuiLocked(sysuiLocked); + mIsImeVisible = (flags & SYSUI_STATE_IME_SHOWING) != 0; + if (mIsImeVisible) { + mBubbleBarViewController.onImeVisible(); + } } // @@ -309,8 +312,7 @@ public class BubbleBarController extends IBubblesListener.Stub { // enabling gesture nav. also suppress animation if the bubble bar is hidden for sysui e.g. // the shade is open, or we're locked. final boolean suppressAnimation = - update.initialState || mBubbleBarViewController.isHiddenForSysui() - || mImeVisibilityChecker.isImeVisible(); + update.initialState || mBubbleBarViewController.isHiddenForSysui() || mIsImeVisible; if (update.initialState && mSharedState.hasSavedBubbles()) { // clear restored state @@ -572,12 +574,6 @@ public class BubbleBarController extends IBubblesListener.Stub { mBubbleBarViewController.addBubble(bubble, isExpanding, suppressAnimation); } - /** Interface for checking whether the IME is visible. */ - public interface ImeVisibilityChecker { - /** Whether the IME is visible. */ - boolean isImeVisible(); - } - /** Listener of {@link BubbleBarLocation} updates. */ public interface BubbleBarLocationListener { diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index d13e0c4d07..203cba1858 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -383,6 +383,13 @@ public class BubbleBarViewController { } } + /** Notifies that the IME became visible. */ + public void onImeVisible() { + if (isAnimatingNewBubble()) { + mBubbleBarViewAnimator.interruptForIme(); + } + } + // // The below animators are exposed to BubbleStashController so it can manage the stashing // animation. diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java index d993685ee2..cb592e6006 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java @@ -89,7 +89,6 @@ public class BubbleControllers { ); bubbleBarController.init(this, bubbleBarLocationListeners, - taskbarControllers.navbarButtonsViewController::isImeVisible, taskbarSharedState); bubbleStashedHandleViewController.ifPresent( controller -> controller.init(/* bubbleControllers = */ this)); diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt index 5184a9f3de..89e42100af 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt @@ -532,6 +532,21 @@ constructor( ) } + /** Interrupts the animation due to the IME becoming visible. */ + fun interruptForIme() { + cancelFlyout() + val hideAnimation = animatingBubble?.hideAnimation ?: return + scheduler.cancel(hideAnimation) + animatingBubble = null + bubbleStashController.getStashedHandlePhysicsAnimator().cancelIfRunning() + bubbleBarView.relativePivotY = 1f + // stash the bubble bar since the IME is now visible + bubbleStashController.onNewBubbleAnimationInterrupted( + /* isStashed= */ true, + bubbleBarView.translationY, + ) + } + fun expandedWhileAnimating() { val animatingBubble = animatingBubble ?: return this.animatingBubble = animatingBubble.copy(expand = true) diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt index 582ea54bf0..fe448f4f82 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt @@ -54,6 +54,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.atLeastOnce +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -1266,6 +1267,50 @@ class BubbleBarViewAnimatorTest { verify(bubbleStashController).stashBubbleBarImmediate() } + @Test + fun interruptForIme() { + setUpBubbleBar() + setUpBubbleStashController() + + val handle = View(context) + val handleAnimator = PhysicsAnimator.getInstance(handle) + whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator) + + val animator = + BubbleBarViewAnimator( + bubbleBarView, + bubbleStashController, + flyoutController, + onExpandedNoOp, + animatorScheduler, + ) + + InstrumentationRegistry.getInstrumentation().runOnMainSync { + animator.animateBubbleInForStashed(bubble, isExpanding = false) + } + + // wait for the animation to start + InstrumentationRegistry.getInstrumentation().runOnMainSync {} + PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(handleAnimator) { true } + + handleAnimator.assertIsRunning() + assertThat(animator.isAnimating).isTrue() + // verify the hide bubble animation is pending + assertThat(animatorScheduler.delayedBlock).isNotNull() + + InstrumentationRegistry.getInstrumentation().runOnMainSync { animator.interruptForIme() } + + // verify that the hide animation was canceled + assertThat(animatorScheduler.delayedBlock).isNull() + assertThat(animator.isAnimating).isFalse() + verify(bubbleStashController).onNewBubbleAnimationInterrupted(eq(true), any()) + + // PhysicsAnimatorTestUtils posts the cancellation to the main thread so we need to wait + // again + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + handleAnimator.assertIsNotRunning() + } + private fun setUpBubbleBar() { bubbleBarView = BubbleBarView(context) InstrumentationRegistry.getInstrumentation().runOnMainSync {