diff options
17 files changed, 311 insertions, 67 deletions
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml index f8f8338e5f04..fd578a959e3b 100644 --- a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -3,6 +3,8 @@ <application android:debuggable="true" android:supportsRtl="true" > <uses-library android:name="android.test.runner" /> + <activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity" + android:exported="true"/> </application> <instrumentation diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml index ffcd7d46fbae..bb111dbeffff 100644 --- a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -1,3 +1,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wm.shell.multivalenttests"> + <application> + <activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity" + android:exported="true"/> + </application> </manifest> + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt index 2b4e5417f188..c62d2a06bad5 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt @@ -35,11 +35,11 @@ import com.android.internal.statusbar.IStatusBarService import com.android.wm.shell.Flags import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.Bubbles.SysuiProxy import com.android.wm.shell.bubbles.properties.ProdBubbleProperties import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.SyncTransactionQueue @@ -268,7 +268,8 @@ class BubbleControllerBubbleBarTest { bubbleDataRepository, mock<IStatusBarService>(), mock<WindowManager>(), - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt index 680d015dfd2f..3043e2bcb0be 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt @@ -36,10 +36,10 @@ import com.android.internal.statusbar.IStatusBarService import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.properties.BubbleProperties import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.SyncTransactionQueue @@ -141,7 +141,8 @@ class BubbleViewInfoTaskTest { bubbleDataRepository, mock<IStatusBarService>(), windowManager, - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt index 3e01256fd67c..957f0ca502a1 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt @@ -17,14 +17,19 @@ package com.android.wm.shell.bubbles.bar import android.animation.AnimatorTestRule +import android.app.Activity import android.app.ActivityManager import android.content.Context import android.graphics.Insets +import android.graphics.Outline import android.graphics.Rect +import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.FrameLayout +import android.widget.FrameLayout.LayoutParams +import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -63,6 +68,7 @@ import org.mockito.kotlin.whenever class BubbleBarAnimationHelperTest { @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this) + private lateinit var activityScenario: ActivityScenario<TestActivity> companion object { const val SCREEN_WIDTH = 2000 @@ -83,6 +89,8 @@ class BubbleBarAnimationHelperTest { fun setUp() { ProtoLog.REQUIRE_PROTOLOGTOOL = false ProtoLog.init() + activityScenario = ActivityScenario.launch(TestActivity::class.java) + activityScenario.onActivity { activity -> container = activity.container } val windowManager = context.getSystemService(WindowManager::class.java) bubblePositioner = BubblePositioner(context, windowManager) bubblePositioner.setShowingInBubbleBar(true) @@ -102,8 +110,6 @@ class BubbleBarAnimationHelperTest { mainExecutor = TestShellExecutor() bgExecutor = TestShellExecutor() - container = FrameLayout(context) - animationHelper = BubbleBarAnimationHelper(context, bubblePositioner) } @@ -121,7 +127,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble, after) animatorTestRule.advanceTimeBy(1000) } @@ -145,7 +151,7 @@ class BubbleBarAnimationHelperTest { .updateHandleColor(/* isRegionDark= */ true, /* animated= */ false) val toBubble = createBubble(key = "to").initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble, /* afterAnimation= */ null) animatorTestRule.advanceTimeBy(1000) } @@ -161,7 +167,7 @@ class BubbleBarAnimationHelperTest { val toBubbleTaskController = mock<TaskViewTaskController>() val toBubble = createBubble("to", toBubbleTaskController).initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble) {} // Start the animation, but don't finish animatorTestRule.advanceTimeBy(100) @@ -183,7 +189,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, overflow, after) animatorTestRule.advanceTimeBy(1000) } @@ -206,7 +212,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(overflow, toBubble, after) animatorTestRule.advanceTimeBy(1000) } @@ -226,7 +232,7 @@ class BubbleBarAnimationHelperTest { val taskController = mock<TaskViewTaskController>() val bubble = createBubble("key", taskController).initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateExpansion(bubble) {} animatorTestRule.advanceTimeBy(1000) } @@ -243,6 +249,80 @@ class BubbleBarAnimationHelperTest { verify(taskController).setWindowBounds(any()) } + @Test + fun animateExpansion() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + assertThat(bbev.alpha).isEqualTo(1) + } + + @Test + fun onImeTopChanged_noOverlap() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + + val bbevBottom = bbev.contentBottomOnScreen + bubblePositioner.insets.top + activityScenario.onActivity { + // notify that the IME top coordinate is greater than the bottom of the expanded view. + // there's no overlap so it should not be clipped. + animationHelper.onImeTopChanged(bbevBottom * 2) + } + val outline = Outline() + bbev.outlineProvider.getOutline(bbev, outline) + assertThat(outline.mRect.bottom).isEqualTo(bbev.height) + } + + @Test + fun onImeTopChanged_overlapsWithExpandedView() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + + activityScenario.onActivity { + // notify that the IME top coordinate is less than the bottom of the expanded view, + // meaning it overlaps with it so we should be clipping the expanded view. + animationHelper.onImeTopChanged(bbev.contentBottomOnScreen - 10) + } + val outline = Outline() + bbev.outlineProvider.getOutline(bbev, outline) + assertThat(outline.mRect.bottom).isEqualTo(bbev.height - 10) + } + private fun createBubble( key: String, taskViewTaskController: TaskViewTaskController = mock<TaskViewTaskController>(), @@ -273,14 +353,24 @@ class BubbleBarAnimationHelperTest { } private fun Bubble.initialize(container: ViewGroup): Bubble { - getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) } + activityScenario.onActivity { container.addView(bubbleBarExpandedView) } // Mark taskView's visible bubbleBarExpandedView!!.onContentVisibilityChanged(true) return this } private fun BubbleOverflow.initialize(container: ViewGroup): BubbleOverflow { - getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) } + activityScenario.onActivity { container.addView(bubbleBarExpandedView) } return this } + + class TestActivity : Activity() { + lateinit var container: FrameLayout + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + container = FrameLayout(applicationContext) + container.layoutParams = LayoutParams(50, 50) + setContentView(container) + } + } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt index 5c86b321b60f..9b1645e9534c 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt @@ -37,7 +37,6 @@ import com.android.internal.statusbar.IStatusBarService import com.android.wm.shell.R import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.BubbleController import com.android.wm.shell.bubbles.BubbleData @@ -54,6 +53,7 @@ import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix import com.android.wm.shell.bubbles.properties.BubbleProperties import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.SyncTransactionQueue @@ -180,7 +180,8 @@ class BubbleBarLayerViewTest { bubbleDataRepository, mock<IStatusBarService>(), windowManager, - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 9aba3aaa3268..348f13a493b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -90,13 +90,15 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.bubbles.shortcut.BubbleShortcutHelper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ImeListener; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; @@ -106,7 +108,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; -import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; @@ -182,7 +183,8 @@ public class BubbleController implements ConfigurationChangeListener, @Nullable private final BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer; private final FloatingContentCoordinator mFloatingContentCoordinator; private final BubbleDataRepository mDataRepository; - private final WindowManagerShellWrapper mWindowManagerShellWrapper; + private final DisplayInsetsController mDisplayInsetsController; + private final DisplayImeController mDisplayImeController; private final UserManager mUserManager; private final LauncherApps mLauncherApps; private final IStatusBarService mBarService; @@ -291,7 +293,8 @@ public class BubbleController implements ConfigurationChangeListener, BubbleDataRepository dataRepository, @Nullable IStatusBarService statusBarService, WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, + DisplayInsetsController displayInsetsController, + DisplayImeController displayImeController, UserManager userManager, LauncherApps launcherApps, BubbleLogger bubbleLogger, @@ -318,7 +321,8 @@ public class BubbleController implements ConfigurationChangeListener, ServiceManager.getService(Context.STATUS_BAR_SERVICE)) : statusBarService; mWindowManager = windowManager; - mWindowManagerShellWrapper = windowManagerShellWrapper; + mDisplayInsetsController = displayInsetsController; + mDisplayImeController = displayImeController; mUserManager = userManager; mFloatingContentCoordinator = floatingContentCoordinator; mDataRepository = dataRepository; @@ -403,11 +407,15 @@ public class BubbleController implements ConfigurationChangeListener, mMainExecutor.execute(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT)); }); - try { - mWindowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener()); - } catch (RemoteException e) { - e.printStackTrace(); - } + BubblesImeListener bubblesImeListener = + new BubblesImeListener(mDisplayController, mContext.getDisplayId()); + // the insets controller is notified whenever the IME visibility changes whether the IME is + // requested by a bubbled task or non-bubbled task. in the latter case, we need to update + // the position of the stack to avoid overlapping with the IME. + mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), + bubblesImeListener); + // the ime controller is notified when the IME is requested only by a bubbled task. + mDisplayImeController.addPositionProcessor(bubblesImeListener); mBubbleData.setCurrentUserId(mCurrentUserId); @@ -2515,16 +2523,57 @@ public class BubbleController implements ConfigurationChangeListener, return contextForUser.getPackageManager(); } - /** PinnedStackListener that dispatches IME visibility updates to the stack. */ - //TODO(b/170442945): Better way to do this / insets listener? - private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener { + /** {@link ImeListener} that dispatches IME visibility updates to the stack. */ + private class BubblesImeListener extends ImeListener implements + DisplayImeController.ImePositionProcessor { + + BubblesImeListener(DisplayController displayController, int displayId) { + super(displayController, displayId); + } + @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mBubblePositioner.setImeVisible(imeVisible, imeHeight); + protected void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + if (getDisplayId() != mContext.getDisplayId()) { + return; + } + // the imeHeight here is actually the ime inset; it only includes the part of the ime + // that overlaps with the Bubbles window. adjust it to include the bottom screen inset, + // so we have the total height of the ime. + int totalImeHeight = imeHeight + mBubblePositioner.getInsets().bottom; + mBubblePositioner.setImeVisible(imeVisible, totalImeHeight); if (mStackView != null) { mStackView.setImeVisible(imeVisible); } } + + @Override + public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean showing, boolean isFloating, SurfaceControl.Transaction t) { + if (mContext.getDisplayId() != displayId) { + return IME_ANIMATION_DEFAULT; + } + + if (showing) { + mBubblePositioner.setImeVisible(true, hiddenTop - shownTop); + } else { + mBubblePositioner.setImeVisible(false, 0); + } + if (mStackView != null) { + mStackView.setImeVisible(showing); + } + + return IME_ANIMATION_DEFAULT; + } + + @Override + public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { + if (mContext.getDisplayId() != displayId) { + return; + } + if (mLayerView != null) { + mLayerView.onImeTopChanged(imeTop); + } + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index de85d9af127d..1a61793eab87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -67,6 +67,11 @@ public class BubblePositioner { private @Surface.Rotation int mRotation = Surface.ROTATION_0; private Insets mInsets; private boolean mImeVisible; + /** + * The height of the IME excluding the bottom inset. If the IME is 100 pixels tall and we have + * 20 pixels bottom inset, the IME height is adjusted to 80 to represent the overlap with the + * Bubbles window. + */ private int mImeHeight; private Rect mPositionRect; private int mDefaultMaxBubbles; @@ -336,10 +341,16 @@ public class BubblePositioner { return mImeVisible; } - /** Sets whether the IME is visible. **/ + /** + * Sets whether the IME is visible and its height. + * + * @param visible whether the IME is visible + * @param height the total height of the IME from the bottom of the physical screen + **/ public void setImeVisible(boolean visible, int height) { mImeVisible = visible; - mImeHeight = height; + // adjust the IME to account for the height as seen by the Bubbles window + mImeHeight = visible ? Math.max(height - getInsets().bottom, 0) : 0; } private int getExpandedViewLargeScreenInsetFurthestEdge(boolean isOverflow) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 3188e5b9c6d2..de6d1f6c8852 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -30,6 +30,8 @@ import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.TASK_VIEW_A import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED; import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE; +import static java.lang.Math.max; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -375,7 +377,6 @@ public class BubbleBarAnimationHelper { return animator; } - /** * Animate the expanded bubble when it is being dragged */ @@ -586,6 +587,18 @@ public class BubbleBarAnimationHelper { } } + /** Handles IME position changes. */ + public void onImeTopChanged(int imeTop) { + BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Bubble bar expanded view was null when IME top changed"); + return; + } + int bbevBottom = bbev.getContentBottomOnScreen(); + int clip = max(bbevBottom - imeTop, 0); + bbev.updateBottomClip(clip); + } + private @Nullable BubbleBarExpandedView getExpandedView() { BubbleViewProvider bubble = mExpandedBubble; if (bubble != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 65c929ab6fb4..e073b02dc630 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -137,6 +137,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private Executor mBackgroundExecutor; private final Rect mSampleRect = new Rect(); private final int[] mLoc = new int[2]; + private final Rect mTempBounds = new Rect(); /** Height of the caption inset at the top of the TaskView */ private int mCaptionHeight; @@ -161,6 +162,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsAnimating; private boolean mIsDragging; + private boolean mIsClipping = false; + private int mBottomClip = 0; + /** An enum value that tracks the visibility state of the task view */ private enum TaskViewVisibilityState { /** The task view is going away, and we're waiting for the surface to be destroyed. */ @@ -203,7 +207,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius); + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight() - mBottomClip, + mCurrentCornerRadius); } }); // Set a touch sink to ensure that clicks on the caption area do not propagate to the parent @@ -661,6 +666,52 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } + /** The y coordinate of the bottom of the expanded view. */ + public int getContentBottomOnScreen() { + if (mOverflowView != null) { + mOverflowView.getBoundsOnScreen(mTempBounds); + } + if (mTaskView != null) { + mTaskView.getBoundsOnScreen(mTempBounds); + } + // return the bottom of the content rect, adjusted for insets so the result is in screen + // coordinate + return mTempBounds.bottom + mPositioner.getInsets().top; + } + + /** Update the amount by which to clip the expanded view at the bottom. */ + public void updateBottomClip(int bottomClip) { + mBottomClip = bottomClip; + onClipUpdate(); + } + + private void onClipUpdate() { + if (mBottomClip == 0) { + if (mIsClipping) { + mIsClipping = false; + if (mTaskView != null) { + mTaskView.setClipBounds(null); + mTaskView.setEnableSurfaceClipping(false); + } + invalidateOutline(); + } + } else { + if (!mIsClipping) { + mIsClipping = true; + if (mTaskView != null) { + mTaskView.setEnableSurfaceClipping(true); + } + } + invalidateOutline(); + if (mTaskView != null) { + Rect clipBounds = new Rect(0, 0, + mTaskView.getWidth(), + mTaskView.getHeight() - mBottomClip); + mTaskView.setClipBounds(clipBounds); + } + } + } + private void recreateRegionSamplingHelper() { if (mRegionSamplingHelper != null) { mRegionSamplingHelper.stopAndDestroy(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 88f34f3043e1..eaa0bd250fc4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -424,6 +424,13 @@ public class BubbleBarLayerView extends FrameLayout } } + /** Handles IME position changes. */ + public void onImeTopChanged(int imeTop) { + if (mIsExpanded) { + mAnimationHelper.onImeTopChanged(imeTop); + } + } + /** * Log the event only if {@link #mExpandedBubble} is a {@link Bubble}. * <p> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 9ebb7f5aa270..eb1e72790a25 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -419,8 +419,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged // already (e.g., when focussing an editText in activity B, while and editText in // activity A is focussed), we will not get a call of #insetsControlChanged, and // therefore have to start the show animation from here - startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */, - statsToken); + if (visible || mImeShowing) { + // only start the animation if we're either already showing or becoming visible. + // otherwise starting another hide animation causes flickers. + startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */, + statsToken); + } // In case of a hide, the statsToken should not been send yet (as the animation // is still ongoing). It will be sent at the end of the animation @@ -723,6 +727,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged * Allows other things to synchronize with the ime position */ public interface ImePositionProcessor { + + /** Default animation flags. */ + int IME_ANIMATION_DEFAULT = 0; + /** * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff * behind the IME shouldn't be visible (for example during split-screen adjustment where @@ -732,6 +740,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged /** @hide */ @IntDef(prefix = {"IME_ANIMATION_"}, value = { + IME_ANIMATION_DEFAULT, IME_ANIMATION_NO_ALPHA, }) @interface ImeAnimationFlags { @@ -758,7 +767,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @ImeAnimationFlags default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, boolean isFloating, SurfaceControl.Transaction t) { - return 0; + return IME_ANIMATION_DEFAULT; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt index a34d7bed497b..8851b6a2107d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt @@ -22,8 +22,8 @@ import android.view.InsetsState import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener abstract class ImeListener( - private val mDisplayController: DisplayController, - private val mDisplayId: Int + private val displayController: DisplayController, + val displayId: Int ) : OnInsetsChangedListener { // The last insets state private val mInsetsState = InsetsState() @@ -36,17 +36,11 @@ abstract class ImeListener( // Get the stable bounds that account for display cutout and system bars to calculate the // relative IME height - val layout = mDisplayController.getDisplayLayout(mDisplayId) - if (layout == null) { - return - } + val layout = displayController.getDisplayLayout(displayId) ?: return layout.getStableBounds(mTmpBounds) - val wasVisible = getImeVisibilityAndHeight(mInsetsState).first - val oldHeight = getImeVisibilityAndHeight(mInsetsState).second - - val isVisible = getImeVisibilityAndHeight(insetsState).first - val newHeight = getImeVisibilityAndHeight(insetsState).second + val (wasVisible, oldHeight) = getImeVisibilityAndHeight(mInsetsState) + val (isVisible, newHeight) = getImeVisibilityAndHeight(insetsState) mInsetsState.set(insetsState, true) if (wasVisible != isVisible || oldHeight != newHeight) { @@ -54,8 +48,7 @@ abstract class ImeListener( } } - private fun getImeVisibilityAndHeight( - insetsState: InsetsState): Pair<Boolean, Int> { + private fun getImeVisibilityAndHeight(insetsState: InsetsState): Pair<Boolean, Int> { val source = insetsState.peekSource(InsetsSource.ID_IME) val frame = if (source != null && source.isVisible) source.frame else null val height = if (frame != null) mTmpBounds.bottom - frame.top else 0 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index d02c6b05e5b6..d8c7f4cbb698 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -47,7 +47,6 @@ import com.android.launcher3.icons.IconProvider; import com.android.window.flags.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; import com.android.wm.shell.apptoweb.AssistContentRequester; @@ -233,7 +232,8 @@ public abstract class WMShellModule { FloatingContentCoordinator floatingContentCoordinator, IStatusBarService statusBarService, WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, + DisplayInsetsController displayInsetsController, + DisplayImeController displayImeController, UserManager userManager, LauncherApps launcherApps, TaskStackListenerImpl taskStackListener, @@ -265,7 +265,8 @@ public abstract class WMShellModule { new BubblePersistentRepository(context)), statusBarService, windowManager, - windowManagerShellWrapper, + displayInsetsController, + displayImeController, userManager, launcherApps, logger, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt index 1f2eaa6757e8..eeb83df48ab5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt @@ -33,10 +33,10 @@ import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.bar.BubbleBarLayerView import com.android.wm.shell.bubbles.properties.BubbleProperties import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.ShellExecutor @@ -123,7 +123,8 @@ class BubbleViewInfoTest : ShellTestCase() { mock<BubbleDataRepository>(), mock<IStatusBarService>(), windowManager, - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java index 4a5ebd057835..aa71b84d7bbc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -25,7 +25,6 @@ import android.view.WindowManager; import com.android.internal.statusbar.IStatusBarService; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; @@ -33,6 +32,8 @@ import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -62,7 +63,8 @@ public class TestableBubbleController extends BubbleController { BubbleDataRepository dataRepository, IStatusBarService statusBarService, WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, + DisplayInsetsController displayInsetsController, + DisplayImeController displayImeController, UserManager userManager, LauncherApps launcherApps, BubbleLogger bubbleLogger, @@ -81,8 +83,8 @@ public class TestableBubbleController extends BubbleController { BubbleProperties bubbleProperties) { super(context, shellInit, shellCommandHandler, shellController, data, Runnable::run, floatingContentCoordinator, dataRepository, statusBarService, windowManager, - windowManagerShellWrapper, userManager, launcherApps, bubbleLogger, - taskStackListener, shellTaskOrganizer, positioner, displayController, + displayInsetsController, displayImeController, userManager, launcherApps, + bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController, oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler, new SyncExecutor(), taskViewTransitions, transitions, syncQueue, wmService, bubbleProperties); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 192d66c44aa0..af12d0119c58 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -167,7 +167,6 @@ import com.android.systemui.util.settings.SystemSettings; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.Flags; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; @@ -184,6 +183,8 @@ import com.android.wm.shell.bubbles.StackEducationView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -325,7 +326,9 @@ public class BubblesTest extends SysuiTestCase { @Mock private LauncherApps mLauncherApps; @Mock - private WindowManagerShellWrapper mWindowManagerShellWrapper; + private DisplayInsetsController mDisplayInsetsController; + @Mock + private DisplayImeController mDisplayImeController; @Mock private BubbleLogger mBubbleLogger; @Mock @@ -503,7 +506,7 @@ public class BubblesTest extends SysuiTestCase { mContext, mock(NotificationManager.class), mock(NotificationSettingsInteractor.class) - ); + ); interruptionDecisionProvider.start(); mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class), @@ -523,7 +526,8 @@ public class BubblesTest extends SysuiTestCase { mDataRepository, mStatusBarService, mWindowManager, - mWindowManagerShellWrapper, + mDisplayInsetsController, + mDisplayImeController, mUserManager, mLauncherApps, mBubbleLogger, @@ -1430,9 +1434,12 @@ public class BubblesTest extends SysuiTestCase { mPositioner, mBubbleController.getStackView(), new BubbleIconFactory(mContext, - mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_size), - mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_badge_size), - mContext.getResources().getColor(com.android.launcher3.icons.R.color.important_conversation), + mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.bubble_size), + mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.bubble_badge_size), + mContext.getResources().getColor( + com.android.launcher3.icons.R.color.important_conversation), mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.importance_ring_stroke_width)), bubble, |