diff options
5 files changed, 156 insertions, 17 deletions
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 99a00b8d33c4..120d68120771 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 @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode +import android.graphics.Rect import android.graphics.Region import android.util.ArrayMap import android.util.ArraySet @@ -55,6 +56,8 @@ class DesktopModeTaskRepository { private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>() // Track corner/caption regions of desktop tasks, used to determine gesture exclusion private val desktopExclusionRegions = SparseArray<Region>() + // Track last bounds of task before toggled to stable bounds + private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>() private var desktopGestureExclusionListener: Consumer<Region>? = null private var desktopGestureExclusionExecutor: Executor? = null @@ -307,6 +310,7 @@ class DesktopModeTaskRepository { taskId ) freeformTasksInZOrder.remove(taskId) + boundsBeforeMaximizeByTaskId.remove(taskId) KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remaining freeform tasks: " + freeformTasksInZOrder.toDumpString() @@ -358,6 +362,20 @@ class DesktopModeTaskRepository { } /** + * Removes and returns the bounds saved before maximizing the given task. + */ + fun removeBoundsBeforeMaximize(taskId: Int): Rect? { + return boundsBeforeMaximizeByTaskId.removeReturnOld(taskId) + } + + /** + * Saves the bounds of the given task before maximizing. + */ + fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) { + boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds)) + } + + /** * Check if display with id [displayId] has desktop tasks stashed */ fun isStashed(displayId: Int): Boolean { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index c369061810d0..e210ea731f7a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -120,7 +120,6 @@ class DesktopTasksController( private var visualIndicator: DesktopModeVisualIndicator? = null private val desktopModeShellCommandHandler: DesktopModeShellCommandHandler = DesktopModeShellCommandHandler(this) - private val mOnAnimationFinishedCallback = Consumer<SurfaceControl.Transaction> { t: SurfaceControl.Transaction -> visualIndicator?.releaseVisualIndicator(t) @@ -570,7 +569,10 @@ class DesktopTasksController( } } - /** Quick-resizes a desktop task, toggling between the stable bounds and the default bounds. */ + /** + * Quick-resizes a desktop task, toggling between the stable bounds and the last saved bounds + * if available or the default bounds otherwise. + */ fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return @@ -578,11 +580,21 @@ class DesktopTasksController( displayLayout.getStableBounds(stableBounds) val destinationBounds = Rect() if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) { - // The desktop task is currently occupying the whole stable bounds, toggle to the - // default bounds. - getDefaultDesktopTaskBounds(displayLayout, destinationBounds) + // The desktop task is currently occupying the whole stable bounds. If the bounds + // before the task was toggled to stable bounds were saved, toggle the task to those + // bounds. Otherwise, toggle to the default bounds. + val taskBoundsBeforeMaximize = + desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId) + if (taskBoundsBeforeMaximize != null) { + destinationBounds.set(taskBoundsBeforeMaximize) + } else { + getDefaultDesktopTaskBounds(displayLayout, destinationBounds) + } } else { - // Toggle to the stable bounds. + // Save current bounds so that task can be restored back to original bounds if necessary + // and toggle to the stable bounds. + val taskBounds = taskInfo.configuration.windowConfiguration.bounds + desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, taskBounds) destinationBounds.set(stableBounds) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt index 9f3a4d9c135d..0c45d52d5320 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode +import android.graphics.Rect import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.Display.INVALID_DISPLAY @@ -406,6 +407,31 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { assertThat(listener.stashedOnSecondaryDisplay).isTrue() } + @Test + fun removeFreeformTask_removesTaskBoundsBeforeMaximize() { + val taskId = 1 + repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200)) + repo.removeFreeformTask(taskId) + assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull() + } + + @Test + fun saveBoundsBeforeMaximize_boundsSavedByTaskId() { + val taskId = 1 + val bounds = Rect(0, 0, 200, 200) + repo.saveBoundsBeforeMaximize(taskId, bounds) + assertThat(repo.removeBoundsBeforeMaximize(taskId)).isEqualTo(bounds) + } + + @Test + fun removeBoundsBeforeMaximize_returnsNullAfterBoundsRemoved() { + val taskId = 1 + val bounds = Rect(0, 0, 200, 200) + repo.saveBoundsBeforeMaximize(taskId, bounds) + repo.removeBoundsBeforeMaximize(taskId) + assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull() + } + class TestListener : DesktopModeTaskRepository.ActiveTasksListener { var activeChangesOnDefaultDisplay = 0 var activeChangesOnSecondaryDisplay = 0 diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 4fbf2bddb7b2..93a967e9bfc9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -95,9 +95,10 @@ import org.mockito.Mockito.anyInt import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.mock import org.mockito.Mockito.verify -import org.mockito.kotlin.times -import org.mockito.Mockito.`when` as whenever +import org.mockito.kotlin.atLeastOnce +import org.mockito.kotlin.capture import org.mockito.quality.Strictness +import org.mockito.Mockito.`when` as whenever /** * Test class for {@link DesktopTasksController} @@ -116,13 +117,14 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var shellCommandHandler: ShellCommandHandler @Mock lateinit var shellController: ShellController @Mock lateinit var displayController: DisplayController + @Mock lateinit var displayLayout: DisplayLayout @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer @Mock lateinit var syncQueue: SyncTransactionQueue @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock lateinit var transitions: Transitions @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler - @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler: + @Mock lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler @Mock lateinit var launchAdjacentController: LaunchAdjacentController @@ -154,6 +156,10 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } + whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } controller = createController() controller.setSplitScreenController(splitScreenController) @@ -179,7 +185,7 @@ class DesktopTasksControllerTest : ShellTestCase() { transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, - mToggleResizeDesktopTaskTransitionHandler, + toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler, desktopModeTaskRepository, desktopModeLoggerTransitionObserver, @@ -929,15 +935,74 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.enterSplit(DEFAULT_DISPLAY, false) verify(splitScreenController).requestEnterSplitSelect( - task2, - any(), - SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT, - task2.configuration.windowConfiguration.bounds + task2, + any(), + SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT, + task2.configuration.windowConfiguration.bounds ) } - private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { - val task = createFreeformTask(displayId) + @Test + fun toggleBounds_togglesToStableBounds() { + val bounds = Rect(0, 0, 100, 100) + val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) + + controller.toggleDesktopTaskSize(task) + // Assert bounds set to stable bounds + val wct = getLatestToggleResizeDesktopTaskWct() + assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds) + .isEqualTo(STABLE_BOUNDS) + } + + @Test + fun toggleBounds_lastBoundsBeforeMaximizeSaved() { + val bounds = Rect(0, 0, 100, 100) + val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) + + controller.toggleDesktopTaskSize(task) + assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)) + .isEqualTo(bounds) + } + + @Test + fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize() { + val boundsBeforeMaximize = Rect(0, 0, 100, 100) + val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) + + // Maximize + controller.toggleDesktopTaskSize(task) + task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) + + // Restore + controller.toggleDesktopTaskSize(task) + + // Assert bounds set to last bounds before maximize + val wct = getLatestToggleResizeDesktopTaskWct() + assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds) + .isEqualTo(boundsBeforeMaximize) + } + + @Test + fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() { + val boundsBeforeMaximize = Rect(0, 0, 100, 100) + val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) + + // Maximize + controller.toggleDesktopTaskSize(task) + task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) + + // Restore + controller.toggleDesktopTaskSize(task) + + // Assert last bounds before maximize removed after use + assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() + } + + private fun setUpFreeformTask( + displayId: Int = DEFAULT_DISPLAY, + bounds: Rect? = null + ): RunningTaskInfo { + val task = createFreeformTask(displayId, bounds) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) desktopModeTaskRepository.addActiveTask(displayId, task.taskId) desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId) @@ -1004,6 +1069,18 @@ class DesktopTasksControllerTest : ShellTestCase() { return arg.value } + private fun getLatestToggleResizeDesktopTaskWct(): WindowContainerTransaction { + val arg: ArgumentCaptor<WindowContainerTransaction> = + ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + if (ENABLE_SHELL_TRANSITIONS) { + verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce()) + .startTransition(capture(arg)) + } else { + verify(shellTaskOrganizer).applyTransaction(capture(arg)) + } + return arg.value + } + private fun getLatestMoveToDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) if (ENABLE_SHELL_TRANSITIONS) { @@ -1042,6 +1119,7 @@ class DesktopTasksControllerTest : ShellTestCase() { companion object { const val SECOND_DISPLAY = 2 + private val STABLE_BOUNDS = Rect(0, 0, 1000, 1000) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt index 2f6f3207137d..52da7fb811d0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt @@ -22,6 +22,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW +import android.graphics.Rect import android.view.Display.DEFAULT_DISPLAY import com.android.wm.shell.MockToken import com.android.wm.shell.TestRunningTaskInfoBuilder @@ -31,13 +32,17 @@ class DesktopTestHelpers { /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */ @JvmStatic @JvmOverloads - fun createFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { + fun createFreeformTask( + displayId: Int = DEFAULT_DISPLAY, + bounds: Rect? = null + ): RunningTaskInfo { return TestRunningTaskInfoBuilder() .setDisplayId(displayId) .setToken(MockToken().token()) .setActivityType(ACTIVITY_TYPE_STANDARD) .setWindowingMode(WINDOWING_MODE_FREEFORM) .setLastActiveTime(100) + .apply { bounds?.let { setBounds(it) }} .build() } |