Loading libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerTest.kt +80 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.bubbles import android.app.ActivityManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.LauncherApps Loading Loading @@ -77,9 +79,11 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.isA import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify Loading @@ -99,6 +103,7 @@ class BubbleControllerTest(flags: FlagsParameterization) { @get:Rule val setFlagsRule = SetFlagsRule(flags) private val bubbleTransitions = mock<BubbleTransitions>() private val context = ApplicationProvider.getApplicationContext<Context>() private val uiEventLoggerFake = UiEventLoggerFake() private val displayImeController = mock<DisplayImeController>() Loading Loading @@ -389,6 +394,78 @@ class BubbleControllerTest(flags: FlagsParameterization) { assertThat(bubbleController.hasStableBubbleForTask(777)).isFalse() } @Test fun expandStackAndSelectBubbleForExistingTransition_reusesExistingBubble() { assumeTrue(BubbleAnythingFlagHelper.enableCreateAnyBubble()) val bubble = createBubble("key", taskId = 123) getInstrumentation().runOnMainSync { bubbleData.notificationEntryUpdated( bubble, true /* suppressFlyout */, true /* showInShade= */, ) } val taskInfo = ActivityManager.RunningTaskInfo().apply { taskId = 123 } getInstrumentation().runOnMainSync { bubbleController.expandStackAndSelectBubbleForExistingTransition( taskInfo, mock(), /* transition */ ) {} } verify(bubbleTransitions).startLaunchNewTaskBubbleForExistingTransition( eq(bubble), /* bubble */ any(), /* expandedViewManager */ any(), /* factory */ any(), /* positioner */ any(), /* stackView */ anyOrNull(), /* layerView */ any(), /* iconFactory */ any(), /* inflateSync */ any(), /* transition */ any(), /* onInflatedCallback */ ) assertThat(bubbleData.selectedBubble).isEqualTo(bubble) assertThat(bubbleController.isStackExpanded).isTrue() } @Test fun expandStackAndSelectBubbleForExistingTransition_newBubble() { assumeTrue(BubbleAnythingFlagHelper.enableCreateAnyBubble()) val taskInfo = ActivityManager.RunningTaskInfo().apply { baseActivity = COMPONENT taskId = 123 } getInstrumentation().runOnMainSync { bubbleController.expandStackAndSelectBubbleForExistingTransition( taskInfo, mock(), /* transition */ ) {} } val bubble = argumentCaptor<Bubble>().let { bubbleCaptor -> verify(bubbleTransitions).startLaunchNewTaskBubbleForExistingTransition( bubbleCaptor.capture(), /* bubble */ any(), /* expandedViewManager */ any(), /* factory */ any(), /* positioner */ any(), /* stackView */ anyOrNull(), /* layerView */ any(), /* iconFactory */ any(), /* inflateSync */ any(), /* transition */ any(), /* onInflatedCallback */ ) bubbleCaptor.lastValue } assertThat(bubble.taskId).isEqualTo(123) assertThat(bubble.key).isEqualTo("key_app_bubble:123") } private fun createBubble(key: String, taskId: Int = 0): Bubble { val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button) val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build() Loading Loading @@ -459,7 +536,7 @@ class BubbleControllerTest(flags: FlagsParameterization) { surfaceSynchronizer, FloatingContentCoordinator(), bubbleDataRepository, mock<BubbleTransitions>(), bubbleTransitions, mock<IStatusBarService>(), windowManager, displayInsetsController, Loading Loading @@ -500,6 +577,8 @@ class BubbleControllerTest(flags: FlagsParameterization) { } companion object { private val COMPONENT = ComponentName("com.example.app", "com.example.app.MainActivity") private fun createFakeInsetsState(imeVisible: Boolean): InsetsState { val insetsState = InsetsState() if (imeVisible) { Loading libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskStackListenerTest.kt +22 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles import android.app.ActivityManager import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.os.Binder import android.os.IBinder Loading Loading @@ -46,6 +47,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions Loading Loading @@ -114,6 +116,26 @@ class BubbleTaskStackListenerTest { verify(bubbleData).setSelectedBubbleAndExpandStack(bubble) } @Test fun onActivityRestartAttempt_inStackAppBubbleMovingToFront_doesNothing() { task.configuration.windowConfiguration.activityType = ACTIVITY_TYPE_STANDARD bubbleController.stub { on { shouldBeAppBubble(task) } doReturn true } bubbleData.stub { on { getBubbleInStackWithTaskId(bubbleTaskId) } doReturn bubble } bubbleTaskStackListener.onActivityRestartAttempt( task, homeTaskVisible = false, clearedTask = false, wasVisible = false, ) verify(bubbleData, never()).setSelectedBubbleAndExpandStack(bubble) } @Test @EnableFlags( FLAG_ENABLE_CREATE_ANY_BUBBLE, Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +8 −2 Original line number Diff line number Diff line Loading @@ -1693,8 +1693,14 @@ public class BubbleController implements ConfigurationChangeListener, Consumer<Transitions.TransitionHandler> onInflatedCallback) { if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return null; // Create a new bubble and show it Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow Bubble b = mBubbleData.getBubbleInStackWithTaskId(taskInfo.taskId); if (b != null) { // Reuse the existing bubble mBubbleData.setSelectedBubbleAndExpandStack(b, BubbleBarLocation.DEFAULT); } else { // Create a new bubble and show it, remove from overflow b = mBubbleData.getOrCreateBubble(taskInfo); } ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition() taskId=%s", taskInfo.taskId); b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt +13 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ package com.android.wm.shell.bubbles import android.app.ActivityManager import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer Loading Loading @@ -64,7 +65,7 @@ class BubbleTaskStackListener( when { isBubbleToFullscreen(task) -> moveCollapsedInStackBubbleToFullscreen(bubble, task) isBubbleToSplit(task) -> return // skip split task restarts else -> selectAndExpandInStackBubble(bubble, task) !isAppBubbleMovingToFront(task) -> selectAndExpandInStackBubble(bubble, task) } } } Loading @@ -75,6 +76,17 @@ class BubbleTaskStackListener( .orElse(false) } /** * Returns whether the given bubble task restart should move the app bubble to front * and be handled in DefaultMixedTransition#animateEnterBubblesFromBubble. * This occurs when a startActivity call resolves to an existing activity, causing the * task to move to front, and the mixed transition will then expand the bubble. */ private fun isAppBubbleMovingToFront(task: ActivityManager.RunningTaskInfo): Boolean { return task.activityType == ACTIVITY_TYPE_STANDARD && bubbleController.shouldBeAppBubble(task) } /** Selects and expands a bubble that is currently in the stack. */ private fun selectAndExpandInStackBubble( bubble: Bubble, Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +7 −9 Original line number Diff line number Diff line Loading @@ -165,12 +165,6 @@ public class BubbleTransitions { } for (IBinder cookie : info.getTriggerTask().launchCookies) { if (mPendingEnterTransitions.containsKey(cookie)) { if (hasBubbleWithTaskId(info.getTriggerTask().taskId)) { // We'll let this transition fall through and let the normal TaskViewTransitions // play it mPendingEnterTransitions.remove(cookie); return false; } return true; } } Loading Loading @@ -245,11 +239,11 @@ public class BubbleTransitions { } /** * Called to initiate axed bubble-to-bubble launch/convert for the given transition. * Initiates axed bubble-to-bubble launch/existing bubble convert for the given transition. * * @return whether a new transition was started for the launch */ public boolean startBubbleToBubbleLaunch(@NonNull IBinder transition, public boolean startBubbleToBubbleLaunchOrExistingBubbleConvert(@NonNull IBinder transition, @NonNull ActivityManager.RunningTaskInfo launchingTask, @NonNull Consumer<TransitionHandler> onInflatedCallback) { TransitionHandler handler = Loading Loading @@ -538,6 +532,9 @@ public class BubbleTransitions { state.mVisible = true; } mTransitionProgress.setInflated(); // Remove any intermediate queued transitions that were started as a result of the // inflation (the task view will be in the right bounds) mTaskViewTransitions.removePendingTransitions(tv.getController()); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { return mTransition; }); Loading Loading @@ -681,7 +678,8 @@ public class BubbleTransitions { } private void playAnimation() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation()"); ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation(): playConvert=%b", mPlayConvertTaskAnimation); final TaskViewTaskController tv = mBubble.getTaskView().getController(); final SurfaceControl.Transaction startT = new SurfaceControl.Transaction(); // Set task position to 0,0 as it will be placed inside the TaskView Loading Loading
libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerTest.kt +80 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.bubbles import android.app.ActivityManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.LauncherApps Loading Loading @@ -77,9 +79,11 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.isA import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify Loading @@ -99,6 +103,7 @@ class BubbleControllerTest(flags: FlagsParameterization) { @get:Rule val setFlagsRule = SetFlagsRule(flags) private val bubbleTransitions = mock<BubbleTransitions>() private val context = ApplicationProvider.getApplicationContext<Context>() private val uiEventLoggerFake = UiEventLoggerFake() private val displayImeController = mock<DisplayImeController>() Loading Loading @@ -389,6 +394,78 @@ class BubbleControllerTest(flags: FlagsParameterization) { assertThat(bubbleController.hasStableBubbleForTask(777)).isFalse() } @Test fun expandStackAndSelectBubbleForExistingTransition_reusesExistingBubble() { assumeTrue(BubbleAnythingFlagHelper.enableCreateAnyBubble()) val bubble = createBubble("key", taskId = 123) getInstrumentation().runOnMainSync { bubbleData.notificationEntryUpdated( bubble, true /* suppressFlyout */, true /* showInShade= */, ) } val taskInfo = ActivityManager.RunningTaskInfo().apply { taskId = 123 } getInstrumentation().runOnMainSync { bubbleController.expandStackAndSelectBubbleForExistingTransition( taskInfo, mock(), /* transition */ ) {} } verify(bubbleTransitions).startLaunchNewTaskBubbleForExistingTransition( eq(bubble), /* bubble */ any(), /* expandedViewManager */ any(), /* factory */ any(), /* positioner */ any(), /* stackView */ anyOrNull(), /* layerView */ any(), /* iconFactory */ any(), /* inflateSync */ any(), /* transition */ any(), /* onInflatedCallback */ ) assertThat(bubbleData.selectedBubble).isEqualTo(bubble) assertThat(bubbleController.isStackExpanded).isTrue() } @Test fun expandStackAndSelectBubbleForExistingTransition_newBubble() { assumeTrue(BubbleAnythingFlagHelper.enableCreateAnyBubble()) val taskInfo = ActivityManager.RunningTaskInfo().apply { baseActivity = COMPONENT taskId = 123 } getInstrumentation().runOnMainSync { bubbleController.expandStackAndSelectBubbleForExistingTransition( taskInfo, mock(), /* transition */ ) {} } val bubble = argumentCaptor<Bubble>().let { bubbleCaptor -> verify(bubbleTransitions).startLaunchNewTaskBubbleForExistingTransition( bubbleCaptor.capture(), /* bubble */ any(), /* expandedViewManager */ any(), /* factory */ any(), /* positioner */ any(), /* stackView */ anyOrNull(), /* layerView */ any(), /* iconFactory */ any(), /* inflateSync */ any(), /* transition */ any(), /* onInflatedCallback */ ) bubbleCaptor.lastValue } assertThat(bubble.taskId).isEqualTo(123) assertThat(bubble.key).isEqualTo("key_app_bubble:123") } private fun createBubble(key: String, taskId: Int = 0): Bubble { val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button) val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build() Loading Loading @@ -459,7 +536,7 @@ class BubbleControllerTest(flags: FlagsParameterization) { surfaceSynchronizer, FloatingContentCoordinator(), bubbleDataRepository, mock<BubbleTransitions>(), bubbleTransitions, mock<IStatusBarService>(), windowManager, displayInsetsController, Loading Loading @@ -500,6 +577,8 @@ class BubbleControllerTest(flags: FlagsParameterization) { } companion object { private val COMPONENT = ComponentName("com.example.app", "com.example.app.MainActivity") private fun createFakeInsetsState(imeVisible: Boolean): InsetsState { val insetsState = InsetsState() if (imeVisible) { Loading
libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskStackListenerTest.kt +22 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles import android.app.ActivityManager import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.os.Binder import android.os.IBinder Loading Loading @@ -46,6 +47,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions Loading Loading @@ -114,6 +116,26 @@ class BubbleTaskStackListenerTest { verify(bubbleData).setSelectedBubbleAndExpandStack(bubble) } @Test fun onActivityRestartAttempt_inStackAppBubbleMovingToFront_doesNothing() { task.configuration.windowConfiguration.activityType = ACTIVITY_TYPE_STANDARD bubbleController.stub { on { shouldBeAppBubble(task) } doReturn true } bubbleData.stub { on { getBubbleInStackWithTaskId(bubbleTaskId) } doReturn bubble } bubbleTaskStackListener.onActivityRestartAttempt( task, homeTaskVisible = false, clearedTask = false, wasVisible = false, ) verify(bubbleData, never()).setSelectedBubbleAndExpandStack(bubble) } @Test @EnableFlags( FLAG_ENABLE_CREATE_ANY_BUBBLE, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +8 −2 Original line number Diff line number Diff line Loading @@ -1693,8 +1693,14 @@ public class BubbleController implements ConfigurationChangeListener, Consumer<Transitions.TransitionHandler> onInflatedCallback) { if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return null; // Create a new bubble and show it Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow Bubble b = mBubbleData.getBubbleInStackWithTaskId(taskInfo.taskId); if (b != null) { // Reuse the existing bubble mBubbleData.setSelectedBubbleAndExpandStack(b, BubbleBarLocation.DEFAULT); } else { // Create a new bubble and show it, remove from overflow b = mBubbleData.getOrCreateBubble(taskInfo); } ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition() taskId=%s", taskInfo.taskId); b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt +13 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ package com.android.wm.shell.bubbles import android.app.ActivityManager import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer Loading Loading @@ -64,7 +65,7 @@ class BubbleTaskStackListener( when { isBubbleToFullscreen(task) -> moveCollapsedInStackBubbleToFullscreen(bubble, task) isBubbleToSplit(task) -> return // skip split task restarts else -> selectAndExpandInStackBubble(bubble, task) !isAppBubbleMovingToFront(task) -> selectAndExpandInStackBubble(bubble, task) } } } Loading @@ -75,6 +76,17 @@ class BubbleTaskStackListener( .orElse(false) } /** * Returns whether the given bubble task restart should move the app bubble to front * and be handled in DefaultMixedTransition#animateEnterBubblesFromBubble. * This occurs when a startActivity call resolves to an existing activity, causing the * task to move to front, and the mixed transition will then expand the bubble. */ private fun isAppBubbleMovingToFront(task: ActivityManager.RunningTaskInfo): Boolean { return task.activityType == ACTIVITY_TYPE_STANDARD && bubbleController.shouldBeAppBubble(task) } /** Selects and expands a bubble that is currently in the stack. */ private fun selectAndExpandInStackBubble( bubble: Bubble, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +7 −9 Original line number Diff line number Diff line Loading @@ -165,12 +165,6 @@ public class BubbleTransitions { } for (IBinder cookie : info.getTriggerTask().launchCookies) { if (mPendingEnterTransitions.containsKey(cookie)) { if (hasBubbleWithTaskId(info.getTriggerTask().taskId)) { // We'll let this transition fall through and let the normal TaskViewTransitions // play it mPendingEnterTransitions.remove(cookie); return false; } return true; } } Loading Loading @@ -245,11 +239,11 @@ public class BubbleTransitions { } /** * Called to initiate axed bubble-to-bubble launch/convert for the given transition. * Initiates axed bubble-to-bubble launch/existing bubble convert for the given transition. * * @return whether a new transition was started for the launch */ public boolean startBubbleToBubbleLaunch(@NonNull IBinder transition, public boolean startBubbleToBubbleLaunchOrExistingBubbleConvert(@NonNull IBinder transition, @NonNull ActivityManager.RunningTaskInfo launchingTask, @NonNull Consumer<TransitionHandler> onInflatedCallback) { TransitionHandler handler = Loading Loading @@ -538,6 +532,9 @@ public class BubbleTransitions { state.mVisible = true; } mTransitionProgress.setInflated(); // Remove any intermediate queued transitions that were started as a result of the // inflation (the task view will be in the right bounds) mTaskViewTransitions.removePendingTransitions(tv.getController()); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { return mTransition; }); Loading Loading @@ -681,7 +678,8 @@ public class BubbleTransitions { } private void playAnimation() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation()"); ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation(): playConvert=%b", mPlayConvertTaskAnimation); final TaskViewTaskController tv = mBubble.getTaskView().getController(); final SurfaceControl.Transaction startT = new SurfaceControl.Transaction(); // Set task position to 0,0 as it will be placed inside the TaskView Loading