diff options
| author | 2025-01-31 16:38:03 -0800 | |
|---|---|---|
| committer | 2025-01-31 16:38:03 -0800 | |
| commit | 72dbc2acfe41ce4c2f5c42760912b4dba66e51f4 (patch) | |
| tree | 699def9dd2c778e253d07da6589aef261182d18b | |
| parent | 43e6543d566f53dcc4e78bda20dc37aa172b57b1 (diff) | |
| parent | b6d9507d8acd7c713bf6e00051ff4b71c3571ca5 (diff) | |
Merge "[7/N] Desks: Implement desk activation" into main
11 files changed, 373 insertions, 79 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 6f16e047a968..89eff5ea618c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -1157,9 +1157,10 @@ public abstract class WMShellModule { @WMSingleton @Provides static DesksTransitionObserver provideDesksTransitionObserver( - @NonNull @DynamicOverride DesktopUserRepositories desktopUserRepositories + @NonNull @DynamicOverride DesktopUserRepositories desktopUserRepositories, + @NonNull DesksOrganizer desksOrganizer ) { - return new DesksTransitionObserver(desktopUserRepositories); + return new DesksTransitionObserver(desktopUserRepositories, desksOrganizer); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt index b93d2e396402..b0a38a85e0b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt @@ -131,8 +131,8 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl pw.println("Error: desk id should be an integer") return false } - pw.println("Not implemented.") - return false + controller.activateDesk(deskId) + return true } private fun runRemoveDesk(args: Array<String>, pw: PrintWriter): Boolean { 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 043b353ba380..9583f3da2ee3 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 @@ -226,6 +226,10 @@ class DesktopRepository( desktopData.setActiveDesk(displayId = displayId, deskId = deskId) } + /** Returns the id of the active desk in the given display, if any. */ + @VisibleForTesting + fun getActiveDeskId(displayId: Int): Int? = desktopData.getActiveDesk(displayId)?.deskId + /** * Adds task with [taskId] to the list of freeform tasks on [displayId]'s active desk. * @@ -435,7 +439,7 @@ class DesktopRepository( ?: error("Expected non-null desk in display $displayId") unminimizeTask(displayId, taskId) } else { - desktopData.getActiveDesk(displayId)?.visibleTasks?.remove(taskId) + desktopData.getDefaultDesk(displayId)?.visibleTasks?.remove(taskId) } val newCount = getVisibleTaskCount(displayId) if (prevCount != newCount) { 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 53f37659cf49..645367780390 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 @@ -315,24 +315,10 @@ class DesktopTasksController( } /** Show all tasks, that are part of the desktop, on top of launcher */ + @Deprecated("Use activateDesk() instead.", ReplaceWith("activateDesk()")) fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) { logV("showDesktopApps") - val wct = WindowContainerTransaction() - bringDesktopAppsToFront(displayId, wct) - - val transitionType = transitionType(remoteTransition) - val handler = - remoteTransition?.let { - OneShotRemoteHandler(transitions.mainExecutor, remoteTransition) - } - transitions.startTransition(transitionType, wct, handler).also { t -> - handler?.setTransition(t) - } - - // launch from recent DesktopTaskView - desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted( - FREEFORM_ANIMATION_DURATION - ) + activateDefaultDeskInDisplay(displayId, remoteTransition) } /** Gets number of visible freeform tasks in [displayId]. */ @@ -606,9 +592,9 @@ class DesktopTasksController( val wct = WindowContainerTransaction() exitSplitIfApplicable(wct, taskInfo) if (Flags.enablePerDisplayDesktopWallpaperActivity()) { - moveHomeTask(wct, toTop = true, taskInfo.displayId) + moveHomeTask(taskInfo.displayId, wct) } else { - moveHomeTask(wct, toTop = true) + moveHomeTask(context.displayId, wct) } val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) @@ -780,7 +766,7 @@ class DesktopTasksController( // We are moving a freeform task to fullscreen, put the home task under the fullscreen task. if (!forceEnterDesktop(task.displayId)) { - moveHomeTask(wct, toTop = true, task.displayId) + moveHomeTask(task.displayId, wct) wct.reorder(task.token, /* onTop= */ true) } @@ -1433,33 +1419,36 @@ class DesktopTasksController( ?: WINDOWING_MODE_UNDEFINED } + private fun prepareForDeskActivation(displayId: Int, wct: WindowContainerTransaction) { + // Move home to front, ensures that we go back home when all desktop windows are closed + val useParamDisplayId = + Flags.enableMultipleDesktopsBackend() || + Flags.enablePerDisplayDesktopWallpaperActivity() + moveHomeTask(displayId = if (useParamDisplayId) displayId else context.displayId, wct = wct) + // Currently, we only handle the desktop on the default display really. + if ( + (displayId == DEFAULT_DISPLAY || Flags.enablePerDisplayDesktopWallpaperActivity()) && + ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() + ) { + // Add translucent wallpaper activity to show the wallpaper underneath. + addWallpaperActivity(displayId, wct) + } + } + private fun bringDesktopAppsToFrontBeforeShowingNewTask( displayId: Int, wct: WindowContainerTransaction, newTaskIdInFront: Int, ): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront) + @Deprecated("Use activeDesk() instead.", ReplaceWith("activateDesk()")) private fun bringDesktopAppsToFront( displayId: Int, wct: WindowContainerTransaction, newTaskIdInFront: Int? = null, ): Int? { logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront) - // Move home to front, ensures that we go back home when all desktop windows are closed - if (Flags.enablePerDisplayDesktopWallpaperActivity()) { - moveHomeTask(wct, toTop = true, displayId) - } else { - moveHomeTask(wct, toTop = true) - } - - // Currently, we only handle the desktop on the default display really. - if ( - (displayId == DEFAULT_DISPLAY || Flags.enablePerDisplayDesktopWallpaperActivity()) && - ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() - ) { - // Add translucent wallpaper activity to show the wallpaper underneath - addWallpaperActivity(displayId, wct) - } + prepareForDeskActivation(displayId, wct) val expandedTasksOrderedFrontToBack = taskRepository.getExpandedTasksOrdered(displayId) // If we're adding a new Task we might need to minimize an old one @@ -1503,15 +1492,11 @@ class DesktopTasksController( return taskIdToMinimize } - private fun moveHomeTask( - wct: WindowContainerTransaction, - toTop: Boolean, - displayId: Int = DEFAULT_DISPLAY, - ) { + private fun moveHomeTask(displayId: Int, wct: WindowContainerTransaction) { shellTaskOrganizer .getRunningTasks(displayId) .firstOrNull { task -> task.activityType == ACTIVITY_TYPE_HOME } - ?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ toTop) } + ?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ true) } } private fun addLaunchHomePendingIntent(wct: WindowContainerTransaction, displayId: Int) { @@ -2389,6 +2374,57 @@ class DesktopTasksController( ) } + private fun activateDefaultDeskInDisplay( + displayId: Int, + remoteTransition: RemoteTransition? = null, + ) { + val deskId = + checkNotNull(taskRepository.getDefaultDeskId(displayId)) { + "Expected a default desk to exist" + } + activateDesk(deskId, remoteTransition) + } + + /** Activates the given desk. */ + fun activateDesk(deskId: Int, remoteTransition: RemoteTransition? = null) { + val displayId = taskRepository.getDisplayForDesk(deskId) + val wct = WindowContainerTransaction() + if (Flags.enableMultipleDesktopsBackend()) { + prepareForDeskActivation(displayId, wct) + desksOrganizer.activateDesk(wct, deskId) + if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) { + // TODO: 362720497 - do non-running tasks need to be restarted with |wct#startTask|? + } + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate( + doesAnyTaskRequireTaskbarRounding(displayId) + ) + } else { + bringDesktopAppsToFront(displayId, wct) + } + + val transitionType = transitionType(remoteTransition) + val handler = + remoteTransition?.let { + OneShotRemoteHandler(transitions.mainExecutor, remoteTransition) + } + + val transition = transitions.startTransition(transitionType, wct, handler) + handler?.setTransition(transition) + if (Flags.enableMultipleDesktopsBackend()) { + desksTransitionObserver.addPendingTransition( + DeskTransition.ActivateDesk( + token = transition, + displayId = displayId, + deskId = deskId, + ) + ) + } + + desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted( + FREEFORM_ANIMATION_DURATION + ) + } + /** 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/DeskTransition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt index 47088c0b545a..4b658caf34ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt @@ -30,4 +30,8 @@ sealed class DeskTransition { val tasks: Set<Int>, val onDeskRemovedListener: OnDeskRemovedListener?, ) : DeskTransition() + + /** A transition to activate a desk in its display. */ + data class ActivateDesk(override val token: IBinder, val displayId: Int, val deskId: Int) : + DeskTransition() } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt index 5cbb59fbf323..547890a6200a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt @@ -43,6 +43,9 @@ interface DesksOrganizer { */ fun getDeskAtEnd(change: TransitionInfo.Change): Int? + /** Whether the desk is activate according to the given change at the end of a transition. */ + fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean + /** A callback that is invoked when the desk container is created. */ fun interface OnCreateCallback { /** Calls back when the [deskId] has been created. */ 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 3e49b8a4538b..c235f204ece4 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 @@ -25,7 +25,10 @@ import com.android.wm.shell.desktopmode.DesktopUserRepositories * Observer of desk-related transitions, such as adding, removing or activating a whole desk. It * tracks pending transitions and updates repository state once they finish. */ -class DesksTransitionObserver(private val desktopUserRepositories: DesktopUserRepositories) { +class DesksTransitionObserver( + private val desktopUserRepositories: DesktopUserRepositories, + private val desksOrganizer: DesksOrganizer, +) { private val deskTransitions = mutableMapOf<IBinder, DeskTransition>() /** Adds a pending desk transition to be tracked. */ @@ -53,6 +56,18 @@ class DesksTransitionObserver(private val desktopUserRepositories: DesktopUserRe desktopRepository.removeDesk(deskTransition.deskId) deskTransition.onDeskRemovedListener?.onDeskRemoved(displayId, deskId) } + is DeskTransition.ActivateDesk -> { + val activeDeskChange = + info.changes.find { change -> + desksOrganizer.isDeskActiveAtEnd(change, deskTransition.deskId) + } + activeDeskChange?.let { + desktopRepository.setActiveDesk( + displayId = deskTransition.displayId, + deskId = deskTransition.deskId, + ) + } + } } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt index 79c48c5e9594..65c0793aab51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt @@ -22,6 +22,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.util.SparseArray import android.view.SurfaceControl +import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.TransitionInfo import android.window.WindowContainerTransaction import androidx.core.util.forEach @@ -94,6 +95,11 @@ class RootTaskDesksOrganizer( override fun getDeskAtEnd(change: TransitionInfo.Change): Int? = change.taskInfo?.parentTaskId?.takeIf { it in roots } + override fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean = + change.taskInfo?.taskId == deskId && + change.taskInfo?.isVisibleRequested == true && + change.mode == TRANSIT_TO_FRONT + override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) { if (taskInfo.parentTaskId in roots) { val deskId = taskInfo.parentTaskId 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 c10d2afbdc99..f5e45be96fc7 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 @@ -66,6 +66,7 @@ import android.widget.Toast import android.window.DisplayAreaInfo import android.window.IWindowContainerToken import android.window.RemoteTransition +import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerToken import android.window.WindowContainerTransaction @@ -519,7 +520,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test - @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val task1 = setUpFreeformTask() @@ -540,6 +544,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() { val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() @@ -558,6 +563,29 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test + @EnableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) + fun showDesktopApps_deskInactive_bringsToFront_multipleDesksEnabled() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) + val deskId = 0 + // Make desk inactive by activating another desk. + taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 1) + taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = 1) + + controller.activateDesk(deskId, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + // Wallpaper is moved to front. + wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent) + // Desk is activated. + verify(desksOrganizer).activateDesk(wct, deskId) + } + + @Test fun isDesktopModeShowing_noTasks_returnsFalse() { assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse() } @@ -631,58 +659,83 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, ) - @DisableFlags( - /** TODO: b/362720497 - re-enable when activation is implemented. */ - Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND - ) - fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_shouldShowWallpaper() { + @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_bringsTasksToFront() { taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) - val homeTask = setUpHomeTask(SECOND_DISPLAY) + setUpHomeTask(SECOND_DISPLAY) val task1 = setUpFreeformTask(SECOND_DISPLAY) val task2 = setUpFreeformTask(SECOND_DISPLAY) markTaskHidden(task1) markTaskHidden(task2) + assertThat(taskRepository.getExpandedTasksOrdered(SECOND_DISPLAY)).contains(task1.taskId) + assertThat(taskRepository.getExpandedTasksOrdered(SECOND_DISPLAY)).contains(task2.taskId) controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition())) val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) - assertThat(wct.hierarchyOps).hasSize(4) - // Expect order to be from bottom: home, wallpaperIntent, task1, task2 - wct.assertReorderAt(index = 0, homeTask) - wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent) - wct.assertReorderAt(index = 2, task1) - wct.assertReorderAt(index = 3, task2) + wct.assertReorder(task1) + wct.assertReorder(task2) } @Test - @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) - @DisableFlags( + @EnableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, - /** TODO: b/362720497 - re-enable when activation is implemented. */ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) + fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_multipleDesksEnabled_bringsDeskToFront() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) + taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2) + setUpHomeTask(SECOND_DISPLAY) + + controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + verify(desksOrganizer).activateDesk(wct, deskId = 2) + } + + @Test + @EnableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY, + ) + fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_shouldShowWallpaper() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) + taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) + setUpHomeTask(SECOND_DISPLAY) + + controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + wct.assertPendingIntent(desktopWallpaperIntent) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY) fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) val homeTask = setUpHomeTask(SECOND_DISPLAY) - val task1 = setUpFreeformTask(SECOND_DISPLAY) - val task2 = setUpFreeformTask(SECOND_DISPLAY) - markTaskHidden(task1) - markTaskHidden(task2) controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition())) val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) - assertThat(wct.hierarchyOps).hasSize(3) - // Expect order to be from bottom: home, task1, task2 (no wallpaper intent) - wct.assertReorderAt(index = 0, homeTask) - wct.assertReorderAt(index = 1, task1) - wct.assertReorderAt(index = 2, task2) + wct.assertWithoutPendingIntent(desktopWallpaperIntent) } @Test - @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val task1 = setUpFreeformTask() @@ -704,7 +757,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @DisableFlags( Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, - /** TODO: b/362720497 - re-enable when activation is implemented. */ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun showDesktopApps_onSecondaryDisplay_desktopWallpaperDisabled_shouldNotMoveLauncher() { @@ -728,6 +780,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() { val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() @@ -746,7 +799,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test - @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val task1 = setUpFreeformTask() @@ -767,6 +823,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() { val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() @@ -785,7 +842,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test - @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() @@ -800,6 +860,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) val wct = @@ -808,7 +870,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test - @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() { taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY) @@ -831,6 +896,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY) val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY) @@ -843,17 +910,63 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) - assertThat(wct.hierarchyOps).hasSize(3) // Move home to front wct.assertReorderAt(index = 0, homeTaskDefaultDisplay) // Add desktop wallpaper activity wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplayTasks_desktopWallpaperEnabled_multiDesksDisabled() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) + taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) + val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY) + val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY) + setUpHomeTask(SECOND_DISPLAY) + val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY) + markTaskHidden(taskDefaultDisplay) + markTaskHidden(taskSecondDisplay) + + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) // Move freeform task to front wct.assertReorderAt(index = 2, taskDefaultDisplay) } @Test - @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @EnableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) + fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplayTasks_desktopWallpaperEnabled_multiDesksEnabled() { + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(Binder()) + taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY) + val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY) + val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY) + setUpHomeTask(SECOND_DISPLAY) + val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY) + markTaskHidden(taskDefaultDisplay) + markTaskHidden(taskSecondDisplay) + + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + // Move desktop tasks to front + verify(desksOrganizer).activateDesk(wct, deskId = DEFAULT_DISPLAY) + } + + @Test + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() { val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() @@ -874,6 +987,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + /** TODO: b/362720497 - add multi-desk version when minimization is implemented. */ + @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() { val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() @@ -3641,6 +3756,34 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() } @Test + @EnableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION, + Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) + fun activateDesk_multipleDesks_addsPendingTransition() { + val deskId = 0 + val transition = Binder() + val deskChange = mock(TransitionInfo.Change::class.java) + whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull())) + .thenReturn(transition) + whenever(desksOrganizer.isDeskActiveAtEnd(deskChange, deskId)).thenReturn(true) + // Make desk inactive by activating another desk. + taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 1) + taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = 1) + + controller.activateDesk(deskId, RemoteTransition(TestRemoteTransition())) + + verify(desksTransitionsObserver) + .addPendingTransition( + argThat { + this is DeskTransition.ActivateDesk && + this.token == transition && + this.deskId == 0 + } + ) + } + + @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS) fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() { val spyController = spy(controller) @@ -5617,6 +5760,29 @@ private fun WindowContainerTransaction.assertIndexInBounds(index: Int) { .isGreaterThan(index) } +private fun WindowContainerTransaction.assertHop( + predicate: (WindowContainerTransaction.HierarchyOp) -> Boolean +) { + assertThat(hierarchyOps.any(predicate)).isTrue() +} + +private fun WindowContainerTransaction.assertWithoutHop( + predicate: (WindowContainerTransaction.HierarchyOp) -> Boolean +) { + assertThat(hierarchyOps.none(predicate)).isTrue() +} + +private fun WindowContainerTransaction.assertReorder( + task: RunningTaskInfo, + toTop: Boolean? = null, +) { + assertHop { hop -> + hop.type == HIERARCHY_OP_TYPE_REORDER && + (toTop == null || hop.toTop == toTop) && + hop.container == task.token.asBinder() + } +} + private fun WindowContainerTransaction.assertReorderAt( index: Int, task: RunningTaskInfo, @@ -5678,6 +5844,20 @@ private fun WindowContainerTransaction.hasRemoveAt(index: Int, token: WindowCont assertThat(op.container).isEqualTo(token.asBinder()) } +private fun WindowContainerTransaction.assertPendingIntent(intent: Intent) { + assertHop { hop -> + hop.type == HIERARCHY_OP_TYPE_PENDING_INTENT && + hop.pendingIntent?.intent?.component == intent.component + } +} + +private fun WindowContainerTransaction.assertWithoutPendingIntent(intent: Intent) { + assertWithoutHop { hop -> + hop.type == HIERARCHY_OP_TYPE_PENDING_INTENT && + hop.pendingIntent?.intent?.component == intent.component + } +} + private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) { assertIndexInBounds(index) val op = hierarchyOps[index] 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 bfbaa84e9312..00c54c2ecc18 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 @@ -21,7 +21,9 @@ import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.WindowManager.TRANSIT_CLOSE +import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.TransitionInfo +import android.window.TransitionInfo.Change import androidx.test.filters.SmallTest import com.android.window.flags.Flags import com.android.wm.shell.ShellTestCase @@ -35,6 +37,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever /** * Tests for [DesksTransitionObserver]. @@ -47,6 +50,8 @@ class DesksTransitionObserverTest : ShellTestCase() { @JvmField @Rule val setFlagsRule = SetFlagsRule() + private val mockDesksOrganizer = mock<DesksOrganizer>() + private lateinit var desktopUserRepositories: DesktopUserRepositories private lateinit var observer: DesksTransitionObserver @@ -65,7 +70,7 @@ class DesksTransitionObserverTest : ShellTestCase() { /* mainCoroutineScope= */ mock(), /* userManager= */ mock(), ) - observer = DesksTransitionObserver(desktopUserRepositories) + observer = DesksTransitionObserver(desktopUserRepositories, mockDesksOrganizer) } @Test @@ -121,4 +126,23 @@ class DesksTransitionObserverTest : ShellTestCase() { assertThat(removeListener.lastDeskRemoved).isEqualTo(5) } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun onTransitionReady_activateDesk_updatesRepository() { + val transition = Binder() + val change = Change(mock(), mock()) + whenever(mockDesksOrganizer.isDeskActiveAtEnd(change, deskId = 5)).thenReturn(true) + val activateTransition = + DeskTransition.ActivateDesk(transition, displayId = DEFAULT_DISPLAY, deskId = 5) + repository.addDesk(DEFAULT_DISPLAY, deskId = 5) + + observer.addPendingTransition(activateTransition) + observer.onTransitionReady( + transition = transition, + info = TransitionInfo(TRANSIT_TO_FRONT, /* flags= */ 0).apply { addChange(change) }, + ) + + assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isEqualTo(5) + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt index a07203d86b75..fbc78b21353e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode.multidesks import android.testing.AndroidTestingRunner import android.view.Display import android.view.SurfaceControl +import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.TransitionInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp @@ -244,6 +245,26 @@ class RootTaskDesksOrganizerTest : ShellTestCase() { assertThat(endDesk).isEqualTo(freeformRoot.taskId) } + @Test + fun testIsDeskActiveAtEnd() { + organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback()) + val freeformRoot = createFreeformTask().apply { parentTaskId = -1 } + freeformRoot.isVisibleRequested = true + organizer.onTaskAppeared(freeformRoot, SurfaceControl()) + + val isActive = + organizer.isDeskActiveAtEnd( + change = + TransitionInfo.Change(freeformRoot.token, SurfaceControl()).apply { + taskInfo = freeformRoot + mode = TRANSIT_TO_FRONT + }, + deskId = freeformRoot.taskId, + ) + + assertThat(isActive).isTrue() + } + private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback { var deskId: Int? = null val created: Boolean |