summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jorge Gil <jorgegil@google.com> 2025-03-06 10:11:36 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-06 10:11:36 -0800
commit5019ad22ceb5c189cc7f09e02ef24def2dad8498 (patch)
treeb6559307d81f0b831613853caafabbb8424d6fb1
parent7e668500c85f945df89a04bbbdeced8611c40416 (diff)
parenta87924539a642e40ad6915bd3c6b69b2e377395a (diff)
Merge changes I05ef9baf,Idf38fef3 into main
* changes: [40/N] Desks: Fix failing WMShellUnitTests [39/N] Desks: Restore persisted desks on reboot
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt174
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt72
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt436
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt210
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt123
14 files changed, 930 insertions, 274 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 478fd6d83661..92c3020a14e6 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
@@ -759,6 +759,7 @@ public abstract class WMShellModule {
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
@DynamicOverride DesktopUserRepositories desktopUserRepositories,
+ DesktopRepositoryInitializer desktopRepositoryInitializer,
Optional<DesktopImmersiveController> desktopImmersiveController,
DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
LaunchAdjacentController launchAdjacentController,
@@ -805,6 +806,7 @@ public abstract class WMShellModule {
dragToDesktopTransitionHandler,
desktopImmersiveController.get(),
desktopUserRepositories,
+ desktopRepositoryInitializer,
recentsTransitionHandler,
multiInstanceHelper,
mainExecutor,
@@ -1313,10 +1315,12 @@ public abstract class WMShellModule {
static Optional<DesktopDisplayEventHandler> provideDesktopDisplayEventHandler(
Context context,
ShellInit shellInit,
+ @ShellMainThread CoroutineScope mainScope,
DisplayController displayController,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
- Optional<DesktopDisplayModeController> desktopDisplayModeController
+ Optional<DesktopDisplayModeController> desktopDisplayModeController,
+ DesktopRepositoryInitializer desktopRepositoryInitializer
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.empty();
@@ -1325,7 +1329,9 @@ public abstract class WMShellModule {
new DesktopDisplayEventHandler(
context,
shellInit,
+ mainScope,
displayController,
+ desktopRepositoryInitializer,
desktopUserRepositories.get(),
desktopTasksController.get(),
desktopDisplayModeController.get()));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index afc48acad4f5..683b74392fa6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -23,15 +23,21 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
/** Handles display events in desktop mode */
class DesktopDisplayEventHandler(
private val context: Context,
shellInit: ShellInit,
+ private val mainScope: CoroutineScope,
private val displayController: DisplayController,
+ private val desktopRepositoryInitializer: DesktopRepositoryInitializer,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopTasksController: DesktopTasksController,
private val desktopDisplayModeController: DesktopDisplayModeController,
@@ -61,15 +67,19 @@ class DesktopDisplayEventHandler(
logV("Display #$displayId does not support desks")
return
}
- logV("Creating new desk in new display#$displayId")
- // TODO: b/362720497 - when SystemUI crashes with a freeform task open for any reason, the
- // task is recreated and received in [FreeformTaskListener] before this display callback
- // is invoked, which results in the repository trying to add the task to a desk before the
- // desk has been recreated here, which may result in a crash-loop if the repository is
- // checking that the desk exists before adding a task to it. See b/391984373.
- desktopTasksController.createDesk(displayId)
- // TODO: b/393978539 - consider activating the desk on creation when applicable, such as
- // for connected displays.
+
+ mainScope.launch {
+ desktopRepositoryInitializer.isInitialized.collect { initialized ->
+ if (!initialized) return@collect
+ if (desktopRepository.getNumberOfDesks(displayId) == 0) {
+ logV("Creating new desk in new display#$displayId")
+ // TODO: b/393978539 - consider activating the desk on creation when
+ // applicable, such as for connected displays.
+ desktopTasksController.createDesk(displayId)
+ }
+ cancel()
+ }
+ }
}
override fun onDisplayRemoved(displayId: Int) {
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 f64bd757de3b..8636bc1f56c2 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
@@ -716,12 +716,15 @@ class DesktopRepository(
}
/**
- * Returns the top transparent fullscreen task id for a given display's active desk, or null.
+ * Returns the top transparent fullscreen task id for a given display, or null.
*
* TODO: b/389960283 - add explicit [deskId] argument.
*/
fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
- desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId
+ desktopData
+ .desksSequence(displayId)
+ .mapNotNull { it.topTransparentFullscreenTaskId }
+ .firstOrNull()
/**
* Clears the top transparent fullscreen task id info for a given display's active desk.
@@ -818,7 +821,6 @@ class DesktopRepository(
}
/** Minimizes the task in its desk. */
- @VisibleForTesting
fun minimizeTaskInDesk(displayId: Int, deskId: Int, taskId: Int) {
logD("MinimizeTaskInDesk: displayId=%d deskId=%d, task=%d", displayId, deskId, taskId)
desktopData.getDesk(deskId)?.minimizedTasks?.add(taskId)
@@ -933,6 +935,12 @@ class DesktopRepository(
listener.onDeskRemoved(displayId = desk.displayId, deskId = desk.deskId)
}
}
+ if (
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ removeDeskFromPersistentRepository(desk)
+ }
return activeTasks
}
@@ -1031,6 +1039,24 @@ class DesktopRepository(
}
}
+ private fun removeDeskFromPersistentRepository(desk: Desk) {
+ mainCoroutineScope.launch {
+ try {
+ logD(
+ "updatePersistentRepositoryForRemovedDesk user=%d desk=%d",
+ userId,
+ desk.deskId,
+ )
+ persistentRepository.removeDesktop(userId = userId, desktopId = desk.deskId)
+ } catch (throwable: Throwable) {
+ logE(
+ "An exception occurred while updating the persistent repository \n%s",
+ throwable.stackTrace,
+ )
+ }
+ }
+ }
+
internal fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopRepository")
@@ -1049,6 +1075,7 @@ class DesktopRepository(
}
.forEach { (displayId, activeDeskId, desks) ->
pw.println("${prefix}Display #$displayId:")
+ pw.println("${innerPrefix}numOfDesks=${desks.size}")
pw.println("${innerPrefix}activeDesk=$activeDeskId")
pw.println("${innerPrefix}desks:")
val desksPrefix = "$innerPrefix "
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 dbc599be57af..d0356d55035d 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
@@ -115,6 +115,9 @@ import com.android.wm.shell.desktopmode.multidesks.DeskTransition
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
+import com.android.wm.shell.desktopmode.multidesks.createDesk
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -194,6 +197,7 @@ class DesktopTasksController(
private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
private val desktopImmersiveController: DesktopImmersiveController,
private val userRepositories: DesktopUserRepositories,
+ desktopRepositoryInitializer: DesktopRepositoryInitializer,
private val recentsTransitionHandler: RecentsTransitionHandler,
private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor,
@@ -276,6 +280,19 @@ class DesktopTasksController(
}
userId = ActivityManager.getCurrentUser()
taskRepository = userRepositories.getProfile(userId)
+
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desktopRepositoryInitializer.deskRecreationFactory =
+ DeskRecreationFactory { deskUserId, destinationDisplayId, deskId ->
+ if (deskUserId != userId) {
+ // TODO: b/400984250 - add multi-user support for multi-desk restoration.
+ logW("Tried to recreated desk of another user.")
+ deskId
+ } else {
+ desksOrganizer.createDesk(destinationDisplayId)
+ }
+ }
+ }
}
private fun onInit() {
@@ -1718,15 +1735,7 @@ class DesktopTasksController(
wct.reorder(runningTaskInfo.token, /* onTop= */ true)
} else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
// Task is not running, start it
- wct.startTask(
- taskId,
- ActivityOptions.makeBasic()
- .apply {
- launchWindowingMode = WINDOWING_MODE_FREEFORM
- splashScreenStyle = SPLASH_SCREEN_STYLE_ICON
- }
- .toBundle(),
- )
+ wct.startTask(taskId, createActivityOptionsForStartTask().toBundle())
}
}
@@ -2826,9 +2835,6 @@ class DesktopTasksController(
}
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)
)
@@ -2846,6 +2852,19 @@ class DesktopTasksController(
desksOrganizer.minimizeTask(wct, deskId, taskToMinimize)
}
}
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
+ expandedTasksOrderedFrontToBack
+ .filter { taskId -> taskId != taskIdToMinimize }
+ .reversed()
+ .forEach { taskId ->
+ val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
+ if (runningTaskInfo == null) {
+ wct.startTask(taskId, createActivityOptionsForStartTask().toBundle())
+ } else {
+ desksOrganizer.reorderTaskToFront(wct, deskId, runningTaskInfo)
+ }
+ }
+ }
return { transition ->
val activateDeskTransition =
if (newTaskIdInFront != null) {
@@ -3497,6 +3516,13 @@ class DesktopTasksController(
}
}
+ private fun createActivityOptionsForStartTask(): ActivityOptions {
+ return ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FREEFORM
+ splashScreenStyle = SPLASH_SCREEN_STYLE_ICON
+ }
+ }
+
private fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopTasksController")
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 8c4bc2598dff..5a988fcd1b77 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
@@ -18,6 +18,8 @@ package com.android.wm.shell.desktopmode.multidesks
import android.app.ActivityManager
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
+import kotlin.coroutines.suspendCoroutine
/** An organizer of desk containers in which to host child desktop windows. */
interface DesksOrganizer {
@@ -82,3 +84,9 @@ interface DesksOrganizer {
fun onCreated(deskId: Int)
}
}
+
+/** Creates a new desk container in the given display. */
+suspend fun DesksOrganizer.createDesk(displayId: Int): Int = suspendCoroutine { cont ->
+ val onCreateCallback = OnCreateCallback { deskId -> cont.resumeWith(Result.success(deskId)) }
+ createDesk(displayId, onCreateCallback)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
index 1566544f5303..f71eacab518d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -132,10 +132,7 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
.toBuilder()
.putDesktopRepoByUser(
userId,
- currentRepository
- .toBuilder()
- .putDesktop(desktopId, desktop.build())
- .build(),
+ currentRepository.toBuilder().putDesktop(desktopId, desktop.build()).build(),
)
.build()
}
@@ -149,6 +146,33 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
}
}
+ /** Removes the desktop from the persistent repository. */
+ suspend fun removeDesktop(userId: Int, desktopId: Int) {
+ try {
+ dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
+ val currentRepository =
+ persistentRepositories.getDesktopRepoByUserOrDefault(
+ userId,
+ DesktopRepositoryState.getDefaultInstance(),
+ )
+ persistentRepositories
+ .toBuilder()
+ .putDesktopRepoByUser(
+ userId,
+ currentRepository.toBuilder().removeDesktop(desktopId).build(),
+ )
+ .build()
+ }
+ } catch (throwable: Throwable) {
+ Log.e(
+ TAG,
+ "Error in removing desktop related data, data is " +
+ "stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
+ throwable,
+ )
+ }
+ }
+
suspend fun removeUsers(uids: List<Int>) {
try {
dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
index a26ebbf4c99a..8191181cac11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
@@ -17,8 +17,22 @@
package com.android.wm.shell.desktopmode.persistence
import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import kotlinx.coroutines.flow.StateFlow
/** Interface for initializing the [DesktopUserRepositories]. */
-fun interface DesktopRepositoryInitializer {
+interface DesktopRepositoryInitializer {
+ /** A factory used to recreate a desk from persistence. */
+ var deskRecreationFactory: DeskRecreationFactory
+
+ /** A flow that emits true when the repository has been initialized. */
+ val isInitialized: StateFlow<Boolean>
+
+ /** Initialize the user repositories from a persistent data store. */
fun initialize(userRepositories: DesktopUserRepositories)
+
+ /** A factory for recreating desks. */
+ fun interface DeskRecreationFactory {
+ /** Recreates a restored desk and returns the new desk id. */
+ suspend fun recreateDesk(userId: Int, destinationDisplayId: Int, deskId: Int): Int
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
index 0507e59c06e1..49cb7391fe97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -17,13 +17,19 @@
package com.android.wm.shell.desktopmode.persistence
import android.content.Context
+import android.view.Display
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
+import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
/**
@@ -37,62 +43,136 @@ class DesktopRepositoryInitializerImpl(
private val persistentRepository: DesktopPersistentRepository,
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
) : DesktopRepositoryInitializer {
+
+ override var deskRecreationFactory: DeskRecreationFactory = DefaultDeskRecreationFactory()
+
+ private val _isInitialized = MutableStateFlow(false)
+ override val isInitialized: StateFlow<Boolean> = _isInitialized
+
override fun initialize(userRepositories: DesktopUserRepositories) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) return
+ if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
+ _isInitialized.value = true
+ return
+ }
// TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
mainCoroutineScope.launch {
- val desktopUserPersistentRepositoryMap =
- persistentRepository.getUserDesktopRepositoryMap() ?: return@launch
- for (userId in desktopUserPersistentRepositoryMap.keys) {
- val repository = userRepositories.getProfile(userId)
- val desktopRepositoryState =
- persistentRepository.getDesktopRepositoryState(userId) ?: continue
- val desktopByDesktopIdMap = desktopRepositoryState.desktopMap
- for (desktopId in desktopByDesktopIdMap.keys) {
- val persistentDesktop =
- persistentRepository.readDesktop(userId, desktopId) ?: continue
- val maxTasks =
- DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
- ?: persistentDesktop.zOrderedTasksCount
- var visibleTasksCount = 0
- repository.addDesk(
- displayId = persistentDesktop.displayId,
- deskId =
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- persistentDesktop.desktopId
- } else {
- // When disabled, desk ids are always the display id.
- persistentDesktop.displayId
- },
+ try {
+ val desktopUserPersistentRepositoryMap =
+ persistentRepository.getUserDesktopRepositoryMap() ?: return@launch
+ for (userId in desktopUserPersistentRepositoryMap.keys) {
+ val repository = userRepositories.getProfile(userId)
+ val desktopRepositoryState =
+ persistentRepository.getDesktopRepositoryState(userId) ?: continue
+ val desksToRestore = getDesksToRestore(desktopRepositoryState, userId)
+ logV(
+ "initialize() will restore desks=%s user=%d",
+ desksToRestore.map { it.desktopId },
+ userId,
)
- persistentDesktop.zOrderedTasksList
- // Reverse it so we initialize the repo from bottom to top.
- .reversed()
- .mapNotNull { taskId -> persistentDesktop.tasksByTaskIdMap[taskId] }
- // TODO: b/362720497 - add tasks to their respective desk when multi-desk
- // persistence is implemented.
- .forEach { task ->
- if (
- task.desktopTaskState == DesktopTaskState.VISIBLE &&
- visibleTasksCount < maxTasks
- ) {
- visibleTasksCount++
- repository.addTask(
- persistentDesktop.displayId,
- task.taskId,
- isVisible = false,
- )
- } else {
- repository.addTask(
- persistentDesktop.displayId,
- task.taskId,
+ desksToRestore.forEach { persistentDesktop ->
+ val maxTasks = getTaskLimit(persistentDesktop)
+ val displayId = persistentDesktop.displayId
+ val deskId = persistentDesktop.desktopId
+ // TODO: b/401107440 - Implement desk restoration to other displays.
+ val newDisplayId = Display.DEFAULT_DISPLAY
+ val newDeskId =
+ deskRecreationFactory.recreateDesk(
+ userId = userId,
+ destinationDisplayId = newDisplayId,
+ deskId = deskId,
+ )
+ logV(
+ "Recreated desk=%d in display=%d using new deskId=%d and displayId=%d",
+ deskId,
+ displayId,
+ newDeskId,
+ newDisplayId,
+ )
+ if (newDeskId != deskId || newDisplayId != displayId) {
+ logV("Removing obsolete desk from persistence under deskId=%d", deskId)
+ persistentRepository.removeDesktop(userId, deskId)
+ }
+
+ // TODO: b/393961770 - [DesktopRepository] doesn't save desks to the
+ // persistent repository until a task is added to them. Update it so that
+ // empty desks can be restored too.
+ repository.addDesk(displayId = displayId, deskId = newDeskId)
+ var visibleTasksCount = 0
+ persistentDesktop.zOrderedTasksList
+ // Reverse it so we initialize the repo from bottom to top.
+ .reversed()
+ .mapNotNull { taskId -> persistentDesktop.tasksByTaskIdMap[taskId] }
+ .forEach { task ->
+ // Visible here means non-minimized a.k.a. expanded, it does not
+ // mean
+ // it is visible in WM (and |DesktopRepository|) terms.
+ val isVisible =
+ task.desktopTaskState == DesktopTaskState.VISIBLE &&
+ visibleTasksCount < maxTasks
+
+ repository.addTaskToDesk(
+ displayId = displayId,
+ deskId = newDeskId,
+ taskId = task.taskId,
isVisible = false,
)
- repository.minimizeTask(persistentDesktop.displayId, task.taskId)
+
+ if (isVisible) {
+ visibleTasksCount++
+ } else {
+ repository.minimizeTaskInDesk(
+ displayId = displayId,
+ deskId = newDeskId,
+ taskId = task.taskId,
+ )
+ }
}
- }
+ }
}
+ } finally {
+ _isInitialized.value = true
}
}
}
+
+ private suspend fun getDesksToRestore(
+ state: DesktopRepositoryState,
+ userId: Int,
+ ): Set<Desktop> {
+ // TODO: b/365873835 - what about desks that won't be restored?
+ // - invalid desk ids from multi-desk -> single-desk switching can be ignored / deleted.
+ val limitToSingleDeskPerDisplay =
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ return state.desktopMap.keys
+ .mapNotNull { deskId ->
+ persistentRepository.readDesktop(userId, deskId)?.takeIf { desk ->
+ // Do not restore invalid desks when multi-desks is disabled. This is
+ // possible if the feature is disabled after having created multiple desks.
+ val isValidSingleDesk = desk.desktopId == desk.displayId
+ (!limitToSingleDeskPerDisplay || isValidSingleDesk)
+ }
+ }
+ .toSet()
+ }
+
+ private fun getTaskLimit(persistedDesk: Desktop): Int =
+ DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
+ ?: persistedDesk.zOrderedTasksCount
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ /** A default implementation of [DeskRecreationFactory] that reuses the desk id. */
+ private class DefaultDeskRecreationFactory : DeskRecreationFactory {
+ override suspend fun recreateDesk(
+ userId: Int,
+ destinationDisplayId: Int,
+ deskId: Int,
+ ): Int = deskId
+ }
+
+ companion object {
+ private const val TAG = "DesktopRepositoryInitializerImpl"
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 8ad54f5a0bb4..275d7b73a112 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -28,14 +28,22 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.whenever
@@ -46,15 +54,18 @@ import org.mockito.quality.Strictness
*
* Usage: atest WMShellUnitTests:DesktopDisplayEventHandlerTest
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var displayController: DisplayController
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
+ @Mock private lateinit var mockDesktopRepositoryInitializer: DesktopRepositoryInitializer
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
@Mock private lateinit var desktopDisplayModeController: DesktopDisplayModeController
+ private val testScope = TestScope()
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var shellInit: ShellInit
@@ -77,7 +88,9 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
DesktopDisplayEventHandler(
context,
shellInit,
+ testScope.backgroundScope,
displayController,
+ mockDesktopRepositoryInitializer,
mockDesktopUserRepositories,
mockDesktopTasksController,
desktopDisplayModeController,
@@ -89,17 +102,66 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@After
fun tearDown() {
+ testScope.cancel()
mockitoSession.finishMocking()
}
@Test
- fun testDisplayAdded_supportsDesks_createsDesk() {
- whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+ fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_createsDesk() =
+ testScope.runTest {
+ val stateFlow = MutableStateFlow(false)
+ whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ stateFlow.emit(true)
+ runCurrent()
- verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
- }
+ verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testDisplayAdded_supportsDesks_desktopRepositoryNotInitialized_doesNotCreateDesk() =
+ testScope.runTest {
+ val stateFlow = MutableStateFlow(false)
+ whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ runCurrent()
+
+ verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testDisplayAdded_supportsDesks_desktopRepositoryInitializedTwice_createsDeskOnce() =
+ testScope.runTest {
+ val stateFlow = MutableStateFlow(false)
+ whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ stateFlow.emit(true)
+ stateFlow.emit(true)
+ runCurrent()
+
+ verify(mockDesktopTasksController, times(1)).createDesk(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_deskExists_doesNotCreateDesk() =
+ testScope.runTest {
+ val stateFlow = MutableStateFlow(false)
+ whenever(mockDesktopRepositoryInitializer.isInitialized).thenReturn(stateFlow)
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+ whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
+
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ stateFlow.emit(true)
+ runCurrent()
+
+ verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ }
@Test
fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index 006c3cae121c..4c18ee1500b7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -114,6 +114,8 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
transactionSupplier = transactionSupplier,
)
desktopRepository = userRepositories.current
+ desktopRepository.addDesk(DEFAULT_DISPLAY, DEFAULT_DESK_ID)
+ desktopRepository.setActiveDesk(DEFAULT_DISPLAY, DEFAULT_DESK_ID)
}
@Test
@@ -835,5 +837,6 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
companion object {
private val STABLE_BOUNDS = Rect(0, 100, 2000, 1900)
private val DISPLAY_BOUNDS = Rect(0, 0, 2000, 2000)
+ private const val DEFAULT_DESK_ID = 0
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index de92d391645a..f84a1a38bdfc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -1203,6 +1203,17 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+ fun removeDesk_removesFromPersistence() =
+ runTest(StandardTestDispatcher()) {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+
+ repo.removeDesk(deskId = 2)
+
+ verify(persistentRepository).removeDesktop(DEFAULT_USER_ID, 2)
+ }
+
+ @Test
fun getTaskInFullImmersiveState_byDisplay() {
repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
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 75308442d76a..34c5ebd6d94d 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
@@ -22,6 +22,7 @@ import android.app.ActivityOptions
import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.PictureInPictureParams
+import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -297,6 +298,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
private val wallpaperToken = MockToken().token()
private val homeComponentName = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "")
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Before
fun setUp() {
Dispatchers.setMain(StandardTestDispatcher())
@@ -433,6 +438,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
dragToDesktopTransitionHandler,
mMockDesktopImmersiveController,
userRepositories,
+ repositoryInitializer,
recentsTransitionHandler,
multiInstanceHelper,
shellExecutor,
@@ -627,11 +633,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isDesktopModeShowing_noTasks_returnsFalse() {
assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
@@ -642,6 +650,15 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun isDesktopModeShowing_noActiveDesk_returnsFalse() {
+ taskRepository.setDeskInactive(deskId = 0)
+
+ assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
@@ -656,6 +673,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
)
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isDesktopModeShowing_topTransparentFullscreenTask_returnsTrue() {
val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
@@ -665,6 +683,20 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun isDesktopModeShowing_deskInactive_topTransparentFullscreenTask_returnsTrue() {
+ val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
+ taskRepository.setDeskInactive(deskId = 0)
+
+ assertThat(controller.isDesktopModeShowing(displayId = DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
)
@@ -1053,11 +1085,29 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isAnyDeskActive_noTasks_returnsFalse() {
assertThat(controller.isAnyDeskActive(DEFAULT_DISPLAY)).isFalse()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun isAnyDeskActive_noActiveDesk_returnsFalse() {
+ taskRepository.setDeskInactive(deskId = 0)
+
+ assertThat(controller.isAnyDeskActive(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun isAnyDeskActive_withActiveDesk_returnsTrue() {
+ taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+
+ assertThat(controller.isAnyDeskActive(DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isAnyDeskActive_twoTasks_bothVisible_returnsTrue() {
setUpHomeTask()
@@ -1068,6 +1118,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isInDesktop_twoTasks_oneVisible_returnsTrue() {
setUpHomeTask()
@@ -1078,6 +1129,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun isAnyDeskActive_twoTasksVisibleOnDifferentDisplays_returnsTrue() {
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -1097,7 +1149,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
+ assertThat(finalBounds).isNull()
}
@Test
@@ -1108,7 +1160,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
+ assertThat(finalBounds).isNull()
}
@Test
@@ -1119,7 +1171,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
+ assertThat(finalBounds).isNull()
}
@Test
@@ -1130,7 +1182,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
val finalBounds = findBoundsChange(wct, task)
- assertThat(finalBounds).isEqualTo(Rect())
+ assertThat(finalBounds).isNull()
}
@Test
@@ -1862,6 +1914,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val wallpaperToken = MockToken().token()
whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
.thenReturn(wallpaperToken)
+ taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
val task = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2, background = true)
controller.moveTaskToDefaultDeskAndActivate(
@@ -2666,7 +2719,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_moveFromFirstToSecondDisplay_multiDesksDisabled() {
// Set up two display ids
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
whenever(rootTaskDisplayAreaOrganizer.displayIds)
@@ -2692,7 +2746,27 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun moveToNextDisplay_moveFromSecondToFirstDisplay() {
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_moveFromFirstToSecondDisplay_multiDesksEnabled() {
+ // Set up two display ids
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ controller.moveToNextDisplay(task.taskId)
+
+ verify(desksOrganizer).moveTaskToDesk(any(), eq(targetDeskId), eq(task))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_moveFromSecondToFirstDisplay_multiDesksDisabled() {
// Set up two display ids
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
@@ -2718,6 +2792,25 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_moveFromSecondToFirstDisplay_multiDesksEnabled() {
+ // Set up two display ids
+ val targetDeskId = 0
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: default display
+ val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .thenReturn(defaultDisplayArea)
+
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+ val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ controller.moveToNextDisplay(task.taskId)
+
+ verify(desksOrganizer).moveTaskToDesk(any(), eq(targetDeskId), eq(task))
+ }
+
+ @Test
@EnableFlags(
FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
@@ -2781,6 +2874,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
fun moveToNextDisplay_sizeInDpPreserved() {
// Set up two display ids
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
// Create a mock for the target display area: second display
@@ -2822,6 +2916,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
fun moveToNextDisplay_shiftWithinDestinationDisplayBounds() {
// Set up two display ids
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
// Create a mock for the target display area: second display
@@ -2863,6 +2958,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
// Set up two display ids
whenever(rootTaskDisplayAreaOrganizer.displayIds)
.thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
// Create a mock for the target display area: second display
val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
@@ -2971,7 +3067,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun moveToNextDisplay_toDesktopInOtherDisplay_bringsExistingTasksToFront() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_toDesktopInOtherDisplay_multiDesksDisabled_bringsExistingTasksToFront() {
val transition = Binder()
val sourceDeskId = 0
val targetDeskId = 2
@@ -2997,6 +3094,34 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_toDesktopInOtherDisplay_multiDesksEnabled_bringsExistingTasksToFront() {
+ val transition = Binder()
+ val sourceDeskId = 0
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+ val task2 = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+
+ controller.moveToNextDisplay(task1.taskId)
+
+ // Existing desktop task in the target display is moved to front.
+ val wct = getLatestTransition()
+ assertNotNull(wct)
+ verify(desksOrganizer).reorderTaskToFront(wct, targetDeskId, task2)
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
@@ -3110,7 +3235,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
fun moveToNextDisplay_movingToDesktop_sendsTaskbarRoundingUpdate() {
val transition = Binder()
- val sourceDeskId = 1
+ val sourceDeskId = 0
val targetDeskId = 2
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
taskRepository.setDeskInactive(deskId = targetDeskId)
@@ -3627,7 +3752,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTask_freeformVisible_multiDesksDisabled_returnSwitchToFreeformWCT() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
markTaskVisible(freeformTask)
@@ -3643,7 +3769,22 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTask_deskActive_multiDesksEnabled_movesToDesk() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = deskId)
+ setUpHomeTask()
+ val fullscreenTask = createFullscreenTask()
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_multiDesksDisabled_returnSwitchToFreeformWCT() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
markTaskVisible(freeformTask)
@@ -3668,6 +3809,21 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTaskWithTaskOnHome_activeDesk_multiDesksEnabled_movesToDesk() {
+ val deskId = 0
+ setUpHomeTask()
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val fullscreenTask = createFullscreenTask()
+ fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksDisabled_dontMinimize() {
val freeformTask = setUpFreeformTask()
@@ -3899,6 +4055,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
@@ -3921,7 +4078,28 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() {
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_freeformDisplay_movesToDesk() {
+ val deskId = 0
+ taskRepository.setDeskInactive(deskId)
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+
+ val fullscreenTask = createFullscreenTask()
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_notInDesk_enforceDesktop_fullscreenDisplay_returnNull() {
+ taskRepository.setDeskInactive(deskId = 0)
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
@@ -3933,6 +4111,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
val freeformTask = setUpFreeformTask()
markTaskHidden(freeformTask)
@@ -3941,12 +4120,23 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
val fullscreenTask = createFullscreenTask()
assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTask_notInDesk_returnNull() {
+ taskRepository.setDeskInactive(deskId = 0)
+ val fullscreenTask = createFullscreenTask()
+
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
createFreeformTask(displayId = SECOND_DISPLAY)
@@ -3957,6 +4147,20 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_fullscreenTask_deskInOtherDisplayActive_returnNull() {
+ taskRepository.setDeskInactive(deskId = 0)
+ val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
+ taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = 2)
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
+
+ assertThat(result).isNull()
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksDisabled_minimize() {
val deskId = 0
@@ -4008,7 +4212,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
- val freeformTask = setUpFreeformTask()
+ taskRepository.setDeskInactive(deskId = 0)
+ val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
markTaskHidden(freeformTask)
val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
@@ -4038,9 +4243,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
- val freeformTask = setUpFreeformTask()
+ taskRepository.setDeskInactive(deskId = 0)
+ val freeformTask = setUpFreeformTask(DEFAULT_DISPLAY, deskId = 0)
markTaskHidden(freeformTask)
+
val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
assertNotNull(wct, "should handle request")
@@ -4049,7 +4255,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 handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
val freeformTask1 = setUpFreeformTask()
val freeformTask2 = createFreeformTask()
@@ -4068,7 +4277,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_multiDesksDisabled_reorderedToTop() {
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTask1 = setUpFreeformTask()
val freeformTask2 = createFreeformTask()
@@ -4091,34 +4301,47 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
- val task = createFreeformTask()
- val result = controller.handleRequest(Binder(), createTransition(task))
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_notInDesk_reorderedToTop() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+ val deskId = 0
+ taskRepository.setDeskInactive(deskId)
+ val freeformTask1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ val freeformTask2 = createFreeformTask()
- assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(1)
- result.assertReorderAt(0, task, toTop = true)
+ val wct =
+ controller.handleRequest(
+ Binder(),
+ createTransition(freeformTask2, type = TRANSIT_TO_FRONT),
+ )
+
+ assertNotNull(wct, "Should handle request")
+ verify(desksOrganizer).reorderTaskToFront(wct, deskId, freeformTask1)
+ wct.assertReorder(freeformTask2, toTop = true)
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
val task = createFreeformTask()
-
val result = controller.handleRequest(Binder(), createTransition(task))
assertNotNull(result, "Should handle request")
- assertThat(result.hierarchyOps?.size).isEqualTo(2)
- // Add desktop wallpaper activity
- result.assertPendingIntentAt(0, desktopWallpaperIntent)
- // Bring new task to front
- result.assertReorderAt(1, task, toTop = true)
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, task, toTop = true)
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
// Second display task
@@ -4134,10 +4357,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+ taskRepository.setDeskInactive(deskId = 0)
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
// Second display task
- createFreeformTask(displayId = SECOND_DISPLAY)
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
+ taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = 2)
+ setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2)
val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
@@ -4273,7 +4499,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_topActivityTransparentWithoutDisplay_multiDesksDisabled_returnSwitchToFreeformWCT() {
val freeformTask = setUpFreeformTask()
markTaskVisible(freeformTask)
@@ -4290,6 +4517,29 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_topActivityTransparentWithoutDisplay_multiDesksEnabled_returnSwitchToFreeformWCT() {
+ val deskId = 0
+ val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ markTaskVisible(freeformTask)
+
+ val task =
+ setUpFullscreenTask().apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = true
+ numActivities = 1
+ }
+
+ val wct = controller.handleRequest(Binder(), createTransition(task))
+
+ assertNotNull(wct)
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, task)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
@DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
@@ -4348,7 +4598,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
)
- fun handleRequest_onlyTopTransparentFullscreenTask_returnSwitchToFreeformWCT() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_onlyTopTransparentFullscreenTask_multiDesksDisabled_returnSwitchToFreeformWCT() {
val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
@@ -4360,8 +4611,28 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_onlyTopTransparentFullscreenTask_multiDesksEnabled_movesToDesktop() {
+ val deskId = 0
+ val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
+ taskRepository.setDeskInactive(deskId = deskId)
+
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+
+ val wct = controller.handleRequest(Binder(), createTransition(task))
+ assertNotNull(wct)
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, task)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun handleRequest_desktopNotShowing_topTransparentFullscreenTask_returnNull() {
+ taskRepository.setDeskInactive(deskId = 0)
val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
@@ -4415,7 +4686,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handleRequest_systemUIActivityWithoutDisplay_multiDesksDisabled_returnSwitchToFreeformWCT() {
val freeformTask = setUpFreeformTask()
markTaskVisible(freeformTask)
@@ -4434,6 +4706,32 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
.isEqualTo(WINDOWING_MODE_FREEFORM)
}
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_systemUIActivityWithoutDisplay_multiDesksEnabled_movesTaskToDesk() {
+ val deskId = 0
+ val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+ markTaskVisible(freeformTask)
+
+ // Set task as systemUI package
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
+ val task =
+ setUpFullscreenTask(displayId = DEFAULT_DISPLAY).apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = true
+ }
+
+ val wct = controller.handleRequest(Binder(), createTransition(task))
+
+ assertNotNull(wct)
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, task)
+ }
+
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun handleRequest_defaultHomePackageWithDisplay_returnSwitchToFullscreenWCT() {
val freeformTask = setUpFreeformTask()
@@ -4476,6 +4774,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT_enforcedDesktop() {
+ taskRepository.setDeskInactive(deskId = 0)
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
@@ -5109,6 +5408,55 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
+ fun activateDesk_hasNonRunningTask_startsTask() {
+ val deskId = 0
+ val nonRunningTask =
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0, background = true)
+
+ 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()))
+
+ val wct = getLatestWct(TRANSIT_TO_FRONT, OneShotRemoteHandler::class.java)
+ assertNotNull(wct)
+ wct.assertLaunchTask(nonRunningTask.taskId, WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun activateDesk_hasRunningTask_reordersTask() {
+ val deskId = 0
+ val runningTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, 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(desksOrganizer).reorderTaskToFront(any(), eq(deskId), eq(runningTask))
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
fun moveTaskToDesk_multipleDesks_addsPendingTransition() {
val transition = Binder()
whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenReturn(transition)
@@ -6833,6 +7181,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
+ taskRepository.setDeskInactive(deskId = 0)
val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
taskRepository.setTaskInFullImmersiveState(
displayId = triggerTask.displayId,
@@ -6909,6 +7258,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.setDeskInactive(deskId = 0)
assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
assertThat(
@@ -6944,6 +7294,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
val triggerTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, active = false)
+ taskRepository.setDeskInactive(deskId = 0)
assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
assertThat(
@@ -6973,6 +7324,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
fun startLaunchTransition_desktopNotShowing_movesWallpaperToFront() {
+ taskRepository.setDeskInactive(deskId = 0)
val launchingTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
val wct = WindowContainerTransaction()
wct.reorder(launchingTask.token, /* onTop= */ true)
@@ -7442,7 +7794,15 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
- wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+ wct.changes.entries
+ .find { (token, change) ->
+ token == task.token.asBinder() &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0
+ }
+ ?.value
+ ?.configuration
+ ?.windowConfiguration
+ ?.bounds
private fun verifyWCTNotExecuted() {
verify(transitions, never()).startTransition(anyInt(), any(), isNull())
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 6b2f90fc0a59..9af504797182 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
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.desktopmode.multidesks
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.testing.AndroidTestingRunner
import android.view.Display
@@ -29,6 +30,7 @@ import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_R
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTaskOrganizer.TaskListener
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.LaunchAdjacentController
@@ -39,14 +41,17 @@ import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
import kotlin.test.assertNotNull
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
/**
* Tests for [RootTaskDesksOrganizer].
@@ -76,48 +81,17 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
)
}
- @Test
- fun testCreateDesk_callsBack() {
- val callback = FakeOnCreateCallback()
- organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
-
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-
- assertThat(callback.created).isTrue()
- assertEquals(freeformRoot.taskId, callback.deskId)
- }
-
- @Test
- fun testCreateDesk_createsMinimizationRoot() {
- val callback = FakeOnCreateCallback()
- organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-
- val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
-
- val minimizationRoot = organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]
- assertNotNull(minimizationRoot)
- assertThat(minimizationRoot.deskId).isEqualTo(freeformRoot.taskId)
- assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
- }
+ @Test fun testCreateDesk_createsDeskAndMinimizationRoots() = runTest { createDesk() }
@Test
- fun testCreateMinimizationRoot_marksHidden() {
- organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-
- val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ fun testCreateMinimizationRoot_marksHidden() = runTest {
+ val desk = createDesk()
verify(mockShellTaskOrganizer)
.applyTransaction(
argThat { wct ->
wct.changes.any { change ->
- change.key == minimizationRootTask.token.asBinder() &&
+ change.key == desk.minimizationRoot.token.asBinder() &&
(change.value.changeMask and Change.CHANGE_HIDDEN != 0) &&
change.value.hidden
}
@@ -126,7 +100,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testOnTaskAppeared_withoutRequest_throws() {
+ fun testOnTaskAppeared_withoutRequest_throws() = runTest {
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
assertThrows(Exception::class.java) {
@@ -135,41 +109,25 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testOnTaskAppeared_withRequestOnlyInAnotherDisplay_throws() {
- organizer.createDesk(displayId = 2, FakeOnCreateCallback())
- val freeformRoot = createFreeformTask(Display.DEFAULT_DISPLAY).apply { parentTaskId = -1 }
-
- assertThrows(Exception::class.java) {
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
- }
- }
-
- @Test
- fun testOnTaskAppeared_duplicateRoot_throws() {
- organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ fun testOnTaskAppeared_duplicateRoot_throws() = runTest {
+ val desk = createDesk()
assertThrows(Exception::class.java) {
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ organizer.onTaskAppeared(desk.deskRoot.taskInfo, SurfaceControl())
}
}
@Test
- fun testOnTaskAppeared_duplicateMinimizedRoot_throws() {
- organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
- val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
- organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ fun testOnTaskAppeared_duplicateMinimizedRoot_throws() = runTest {
+ val desk = createDesk()
assertThrows(Exception::class.java) {
- organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ organizer.onTaskAppeared(desk.minimizationRoot.taskInfo, SurfaceControl())
}
}
@Test
- fun testOnTaskVanished_removesRoot() {
+ fun testOnTaskVanished_removesRoot() = runTest {
val desk = createDesk()
organizer.onTaskVanished(desk.deskRoot.taskInfo)
@@ -178,7 +136,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testOnTaskVanished_removesMinimizedRoot() {
+ fun testOnTaskVanished_removesMinimizedRoot() = runTest {
val desk = createDesk()
organizer.onTaskVanished(desk.deskRoot.taskInfo)
@@ -188,7 +146,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testDesktopWindowAppearsInDesk() {
+ fun testDesktopWindowAppearsInDesk() = runTest {
val desk = createDesk()
val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
@@ -198,7 +156,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testDesktopWindowAppearsInDeskMinimizationRoot() {
+ fun testDesktopWindowAppearsInDeskMinimizationRoot() = runTest {
val desk = createDesk()
val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
@@ -208,7 +166,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testDesktopWindowMovesToMinimizationRoot() {
+ fun testDesktopWindowMovesToMinimizationRoot() = runTest {
val desk = createDesk()
val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
@@ -221,7 +179,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testDesktopWindowDisappearsFromDesk() {
+ fun testDesktopWindowDisappearsFromDesk() = runTest {
val desk = createDesk()
val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
@@ -232,7 +190,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testDesktopWindowDisappearsFromDeskMinimizationRoot() {
+ fun testDesktopWindowDisappearsFromDeskMinimizationRoot() = runTest {
val desk = createDesk()
val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
@@ -243,7 +201,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testRemoveDesk_removesDeskRoot() {
+ fun testRemoveDesk_removesDeskRoot() = runTest {
val desk = createDesk()
val wct = WindowContainerTransaction()
@@ -259,7 +217,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testRemoveDesk_removesMinimizationRoot() {
+ fun testRemoveDesk_removesMinimizationRoot() = runTest {
val desk = createDesk()
val wct = WindowContainerTransaction()
@@ -275,7 +233,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testActivateDesk() {
+ fun testActivateDesk() = runTest {
val desk = createDesk()
val wct = WindowContainerTransaction()
@@ -299,7 +257,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testActivateDesk_didNotExist_throws() {
+ fun testActivateDesk_didNotExist_throws() = runTest {
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
val wct = WindowContainerTransaction()
@@ -307,7 +265,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testMoveTaskToDesk() {
+ fun testMoveTaskToDesk() = runTest {
val desk = createDesk()
val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
@@ -333,7 +291,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testMoveTaskToDesk_didNotExist_throws() {
+ fun testMoveTaskToDesk_didNotExist_throws() = runTest {
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
@@ -344,7 +302,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testGetDeskAtEnd() {
+ fun testGetDeskAtEnd() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
@@ -357,7 +315,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testGetDeskAtEnd_inMinimizationRoot() {
+ fun testGetDeskAtEnd_inMinimizationRoot() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
@@ -370,27 +328,24 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun testIsDeskActiveAtEnd() {
- organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
- freeformRoot.isVisibleRequested = true
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ fun testIsDeskActiveAtEnd() = runTest {
+ val desk = createDesk()
val isActive =
organizer.isDeskActiveAtEnd(
change =
- TransitionInfo.Change(freeformRoot.token, SurfaceControl()).apply {
- taskInfo = freeformRoot
+ TransitionInfo.Change(desk.deskRoot.token, SurfaceControl()).apply {
+ taskInfo = desk.deskRoot.taskInfo
mode = TRANSIT_TO_FRONT
},
- deskId = freeformRoot.taskId,
+ deskId = desk.deskRoot.deskId,
)
assertThat(isActive).isTrue()
}
@Test
- fun deactivateDesk_clearsLaunchRoot() {
+ fun deactivateDesk_clearsLaunchRoot() = runTest {
val wct = WindowContainerTransaction()
val desk = createDesk()
organizer.activateDesk(wct, desk.deskRoot.deskId)
@@ -409,7 +364,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun isDeskChange_forDeskId() {
+ fun isDeskChange_forDeskId() = runTest {
val desk = createDesk()
assertThat(
@@ -424,7 +379,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun isDeskChange_forDeskId_inMinimizationRoot() {
+ fun isDeskChange_forDeskId_inMinimizationRoot() = runTest {
val desk = createDesk()
assertThat(
@@ -442,7 +397,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun isDeskChange_anyDesk() {
+ fun isDeskChange_anyDesk() = runTest {
val desk = createDesk()
assertThat(
@@ -456,7 +411,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun isDeskChange_anyDesk_inMinimizationRoot() {
+ fun isDeskChange_anyDesk_inMinimizationRoot() = runTest {
val desk = createDesk()
assertThat(
@@ -473,7 +428,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun minimizeTask() {
+ fun minimizeTask() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
@@ -486,7 +441,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun minimizeTask_alreadyMinimized_noOp() {
+ fun minimizeTask_alreadyMinimized_noOp() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
val wct = WindowContainerTransaction()
@@ -498,7 +453,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun minimizeTask_inDifferentDesk_noOp() {
+ fun minimizeTask_inDifferentDesk_noOp() = runTest {
val desk = createDesk()
val otherDesk = createDesk()
val task = createFreeformTask().apply { parentTaskId = otherDesk.deskRoot.deskId }
@@ -511,7 +466,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun unminimizeTask() {
+ fun unminimizeTask() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
@@ -528,7 +483,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun unminimizeTask_alreadyUnminimized_noOp() {
+ fun unminimizeTask_alreadyUnminimized_noOp() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
@@ -542,7 +497,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun unminimizeTask_notInDesk_noOp() {
+ fun unminimizeTask_notInDesk_noOp() = runTest {
val desk = createDesk()
val task = createFreeformTask()
val wct = WindowContainerTransaction()
@@ -553,7 +508,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun reorderTaskToFront() {
+ fun reorderTaskToFront() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
@@ -573,7 +528,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun reorderTaskToFront_notInDesk_noOp() {
+ fun reorderTaskToFront_notInDesk_noOp() = runTest {
val desk = createDesk()
val task = createFreeformTask()
val wct = WindowContainerTransaction()
@@ -592,7 +547,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun reorderTaskToFront_minimized_unminimizesAndReorders() {
+ fun reorderTaskToFront_minimized_unminimizesAndReorders() = runTest {
val desk = createDesk()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
@@ -615,7 +570,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskAppeared_visibleDesk_onlyDesk_disablesLaunchAdjacent() {
+ fun onTaskAppeared_visibleDesk_onlyDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
createDesk(visible = true)
@@ -624,7 +579,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskAppeared_invisibleDesk_onlyDesk_enablesLaunchAdjacent() {
+ fun onTaskAppeared_invisibleDesk_onlyDesk_enablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = false
createDesk(visible = false)
@@ -633,7 +588,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskAppeared_invisibleDesk_otherVisibleDesk_disablesLaunchAdjacent() {
+ fun onTaskAppeared_invisibleDesk_otherVisibleDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
createDesk(visible = true)
@@ -643,7 +598,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskInfoChanged_deskBecomesVisible_onlyDesk_disablesLaunchAdjacent() {
+ fun onTaskInfoChanged_deskBecomesVisible_onlyDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
val desk = createDesk(visible = false)
@@ -654,7 +609,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskInfoChanged_deskBecomesInvisible_onlyDesk_enablesLaunchAdjacent() {
+ fun onTaskInfoChanged_deskBecomesInvisible_onlyDesk_enablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = false
val desk = createDesk(visible = true)
@@ -665,7 +620,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskInfoChanged_deskBecomesInvisible_otherVisibleDesk_disablesLaunchAdjacent() {
+ fun onTaskInfoChanged_deskBecomesInvisible_otherVisibleDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
createDesk(visible = true)
@@ -677,7 +632,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskVanished_visibleDeskDisappears_onlyDesk_enablesLaunchAdjacent() {
+ fun onTaskVanished_visibleDeskDisappears_onlyDesk_enablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = false
val desk = createDesk(visible = true)
@@ -687,7 +642,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun onTaskVanished_visibleDeskDisappears_otherDeskVisible_disablesLaunchAdjacent() {
+ fun onTaskVanished_visibleDeskDisappears_otherDeskVisible_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
createDesk(visible = true)
@@ -702,20 +657,39 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
val minimizationRoot: DeskMinimizationRoot,
)
- private fun createDesk(visible: Boolean = true): DeskRoots {
- organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
- val freeformRoot =
+ private suspend fun createDesk(visible: Boolean = true): DeskRoots {
+ val freeformRootTask =
createFreeformTask().apply {
parentTaskId = -1
isVisible = visible
+ isVisibleRequested = visible
}
- organizer.onTaskAppeared(freeformRoot, SurfaceControl())
- val minimizationRoot = createFreeformTask().apply { parentTaskId = -1 }
- organizer.onTaskAppeared(minimizationRoot, SurfaceControl())
- return DeskRoots(
- organizer.deskRootsByDeskId[freeformRoot.taskId],
- checkNotNull(organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]),
- )
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ Mockito.reset(mockShellTaskOrganizer)
+ whenever(
+ mockShellTaskOrganizer.createRootTask(
+ Display.DEFAULT_DISPLAY,
+ WINDOWING_MODE_FREEFORM,
+ organizer,
+ true,
+ )
+ )
+ .thenAnswer { invocation ->
+ val listener = (invocation.arguments[2] as TaskListener)
+ listener.onTaskAppeared(freeformRootTask, SurfaceControl())
+ }
+ .thenAnswer { invocation ->
+ val listener = (invocation.arguments[2] as TaskListener)
+ listener.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ }
+ val deskId = organizer.createDesk(Display.DEFAULT_DISPLAY)
+ assertEquals(freeformRootTask.taskId, deskId)
+ val deskRoot = assertNotNull(organizer.deskRootsByDeskId.get(freeformRootTask.taskId))
+ val minimizationRoot =
+ assertNotNull(organizer.deskMinimizationRootsByDeskId[freeformRootTask.taskId])
+ assertThat(minimizationRoot.deskId).isEqualTo(freeformRootTask.taskId)
+ assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
+ return DeskRoots(deskRoot, minimizationRoot)
}
private fun WindowContainerTransaction.hasMinimizationHops(
@@ -738,14 +712,4 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
hop.newParent == desk.deskRoot.token.asBinder() &&
hop.toTop
}
-
- private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {
- var deskId: Int? = null
- val created: Boolean
- get() = deskId != null
-
- override fun onCreated(deskId: Int) {
- this.deskId = deskId
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
index dd9e6ca0deae..4440d4e801fe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.desktopmode.persistence
import android.os.UserManager
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
@@ -82,10 +81,27 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
}
@Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_DESKTOP_WINDOWING_HSUM)
- /** TODO: b/362720497 - add multi-desk version when implemented. */
- @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun initWithPersistence_multipleUsers_addedCorrectly_multiDesksDisabled() =
+ fun init_updatesFlow() =
+ runTest(StandardTestDispatcher()) {
+ whenever(persistentRepository.getUserDesktopRepositoryMap())
+ .thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1))
+ whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
+ .thenReturn(desktopRepositoryState1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2)
+
+ repositoryInitializer.initialize(desktopUserRepositories)
+
+ assertThat(repositoryInitializer.isInitialized.value).isTrue()
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_HSUM,
+ FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun initWithPersistence_multipleUsers_addedCorrectly() =
runTest(StandardTestDispatcher()) {
whenever(persistentRepository.getUserDesktopRepositoryMap())
.thenReturn(
@@ -104,50 +120,74 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
repositoryInitializer.initialize(desktopUserRepositories)
- // Desktop Repository currently returns all tasks across desktops for a specific user
- // since the repository currently doesn't handle desktops. This test logic should be
- // updated
- // once the repository handles multiple desktops.
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getActiveTaskIdsInDesk(DESKTOP_ID_1)
)
- .containsExactly(1, 3, 4, 5)
+ .containsExactly(1, 3)
.inOrder()
assertThat(
desktopUserRepositories
.getProfile(USER_ID_1)
- .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ .getActiveTaskIdsInDesk(DESKTOP_ID_2)
)
- .containsExactly(5, 1)
+ .containsExactly(4, 5)
.inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_1).getMinimizedTasks(DEFAULT_DISPLAY)
+ desktopUserRepositories
+ .getProfile(USER_ID_2)
+ .getActiveTaskIdsInDesk(DESKTOP_ID_3)
)
- .containsExactly(3, 4)
+ .containsExactly(7, 8)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_1)
+ )
+ .containsExactly(1)
.inOrder()
-
assertThat(
- desktopUserRepositories.getProfile(USER_ID_2).getActiveTasks(DEFAULT_DISPLAY)
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_2)
)
- .containsExactly(7, 8)
+ .containsExactly(5)
.inOrder()
assertThat(
desktopUserRepositories
.getProfile(USER_ID_2)
- .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_3)
+ )
+ .containsExactly(7)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getMinimizedTaskIdsInDesk(DESKTOP_ID_1)
+ )
+ .containsExactly(3)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getMinimizedTaskIdsInDesk(DESKTOP_ID_2)
)
- .contains(7)
+ .containsExactly(4)
+ .inOrder()
assertThat(
- desktopUserRepositories.getProfile(USER_ID_2).getMinimizedTasks(DEFAULT_DISPLAY)
+ desktopUserRepositories
+ .getProfile(USER_ID_2)
+ .getMinimizedTaskIdsInDesk(DESKTOP_ID_3)
)
.containsExactly(8)
+ .inOrder()
}
@Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
- /** TODO: b/362720497 - add multi-desk version when implemented. */
- @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun initWithPersistence_singleUser_addedCorrectly_multiDesksDisabled() =
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun initWithPersistence_singleUser_addedCorrectly() =
runTest(StandardTestDispatcher()) {
whenever(persistentRepository.getUserDesktopRepositoryMap())
.thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1))
@@ -161,23 +201,44 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
assertThat(
desktopUserRepositories
.getProfile(USER_ID_1)
- .getActiveTaskIdsInDesk(deskId = DEFAULT_DISPLAY)
+ .getActiveTaskIdsInDesk(DESKTOP_ID_1)
+ )
+ .containsExactly(1, 3)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getActiveTaskIdsInDesk(DESKTOP_ID_2)
+ )
+ .containsExactly(4, 5)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_1)
+ )
+ .containsExactly(1)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories
+ .getProfile(USER_ID_1)
+ .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_2)
)
- .containsExactly(1, 3, 4, 5)
+ .containsExactly(5)
.inOrder()
assertThat(
desktopUserRepositories
.getProfile(USER_ID_1)
- .getExpandedTasksIdsInDeskOrdered(deskId = DEFAULT_DISPLAY)
+ .getMinimizedTaskIdsInDesk(DESKTOP_ID_1)
)
- .containsExactly(5, 1)
+ .containsExactly(3)
.inOrder()
assertThat(
desktopUserRepositories
.getProfile(USER_ID_1)
- .getMinimizedTaskIdsInDesk(deskId = DEFAULT_DISPLAY)
+ .getMinimizedTaskIdsInDesk(DESKTOP_ID_2)
)
- .containsExactly(3, 4)
+ .containsExactly(4)
.inOrder()
}