diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 9707c964c849d..17ba246910d5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -259,21 +259,36 @@ public class DesktopModeController implements RemoteCallable activeTasks = mDesktopModeTaskRepository.getActiveTasks(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final ArraySet activeTasks = mDesktopModeTaskRepository.getActiveTasks(); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); - ArrayList taskInfos = new ArrayList<>(); + + final List taskInfos = new ArrayList<>(); for (Integer taskId : activeTasks) { RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId); if (taskInfo != null) { taskInfos.add(taskInfo); } } + + if (taskInfos.isEmpty()) { + return wct; + } + + final boolean allActiveTasksAreVisible = taskInfos.stream() + .allMatch(info -> mDesktopModeTaskRepository.isVisibleTask(info.taskId)); + if (allActiveTasksAreVisible) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, + "bringDesktopAppsToFront: active tasks are already in front, skipping."); + return wct; + } + ProtoLog.d(WM_SHELL_DESKTOP_MODE, + "bringDesktopAppsToFront: reordering all active tasks to the front"); final List allTasksInZOrder = mDesktopModeTaskRepository.getFreeformTasksInZOrder(); // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last // in the WCT. taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId))); - final WindowContainerTransaction wct = new WindowContainerTransaction(); for (RunningTaskInfo task : taskInfos) { wct.reorder(task.token, true); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 8ec5251bdc818..600ccc17ecaaf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -102,6 +102,13 @@ class DesktopModeTaskRepository { return activeTasks.contains(taskId) } + /** + * Whether a task is visible. + */ + fun isVisibleTask(taskId: Int): Boolean { + return visibleTasks.contains(taskId) + } + /** * Get a set of the active tasks */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 210f09e22efb8..01584a067cc2a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -50,6 +50,7 @@ import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction.Change; +import android.window.WindowContainerTransaction.HierarchyOp; import androidx.test.filters.SmallTest; @@ -222,25 +223,29 @@ public class DesktopModeControllerTest extends ShellTestCase { // Check that there are hierarchy changes for home task and visible task assertThat(wct.getHierarchyOps()).hasSize(2); // First show home task - WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); + HierarchyOp op1 = wct.getHierarchyOps().get(0); assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder()); // Then visible task on top of it - WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1); + HierarchyOp op2 = wct.getHierarchyOps().get(1); assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder()); } @Test - public void testShowDesktopApps() { + public void testShowDesktopApps_allAppsInvisible_bringsToFront() { // Set up two active tasks on desktop, task2 is on top of task1. RunningTaskInfo freeformTask1 = createFreeformTask(); mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks( + freeformTask1.taskId, false /* visible */); RunningTaskInfo freeformTask2 = createFreeformTask(); mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks( + freeformTask2.taskId, false /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn( freeformTask1); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn( @@ -248,29 +253,68 @@ public class DesktopModeControllerTest extends ShellTestCase { // Run show desktop apps logic mController.showDesktopApps(); - ArgumentCaptor wctCaptor = ArgumentCaptor.forClass( - WindowContainerTransaction.class); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), wctCaptor.capture(), any()); - } else { - verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture()); - } - WindowContainerTransaction wct = wctCaptor.getValue(); + final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); // Check wct has reorder calls assertThat(wct.getHierarchyOps()).hasSize(2); // Task 1 appeared first, must be first reorder to top. - WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); + HierarchyOp op1 = wct.getHierarchyOps().get(0); assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder()); // Task 2 appeared last, must be last reorder to top. - WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1); + HierarchyOp op2 = wct.getHierarchyOps().get(1); assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder()); } + @Test + public void testShowDesktopApps_appsAlreadyVisible_doesNothing() { + final RunningTaskInfo task1 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); + final RunningTaskInfo task2 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); + + mController.showDesktopApps(); + + final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); + // No reordering needed. + assertThat(wct.getHierarchyOps()).isEmpty(); + } + + @Test + public void testShowDesktopApps_someAppsInvisible_reordersAll() { + final RunningTaskInfo task1 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, false /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); + final RunningTaskInfo task2 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); + + mController.showDesktopApps(); + + final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); + // Both tasks should be reordered to top, even if one was already visible. + assertThat(wct.getHierarchyOps()).hasSize(2); + final HierarchyOp op1 = wct.getHierarchyOps().get(0); + assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder()); + final HierarchyOp op2 = wct.getHierarchyOps().get(1); + assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder()); + } + @Test public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() { when(DesktopModeStatus.isActive(any())).thenReturn(false); @@ -355,6 +399,17 @@ public class DesktopModeControllerTest extends ShellTestCase { return arg.getValue(); } + private WindowContainerTransaction getBringAppsToFrontTransaction() { + final ArgumentCaptor arg = ArgumentCaptor.forClass( + WindowContainerTransaction.class); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), arg.capture(), any()); + } else { + verify(mShellTaskOrganizer).applyTransaction(arg.capture()); + } + return arg.getValue(); + } + private void assertThatBoundsCleared(Change change) { assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue(); assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();