diff options
author | 2025-02-13 21:33:43 -0800 | |
---|---|---|
committer | 2025-02-13 21:33:43 -0800 | |
commit | dc6484132ee0e9314a364d4d07ee9fcf247a7c0e (patch) | |
tree | 8a3c6cd2e845d33b7740a063c528ff67ff05b4fe | |
parent | 54007d84a58bf89cb1fa209777035672372077e3 (diff) | |
parent | c6e124f46ab9b3a8958c12a0364cac61dc556aed (diff) |
Merge changes I25eb4365,I2b51e3ab into main
* changes:
[15/N] Desks: Deactivate desk when last window is minimized
[14/N] Desks: Deactivate desk when last window is closed
6 files changed, 199 insertions, 41 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index 4dc82b66b916..eba1be517147 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -821,7 +821,7 @@ class DesktopRepository( /** Removes the given task from the given desk. */ fun removeTaskFromDesk(deskId: Int, taskId: Int) { - logD("removeTaskFromDesk: deskId=%d, taskId=%d", taskId, deskId) + logD("removeTaskFromDesk: deskId=%d, taskId=%d", deskId, taskId) // TODO: b/362720497 - consider not clearing bounds on any removal, such as when moving // it between desks. It might be better to allow restoring to the previous bounds as long // as they're valid (probably valid if in the same display). 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 5eb86506d876..0afceac8a861 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 @@ -153,6 +153,16 @@ import java.util.concurrent.TimeUnit import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * A callback to be invoked when a transition is started via |Transitions.startTransition| with the + * transition binder token that it produces. + * + * Useful when multiple components are appending WCT operations to a single transition that is + * started outside of their control, and each of them wants to track the transition lifecycle + * independently by cross-referencing the transition token with future ready-transitions. + */ +typealias RunOnTransitStart = (IBinder) -> Unit + /** Handles moving tasks in and out of desktop */ class DesktopTasksController( private val context: Context, @@ -481,7 +491,7 @@ class DesktopTasksController( ): Boolean { val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId) if (runningTask != null) { - moveRunningTaskToDesk( + return moveRunningTaskToDesk( task = runningTask, deskId = deskId, wct = wct, @@ -563,10 +573,10 @@ class DesktopTasksController( transitionSource: DesktopModeTransitionSource, remoteTransition: RemoteTransition? = null, callback: IMoveToDesktopCallback? = null, - ) { + ): Boolean { if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)) { logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId) - return + return false } val displayId = taskRepository.getDisplayForDesk(deskId) logV( @@ -621,6 +631,7 @@ class DesktopTasksController( } else { taskRepository.setActiveDesk(displayId = displayId, deskId = deskId) } + return true } /** @@ -789,24 +800,44 @@ class DesktopTasksController( wct: WindowContainerTransaction, displayId: Int, taskInfo: RunningTaskInfo, - ): ((IBinder) -> Unit)? { + ): ((IBinder) -> Unit) { val taskId = taskInfo.taskId + val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId) snapEventHandler.removeTaskIfTiled(displayId, taskId) - // TODO: b/394268248 - desk needs to be deactivated when closing the last task and going - // home. - performDesktopExitCleanupIfNeeded(taskId, displayId, wct, forceToFullscreen = false) + val shouldExitDesktop = + willExitDesktop( + triggerTaskId = taskInfo.taskId, + displayId = displayId, + forceToFullscreen = false, + ) + taskRepository.setPipShouldKeepDesktopActive(displayId, keepActive = true) + val desktopExitRunnable = + performDesktopExitCleanUp( + wct = wct, + deskId = deskId, + displayId = displayId, + willExitDesktop = shouldExitDesktop, + shouldEndUpAtHome = true, + ) + taskRepository.addClosingTask(displayId, taskId) taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate( doesAnyTaskRequireTaskbarRounding(displayId, taskId) ) - return desktopImmersiveController - .exitImmersiveIfApplicable( - wct = wct, - taskInfo = taskInfo, - reason = DesktopImmersiveController.ExitReason.CLOSED, - ) - .asExit() - ?.runOnTransitionStart + + val immersiveRunnable = + desktopImmersiveController + .exitImmersiveIfApplicable( + wct = wct, + taskInfo = taskInfo, + reason = DesktopImmersiveController.ExitReason.CLOSED, + ) + .asExit() + ?.runOnTransitionStart + return { transitionToken -> + immersiveRunnable?.invoke(transitionToken) + desktopExitRunnable?.invoke(transitionToken) + } } fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) { @@ -840,12 +871,20 @@ class DesktopTasksController( private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) { val taskId = taskInfo.taskId + val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId) val displayId = taskInfo.displayId val wct = WindowContainerTransaction() + snapEventHandler.removeTaskIfTiled(displayId, taskId) - // TODO: b/394268248 - desk needs to be deactivated when minimizing the last task and going - // home. - performDesktopExitCleanupIfNeeded(taskId, displayId, wct, forceToFullscreen = false) + taskRepository.setPipShouldKeepDesktopActive(displayId, keepActive = true) + val willExitDesktop = willExitDesktop(taskId, displayId, forceToFullscreen = false) + val desktopExitRunnable = + performDesktopExitCleanUp( + wct = wct, + deskId = deskId, + displayId = displayId, + willExitDesktop = willExitDesktop, + ) // Notify immersive handler as it might need to exit immersive state. val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( @@ -867,6 +906,7 @@ class DesktopTasksController( ) } exitResult.asExit()?.runOnTransitionStart?.invoke(transition) + desktopExitRunnable?.invoke(transition) } /** Move a task with given `taskId` to fullscreen */ @@ -915,7 +955,7 @@ class DesktopTasksController( logV("moveToFullscreenWithAnimation taskId=%d", task.taskId) val wct = WindowContainerTransaction() val willExitDesktop = willExitDesktop(task.taskId, task.displayId, forceToFullscreen = true) - val deactivatingDeskId = addMoveToFullscreenChanges(wct, task, willExitDesktop) + val deactivationRunnable = addMoveToFullscreenChanges(wct, task, willExitDesktop) // We are moving a freeform task to fullscreen, put the home task under the fullscreen task. if (!forceEnterDesktop(task.displayId)) { @@ -930,11 +970,7 @@ class DesktopTasksController( position, mOnAnimationFinishedCallback, ) - if (deactivatingDeskId != null) { - desksTransitionObserver.addPendingTransition( - DeskTransition.DeactivateDesk(token = transition, deskId = deactivatingDeskId) - ) - } + deactivationRunnable?.invoke(transition) // handles case where we are moving to full screen without closing all DW tasks. if (!taskRepository.isOnlyVisibleNonClosingTask(task.taskId)) { @@ -1770,19 +1806,30 @@ class DesktopTasksController( wct: WindowContainerTransaction, forceToFullscreen: Boolean, shouldEndUpAtHome: Boolean = true, - ) { + ): RunOnTransitStart? { taskRepository.setPipShouldKeepDesktopActive(displayId, keepActive = !forceToFullscreen) if (!willExitDesktop(taskId, displayId, forceToFullscreen)) { - return + return null } - performDesktopExitCleanUp(wct, displayId, shouldEndUpAtHome) + // TODO: b/394268248 - update remaining callers to pass in a |deskId| and apply the + // |RunOnTransitStart| when the transition is started. + return performDesktopExitCleanUp( + wct = wct, + deskId = null, + displayId = displayId, + willExitDesktop = true, + shouldEndUpAtHome = shouldEndUpAtHome, + ) } private fun performDesktopExitCleanUp( wct: WindowContainerTransaction, + deskId: Int?, displayId: Int, + willExitDesktop: Boolean, shouldEndUpAtHome: Boolean = true, - ) { + ): RunOnTransitStart? { + if (!willExitDesktop) return null desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted( FULLSCREEN_ANIMATION_DURATION ) @@ -1792,6 +1839,7 @@ class DesktopTasksController( // intent. addLaunchHomePendingIntent(wct, displayId) } + return prepareDeskDeactivationIfNeeded(wct, deskId) } fun releaseVisualIndicator() { @@ -2473,7 +2521,7 @@ class DesktopTasksController( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo, willExitDesktop: Boolean, - ): Int? { + ): RunOnTransitStart? { val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!! val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode val targetWindowingMode = @@ -2492,15 +2540,14 @@ class DesktopTasksController( wct.reparent(taskInfo.token, tdaInfo.token, /* onTop= */ true) } taskRepository.setPipShouldKeepDesktopActive(taskInfo.displayId, keepActive = false) - if (willExitDesktop) { - performDesktopExitCleanUp(wct, taskInfo.displayId, shouldEndUpAtHome = false) - val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId) - if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue && deskId != null) { - desksOrganizer.deactivateDesk(wct, deskId) - return deskId - } - } - return null + val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId) + return performDesktopExitCleanUp( + wct = wct, + deskId = deskId, + displayId = taskInfo.displayId, + willExitDesktop = willExitDesktop, + shouldEndUpAtHome = false, + ) } private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) { @@ -2661,6 +2708,23 @@ class DesktopTasksController( ) } + /** + * TODO: b/393978539 - Deactivation should not happen in desktop-first devices when going home. + */ + private fun prepareDeskDeactivationIfNeeded( + wct: WindowContainerTransaction, + deskId: Int?, + ): RunOnTransitStart? { + if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return null + if (deskId == null) return null + desksOrganizer.deactivateDesk(wct, deskId) + return { transition -> + desksTransitionObserver.addPendingTransition( + DeskTransition.DeactivateDesk(token = transition, deskId = deskId) + ) + } + } + /** Removes the default desk in the given display. */ @Deprecated("Deprecated with multi-desks.", ReplaceWith("removeDesk()")) fun removeDefaultDeskInDisplay(displayId: Int) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt index d4586abc8ec4..e57b56378fb3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt @@ -92,10 +92,11 @@ class DesksTransitionObserver( } } is DeskTransition.DeactivateDesk -> { + var visibleDeactivation = false for (change in info.changes) { val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId) if (isDeskChange) { - desktopRepository.setDeskInactive(deskId = deskTransition.deskId) + visibleDeactivation = true continue } val taskId = change.taskInfo?.taskId ?: continue @@ -109,6 +110,14 @@ class DesksTransitionObserver( ) } } + // Always deactivate even if there's no change that confirms the desk was + // deactivated. Some interactions, such as the desk deactivating because it's + // occluded by a fullscreen task result in a transition change, but others, such + // as transitioning from an empty desk to home may not. + if (!visibleDeactivation) { + logD("Deactivating desk without transition change") + } + desktopRepository.setDeskInactive(deskId = deskTransition.deskId) } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index c7134c53aad9..a25289d0ea79 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -984,7 +984,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksController.onDesktopWindowClose( wct, mDisplayId, decoration.mTaskInfo); final IBinder transition = mTaskOperations.closeTask(mTaskToken, wct); - if (transition != null && runOnTransitionStart != null) { + if (transition != null) { runOnTransitionStart.invoke(transition); } } 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 ed40acfdb705..e2c3dda0d927 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 @@ -2922,6 +2922,32 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun onDesktopWindowClose_lastWindow_deactivatesDesk() { + val task = setUpFreeformTask() + val wct = WindowContainerTransaction() + + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) + + verify(desksOrganizer).deactivateDesk(wct, deskId = 0) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun onDesktopWindowClose_lastWindow_addsPendingDeactivateTransition() { + val task = setUpFreeformTask() + val wct = WindowContainerTransaction() + + val transition = Binder() + val runOnTransitStart = + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) + runOnTransitStart(transition) + + verify(desksTransitionsObserver) + .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId = 0)) + } + + @Test fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() { val task = setUpFreeformTask(active = false) val transition = Binder() @@ -2945,6 +2971,48 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun onDesktopWindowMinimize_lastWindow_deactivatesDesk() { + val task = setUpFreeformTask() + val transition = Binder() + whenever( + freeformTaskTransitionStarter.startMinimizedModeTransition( + any(), + anyInt(), + anyBoolean(), + ) + ) + .thenReturn(transition) + + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) + + val captor = argumentCaptor<WindowContainerTransaction>() + verify(freeformTaskTransitionStarter) + .startMinimizedModeTransition(captor.capture(), eq(task.taskId), eq(true)) + verify(desksOrganizer).deactivateDesk(captor.firstValue, deskId = 0) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun onDesktopWindowMinimize_lastWindow_addsPendingDeactivateTransition() { + val task = setUpFreeformTask() + val transition = Binder() + whenever( + freeformTaskTransitionStarter.startMinimizedModeTransition( + any(), + anyInt(), + anyBoolean(), + ) + ) + .thenReturn(transition) + + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) + + verify(desksTransitionsObserver) + .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0)) + } + + @Test fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() { val task = setUpPipTask(autoEnterEnabled = true) val handler = mock(TransitionHandler::class.java) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt index 79310c9ce6c2..4dcf669f4d25 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt @@ -227,4 +227,21 @@ class DesksTransitionObserverTest : ShellTestCase() { assertThat(repository.isActiveTaskInDesk(deskId = 5, taskId = exitingTask.taskId)).isFalse() } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun onTransitionReady_deactivateDeskWithoutVisibleChange_updatesRepository() { + val transition = Binder() + val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5) + repository.addDesk(DEFAULT_DISPLAY, deskId = 5) + repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5) + + observer.addPendingTransition(deactivateTransition) + observer.onTransitionReady( + transition = transition, + info = TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0), + ) + + assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull() + } } |