Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -1382,6 +1382,7 @@ public abstract class WMShellModule { Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler, Optional<BackAnimationController> backAnimationController, DesksOrganizer desksOrganizer, DesktopState desktopState, ShellInit shellInit) { return desktopUserRepositories.flatMap( Loading @@ -1391,6 +1392,7 @@ public abstract class WMShellModule { repository, desktopMixedTransitionHandler.get(), backAnimationController.get(), desksOrganizer, desktopState, shellInit))); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavTransitionObserver.kt +38 −13 Original line number Diff line number Diff line Loading @@ -16,8 +16,7 @@ package com.android.wm.shell.desktopmode import android.app.ActivityManager import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.ActivityManager.RunningTaskInfo import android.os.IBinder import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_TO_BACK Loading @@ -27,6 +26,7 @@ import android.window.TransitionInfo import com.android.internal.protolog.ProtoLog import com.android.wm.shell.back.BackAnimationController import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.shared.desktopmode.DesktopState Loading @@ -41,6 +41,7 @@ class DesktopBackNavTransitionObserver( private val desktopUserRepositories: DesktopUserRepositories, private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler, private val backAnimationController: BackAnimationController, private val desksOrganizer: DesksOrganizer, desktopState: DesktopState, shellInit: ShellInit, ) { Loading @@ -51,7 +52,7 @@ class DesktopBackNavTransitionObserver( } fun onInit() { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopBackNavigationTransitionObserver: onInit") logD("onInit") } fun onTransitionReady(transition: IBinder, info: TransitionInfo) { Loading @@ -73,10 +74,7 @@ class DesktopBackNavTransitionObserver( if (taskInfo == null || taskInfo.taskId == -1) continue val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId) if ( desktopRepository.isActiveTask(taskInfo.taskId) && taskInfo.windowingMode != WINDOWING_MODE_FREEFORM ) { if (desktopRepository.isExitingDesktopTask(change)) { desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId) } } Loading @@ -96,7 +94,7 @@ class DesktopBackNavTransitionObserver( if ( isInDesktop && change.mode == TRANSIT_TO_BACK && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM desktopRepository.isDesktopTask(taskInfo) ) { val isLastTask = if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { Loading @@ -104,6 +102,10 @@ class DesktopBackNavTransitionObserver( } else { desktopRepository.isOnlyVisibleTask(taskInfo.taskId, taskInfo.displayId) } logD( "handleBackNavigation marking to-back taskId=%d as minimized", taskInfo.taskId, ) desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId) desktopMixedTransitionHandler.addPendingMixedTransition( DesktopMixedTransitionHandler.PendingMixedTransition.Minimize( Loading Loading @@ -137,6 +139,7 @@ class DesktopBackNavTransitionObserver( if (minimizingTask == null) return // If the transition has wallpaper closing, it means we are moving out of desktop. logD("handleBackNavigation marking close taskId=%d as minimized", minimizingTask) desktopMixedTransitionHandler.addPendingMixedTransition( DesktopMixedTransitionHandler.PendingMixedTransition.Minimize( transition, Loading @@ -151,7 +154,7 @@ class DesktopBackNavTransitionObserver( * Given this a closing task in a closing transition, a task is assumed to be closed by back * navigation if: * 1) Desktop mode is visible. * 2) Task is in freeform. * 2) It is a desktop task. * 3) Task is the latest task that the back gesture is triggered on. * 4) It's not marked as a closing task as a result of closing it by the app header. * Loading @@ -159,14 +162,12 @@ class DesktopBackNavTransitionObserver( * will be rare. E.g. triggering back navigation on an app that pops up a close dialog, and * closing it will minimize it here. */ private fun getMinimizingTaskForClosingTransition( taskInfo: ActivityManager.RunningTaskInfo ): Int? { private fun getMinimizingTaskForClosingTransition(taskInfo: RunningTaskInfo): Int? { val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId) val isInDesktop = desktopRepository.isAnyDeskActive(taskInfo.displayId) if ( isInDesktop && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM && desktopRepository.isDesktopTask(taskInfo) && backAnimationController.latestTriggerBackTask == taskInfo.taskId && !desktopRepository.isClosingTask(taskInfo.taskId) ) { Loading @@ -175,4 +176,28 @@ class DesktopBackNavTransitionObserver( } return null } private fun DesktopRepository.isDesktopTask(task: RunningTaskInfo): Boolean = if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { isActiveTask(task.taskId) } else { task.isFreeform } private fun DesktopRepository.isExitingDesktopTask(change: TransitionInfo.Change): Boolean { val task = change.taskInfo ?: return false return if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { isActiveTask(task.taskId) && desksOrganizer.getDeskAtEnd(change) == null } else { isActiveTask(task.taskId) && !task.isFreeform } } private fun logD(msg: String, vararg arguments: Any?) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } companion object { private const val TAG = "DesktopBackNavTransitionObserver" } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +31 −14 Original line number Diff line number Diff line Loading @@ -60,7 +60,9 @@ import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_PIP import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TRANSIT_TO_FRONT import android.view.WindowManager.transitTypeToString import android.widget.Toast import android.window.DesktopExperienceFlags import android.window.DesktopExperienceFlags.DesktopExperienceFlag Loading Loading @@ -1516,7 +1518,7 @@ class DesktopTasksController( ): IBinder { logV( "startLaunchTransition type=%s launchingTaskId=%d deskId=%d displayId=%d", WindowManager.transitTypeToString(transitionType), transitTypeToString(transitionType), launchingTaskId, deskId, displayId, Loading Loading @@ -2894,7 +2896,7 @@ class DesktopTasksController( ) val wct = WindowContainerTransaction() if (!anyDeskActive && !shouldEnterDesktop) { // We are outside of desktop mode and already existing desktop task is being // We are outside of desktop mode and an already existing desktop task is being // launched. We should make this task go to fullscreen instead of freeform. Note // that this means any re-launch of a freeform window outside of desktop will be in // fullscreen as long as default-desktop flag is disabled. Loading @@ -2902,12 +2904,7 @@ class DesktopTasksController( addMoveToFullscreenChanges( wct = wct, taskInfo = task, willExitDesktop = willExitDesktop( triggerTaskId = task.taskId, displayId = task.displayId, forceExitDesktop = true, ), willExitDesktop = false, // Already outside desktop. ) runOnTransitStart?.invoke(transition) return wct Loading Loading @@ -3174,23 +3171,43 @@ class DesktopTasksController( } /** * Handle task closing by removing wallpaper activity if it's the last active task. * * TODO: b/394268248 - desk needs to be deactivated. * Handles a closing task. This usually means deactivating and cleaning up the desk if it was * the last task in it. It also handles to-back transitions of the last desktop task as a * minimize operation. */ private fun handleTaskClosing( task: RunningTaskInfo, transition: IBinder, requestType: Int, @WindowManager.TransitionType requestType: Int, ): WindowContainerTransaction? { logV("handleTaskClosing") logV( "handleTaskClosing taskId=%d closingType=%s", task.taskId, transitTypeToString(requestType), ) if (!isAnyDeskActive(task.displayId)) return null val deskId = taskRepository.getDeskIdForTask(task.taskId) if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { return null } val wct = WindowContainerTransaction() if ( DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue && requestType == TRANSIT_TO_BACK ) { val isLastTask = taskRepository.isOnlyVisibleTask(task.taskId, task.displayId) logV( "Handling to-back of taskId=%d (isLast=%b) as minimize in deskId=%d", task.taskId, isLastTask, deskId, ) desksOrganizer.minimizeTask( wct = wct, deskId = checkNotNull(deskId) { "Expected non-null deskId" }, task = task, ) } val deactivationRunnable = performDesktopExitCleanupIfNeeded( taskId = task.taskId, Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavTransitionObserverTest.kt +98 −10 Original line number Diff line number Diff line Loading @@ -32,17 +32,14 @@ import android.window.IWindowContainerToken import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerToken import android.window.WindowContainerTransaction import com.android.window.flags.Flags import com.android.wm.shell.MockToken import com.android.wm.shell.ShellTestCase import com.android.wm.shell.back.BackAnimationController import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer import com.android.wm.shell.shared.desktopmode.FakeDesktopState import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import com.google.common.truth.Truth.assertWithMessage import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers.anyInt Loading @@ -62,12 +59,11 @@ import org.mockito.kotlin.whenever class DesktopBackNavTransitionObserverTest : ShellTestCase() { private val testExecutor = mock<ShellExecutor>() private val transitions = mock<Transitions>() private val userRepositories = mock<DesktopUserRepositories>() private val taskRepository = mock<DesktopRepository>() private val mixedHandler = mock<DesktopMixedTransitionHandler>() private val backAnimationController = mock<BackAnimationController>() private val wallpaperToken = MockToken().token() private val desksOrganizer = mock<DesksOrganizer>() private val desktopState = FakeDesktopState() private lateinit var transitionObserver: DesktopBackNavTransitionObserver Loading @@ -86,6 +82,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { userRepositories, mixedHandler, backAnimationController, desksOrganizer, desktopState, shellInit, ) Loading @@ -96,6 +93,26 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { fun backNavigation_taskMinimized() { val task = createTaskInfo(1) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) transitionObserver.onTransitionReady( transition = mock(), info = createBackNavigationTransition(task), ) verify(taskRepository).minimizeTask(task.displayId, task.taskId) verify(mixedHandler).addPendingMixedTransition(any()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun backNavigation_nonFreeformDesktopTask_taskMinimized() { val task = createTaskInfo(1, windowingMode = WINDOWING_MODE_FULLSCREEN) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) transitionObserver.onTransitionReady( transition = mock(), Loading @@ -112,6 +129,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { val task = createTaskInfo(1) val transition = mock<IBinder>() whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(taskRepository.isOnlyVisibleTask(task.taskId, task.displayId)).thenReturn(false) whenever(taskRepository.hasOnlyOneVisibleTask(task.displayId)).thenReturn(false) whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false) Loading Loading @@ -139,6 +157,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { val task = createTaskInfo(1) val transition = mock<IBinder>() whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false) whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId) Loading Loading @@ -166,6 +185,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { val task = createTaskInfo(1) val transition = mock<IBinder>() whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false) whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId) Loading Loading @@ -228,6 +248,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION) @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun removeTasks_onTaskFullscreenLaunchWithOpenTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) Loading @@ -242,8 +263,45 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { verify(taskRepository).removeTask(task.displayId, task.taskId) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskFullscreenInDeskLaunchWithOpenTransition_taskNotRemovedFromRepo() { val deskId = 0 val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(deskId) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).removeTask(task.displayId, task.taskId) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskOutsideDeskLaunchWithOpenTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(null) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId) verify(taskRepository).removeTask(task.displayId, task.taskId) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION) @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun removeTasks_onTaskFullscreenLaunchExitDesktopTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) Loading @@ -258,10 +316,40 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { verify(taskRepository).removeTask(task.displayId, task.taskId) } private fun WindowContainerTransaction.assertIndexInBounds(index: Int) { assertWithMessage("WCT does not have a hierarchy operation at index $index") .that(hierarchyOps.size) .isGreaterThan(index) @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskFullscreenInDeskLaunchExitDesktopTransition_taskNotRemovedFromRepo() { val deskId = 0 val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task, TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(deskId) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).removeTask(task.displayId, task.taskId) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskOutsideDeskLaunchExitDesktopTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task, TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(null) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId) verify(taskRepository).removeTask(task.displayId, task.taskId) } private fun createOpenChangeTransition( Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +71 −22 Original line number Diff line number Diff line Loading @@ -4973,20 +4973,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue() } @Test @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() { val deskId = 0 val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId) taskRepository.setDeskInactive(deskId = deskId) val transition = Binder() controller.handleRequest(transition, createTransition(freeformTask)) verify(desksTransitionsObserver) .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId)) } @Test fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() { taskRepository.setDeskInactive(deskId = 0) Loading Loading @@ -5982,20 +5968,83 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_closeTransition_minimizadTask_withWallpaper_removesWallpaper() { val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_noActiveDesk_notHandled() { taskRepository.setDeskInactive(deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId) // Task is being minimized so mark it as not visible. taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false) val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK)) controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) assertNull(result, "Should not handle request") } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_minimizesTask() { taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) assertNotNull(result) { "Should handle request" } verify(desksOrganizer).minimizeTask(result, deskId = 0, task) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_lastTask_deactivatesDesk() { taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val transition = Binder() val result = controller.handleRequest(transition, createTransition(task, type = TRANSIT_TO_BACK)) assertNotNull(result) { "Should handle request" } verify(desksOrganizer).deactivateDesk(result, deskId = 0) verify(desksTransitionsObserver) .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0)) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_notLastTask_doesNotDeactivateDesk() { taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0) setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val transition = Binder() controller.handleRequest(transition, createTransition(task, type = TRANSIT_TO_BACK)) verify(desksOrganizer, never()).deactivateDesk(any(), deskId = eq(0)) verify(desksTransitionsObserver, never()) .addPendingTransition( argThat { this is DeskTransition.RemoveDesk && this.token == transition && this.deskId == 0 } ) } @Test fun handleRequest_freeformTask_displayDoesntHandleDesktop_returnNull() { desktopState.overrideDesktopModeSupportPerDisplay[SECOND_DISPLAY] = false Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +2 −0 Original line number Diff line number Diff line Loading @@ -1382,6 +1382,7 @@ public abstract class WMShellModule { Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler, Optional<BackAnimationController> backAnimationController, DesksOrganizer desksOrganizer, DesktopState desktopState, ShellInit shellInit) { return desktopUserRepositories.flatMap( Loading @@ -1391,6 +1392,7 @@ public abstract class WMShellModule { repository, desktopMixedTransitionHandler.get(), backAnimationController.get(), desksOrganizer, desktopState, shellInit))); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavTransitionObserver.kt +38 −13 Original line number Diff line number Diff line Loading @@ -16,8 +16,7 @@ package com.android.wm.shell.desktopmode import android.app.ActivityManager import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.ActivityManager.RunningTaskInfo import android.os.IBinder import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_TO_BACK Loading @@ -27,6 +26,7 @@ import android.window.TransitionInfo import com.android.internal.protolog.ProtoLog import com.android.wm.shell.back.BackAnimationController import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.shared.desktopmode.DesktopState Loading @@ -41,6 +41,7 @@ class DesktopBackNavTransitionObserver( private val desktopUserRepositories: DesktopUserRepositories, private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler, private val backAnimationController: BackAnimationController, private val desksOrganizer: DesksOrganizer, desktopState: DesktopState, shellInit: ShellInit, ) { Loading @@ -51,7 +52,7 @@ class DesktopBackNavTransitionObserver( } fun onInit() { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopBackNavigationTransitionObserver: onInit") logD("onInit") } fun onTransitionReady(transition: IBinder, info: TransitionInfo) { Loading @@ -73,10 +74,7 @@ class DesktopBackNavTransitionObserver( if (taskInfo == null || taskInfo.taskId == -1) continue val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId) if ( desktopRepository.isActiveTask(taskInfo.taskId) && taskInfo.windowingMode != WINDOWING_MODE_FREEFORM ) { if (desktopRepository.isExitingDesktopTask(change)) { desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId) } } Loading @@ -96,7 +94,7 @@ class DesktopBackNavTransitionObserver( if ( isInDesktop && change.mode == TRANSIT_TO_BACK && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM desktopRepository.isDesktopTask(taskInfo) ) { val isLastTask = if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { Loading @@ -104,6 +102,10 @@ class DesktopBackNavTransitionObserver( } else { desktopRepository.isOnlyVisibleTask(taskInfo.taskId, taskInfo.displayId) } logD( "handleBackNavigation marking to-back taskId=%d as minimized", taskInfo.taskId, ) desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId) desktopMixedTransitionHandler.addPendingMixedTransition( DesktopMixedTransitionHandler.PendingMixedTransition.Minimize( Loading Loading @@ -137,6 +139,7 @@ class DesktopBackNavTransitionObserver( if (minimizingTask == null) return // If the transition has wallpaper closing, it means we are moving out of desktop. logD("handleBackNavigation marking close taskId=%d as minimized", minimizingTask) desktopMixedTransitionHandler.addPendingMixedTransition( DesktopMixedTransitionHandler.PendingMixedTransition.Minimize( transition, Loading @@ -151,7 +154,7 @@ class DesktopBackNavTransitionObserver( * Given this a closing task in a closing transition, a task is assumed to be closed by back * navigation if: * 1) Desktop mode is visible. * 2) Task is in freeform. * 2) It is a desktop task. * 3) Task is the latest task that the back gesture is triggered on. * 4) It's not marked as a closing task as a result of closing it by the app header. * Loading @@ -159,14 +162,12 @@ class DesktopBackNavTransitionObserver( * will be rare. E.g. triggering back navigation on an app that pops up a close dialog, and * closing it will minimize it here. */ private fun getMinimizingTaskForClosingTransition( taskInfo: ActivityManager.RunningTaskInfo ): Int? { private fun getMinimizingTaskForClosingTransition(taskInfo: RunningTaskInfo): Int? { val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId) val isInDesktop = desktopRepository.isAnyDeskActive(taskInfo.displayId) if ( isInDesktop && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM && desktopRepository.isDesktopTask(taskInfo) && backAnimationController.latestTriggerBackTask == taskInfo.taskId && !desktopRepository.isClosingTask(taskInfo.taskId) ) { Loading @@ -175,4 +176,28 @@ class DesktopBackNavTransitionObserver( } return null } private fun DesktopRepository.isDesktopTask(task: RunningTaskInfo): Boolean = if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { isActiveTask(task.taskId) } else { task.isFreeform } private fun DesktopRepository.isExitingDesktopTask(change: TransitionInfo.Change): Boolean { val task = change.taskInfo ?: return false return if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { isActiveTask(task.taskId) && desksOrganizer.getDeskAtEnd(change) == null } else { isActiveTask(task.taskId) && !task.isFreeform } } private fun logD(msg: String, vararg arguments: Any?) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } companion object { private const val TAG = "DesktopBackNavTransitionObserver" } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +31 −14 Original line number Diff line number Diff line Loading @@ -60,7 +60,9 @@ import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_PIP import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TRANSIT_TO_FRONT import android.view.WindowManager.transitTypeToString import android.widget.Toast import android.window.DesktopExperienceFlags import android.window.DesktopExperienceFlags.DesktopExperienceFlag Loading Loading @@ -1516,7 +1518,7 @@ class DesktopTasksController( ): IBinder { logV( "startLaunchTransition type=%s launchingTaskId=%d deskId=%d displayId=%d", WindowManager.transitTypeToString(transitionType), transitTypeToString(transitionType), launchingTaskId, deskId, displayId, Loading Loading @@ -2894,7 +2896,7 @@ class DesktopTasksController( ) val wct = WindowContainerTransaction() if (!anyDeskActive && !shouldEnterDesktop) { // We are outside of desktop mode and already existing desktop task is being // We are outside of desktop mode and an already existing desktop task is being // launched. We should make this task go to fullscreen instead of freeform. Note // that this means any re-launch of a freeform window outside of desktop will be in // fullscreen as long as default-desktop flag is disabled. Loading @@ -2902,12 +2904,7 @@ class DesktopTasksController( addMoveToFullscreenChanges( wct = wct, taskInfo = task, willExitDesktop = willExitDesktop( triggerTaskId = task.taskId, displayId = task.displayId, forceExitDesktop = true, ), willExitDesktop = false, // Already outside desktop. ) runOnTransitStart?.invoke(transition) return wct Loading Loading @@ -3174,23 +3171,43 @@ class DesktopTasksController( } /** * Handle task closing by removing wallpaper activity if it's the last active task. * * TODO: b/394268248 - desk needs to be deactivated. * Handles a closing task. This usually means deactivating and cleaning up the desk if it was * the last task in it. It also handles to-back transitions of the last desktop task as a * minimize operation. */ private fun handleTaskClosing( task: RunningTaskInfo, transition: IBinder, requestType: Int, @WindowManager.TransitionType requestType: Int, ): WindowContainerTransaction? { logV("handleTaskClosing") logV( "handleTaskClosing taskId=%d closingType=%s", task.taskId, transitTypeToString(requestType), ) if (!isAnyDeskActive(task.displayId)) return null val deskId = taskRepository.getDeskIdForTask(task.taskId) if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { return null } val wct = WindowContainerTransaction() if ( DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue && requestType == TRANSIT_TO_BACK ) { val isLastTask = taskRepository.isOnlyVisibleTask(task.taskId, task.displayId) logV( "Handling to-back of taskId=%d (isLast=%b) as minimize in deskId=%d", task.taskId, isLastTask, deskId, ) desksOrganizer.minimizeTask( wct = wct, deskId = checkNotNull(deskId) { "Expected non-null deskId" }, task = task, ) } val deactivationRunnable = performDesktopExitCleanupIfNeeded( taskId = task.taskId, Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavTransitionObserverTest.kt +98 −10 Original line number Diff line number Diff line Loading @@ -32,17 +32,14 @@ import android.window.IWindowContainerToken import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerToken import android.window.WindowContainerTransaction import com.android.window.flags.Flags import com.android.wm.shell.MockToken import com.android.wm.shell.ShellTestCase import com.android.wm.shell.back.BackAnimationController import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer import com.android.wm.shell.shared.desktopmode.FakeDesktopState import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import com.google.common.truth.Truth.assertWithMessage import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers.anyInt Loading @@ -62,12 +59,11 @@ import org.mockito.kotlin.whenever class DesktopBackNavTransitionObserverTest : ShellTestCase() { private val testExecutor = mock<ShellExecutor>() private val transitions = mock<Transitions>() private val userRepositories = mock<DesktopUserRepositories>() private val taskRepository = mock<DesktopRepository>() private val mixedHandler = mock<DesktopMixedTransitionHandler>() private val backAnimationController = mock<BackAnimationController>() private val wallpaperToken = MockToken().token() private val desksOrganizer = mock<DesksOrganizer>() private val desktopState = FakeDesktopState() private lateinit var transitionObserver: DesktopBackNavTransitionObserver Loading @@ -86,6 +82,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { userRepositories, mixedHandler, backAnimationController, desksOrganizer, desktopState, shellInit, ) Loading @@ -96,6 +93,26 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { fun backNavigation_taskMinimized() { val task = createTaskInfo(1) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) transitionObserver.onTransitionReady( transition = mock(), info = createBackNavigationTransition(task), ) verify(taskRepository).minimizeTask(task.displayId, task.taskId) verify(mixedHandler).addPendingMixedTransition(any()) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun backNavigation_nonFreeformDesktopTask_taskMinimized() { val task = createTaskInfo(1, windowingMode = WINDOWING_MODE_FULLSCREEN) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) transitionObserver.onTransitionReady( transition = mock(), Loading @@ -112,6 +129,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { val task = createTaskInfo(1) val transition = mock<IBinder>() whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(taskRepository.isOnlyVisibleTask(task.taskId, task.displayId)).thenReturn(false) whenever(taskRepository.hasOnlyOneVisibleTask(task.displayId)).thenReturn(false) whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false) Loading Loading @@ -139,6 +157,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { val task = createTaskInfo(1) val transition = mock<IBinder>() whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false) whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId) Loading Loading @@ -166,6 +185,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { val task = createTaskInfo(1) val transition = mock<IBinder>() whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false) whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId) Loading Loading @@ -228,6 +248,7 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION) @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun removeTasks_onTaskFullscreenLaunchWithOpenTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) Loading @@ -242,8 +263,45 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { verify(taskRepository).removeTask(task.displayId, task.taskId) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskFullscreenInDeskLaunchWithOpenTransition_taskNotRemovedFromRepo() { val deskId = 0 val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(deskId) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).removeTask(task.displayId, task.taskId) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskOutsideDeskLaunchWithOpenTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(null) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId) verify(taskRepository).removeTask(task.displayId, task.taskId) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION) @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun removeTasks_onTaskFullscreenLaunchExitDesktopTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) Loading @@ -258,10 +316,40 @@ class DesktopBackNavTransitionObserverTest : ShellTestCase() { verify(taskRepository).removeTask(task.displayId, task.taskId) } private fun WindowContainerTransaction.assertIndexInBounds(index: Int) { assertWithMessage("WCT does not have a hierarchy operation at index $index") .that(hierarchyOps.size) .isGreaterThan(index) @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskFullscreenInDeskLaunchExitDesktopTransition_taskNotRemovedFromRepo() { val deskId = 0 val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task, TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(deskId) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).removeTask(task.displayId, task.taskId) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun removeTasks_onTaskOutsideDeskLaunchExitDesktopTransition_taskRemovedFromRepo() { val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN) val transitionInfo = createOpenChangeTransition(task, TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG) whenever(taskRepository.isAnyDeskActive(any())).thenReturn(true) whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true) whenever(desksOrganizer.getDeskAtEnd(transitionInfo.changes.first())).thenReturn(null) transitionObserver.onTransitionReady(transition = mock(), info = transitionInfo) verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId) verify(taskRepository).removeTask(task.displayId, task.taskId) } private fun createOpenChangeTransition( Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +71 −22 Original line number Diff line number Diff line Loading @@ -4973,20 +4973,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() assertThat(desktopTasksLimiter.hasTaskLimitTransitionForTesting(transition)).isTrue() } @Test @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() { val deskId = 0 val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId) taskRepository.setDeskInactive(deskId = deskId) val transition = Binder() controller.handleRequest(transition, createTransition(freeformTask)) verify(desksTransitionsObserver) .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId)) } @Test fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() { taskRepository.setDeskInactive(deskId = 0) Loading Loading @@ -5982,20 +5968,83 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_closeTransition_minimizadTask_withWallpaper_removesWallpaper() { val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY) @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_noActiveDesk_notHandled() { taskRepository.setDeskInactive(deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId) // Task is being minimized so mark it as not visible. taskRepository.updateTask(displayId = DEFAULT_DISPLAY, task2.taskId, isVisible = false) val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK)) controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) assertNull(result, "Should not handle request") } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_minimizesTask() { taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) assertNotNull(result) { "Should handle request" } verify(desksOrganizer).minimizeTask(result, deskId = 0, task) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_lastTask_deactivatesDesk() { taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val transition = Binder() val result = controller.handleRequest(transition, createTransition(task, type = TRANSIT_TO_BACK)) assertNotNull(result) { "Should handle request" } verify(desksOrganizer).deactivateDesk(result, deskId = 0) verify(desksTransitionsObserver) .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0)) } @Test @EnableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) fun handleRequest_toBackTransition_notLastTask_doesNotDeactivateDesk() { taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0) setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0) val transition = Binder() controller.handleRequest(transition, createTransition(task, type = TRANSIT_TO_BACK)) verify(desksOrganizer, never()).deactivateDesk(any(), deskId = eq(0)) verify(desksTransitionsObserver, never()) .addPendingTransition( argThat { this is DeskTransition.RemoveDesk && this.token == transition && this.deskId == 0 } ) } @Test fun handleRequest_freeformTask_displayDoesntHandleDesktop_returnNull() { desktopState.overrideDesktopModeSupportPerDisplay[SECOND_DISPLAY] = false Loading