summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt74
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt285
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt74
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt280
7 files changed, 657 insertions, 119 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index c60fc1646c32..7c6cf4a8b37f 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
@@ -332,29 +332,22 @@ class DesktopRepository(
return false
}
- /**
- * Adds given task to the closing task list for [displayId]'s active desk.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun addClosingTask(displayId: Int, taskId: Int) {
- val activeDesk =
- desktopData.getActiveDesk(displayId)
- ?: error("Expected active desk in display: $displayId")
- if (activeDesk.closingTasks.add(taskId)) {
- logD(
- "Added closing task=%d displayId=%d deskId=%d",
- taskId,
- displayId,
- activeDesk.deskId,
- )
+ /** Adds given task to the closing task list of its desk. */
+ fun addClosingTask(displayId: Int, deskId: Int?, taskId: Int) {
+ val desk =
+ deskId?.let { desktopData.getDesk(it) }
+ ?: checkNotNull(desktopData.getActiveDesk(displayId)) {
+ "Expected active desk in display: $displayId"
+ }
+ if (desk.closingTasks.add(taskId)) {
+ logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, desk.deskId)
} else {
// If the task hasn't been removed from closing list after it disappeared.
logW(
"Task with taskId=%d displayId=%d deskId=%d is already closing",
taskId,
displayId,
- activeDesk.deskId,
+ desk.deskId,
)
}
}
@@ -392,7 +385,8 @@ class DesktopRepository(
* Checks if a task is the only visible, non-closing, non-minimized task on the active desk of
* the given display, or any display's active desk if [displayId] is [INVALID_DISPLAY].
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - consider forcing callers to use [isOnlyVisibleNonClosingTaskInDesk] with
+ * an explicit desk id instead of using this function and defaulting to the active one.
*/
fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
val activeDesks =
@@ -402,14 +396,27 @@ class DesktopRepository(
desktopData.getAllActiveDesks()
}
return activeDesks.any { desk ->
- desk.visibleTasks
- .subtract(desk.closingTasks)
- .subtract(desk.minimizedTasks)
- .singleOrNull() == taskId
+ isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = desk.deskId,
+ displayId = desk.displayId,
+ )
}
}
/**
+ * Checks if a task is the only visible, non-closing, non-minimized task on the given desk of
+ * the given display.
+ */
+ fun isOnlyVisibleNonClosingTaskInDesk(taskId: Int, deskId: Int, displayId: Int): Boolean {
+ val desk = desktopData.getDesk(deskId) ?: return false
+ return desk.visibleTasks
+ .subtract(desk.closingTasks)
+ .subtract(desk.minimizedTasks)
+ .singleOrNull() == taskId
+ }
+
+ /**
* Returns the active tasks in the given display's active desk.
*
* TODO: b/389960283 - migrate callers to [getActiveTaskIdsInDesk].
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 0fbb84075179..45adfe4112a6 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
@@ -802,6 +802,9 @@ class DesktopTasksController(
): ((IBinder) -> Unit) {
val taskId = taskInfo.taskId
val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ error("Did not find desk for task: $taskId")
+ }
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val shouldExitDesktop =
willExitDesktop(
@@ -819,7 +822,7 @@ class DesktopTasksController(
shouldEndUpAtHome = true,
)
- taskRepository.addClosingTask(displayId, taskId)
+ taskRepository.addClosingTask(displayId = displayId, deskId = deskId, taskId = taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(displayId, taskId)
)
@@ -871,6 +874,10 @@ class DesktopTasksController(
private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val taskId = taskInfo.taskId
val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ logW("minimizeTaskInner: desk not found for task: ${taskInfo.taskId}")
+ return
+ }
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
@@ -891,10 +898,26 @@ class DesktopTasksController(
taskInfo = taskInfo,
reason = DesktopImmersiveController.ExitReason.MINIMIZED,
)
-
- wct.reorder(taskInfo.token, false)
- val isLastTask = taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)
- val transition: IBinder =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.minimizeTask(
+ wct = wct,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ task = taskInfo,
+ )
+ } else {
+ wct.reorder(taskInfo.token, /* onTop= */ false)
+ }
+ val isLastTask =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ taskRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ displayId = displayId,
+ )
+ } else {
+ taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+ }
+ val transition =
freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(
@@ -1232,9 +1255,9 @@ class DesktopTasksController(
// home.
if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
performDesktopExitCleanupIfNeeded(
- task.taskId,
- task.displayId,
- wct,
+ taskId = task.taskId,
+ displayId = task.displayId,
+ wct = wct,
forceToFullscreen = false,
// TODO: b/371096166 - Temporary turing home relaunch off to prevent home stealing
// display focus. Remove shouldEndUpAtHome = false when home focus handling
@@ -1801,6 +1824,7 @@ class DesktopTasksController(
private fun performDesktopExitCleanupIfNeeded(
taskId: Int,
+ deskId: Int? = null,
displayId: Int,
wct: WindowContainerTransaction,
forceToFullscreen: Boolean,
@@ -1814,13 +1838,14 @@ class DesktopTasksController(
// |RunOnTransitStart| when the transition is started.
return performDesktopExitCleanUp(
wct = wct,
- deskId = null,
+ deskId = deskId,
displayId = displayId,
willExitDesktop = true,
shouldEndUpAtHome = shouldEndUpAtHome,
)
}
+ /** TODO: b/394268248 - update [deskId] to be non-null. */
private fun performDesktopExitCleanUp(
wct: WindowContainerTransaction,
deskId: Int?,
@@ -2373,17 +2398,28 @@ class DesktopTasksController(
): WindowContainerTransaction? {
logV("handleTaskClosing")
if (!isDesktopModeShowing(task.displayId)) return null
+ val deskId = taskRepository.getDeskIdForTask(task.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ return null
+ }
val wct = WindowContainerTransaction()
- performDesktopExitCleanupIfNeeded(
- task.taskId,
- task.displayId,
- wct,
- forceToFullscreen = false,
- )
+ val deactivationRunnable =
+ performDesktopExitCleanupIfNeeded(
+ taskId = task.taskId,
+ deskId = deskId,
+ displayId = task.displayId,
+ wct = wct,
+ forceToFullscreen = false,
+ )
+ deactivationRunnable?.invoke(transition)
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
- taskRepository.addClosingTask(task.displayId, task.taskId)
+ taskRepository.addClosingTask(
+ displayId = task.displayId,
+ deskId = deskId,
+ taskId = task.taskId,
+ )
snapEventHandler.removeTaskIfTiled(task.displayId, task.taskId)
}
@@ -2587,9 +2623,9 @@ class DesktopTasksController(
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
performDesktopExitCleanupIfNeeded(
- taskInfo.taskId,
- taskInfo.displayId,
- wct,
+ taskId = taskInfo.taskId,
+ displayId = taskInfo.displayId,
+ wct = wct,
forceToFullscreen = false,
shouldEndUpAtHome = false,
)
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 0f2f3711a9a3..fc359d7d67b6 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
@@ -40,9 +40,19 @@ interface DesksOrganizer {
task: ActivityManager.RunningTaskInfo,
)
+ /** Minimizes the given task of the given deskId. */
+ fun minimizeTask(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ )
+
/** Whether the change is for the given desk id. */
fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean
+ /** Whether the change is for a known desk. */
+ fun isDeskChange(change: TransitionInfo.Change): Boolean
+
/**
* Returns the desk id in which the task in the given change is located at the end of a
* transition, if any.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index 339932cabd2c..f576258ebdaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -15,7 +15,9 @@
*/
package com.android.wm.shell.desktopmode.multidesks
+import android.annotation.SuppressLint
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -25,6 +27,7 @@ import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DesktopExperienceFlags
import android.window.TransitionInfo
+import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import androidx.core.util.forEach
import com.android.internal.annotations.VisibleForTesting
@@ -43,8 +46,12 @@ class RootTaskDesksOrganizer(
private val shellTaskOrganizer: ShellTaskOrganizer,
) : DesksOrganizer, ShellTaskOrganizer.TaskListener {
- private val deskCreateRequests = mutableListOf<CreateRequest>()
- @VisibleForTesting val roots = SparseArray<DeskRoot>()
+ private val createDeskRootRequests = mutableListOf<CreateDeskRequest>()
+ @VisibleForTesting val deskRootsByDeskId = SparseArray<DeskRoot>()
+ private val createDeskMinimizationRootRequests =
+ mutableListOf<CreateDeskMinimizationRootRequest>()
+ @VisibleForTesting
+ val deskMinimizationRootsByDeskId: MutableMap<Int, DeskMinimizationRoot> = mutableMapOf()
init {
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -57,7 +64,7 @@ class RootTaskDesksOrganizer(
override fun createDesk(displayId: Int, callback: OnCreateCallback) {
logV("createDesk in display: %d", displayId)
- deskCreateRequests += CreateRequest(displayId, callback)
+ createDeskRootRequests += CreateDeskRequest(displayId, callback)
shellTaskOrganizer.createRootTask(
displayId,
WINDOWING_MODE_FREEFORM,
@@ -68,14 +75,14 @@ class RootTaskDesksOrganizer(
override fun removeDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("removeDesk %d", deskId)
- val desk = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
- wct.removeRootTask(desk.taskInfo.token)
+ deskRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
+ deskMinimizationRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
}
override fun activateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("activateDesk %d", deskId)
- val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
- wct.reorder(root.taskInfo.token, /* onTop= */ true)
+ val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ wct.reorder(root.token, /* onTop= */ true)
wct.setLaunchRoot(
/* container= */ root.taskInfo.token,
/* windowingModes= */ intArrayOf(WINDOWING_MODE_FREEFORM, WINDOWING_MODE_UNDEFINED),
@@ -85,7 +92,7 @@ class RootTaskDesksOrganizer(
override fun deactivateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("deactivateDesk %d", deskId)
- val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
+ val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
wct.setLaunchRoot(
/* container= */ root.taskInfo.token,
/* windowingModes= */ null,
@@ -98,16 +105,58 @@ class RootTaskDesksOrganizer(
deskId: Int,
task: RunningTaskInfo,
) {
- val root = roots[deskId] ?: error("Root not found for desk: $deskId")
+ val root = deskRootsByDeskId[deskId] ?: error("Root not found for desk: $deskId")
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
wct.reparent(task.token, root.taskInfo.token, /* onTop= */ true)
}
+ override fun minimizeTask(wct: WindowContainerTransaction, deskId: Int, task: RunningTaskInfo) {
+ val deskRoot =
+ checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ val minimizationRoot =
+ checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+ "Minimization root not found for desk: $deskId"
+ }
+ val taskId = task.taskId
+ if (taskId in minimizationRoot.children) {
+ logV("Task #$taskId is already minimized in desk #$deskId")
+ return
+ }
+ if (taskId !in deskRoot.children) {
+ logE("Attempted to minimize task=${task.taskId} in desk=$deskId but it was not a child")
+ return
+ }
+ wct.reparent(task.token, minimizationRoot.token, /* onTop= */ true)
+ }
+
override fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean =
- roots.contains(deskId) && change.taskInfo?.taskId == deskId
+ (isDeskRootChange(change) && change.taskId == deskId) ||
+ (getDeskMinimizationRootInChange(change)?.deskId == deskId)
+
+ override fun isDeskChange(change: TransitionInfo.Change): Boolean =
+ isDeskRootChange(change) || getDeskMinimizationRootInChange(change) != null
+
+ private fun isDeskRootChange(change: TransitionInfo.Change): Boolean =
+ change.taskId in deskRootsByDeskId
- override fun getDeskAtEnd(change: TransitionInfo.Change): Int? =
- change.taskInfo?.parentTaskId?.takeIf { it in roots }
+ private fun getDeskMinimizationRootInChange(
+ change: TransitionInfo.Change
+ ): DeskMinimizationRoot? =
+ deskMinimizationRootsByDeskId.values.find { it.rootId == change.taskId }
+
+ private val TransitionInfo.Change.taskId: Int
+ get() = taskInfo?.taskId ?: INVALID_TASK_ID
+
+ override fun getDeskAtEnd(change: TransitionInfo.Change): Int? {
+ val parentTaskId = change.taskInfo?.parentTaskId ?: return null
+ if (parentTaskId in deskRootsByDeskId) {
+ return parentTaskId
+ }
+ val deskMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.find { root -> root.rootId == parentTaskId }
+ ?: return null
+ return deskMinimizationRoot.deskId
+ }
override fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean =
change.taskInfo?.taskId == deskId &&
@@ -115,51 +164,176 @@ class RootTaskDesksOrganizer(
change.mode == TRANSIT_TO_FRONT
override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
- if (taskInfo.parentTaskId in roots) {
+ // Check whether this task is appearing inside a desk.
+ if (taskInfo.parentTaskId in deskRootsByDeskId) {
val deskId = taskInfo.parentTaskId
val taskId = taskInfo.taskId
logV("Task #$taskId appeared in desk #$deskId")
addChildToDesk(taskId = taskId, deskId = deskId)
return
}
- val deskId = taskInfo.taskId
- check(deskId !in roots) { "A root already exists for desk: $deskId" }
- val request =
- checkNotNull(deskCreateRequests.firstOrNull { it.displayId == taskInfo.displayId }) {
- "Task ${taskInfo.taskId} appeared without pending create request"
- }
- logV("Desk #$deskId appeared")
- roots[deskId] = DeskRoot(deskId, taskInfo, leash)
- deskCreateRequests.remove(request)
- request.onCreateCallback.onCreated(deskId)
+ // Check whether this task is appearing in a minimization root.
+ val minimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == taskInfo.parentTaskId }
+ if (minimizationRoot != null) {
+ val deskId = minimizationRoot.deskId
+ val taskId = taskInfo.taskId
+ logV("Task #$taskId was minimized in desk #$deskId ")
+ addChildToMinimizationRoot(taskId = taskId, deskId = deskId)
+ return
+ }
+ // The appearing task is a root (either a desk or a minimization root), it should not exist
+ // already.
+ check(taskInfo.taskId !in deskRootsByDeskId) {
+ "A root already exists for desk: ${taskInfo.taskId}"
+ }
+ check(deskMinimizationRootsByDeskId.values.none { it.rootId == taskInfo.taskId }) {
+ "A minimization root already exists with rootId: ${taskInfo.taskId}"
+ }
+
+ val appearingInDisplayId = taskInfo.displayId
+ // Check if there's any pending desk creation requests under this display.
+ val deskRequest =
+ createDeskRootRequests.firstOrNull { it.displayId == appearingInDisplayId }
+ if (deskRequest != null) {
+ // Appearing root matches desk request.
+ val deskId = taskInfo.taskId
+ logV("Desk #$deskId appeared")
+ deskRootsByDeskId[deskId] = DeskRoot(deskId, taskInfo, leash)
+ createDeskRootRequests.remove(deskRequest)
+ deskRequest.onCreateCallback.onCreated(deskId)
+ createDeskMinimizationRoot(displayId = appearingInDisplayId, deskId = deskId)
+ return
+ }
+ // Check if there's any pending minimization container creation requests under this display.
+ val deskMinimizationRootRequest =
+ createDeskMinimizationRootRequests.first { it.displayId == appearingInDisplayId }
+ val deskId = deskMinimizationRootRequest.deskId
+ logV("Minimization container for desk #$deskId appeared with id=${taskInfo.taskId}")
+ val deskMinimizationRoot = DeskMinimizationRoot(deskId, taskInfo, leash)
+ deskMinimizationRootsByDeskId[deskId] = deskMinimizationRoot
+ createDeskMinimizationRootRequests.remove(deskMinimizationRootRequest)
+ hideMinimizationRoot(deskMinimizationRoot)
}
override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
- if (roots.contains(taskInfo.taskId)) {
+ if (deskRootsByDeskId.contains(taskInfo.taskId)) {
val deskId = taskInfo.taskId
- roots[deskId] = roots[deskId].copy(taskInfo = taskInfo)
+ deskRootsByDeskId[deskId] = deskRootsByDeskId[deskId].copy(taskInfo = taskInfo)
+ logV("Desk #$deskId's task info changed")
+ return
}
+ val minimizationRoot =
+ deskMinimizationRootsByDeskId.values.find { root -> root.rootId == taskInfo.taskId }
+ if (minimizationRoot != null) {
+ deskMinimizationRootsByDeskId.remove(minimizationRoot.deskId)
+ deskMinimizationRootsByDeskId[minimizationRoot.deskId] =
+ minimizationRoot.copy(taskInfo = taskInfo)
+ logV("Minimization root for desk#${minimizationRoot.deskId} task info changed")
+ return
+ }
+
+ val parentTaskId = taskInfo.parentTaskId
+ if (parentTaskId in deskRootsByDeskId) {
+ val deskId = taskInfo.parentTaskId
+ val taskId = taskInfo.taskId
+ logV("onTaskInfoChanged: Task #$taskId appeared in desk #$deskId")
+ addChildToDesk(taskId = taskId, deskId = deskId)
+ return
+ }
+ // Check whether this task is appearing in a minimization root.
+ val parentMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == parentTaskId }
+ if (parentMinimizationRoot != null) {
+ val deskId = parentMinimizationRoot.deskId
+ val taskId = taskInfo.taskId
+ logV("onTaskInfoChanged: Task #$taskId was minimized in desk #$deskId ")
+ addChildToMinimizationRoot(taskId = taskId, deskId = deskId)
+ return
+ }
+ logE("onTaskInfoChanged: unknown task: ${taskInfo.taskId}")
}
override fun onTaskVanished(taskInfo: RunningTaskInfo) {
- if (roots.contains(taskInfo.taskId)) {
+ if (deskRootsByDeskId.contains(taskInfo.taskId)) {
val deskId = taskInfo.taskId
- val deskRoot = roots[deskId]
+ val deskRoot = deskRootsByDeskId[deskId]
// Use the last saved taskInfo to obtain the displayId. Using the local one here will
// return -1 since the task is not unassociated with a display.
val displayId = deskRoot.taskInfo.displayId
logV("Desk #$deskId vanished from display #$displayId")
- roots.remove(deskId)
+ deskRootsByDeskId.remove(deskId)
+ return
+ }
+ val deskMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == taskInfo.taskId }
+ if (deskMinimizationRoot != null) {
+ logV("Minimization root for desk ${deskMinimizationRoot.deskId} vanished")
+ deskMinimizationRootsByDeskId.remove(deskMinimizationRoot.deskId)
return
}
+
+ // Check whether the vanishing task was a child of any desk.
// At this point, [parentTaskId] may be unset even if this is a task vanishing from a desk,
// so search through each root to remove this if it's a child.
- roots.forEach { deskId, deskRoot ->
+ deskRootsByDeskId.forEach { deskId, deskRoot ->
if (deskRoot.children.remove(taskInfo.taskId)) {
logV("Task #${taskInfo.taskId} vanished from desk #$deskId")
return
}
}
+ // Check whether the vanishing task was a child of the minimized root and remove it.
+ deskMinimizationRootsByDeskId.values.forEach { root ->
+ val taskId = taskInfo.taskId
+ if (root.children.remove(taskId)) {
+ logV("Task #$taskId vanished from minimization root of desk #${root.deskId}")
+ return
+ }
+ }
+ }
+
+ private fun createDeskMinimizationRoot(displayId: Int, deskId: Int) {
+ createDeskMinimizationRootRequests +=
+ CreateDeskMinimizationRootRequest(displayId = displayId, deskId = deskId)
+ shellTaskOrganizer.createRootTask(
+ displayId,
+ WINDOWING_MODE_FREEFORM,
+ /* listener = */ this,
+ /* removeWithTaskOrganizer = */ true,
+ )
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun hideMinimizationRoot(root: DeskMinimizationRoot) {
+ shellTaskOrganizer.applyTransaction(
+ WindowContainerTransaction().apply { setHidden(root.token, /* hidden= */ true) }
+ )
+ }
+
+ private fun addChildToDesk(taskId: Int, deskId: Int) {
+ deskRootsByDeskId.forEach { _, deskRoot ->
+ if (deskRoot.deskId == deskId) {
+ deskRoot.children.add(taskId)
+ } else {
+ deskRoot.children.remove(taskId)
+ }
+ }
+ // A task cannot be in both a desk root and a minimization root at the same time, so make
+ // sure to remove them if needed.
+ deskMinimizationRootsByDeskId.values.forEach { root -> root.children.remove(taskId) }
+ }
+
+ private fun addChildToMinimizationRoot(taskId: Int, deskId: Int) {
+ deskMinimizationRootsByDeskId.forEach { _, minimizationRoot ->
+ if (minimizationRoot.deskId == deskId) {
+ minimizationRoot.children += taskId
+ } else {
+ minimizationRoot.children -= taskId
+ }
+ }
+ // A task cannot be in both a desk root and a minimization root at the same time, so make
+ // sure to remove them if needed.
+ deskRootsByDeskId.forEach { _, deskRoot -> deskRoot.children -= taskId }
}
@VisibleForTesting
@@ -168,34 +342,55 @@ class RootTaskDesksOrganizer(
val taskInfo: RunningTaskInfo,
val leash: SurfaceControl,
val children: MutableSet<Int> = mutableSetOf(),
+ ) {
+ val token: WindowContainerToken = taskInfo.token
+ }
+
+ @VisibleForTesting
+ data class DeskMinimizationRoot(
+ val deskId: Int,
+ val taskInfo: RunningTaskInfo,
+ val leash: SurfaceControl,
+ val children: MutableSet<Int> = mutableSetOf(),
+ ) {
+ val rootId: Int
+ get() = taskInfo.taskId
+
+ val token: WindowContainerToken = taskInfo.token
+ }
+
+ private data class CreateDeskRequest(
+ val displayId: Int,
+ val onCreateCallback: OnCreateCallback,
)
+ private data class CreateDeskMinimizationRootRequest(val displayId: Int, val deskId: Int)
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logE(msg: String, vararg arguments: Any?) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
override fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("$prefix$TAG")
pw.println("${innerPrefix}Desk Roots:")
- roots.forEach { deskId, root ->
+ deskRootsByDeskId.forEach { deskId, root ->
+ val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
pw.println("$innerPrefix #$deskId visible=${root.taskInfo.isVisible}")
+ pw.println("$innerPrefix displayId=${root.taskInfo.displayId}")
pw.println("$innerPrefix children=${root.children}")
- }
- }
-
- private fun addChildToDesk(taskId: Int, deskId: Int) {
- roots.forEach { _, deskRoot ->
- if (deskRoot.deskId == deskId) {
- deskRoot.children.add(taskId)
- } else {
- deskRoot.children.remove(taskId)
+ pw.println("$innerPrefix minimization root:")
+ pw.println("$innerPrefix rootId=${minimizationRoot?.rootId}")
+ if (minimizationRoot != null) {
+ pw.println("$innerPrefix children=${minimizationRoot.children}")
}
}
}
- private data class CreateRequest(val displayId: Int, val onCreateCallback: OnCreateCallback)
-
- private fun logV(msg: String, vararg arguments: Any?) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
companion object {
private const val TAG = "RootTaskDesksOrganizer"
}
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 ed9b97d264f7..9bff287e314a 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
@@ -333,7 +333,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
@Test
fun isOnlyVisibleNonClosingTask_singleVisibleClosingTask() {
repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.addClosingTask(DEFAULT_DISPLAY, 1)
+ repo.addClosingTask(displayId = DEFAULT_DISPLAY, deskId = 0, taskId = 1)
// A visible task that's closing
assertThat(repo.isVisibleTask(1)).isTrue()
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 fcd92ac2678a..2e63c4f51792 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
@@ -2827,7 +2827,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
fun onDesktopWindowClose_singleActiveTask_isClosing() {
val task = setUpFreeformTask()
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+ taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, deskId = 0, taskId = task.taskId)
val wct = WindowContainerTransaction()
controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
@@ -2864,7 +2864,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val wct = WindowContainerTransaction()
controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
@@ -3225,6 +3229,30 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onDesktopWindowMinimize_minimizesTask() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ val runOnTransit = RunOnStartTransitionCallback()
+ whenever(
+ freeformTaskTransitionStarter.startMinimizedModeTransition(
+ any(),
+ anyInt(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(transition)
+ whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
+ .thenReturn(
+ ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
+ )
+
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+ verify(desksOrganizer).minimizeTask(any(), /* deskId= */ eq(0), eq(task))
+ }
+
+ @Test
fun onDesktopWindowMinimize_triesToStopTiling() {
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val transition = Binder()
@@ -3972,7 +4000,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val result =
controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
@@ -4083,6 +4115,36 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_closeTransition_onlyDesktopTask_deactivatesDesk() {
+ val task = setUpFreeformTask()
+
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ verify(desksOrganizer).deactivateDesk(any(), /* deskId= */ eq(0))
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_closeTransition_onlyDesktopTask_addsDeactivatesDeskTransition() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+
+ controller.handleRequest(transition, createTransition(task, type = TRANSIT_CLOSE))
+
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
val task1 = setUpFreeformTask()
@@ -4115,7 +4177,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val result =
controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
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 8b10ca1a2a70..96b85ad2729e 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
@@ -22,6 +22,7 @@ import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.Change
import android.window.WindowContainerTransaction.HierarchyOp
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
import androidx.test.filters.SmallTest
@@ -29,15 +30,19 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskMinimizationRoot
import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskRoot
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 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.verify
+import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
/**
@@ -75,6 +80,43 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@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 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())
+
+ verify(mockShellTaskOrganizer)
+ .applyTransaction(
+ argThat { wct ->
+ wct.changes.any { change ->
+ change.key == minimizationRootTask.token.asBinder() &&
+ (change.value.changeMask and Change.CHANGE_HIDDEN != 0) &&
+ change.value.hidden
+ }
+ }
+ )
+ }
+
+ @Test
fun testOnTaskAppeared_withoutRequest_throws() {
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
@@ -105,57 +147,122 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@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())
+
+ assertThrows(Exception::class.java) {
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ }
+ }
+
+ @Test
fun testOnTaskVanished_removesRoot() {
val desk = createDesk()
- organizer.onTaskVanished(desk.taskInfo)
+ organizer.onTaskVanished(desk.deskRoot.taskInfo)
+
+ assertThat(organizer.deskRootsByDeskId.contains(desk.deskRoot.deskId)).isFalse()
+ }
+
+ @Test
+ fun testOnTaskVanished_removesMinimizedRoot() {
+ val desk = createDesk()
+
+ organizer.onTaskVanished(desk.deskRoot.taskInfo)
+ organizer.onTaskVanished(desk.minimizationRoot.taskInfo)
- assertThat(organizer.roots.contains(desk.deskId)).isFalse()
+ assertThat(organizer.deskMinimizationRootsByDeskId.contains(desk.deskRoot.deskId)).isFalse()
}
@Test
fun testDesktopWindowAppearsInDesk() {
val desk = createDesk()
- val child = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
- assertThat(desk.children).contains(child.taskId)
+ assertThat(desk.deskRoot.children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowAppearsInDeskMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ assertThat(desk.minimizationRoot.children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowMovesToMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ child.parentTaskId = desk.minimizationRoot.rootId
+ organizer.onTaskInfoChanged(child)
+
+ assertThat(desk.deskRoot.children).doesNotContain(child.taskId)
+ assertThat(desk.minimizationRoot.children).contains(child.taskId)
}
@Test
fun testDesktopWindowDisappearsFromDesk() {
val desk = createDesk()
- val child = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
organizer.onTaskVanished(child)
- assertThat(desk.children).doesNotContain(child.taskId)
+ assertThat(desk.deskRoot.children).doesNotContain(child.taskId)
}
@Test
- fun testRemoveDesk() {
+ fun testDesktopWindowDisappearsFromDeskMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+ organizer.onTaskVanished(child)
+
+ assertThat(desk.minimizationRoot.children).doesNotContain(child.taskId)
+ }
+
+ @Test
+ fun testRemoveDesk_removesDeskRoot() {
val desk = createDesk()
val wct = WindowContainerTransaction()
- organizer.removeDesk(wct, desk.deskId)
+ organizer.removeDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.token.asBinder()
}
)
.isTrue()
}
@Test
- fun testRemoveDesk_didNotExist_throws() {
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ fun testRemoveDesk_removesMinimizationRoot() {
+ val desk = createDesk()
val wct = WindowContainerTransaction()
- assertThrows(Exception::class.java) { organizer.removeDesk(wct, freeformRoot.taskId) }
+ organizer.removeDesk(wct, desk.deskRoot.deskId)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
+ hop.container == desk.minimizationRoot.token.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -163,20 +270,20 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
val desk = createDesk()
val wct = WindowContainerTransaction()
- organizer.activateDesk(wct, desk.deskId)
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REORDER &&
hop.toTop &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
@@ -196,14 +303,14 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
val wct = WindowContainerTransaction()
- organizer.moveTaskToDesk(wct, desk.deskId, desktopTask)
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, desktopTask)
assertThat(
wct.hierarchyOps.any { hop ->
hop.isReparent &&
hop.toTop &&
hop.container == desktopTask.token.asBinder() &&
- hop.newParent == desk.taskInfo.token.asBinder()
+ hop.newParent == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
@@ -231,13 +338,26 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun testGetDeskAtEnd() {
val desk = createDesk()
- val task = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val endDesk =
+ organizer.getDeskAtEnd(
+ TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+ )
+
+ assertThat(endDesk).isEqualTo(desk.deskRoot.deskId)
+ }
+
+ @Test
+ fun testGetDeskAtEnd_inMinimizationRoot() {
+ val desk = createDesk()
+
+ val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
val endDesk =
organizer.getDeskAtEnd(
TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
)
- assertThat(endDesk).isEqualTo(desk.deskId)
+ assertThat(endDesk).isEqualTo(desk.deskRoot.deskId)
}
@Test
@@ -264,14 +384,14 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun deactivateDesk_clearsLaunchRoot() {
val wct = WindowContainerTransaction()
val desk = createDesk()
- organizer.activateDesk(wct, desk.deskId)
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
- organizer.deactivateDesk(wct, desk.deskId)
+ organizer.deactivateDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
- hop.container == desk.taskInfo.token.asBinder() &&
+ hop.container == desk.deskRoot.taskInfo.token.asBinder() &&
hop.windowingModes == null &&
hop.activityTypes == null
}
@@ -280,25 +400,129 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun isDeskChange() {
+ fun isDeskChange_forDeskId() {
val desk = createDesk()
assertThat(
organizer.isDeskChange(
- TransitionInfo.Change(desk.taskInfo.token, desk.leash).apply {
- taskInfo = desk.taskInfo
+ TransitionInfo.Change(desk.deskRoot.taskInfo.token, desk.deskRoot.leash).apply {
+ taskInfo = desk.deskRoot.taskInfo
},
- desk.deskId,
+ desk.deskRoot.deskId,
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_forDeskId_inMinimizationRoot() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(
+ desk.minimizationRoot.token,
+ desk.minimizationRoot.leash,
+ )
+ .apply { taskInfo = desk.minimizationRoot.taskInfo },
+ deskId = desk.deskRoot.deskId,
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_anyDesk() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(desk.deskRoot.taskInfo.token, desk.deskRoot.leash)
+ .apply { taskInfo = desk.deskRoot.taskInfo }
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_anyDesk_inMinimizationRoot() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(
+ desk.minimizationRoot.taskInfo.token,
+ desk.minimizationRoot.leash,
+ )
+ .apply { taskInfo = desk.minimizationRoot.taskInfo }
)
)
.isTrue()
}
- private fun createDesk(): DeskRoot {
+ @Test
+ fun minimizeTask() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.isReparent &&
+ hop.container == task.token.asBinder() &&
+ hop.newParent == desk.minimizationRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun minimizeTask_alreadyMinimized_noOp() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ @Test
+ fun minimizeTask_inDifferentDesk_noOp() {
+ val desk = createDesk()
+ val otherDesk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = otherDesk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ private data class DeskRoots(
+ val deskRoot: DeskRoot,
+ val minimizationRoot: DeskMinimizationRoot,
+ )
+
+ private fun createDesk(): DeskRoots {
organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
organizer.onTaskAppeared(freeformRoot, SurfaceControl())
- return organizer.roots[freeformRoot.taskId]
+ val minimizationRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRoot, SurfaceControl())
+ return DeskRoots(
+ organizer.deskRootsByDeskId[freeformRoot.taskId],
+ checkNotNull(organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]),
+ )
}
private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {