Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +6 −4 Original line number Diff line number Diff line Loading @@ -217,8 +217,8 @@ public class BubbleTransitions { ProtoLog.d( WM_SHELL_BUBBLES, "notifyUnfoldTransitionStarting transition=%s", transition); Bubble bubble = (Bubble) mBubbleData.getSelectedBubble(); mTaskViewTransitions.enqueueExternal( bubble.getTaskView().getController(), () -> transition); mTaskViewTransitions.enqueueRunningExternal(bubble.getTaskView().getController(), transition); } } Loading Loading @@ -539,7 +539,7 @@ public class BubbleTransitions { // 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(), () -> mTransition); mTaskViewTransitions.enqueueRunningExternal(tv.getController(), mTransition); } @Override Loading Loading @@ -826,6 +826,7 @@ public class BubbleTransitions { } opts.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); opts.setLaunchBounds(launchBounds); // TODO(b/437451940): start the pending intent or shortcut via WCT if (mBubble.isShortcut()) { final LauncherApps launcherApps = mContext.getSystemService( LauncherApps.class); Loading Loading @@ -1769,7 +1770,8 @@ public class BubbleTransitions { final TaskView tv = mBubble.getTaskView(); mTransition = mTransitions.startTransition(TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR, mWct, this); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> mTransition); mTaskViewTransitions.removePendingTransitions(tv.getController()); mTaskViewTransitions.enqueueRunningExternal(tv.getController(), mTransition); } @Override Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java +1 −1 Original line number Diff line number Diff line Loading @@ -177,7 +177,7 @@ public class BubblesTransitionObserver implements Transitions.TransitionObserver // Notify the task removal, but block all TaskViewTransitions during removal so we can // clear them without triggering final IBinder gate = new Binder(); mTaskViewTransitions.enqueueExternal(controller, () -> gate); mTaskViewTransitions.enqueueRunningExternal(controller, gate); taskOrganizer.applyTransaction(wct); controller.notifyTaskRemovalStarted(taskInfo); Loading libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +19 −0 Original line number Diff line number Diff line Loading @@ -217,6 +217,25 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV startNextTransition(); } /** * Add an already running external transition into the pending queue. * This transition has to be started externally. And it will block any new transitions from * starting in the pending queue. * * The external operation *must* call {@link #onExternalDone(IBinder)} once it has finished. */ public void enqueueRunningExternal(@NonNull TaskViewTaskController taskView, IBinder transition) { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.enqueueRunningExternal(): taskView=%d pending=%d", taskView.hashCode(), mPending.size()); final PendingTransition pending = new PendingTransition( TRANSIT_NONE, null /* wct */, taskView, null /* cookie */); pending.mExternalTransition = () -> transition; pending.mClaimed = transition; mPending.add(pending); } /** * An external transition run in this "queue" is required to call this once it becomes ready. */ Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java +79 −6 Original line number Diff line number Diff line Loading @@ -164,22 +164,32 @@ public class BubbleTransitionsTest extends ShellTestCase { } private ActivityManager.RunningTaskInfo setupBubble() { return setupBubble(mTaskView, mTaskViewTaskController); } private ActivityManager.RunningTaskInfo setupBubble(TaskView taskView, TaskViewTaskController taskViewTaskController) { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); final WindowContainerToken token = new MockToken().token(); taskInfo.token = token; when(mTaskViewTaskController.getTaskInfo()).thenReturn(taskInfo); when(mTaskView.getController()).thenReturn(mTaskViewTaskController); when(mBubble.getTaskView()).thenReturn(mTaskView); when(mTaskView.getTaskInfo()).thenReturn(taskInfo); mRepository.add(mTaskViewTaskController); when(taskViewTaskController.getTaskInfo()).thenReturn(taskInfo); when(taskView.getController()).thenReturn(taskViewTaskController); when(mBubble.getTaskView()).thenReturn(taskView); when(taskView.getTaskInfo()).thenReturn(taskInfo); mRepository.add(taskViewTaskController); return taskInfo; } private ActivityManager.RunningTaskInfo setupAppBubble() { return setupAppBubble(mTaskView, mTaskViewTaskController); } private ActivityManager.RunningTaskInfo setupAppBubble(TaskView taskView, TaskViewTaskController taskViewTaskController) { when(mBubble.isApp()).thenReturn(true); when(mBubble.getIntent()).thenReturn(new Intent()); when(mBubble.getUser()).thenReturn(new UserHandle(0)); return setupBubble(); return setupBubble(taskView, taskViewTaskController); } private TransitionInfo setupFullscreenTaskTransition(ActivityManager.RunningTaskInfo taskInfo, Loading Loading @@ -966,4 +976,67 @@ public class BubbleTransitionsTest extends ShellTestCase { verify(mBubble).setPreparingTransition(null); assertThat(mTaskViewTransitions.hasPending()).isFalse(); } /** * Test a scenario where the TaskViewTransitions queue has a pending TaskView transition. And * a new transition for launching a different bubble comes in during it. Once both transitions * are handled, the TaskViewTransitions pending queue should be empty. */ @Test public void launchNewTaskBubbleForExistingTransition_withExistingTransitionInQueue() { // Set up a bubble and have it queue a transition in the queue that will remain pending TaskView existingTaskView = mock(TaskView.class); TaskViewTaskController existingTvc = mock(TaskViewTaskController.class); setupAppBubble(existingTaskView, existingTvc); final IBinder existingTransition = mock(IBinder.class); when(mTransitions.startTransition(anyInt(), any(), any())).thenReturn(existingTransition); mTaskViewTransitions.setTaskViewVisible(existingTvc, true); // Check that there is a pending transition before we create the new bubble assertThat(mTaskViewTransitions.hasPending()).isTrue(); when(mLayerView.canExpandView(mBubble)).thenReturn(true); final ActivityManager.RunningTaskInfo bubbleTask = setupAppBubble(); final IBinder transition = mock(IBinder.class); final BubbleTransitions.LaunchNewTaskBubbleForExistingTransition bt = (BubbleTransitions.LaunchNewTaskBubbleForExistingTransition) mBubbleTransitions .startLaunchNewTaskBubbleForExistingTransition( mBubble, mExpandedViewManager, mTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mIconFactory, false /* inflateSync */, transition, transitionHandler -> {}); verify(mBubble).setPreparingTransition(bt); // Prepare for startAnimation call final SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build(); final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0); final TransitionInfo.Change chg = new TransitionInfo.Change(bubbleTask.token, taskLeash); chg.setTaskInfo(bubbleTask); chg.setMode(TRANSIT_CHANGE); info.addChange(chg); info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0)); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final Transitions.TransitionFinishCallback finishCb = wct -> {}; // Start playing the new bubble transition bt.startAnimation(transition, info, startT, finishT, finishCb); verify(mBubble, never()).setPreparingTransition(null); bt.onInflated(mBubble); bt.surfaceCreated(); bt.continueExpand(); // The pending queue should still have the transition from the existing bubble assertThat(mTaskViewTransitions.hasPending()).isTrue(); // Now start the existing bubble transition mTaskViewTransitions.startAnimation(existingTransition, new TransitionInfo(TRANSIT_CHANGE, 0), startT, finishT, wct -> { }); // Now the queue should be empty assertThat(mTaskViewTransitions.hasPending()).isFalse(); } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +26 −0 Original line number Diff line number Diff line Loading @@ -446,6 +446,32 @@ public class TaskViewTransitionsTest extends ShellTestCase { assertThat(mTaskViewTransitions.hasPending()).isFalse(); } @Test public void enqueueRunningExternal_clearedFromPendingWithoutStarting() { // Add a normal transition to the queue first. mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true); assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT)).isNotNull(); // Simulate an already running external transition by adding its binder ref. IBinder externalTransition = new Binder(); mTaskViewTransitions.enqueueRunningExternal(mTaskViewTaskController, externalTransition); // Verify that both transitions are in the pending queue. assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT)).isNotNull(); assertThat(mTaskViewTransitions.findPending(externalTransition)).isNotNull(); // Now, clear the external gate transition. mTaskViewTransitions.onExternalDone(externalTransition); // Verify that the external gate is removed, but the original transition is still pending. assertThat(mTaskViewTransitions.findPending(externalTransition)).isNull(); assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT)).isNotNull(); assertThat(mTaskViewTransitions.hasPending()).isTrue(); } private ActivityManager.RunningTaskInfo createMockTaskInfo(int taskId, WindowContainerToken token) { ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +6 −4 Original line number Diff line number Diff line Loading @@ -217,8 +217,8 @@ public class BubbleTransitions { ProtoLog.d( WM_SHELL_BUBBLES, "notifyUnfoldTransitionStarting transition=%s", transition); Bubble bubble = (Bubble) mBubbleData.getSelectedBubble(); mTaskViewTransitions.enqueueExternal( bubble.getTaskView().getController(), () -> transition); mTaskViewTransitions.enqueueRunningExternal(bubble.getTaskView().getController(), transition); } } Loading Loading @@ -539,7 +539,7 @@ public class BubbleTransitions { // 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(), () -> mTransition); mTaskViewTransitions.enqueueRunningExternal(tv.getController(), mTransition); } @Override Loading Loading @@ -826,6 +826,7 @@ public class BubbleTransitions { } opts.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); opts.setLaunchBounds(launchBounds); // TODO(b/437451940): start the pending intent or shortcut via WCT if (mBubble.isShortcut()) { final LauncherApps launcherApps = mContext.getSystemService( LauncherApps.class); Loading Loading @@ -1769,7 +1770,8 @@ public class BubbleTransitions { final TaskView tv = mBubble.getTaskView(); mTransition = mTransitions.startTransition(TRANSIT_BUBBLE_CONVERT_FLOATING_TO_BAR, mWct, this); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> mTransition); mTaskViewTransitions.removePendingTransitions(tv.getController()); mTaskViewTransitions.enqueueRunningExternal(tv.getController(), mTransition); } @Override Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java +1 −1 Original line number Diff line number Diff line Loading @@ -177,7 +177,7 @@ public class BubblesTransitionObserver implements Transitions.TransitionObserver // Notify the task removal, but block all TaskViewTransitions during removal so we can // clear them without triggering final IBinder gate = new Binder(); mTaskViewTransitions.enqueueExternal(controller, () -> gate); mTaskViewTransitions.enqueueRunningExternal(controller, gate); taskOrganizer.applyTransaction(wct); controller.notifyTaskRemovalStarted(taskInfo); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +19 −0 Original line number Diff line number Diff line Loading @@ -217,6 +217,25 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV startNextTransition(); } /** * Add an already running external transition into the pending queue. * This transition has to be started externally. And it will block any new transitions from * starting in the pending queue. * * The external operation *must* call {@link #onExternalDone(IBinder)} once it has finished. */ public void enqueueRunningExternal(@NonNull TaskViewTaskController taskView, IBinder transition) { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "Transitions.enqueueRunningExternal(): taskView=%d pending=%d", taskView.hashCode(), mPending.size()); final PendingTransition pending = new PendingTransition( TRANSIT_NONE, null /* wct */, taskView, null /* cookie */); pending.mExternalTransition = () -> transition; pending.mClaimed = transition; mPending.add(pending); } /** * An external transition run in this "queue" is required to call this once it becomes ready. */ Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java +79 −6 Original line number Diff line number Diff line Loading @@ -164,22 +164,32 @@ public class BubbleTransitionsTest extends ShellTestCase { } private ActivityManager.RunningTaskInfo setupBubble() { return setupBubble(mTaskView, mTaskViewTaskController); } private ActivityManager.RunningTaskInfo setupBubble(TaskView taskView, TaskViewTaskController taskViewTaskController) { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); final WindowContainerToken token = new MockToken().token(); taskInfo.token = token; when(mTaskViewTaskController.getTaskInfo()).thenReturn(taskInfo); when(mTaskView.getController()).thenReturn(mTaskViewTaskController); when(mBubble.getTaskView()).thenReturn(mTaskView); when(mTaskView.getTaskInfo()).thenReturn(taskInfo); mRepository.add(mTaskViewTaskController); when(taskViewTaskController.getTaskInfo()).thenReturn(taskInfo); when(taskView.getController()).thenReturn(taskViewTaskController); when(mBubble.getTaskView()).thenReturn(taskView); when(taskView.getTaskInfo()).thenReturn(taskInfo); mRepository.add(taskViewTaskController); return taskInfo; } private ActivityManager.RunningTaskInfo setupAppBubble() { return setupAppBubble(mTaskView, mTaskViewTaskController); } private ActivityManager.RunningTaskInfo setupAppBubble(TaskView taskView, TaskViewTaskController taskViewTaskController) { when(mBubble.isApp()).thenReturn(true); when(mBubble.getIntent()).thenReturn(new Intent()); when(mBubble.getUser()).thenReturn(new UserHandle(0)); return setupBubble(); return setupBubble(taskView, taskViewTaskController); } private TransitionInfo setupFullscreenTaskTransition(ActivityManager.RunningTaskInfo taskInfo, Loading Loading @@ -966,4 +976,67 @@ public class BubbleTransitionsTest extends ShellTestCase { verify(mBubble).setPreparingTransition(null); assertThat(mTaskViewTransitions.hasPending()).isFalse(); } /** * Test a scenario where the TaskViewTransitions queue has a pending TaskView transition. And * a new transition for launching a different bubble comes in during it. Once both transitions * are handled, the TaskViewTransitions pending queue should be empty. */ @Test public void launchNewTaskBubbleForExistingTransition_withExistingTransitionInQueue() { // Set up a bubble and have it queue a transition in the queue that will remain pending TaskView existingTaskView = mock(TaskView.class); TaskViewTaskController existingTvc = mock(TaskViewTaskController.class); setupAppBubble(existingTaskView, existingTvc); final IBinder existingTransition = mock(IBinder.class); when(mTransitions.startTransition(anyInt(), any(), any())).thenReturn(existingTransition); mTaskViewTransitions.setTaskViewVisible(existingTvc, true); // Check that there is a pending transition before we create the new bubble assertThat(mTaskViewTransitions.hasPending()).isTrue(); when(mLayerView.canExpandView(mBubble)).thenReturn(true); final ActivityManager.RunningTaskInfo bubbleTask = setupAppBubble(); final IBinder transition = mock(IBinder.class); final BubbleTransitions.LaunchNewTaskBubbleForExistingTransition bt = (BubbleTransitions.LaunchNewTaskBubbleForExistingTransition) mBubbleTransitions .startLaunchNewTaskBubbleForExistingTransition( mBubble, mExpandedViewManager, mTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mIconFactory, false /* inflateSync */, transition, transitionHandler -> {}); verify(mBubble).setPreparingTransition(bt); // Prepare for startAnimation call final SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build(); final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0); final TransitionInfo.Change chg = new TransitionInfo.Change(bubbleTask.token, taskLeash); chg.setTaskInfo(bubbleTask); chg.setMode(TRANSIT_CHANGE); info.addChange(chg); info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0)); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final Transitions.TransitionFinishCallback finishCb = wct -> {}; // Start playing the new bubble transition bt.startAnimation(transition, info, startT, finishT, finishCb); verify(mBubble, never()).setPreparingTransition(null); bt.onInflated(mBubble); bt.surfaceCreated(); bt.continueExpand(); // The pending queue should still have the transition from the existing bubble assertThat(mTaskViewTransitions.hasPending()).isTrue(); // Now start the existing bubble transition mTaskViewTransitions.startAnimation(existingTransition, new TransitionInfo(TRANSIT_CHANGE, 0), startT, finishT, wct -> { }); // Now the queue should be empty assertThat(mTaskViewTransitions.hasPending()).isFalse(); } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +26 −0 Original line number Diff line number Diff line Loading @@ -446,6 +446,32 @@ public class TaskViewTransitionsTest extends ShellTestCase { assertThat(mTaskViewTransitions.hasPending()).isFalse(); } @Test public void enqueueRunningExternal_clearedFromPendingWithoutStarting() { // Add a normal transition to the queue first. mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true); assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT)).isNotNull(); // Simulate an already running external transition by adding its binder ref. IBinder externalTransition = new Binder(); mTaskViewTransitions.enqueueRunningExternal(mTaskViewTaskController, externalTransition); // Verify that both transitions are in the pending queue. assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT)).isNotNull(); assertThat(mTaskViewTransitions.findPending(externalTransition)).isNotNull(); // Now, clear the external gate transition. mTaskViewTransitions.onExternalDone(externalTransition); // Verify that the external gate is removed, but the original transition is still pending. assertThat(mTaskViewTransitions.findPending(externalTransition)).isNull(); assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT)).isNotNull(); assertThat(mTaskViewTransitions.hasPending()).isTrue(); } private ActivityManager.RunningTaskInfo createMockTaskInfo(int taskId, WindowContainerToken token) { ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); Loading