summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml5
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt5
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt5
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt110
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java79
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java21
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,