Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +4 −0 Original line number Original line Diff line number Diff line Loading @@ -250,6 +250,10 @@ class DesktopModeTaskRepository { return ArraySet(displayData[displayId]?.activeTasks) return ArraySet(displayData[displayId]?.activeTasks) } } /** Returns the minimized tasks for the given [displayId]. */ fun getMinimizedTasks(displayId: Int): ArraySet<Int> = ArraySet(displayData[displayId]?.minimizedTasks) /** /** * Returns whether Desktop Mode is currently showing any tasks, i.e. whether any Desktop Tasks * Returns whether Desktop Mode is currently showing any tasks, i.e. whether any Desktop Tasks * are visible. * are visible. Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +32 −0 Original line number Original line Diff line number Diff line Loading @@ -42,9 +42,12 @@ class DesktopTasksLimiter ( private val shellTaskOrganizer: ShellTaskOrganizer, private val shellTaskOrganizer: ShellTaskOrganizer, ) { ) { private val minimizeTransitionObserver = MinimizeTransitionObserver() private val minimizeTransitionObserver = MinimizeTransitionObserver() @VisibleForTesting val leftoverMinimizedTasksRemover = LeftoverMinimizedTasksRemover() init { init { transitions.registerObserver(minimizeTransitionObserver) transitions.registerObserver(minimizeTransitionObserver) taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover) } } private data class TaskDetails (val displayId: Int, val taskId: Int) private data class TaskDetails (val displayId: Int, val taskId: Int) Loading Loading @@ -113,6 +116,35 @@ class DesktopTasksLimiter ( } } } } @VisibleForTesting inner class LeftoverMinimizedTasksRemover : DesktopModeTaskRepository.ActiveTasksListener { override fun onActiveTasksChanged(displayId: Int) { val wct = WindowContainerTransaction() removeLeftoverMinimizedTasks(displayId, wct) shellTaskOrganizer.applyTransaction(wct) } fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) { if (taskRepository .getActiveNonMinimizedTasksOrderedFrontToBack(displayId).isNotEmpty()) { return } val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId) if (remainingMinimizedTasks.isEmpty()) { return } KtProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: removing leftover minimized tasks: $remainingMinimizedTasks") remainingMinimizedTasks.forEach { taskIdToRemove -> val taskToRemove = shellTaskOrganizer.getRunningTaskInfo(taskIdToRemove) if (taskToRemove != null) { wct.removeTask(taskToRemove.token) } } } } /** /** * Mark a task as minimized, this should only be done after the corresponding transition has * Mark a task as minimized, this should only be done after the corresponding transition has * finished so we don't minimize the task if the transition fails. * finished so we don't minimize the task if the transition fails. Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +41 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TRANSIT_TO_BACK import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.dx.mockito.inline.extended.ExtendedMockito Loading Loading @@ -204,6 +205,46 @@ class DesktopTasksLimiterTest : ShellTestCase() { assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue() assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue() } } @Test fun removeLeftoverMinimizedTasks_activeNonMinimizedTasksStillAround_doesNothing() { desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1) desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2) desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2) val wct = WindowContainerTransaction() desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks( DEFAULT_DISPLAY, wct) assertThat(wct.isEmpty).isTrue() } @Test fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() { val wct = WindowContainerTransaction() desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks( DEFAULT_DISPLAY, wct) assertThat(wct.isEmpty).isTrue() } @Test fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_removesAllMinimizedTasks() { val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task1.taskId) desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId) val wct = WindowContainerTransaction() desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks( DEFAULT_DISPLAY, wct) assertThat(wct.hierarchyOps).hasSize(2) assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK) assertThat(wct.hierarchyOps[0].container).isEqualTo(task1.token.asBinder()) assertThat(wct.hierarchyOps[1].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK) assertThat(wct.hierarchyOps[1].container).isEqualTo(task2.token.asBinder()) } @Test @Test fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() { fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() { val taskLimit = desktopTasksLimiter.getMaxTaskLimit() val taskLimit = desktopTasksLimiter.getMaxTaskLimit() Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +4 −0 Original line number Original line Diff line number Diff line Loading @@ -250,6 +250,10 @@ class DesktopModeTaskRepository { return ArraySet(displayData[displayId]?.activeTasks) return ArraySet(displayData[displayId]?.activeTasks) } } /** Returns the minimized tasks for the given [displayId]. */ fun getMinimizedTasks(displayId: Int): ArraySet<Int> = ArraySet(displayData[displayId]?.minimizedTasks) /** /** * Returns whether Desktop Mode is currently showing any tasks, i.e. whether any Desktop Tasks * Returns whether Desktop Mode is currently showing any tasks, i.e. whether any Desktop Tasks * are visible. * are visible. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +32 −0 Original line number Original line Diff line number Diff line Loading @@ -42,9 +42,12 @@ class DesktopTasksLimiter ( private val shellTaskOrganizer: ShellTaskOrganizer, private val shellTaskOrganizer: ShellTaskOrganizer, ) { ) { private val minimizeTransitionObserver = MinimizeTransitionObserver() private val minimizeTransitionObserver = MinimizeTransitionObserver() @VisibleForTesting val leftoverMinimizedTasksRemover = LeftoverMinimizedTasksRemover() init { init { transitions.registerObserver(minimizeTransitionObserver) transitions.registerObserver(minimizeTransitionObserver) taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover) } } private data class TaskDetails (val displayId: Int, val taskId: Int) private data class TaskDetails (val displayId: Int, val taskId: Int) Loading Loading @@ -113,6 +116,35 @@ class DesktopTasksLimiter ( } } } } @VisibleForTesting inner class LeftoverMinimizedTasksRemover : DesktopModeTaskRepository.ActiveTasksListener { override fun onActiveTasksChanged(displayId: Int) { val wct = WindowContainerTransaction() removeLeftoverMinimizedTasks(displayId, wct) shellTaskOrganizer.applyTransaction(wct) } fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) { if (taskRepository .getActiveNonMinimizedTasksOrderedFrontToBack(displayId).isNotEmpty()) { return } val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId) if (remainingMinimizedTasks.isEmpty()) { return } KtProtoLog.v( ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "DesktopTasksLimiter: removing leftover minimized tasks: $remainingMinimizedTasks") remainingMinimizedTasks.forEach { taskIdToRemove -> val taskToRemove = shellTaskOrganizer.getRunningTaskInfo(taskIdToRemove) if (taskToRemove != null) { wct.removeTask(taskToRemove.token) } } } } /** /** * Mark a task as minimized, this should only be done after the corresponding transition has * Mark a task as minimized, this should only be done after the corresponding transition has * finished so we don't minimize the task if the transition fails. * finished so we don't minimize the task if the transition fails. Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +41 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TRANSIT_TO_BACK import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.dx.mockito.inline.extended.ExtendedMockito Loading Loading @@ -204,6 +205,46 @@ class DesktopTasksLimiterTest : ShellTestCase() { assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue() assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue() } } @Test fun removeLeftoverMinimizedTasks_activeNonMinimizedTasksStillAround_doesNothing() { desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1) desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2) desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2) val wct = WindowContainerTransaction() desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks( DEFAULT_DISPLAY, wct) assertThat(wct.isEmpty).isTrue() } @Test fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() { val wct = WindowContainerTransaction() desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks( DEFAULT_DISPLAY, wct) assertThat(wct.isEmpty).isTrue() } @Test fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_removesAllMinimizedTasks() { val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task1.taskId) desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId) val wct = WindowContainerTransaction() desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks( DEFAULT_DISPLAY, wct) assertThat(wct.hierarchyOps).hasSize(2) assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK) assertThat(wct.hierarchyOps[0].container).isEqualTo(task1.token.asBinder()) assertThat(wct.hierarchyOps[1].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK) assertThat(wct.hierarchyOps[1].container).isEqualTo(task2.token.asBinder()) } @Test @Test fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() { fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() { val taskLimit = desktopTasksLimiter.getMaxTaskLimit() val taskLimit = desktopTasksLimiter.getMaxTaskLimit() Loading