Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +23 −6 Original line number Diff line number Diff line Loading @@ -108,6 +108,8 @@ import com.android.wm.shell.desktopmode.education.AppToWebEducationController; import com.android.wm.shell.desktopmode.education.AppToWebEducationFilter; import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository; import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository; import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer; import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer; import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository; import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer; import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializerImpl; Loading Loading @@ -701,6 +703,16 @@ public abstract class WMShellModule { // Desktop mode (optional feature) // @WMSingleton @Provides static DesksOrganizer provideDesksOrganizer( @NonNull ShellInit shellInit, @NonNull ShellCommandHandler shellCommandHandler, @NonNull ShellTaskOrganizer shellTaskOrganizer ) { return new RootTaskDesksOrganizer(shellInit, shellCommandHandler, shellTaskOrganizer); } @WMSingleton @Provides @DynamicOverride Loading Loading @@ -741,7 +753,8 @@ public abstract class WMShellModule { DesktopTilingDecorViewModel desktopTilingDecorViewModel, DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider, Optional<BubbleController> bubbleController, OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver) { OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver, DesksOrganizer desksOrganizer) { return new DesktopTasksController( context, shellInit, Loading Loading @@ -775,7 +788,8 @@ public abstract class WMShellModule { desktopTilingDecorViewModel, desktopWallpaperActivityTokenProvider, bubbleController, overviewToDesktopTransitionObserver); overviewToDesktopTransitionObserver, desksOrganizer); } @WMSingleton Loading Loading @@ -1183,10 +1197,11 @@ public abstract class WMShellModule { Transitions transitions, DisplayController displayController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, IWindowManager windowManager IWindowManager windowManager, Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController ) { if (!DesktopModeStatus.canEnterDesktopMode(context) || !Flags.enableDisplayWindowingModeSwitching()) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); } return Optional.of( Loading @@ -1196,7 +1211,9 @@ public abstract class WMShellModule { transitions, displayController, rootTaskDisplayAreaOrganizer, windowManager)); windowManager, desktopUserRepositories.get(), desktopTasksController.get())); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +48 −6 Original line number Diff line number Diff line Loading @@ -24,9 +24,14 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.IWindowManager import android.view.WindowManager.TRANSIT_CHANGE import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer 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.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions Loading @@ -38,7 +43,12 @@ class DesktopDisplayEventHandler( private val displayController: DisplayController, private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, private val windowManager: IWindowManager, ) : OnDisplaysChangedListener { private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, ) : OnDisplaysChangedListener, OnDeskRemovedListener { private val desktopRepository: DesktopRepository get() = desktopUserRepositories.current init { shellInit.addInitCallback({ onInit() }, this) Loading @@ -46,23 +56,43 @@ class DesktopDisplayEventHandler( private fun onInit() { displayController.addDisplayWindowListener(this) if (Flags.enableMultipleDesktopsBackend()) { desktopTasksController.onDeskRemovedListener = this } } override fun onDisplayAdded(displayId: Int) { if (displayId == DEFAULT_DISPLAY) { return } if (displayId != DEFAULT_DISPLAY) { refreshDisplayWindowingMode() } override fun onDisplayRemoved(displayId: Int) { if (displayId == DEFAULT_DISPLAY) { if (!supportsDesks(displayId)) { logV("Display #$displayId does not support desks") return } logV("Creating new desk in new display#$displayId") desktopTasksController.createDesk(displayId) } override fun onDisplayRemoved(displayId: Int) { if (displayId != DEFAULT_DISPLAY) { refreshDisplayWindowingMode() } // TODO: b/362720497 - move desks in closing display to the remaining desk. } override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) { val remainingDesks = desktopRepository.getNumberOfDesks(lastDisplayId) if (remainingDesks == 0) { logV("All desks removed from display#$lastDisplayId, creating empty desk") desktopTasksController.createDesk(lastDisplayId) } } private fun refreshDisplayWindowingMode() { if (!Flags.enableDisplayWindowingModeSwitching()) return // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available. val isExtendedDisplayEnabled = 0 != Loading Loading @@ -98,4 +128,16 @@ class DesktopDisplayEventHandler( wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode) transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) } // TODO: b/362720497 - connected/projected display considerations. private fun supportsDesks(displayId: Int): Boolean = DesktopModeStatus.canEnterDesktopMode(context) private fun logV(msg: String, vararg arguments: Any?) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } companion object { private const val TAG = "DesktopDisplayEventHandler" } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt +2 −2 Original line number Diff line number Diff line Loading @@ -110,8 +110,8 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl pw.println("Error: display id should be an integer") return false } pw.println("Not implemented.") return false controller.createDesk(displayId) return true } private fun runActivateDesk(args: Array<String>, pw: PrintWriter): Boolean { Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +61 −48 Original line number Diff line number Diff line Loading @@ -171,6 +171,9 @@ class DesktopRepository( /** Returns a list of all [Desk]s in the repository. */ private fun desksSequence(): Sequence<Desk> = desktopData.desksSequence() /** Returns the number of desks in the given display. */ fun getNumberOfDesks(displayId: Int) = desktopData.getNumberOfDesks(displayId) /** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */ fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) { desktopGestureExclusionListener = regionListener Loading Loading @@ -201,11 +204,11 @@ class DesktopRepository( /** Adds the given desk under the given display. */ fun addDesk(displayId: Int, deskId: Int) { desktopData.getOrCreateDesk(displayId, deskId) desktopData.createDesk(displayId, deskId) } /** Returns the default desk in the given display. */ fun getDefaultDesk(displayId: Int): Int? = desktopData.getDefaultDesk(displayId)?.deskId private fun getDefaultDesk(displayId: Int): Desk? = desktopData.getDefaultDesk(displayId) /** Sets the given desk as the active one in the given display. */ fun setActiveDesk(displayId: Int, deskId: Int) { Loading @@ -229,15 +232,14 @@ class DesktopRepository( * TODO: b/389960283 - add explicit [deskId] argument. */ private fun addActiveTask(displayId: Int, taskId: Int) { val activeDeskId = desktopData.getActiveDesk(displayId)?.deskId ?: error("Expected active desk in display: $displayId") val activeDesk = desktopData.getDefaultDesk(displayId) checkNotNull(activeDesk) { "Expected desk in display: $displayId" } // Removes task if it is active on another desk excluding [activeDesk]. removeActiveTask(taskId, excludedDeskId = activeDeskId) removeActiveTask(taskId, excludedDeskId = activeDesk.deskId) if (desktopData.getOrCreateDesk(displayId, activeDeskId).activeTasks.add(taskId)) { logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId) if (activeDesk.activeTasks.add(taskId)) { logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDesk.deskId) updateActiveTasksListeners(displayId) } } Loading Loading @@ -266,18 +268,23 @@ class DesktopRepository( * TODO: b/389960283 - add explicit [deskId] argument. */ fun addClosingTask(displayId: Int, taskId: Int) { val activeDeskId = desktopData.getActiveDesk(displayId)?.deskId val activeDesk = desktopData.getActiveDesk(displayId) ?: error("Expected active desk in display: $displayId") if (desktopData.getOrCreateDesk(displayId, activeDeskId).closingTasks.add(taskId)) { logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId) if (activeDesk.closingTasks.add(taskId)) { logD( "Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDesk.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, activeDeskId, activeDesk.deskId, ) } } Loading Loading @@ -323,7 +330,7 @@ class DesktopRepository( /** * Returns the active tasks in the given display's active desk. * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getActiveTaskIdsInDesk]. */ @VisibleForTesting fun getActiveTasks(displayId: Int): ArraySet<Int> = Loading @@ -332,19 +339,27 @@ class DesktopRepository( /** * Returns the minimized tasks in the given display's active desk. * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getMinimizedTaskIdsInDesk]. */ fun getMinimizedTasks(displayId: Int): ArraySet<Int> = ArraySet(desktopData.getActiveDesk(displayId)?.minimizedTasks) @VisibleForTesting fun getMinimizedTaskIdsInDesk(deskId: Int): ArraySet<Int> = ArraySet(desktopData.getDesk(deskId)?.minimizedTasks) /** * Returns all active non-minimized tasks for [displayId] ordered from top to bottom. * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getExpandedTasksIdsInDeskOrdered]. */ fun getExpandedTasksOrdered(displayId: Int): List<Int> = getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) } @VisibleForTesting fun getExpandedTasksIdsInDeskOrdered(deskId: Int): List<Int> = getFreeformTasksIdsInDeskInZOrder(deskId).filter { !isMinimizedTask(it) } /** * Returns the count of active non-minimized tasks for [displayId]. * Loading @@ -357,11 +372,15 @@ class DesktopRepository( /** * Returns a list of freeform tasks, ordered from top-bottom (top at index 0). * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getFreeformTasksIdsInDeskInZOrder]. */ @VisibleForTesting fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> = ArrayList(desktopData.getActiveDesk(displayId)?.freeformTasksInZOrder ?: emptyList()) ArrayList(desktopData.getDefaultDesk(displayId)?.freeformTasksInZOrder ?: emptyList()) @VisibleForTesting fun getFreeformTasksIdsInDeskInZOrder(deskId: Int): ArrayList<Int> = ArrayList(desktopData.getDesk(deskId)?.freeformTasksInZOrder ?: emptyList()) /** Returns the tasks inside the given desk. */ fun getActiveTaskIdsInDesk(deskId: Int): Set<Int> = Loading Loading @@ -401,8 +420,8 @@ class DesktopRepository( } val prevCount = getVisibleTaskCount(displayId) if (isVisible) { desktopData.getActiveDesk(displayId)?.visibleTasks?.add(taskId) ?: error("Expected non-null active desk in display $displayId") desktopData.getDefaultDesk(displayId)?.visibleTasks?.add(taskId) ?: error("Expected non-null desk in display $displayId") unminimizeTask(displayId, taskId) } else { desktopData.getActiveDesk(displayId)?.visibleTasks?.remove(taskId) Loading Loading @@ -587,17 +606,15 @@ class DesktopRepository( * TODO: b/389960283 - add explicit [deskId] argument. */ private fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) { val activeDesk = desktopData.getActiveDesk(displayId) ?: error("Expected a desk to be active in display: $displayId") val desk = getDefaultDesk(displayId) ?: error("Expected a desk in display: $displayId") logD( "Add or move task to top: display=%d taskId=%d deskId=%d", taskId, displayId, activeDesk.deskId, desk.deskId, ) desktopData.forAllDesks { _, desk -> desk.freeformTasksInZOrder.remove(taskId) } activeDesk.freeformTasksInZOrder.add(0, taskId) desktopData.forAllDesks { _, desk1 -> desk1.freeformTasksInZOrder.remove(taskId) } desk.freeformTasksInZOrder.add(0, taskId) // Unminimize the task if it is minimized. unminimizeTask(displayId, taskId) if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) { Loading Loading @@ -835,13 +852,8 @@ class DesktopRepository( /** An interface for the desktop hierarchy's data managed by this repository. */ private interface DesktopData { /** * Returns the existing desk or creates a new entry if needed. * * TODO: 389787966 - consider removing this as it cannot be assumed a desk can be created in * all devices / form-factors. */ fun getOrCreateDesk(displayId: Int, deskId: Int): Desk /** Creates a desk record. */ fun createDesk(displayId: Int, deskId: Int) /** Returns the desk with the given id, or null if it does not exist. */ fun getDesk(deskId: Int): Desk? Loading Loading @@ -894,7 +906,8 @@ class DesktopRepository( /** * A [DesktopData] implementation that only supports one desk per display. * * Internally, it reuses the displayId as that display's single desk's id. * Internally, it reuses the displayId as that display's single desk's id. It also never truly * "removes" a desk, it just clears its content. */ private class SingleDesktopData : DesktopData { private val deskByDisplayId = Loading @@ -907,12 +920,13 @@ class DesktopRepository( } } override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk { check(displayId == deskId) return deskByDisplayId.getOrCreate(displayId) override fun createDesk(displayId: Int, deskId: Int) { check(displayId == deskId) { "Display and desk ids must match" } deskByDisplayId.getOrCreate(displayId) } override fun getDesk(deskId: Int): Desk = getOrCreateDesk(deskId, deskId) override fun getDesk(deskId: Int): Desk = checkNotNull(deskByDisplayId[deskId]) { "Expected desk $deskId to exist" } override fun getActiveDesk(displayId: Int): Desk { // TODO: 389787966 - consider migrating to an "active" state instead of checking the Loading @@ -927,7 +941,7 @@ class DesktopRepository( // existence of visible desktop windows, among other factors. } override fun getDefaultDesk(displayId: Int): Desk = getOrCreateDesk(displayId, displayId) override fun getDefaultDesk(displayId: Int): Desk = getDesk(deskId = displayId) override fun getAllActiveDesks(): Set<Desk> = deskByDisplayId.valueIterator().asSequence().toSet() Loading @@ -943,7 +957,7 @@ class DesktopRepository( } override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) { consumer(getOrCreateDesk(displayId, displayId)) consumer(getDesk(deskId = displayId)) } override fun desksSequence(): Sequence<Desk> = deskByDisplayId.valueIterator().asSequence() Loading @@ -962,16 +976,14 @@ class DesktopRepository( private class MultiDesktopData : DesktopData { private val desktopDisplays = SparseArray<DesktopDisplay>() override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk { override fun createDesk(displayId: Int, deskId: Int) { val display = desktopDisplays[displayId] ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it } val desk = display.orderedDesks.find { desk -> desk.deskId == deskId } ?: Desk(deskId = deskId, displayId = displayId).also { display.orderedDesks.add(it) check(display.orderedDesks.none { desk -> desk.deskId == deskId }) { "Attempting to create desk#$deskId that already exists in display#$displayId" } return desk display.orderedDesks.add(Desk(deskId = deskId, displayId = displayId)) } override fun getDesk(deskId: Int): Desk? { Loading Loading @@ -999,7 +1011,8 @@ class DesktopRepository( override fun getDefaultDesk(displayId: Int): Desk? { val display = desktopDisplays[displayId] ?: return null return display.orderedDesks.firstOrNull() return display.orderedDesks.find { it.deskId == display.activeDeskId } ?: display.orderedDesks.firstOrNull() } override fun getAllActiveDesks(): Set<Desk> { Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +18 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,8 @@ import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCR import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener 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 Loading Loading @@ -180,6 +182,7 @@ class DesktopTasksController( private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider, private val bubbleController: Optional<BubbleController>, private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver, private val desksOrganizer: DesksOrganizer, ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler, Loading Loading @@ -232,6 +235,9 @@ class DesktopTasksController( // Used to prevent handleRequest from moving the new fullscreen task to freeform. private var dragAndDropFullscreenCookie: Binder? = null // A listener that is invoked after a desk has been remove from the system. */ var onDeskRemovedListener: OnDeskRemovedListener? = null init { desktopMode = DesktopModeImpl() if (DesktopModeStatus.canEnterDesktopMode(context)) { Loading Loading @@ -415,6 +421,18 @@ class DesktopTasksController( return isFreeformDisplay } /** Creates a new desk in the given display. */ fun createDesk(displayId: Int) { if (Flags.enableMultipleDesktopsBackend()) { desksOrganizer.createDesk(displayId) { deskId -> taskRepository.addDesk(displayId = displayId, deskId = deskId) } } else { // In single-desk, the desk reuses the display id. taskRepository.addDesk(displayId = displayId, deskId = displayId) } } /** Moves task to desktop mode if task is running, else launches it in desktop mode. */ @JvmOverloads fun moveTaskToDesktop( Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +23 −6 Original line number Diff line number Diff line Loading @@ -108,6 +108,8 @@ import com.android.wm.shell.desktopmode.education.AppToWebEducationController; import com.android.wm.shell.desktopmode.education.AppToWebEducationFilter; import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository; import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository; import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer; import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer; import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository; import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer; import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializerImpl; Loading Loading @@ -701,6 +703,16 @@ public abstract class WMShellModule { // Desktop mode (optional feature) // @WMSingleton @Provides static DesksOrganizer provideDesksOrganizer( @NonNull ShellInit shellInit, @NonNull ShellCommandHandler shellCommandHandler, @NonNull ShellTaskOrganizer shellTaskOrganizer ) { return new RootTaskDesksOrganizer(shellInit, shellCommandHandler, shellTaskOrganizer); } @WMSingleton @Provides @DynamicOverride Loading Loading @@ -741,7 +753,8 @@ public abstract class WMShellModule { DesktopTilingDecorViewModel desktopTilingDecorViewModel, DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider, Optional<BubbleController> bubbleController, OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver) { OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver, DesksOrganizer desksOrganizer) { return new DesktopTasksController( context, shellInit, Loading Loading @@ -775,7 +788,8 @@ public abstract class WMShellModule { desktopTilingDecorViewModel, desktopWallpaperActivityTokenProvider, bubbleController, overviewToDesktopTransitionObserver); overviewToDesktopTransitionObserver, desksOrganizer); } @WMSingleton Loading Loading @@ -1183,10 +1197,11 @@ public abstract class WMShellModule { Transitions transitions, DisplayController displayController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, IWindowManager windowManager IWindowManager windowManager, Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController ) { if (!DesktopModeStatus.canEnterDesktopMode(context) || !Flags.enableDisplayWindowingModeSwitching()) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); } return Optional.of( Loading @@ -1196,7 +1211,9 @@ public abstract class WMShellModule { transitions, displayController, rootTaskDisplayAreaOrganizer, windowManager)); windowManager, desktopUserRepositories.get(), desktopTasksController.get())); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +48 −6 Original line number Diff line number Diff line Loading @@ -24,9 +24,14 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.IWindowManager import android.view.WindowManager.TRANSIT_CHANGE import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer 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.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions Loading @@ -38,7 +43,12 @@ class DesktopDisplayEventHandler( private val displayController: DisplayController, private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, private val windowManager: IWindowManager, ) : OnDisplaysChangedListener { private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, ) : OnDisplaysChangedListener, OnDeskRemovedListener { private val desktopRepository: DesktopRepository get() = desktopUserRepositories.current init { shellInit.addInitCallback({ onInit() }, this) Loading @@ -46,23 +56,43 @@ class DesktopDisplayEventHandler( private fun onInit() { displayController.addDisplayWindowListener(this) if (Flags.enableMultipleDesktopsBackend()) { desktopTasksController.onDeskRemovedListener = this } } override fun onDisplayAdded(displayId: Int) { if (displayId == DEFAULT_DISPLAY) { return } if (displayId != DEFAULT_DISPLAY) { refreshDisplayWindowingMode() } override fun onDisplayRemoved(displayId: Int) { if (displayId == DEFAULT_DISPLAY) { if (!supportsDesks(displayId)) { logV("Display #$displayId does not support desks") return } logV("Creating new desk in new display#$displayId") desktopTasksController.createDesk(displayId) } override fun onDisplayRemoved(displayId: Int) { if (displayId != DEFAULT_DISPLAY) { refreshDisplayWindowingMode() } // TODO: b/362720497 - move desks in closing display to the remaining desk. } override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) { val remainingDesks = desktopRepository.getNumberOfDesks(lastDisplayId) if (remainingDesks == 0) { logV("All desks removed from display#$lastDisplayId, creating empty desk") desktopTasksController.createDesk(lastDisplayId) } } private fun refreshDisplayWindowingMode() { if (!Flags.enableDisplayWindowingModeSwitching()) return // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available. val isExtendedDisplayEnabled = 0 != Loading Loading @@ -98,4 +128,16 @@ class DesktopDisplayEventHandler( wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode) transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) } // TODO: b/362720497 - connected/projected display considerations. private fun supportsDesks(displayId: Int): Boolean = DesktopModeStatus.canEnterDesktopMode(context) private fun logV(msg: String, vararg arguments: Any?) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } companion object { private const val TAG = "DesktopDisplayEventHandler" } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt +2 −2 Original line number Diff line number Diff line Loading @@ -110,8 +110,8 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl pw.println("Error: display id should be an integer") return false } pw.println("Not implemented.") return false controller.createDesk(displayId) return true } private fun runActivateDesk(args: Array<String>, pw: PrintWriter): Boolean { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +61 −48 Original line number Diff line number Diff line Loading @@ -171,6 +171,9 @@ class DesktopRepository( /** Returns a list of all [Desk]s in the repository. */ private fun desksSequence(): Sequence<Desk> = desktopData.desksSequence() /** Returns the number of desks in the given display. */ fun getNumberOfDesks(displayId: Int) = desktopData.getNumberOfDesks(displayId) /** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */ fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) { desktopGestureExclusionListener = regionListener Loading Loading @@ -201,11 +204,11 @@ class DesktopRepository( /** Adds the given desk under the given display. */ fun addDesk(displayId: Int, deskId: Int) { desktopData.getOrCreateDesk(displayId, deskId) desktopData.createDesk(displayId, deskId) } /** Returns the default desk in the given display. */ fun getDefaultDesk(displayId: Int): Int? = desktopData.getDefaultDesk(displayId)?.deskId private fun getDefaultDesk(displayId: Int): Desk? = desktopData.getDefaultDesk(displayId) /** Sets the given desk as the active one in the given display. */ fun setActiveDesk(displayId: Int, deskId: Int) { Loading @@ -229,15 +232,14 @@ class DesktopRepository( * TODO: b/389960283 - add explicit [deskId] argument. */ private fun addActiveTask(displayId: Int, taskId: Int) { val activeDeskId = desktopData.getActiveDesk(displayId)?.deskId ?: error("Expected active desk in display: $displayId") val activeDesk = desktopData.getDefaultDesk(displayId) checkNotNull(activeDesk) { "Expected desk in display: $displayId" } // Removes task if it is active on another desk excluding [activeDesk]. removeActiveTask(taskId, excludedDeskId = activeDeskId) removeActiveTask(taskId, excludedDeskId = activeDesk.deskId) if (desktopData.getOrCreateDesk(displayId, activeDeskId).activeTasks.add(taskId)) { logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId) if (activeDesk.activeTasks.add(taskId)) { logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDesk.deskId) updateActiveTasksListeners(displayId) } } Loading Loading @@ -266,18 +268,23 @@ class DesktopRepository( * TODO: b/389960283 - add explicit [deskId] argument. */ fun addClosingTask(displayId: Int, taskId: Int) { val activeDeskId = desktopData.getActiveDesk(displayId)?.deskId val activeDesk = desktopData.getActiveDesk(displayId) ?: error("Expected active desk in display: $displayId") if (desktopData.getOrCreateDesk(displayId, activeDeskId).closingTasks.add(taskId)) { logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId) if (activeDesk.closingTasks.add(taskId)) { logD( "Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDesk.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, activeDeskId, activeDesk.deskId, ) } } Loading Loading @@ -323,7 +330,7 @@ class DesktopRepository( /** * Returns the active tasks in the given display's active desk. * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getActiveTaskIdsInDesk]. */ @VisibleForTesting fun getActiveTasks(displayId: Int): ArraySet<Int> = Loading @@ -332,19 +339,27 @@ class DesktopRepository( /** * Returns the minimized tasks in the given display's active desk. * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getMinimizedTaskIdsInDesk]. */ fun getMinimizedTasks(displayId: Int): ArraySet<Int> = ArraySet(desktopData.getActiveDesk(displayId)?.minimizedTasks) @VisibleForTesting fun getMinimizedTaskIdsInDesk(deskId: Int): ArraySet<Int> = ArraySet(desktopData.getDesk(deskId)?.minimizedTasks) /** * Returns all active non-minimized tasks for [displayId] ordered from top to bottom. * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getExpandedTasksIdsInDeskOrdered]. */ fun getExpandedTasksOrdered(displayId: Int): List<Int> = getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) } @VisibleForTesting fun getExpandedTasksIdsInDeskOrdered(deskId: Int): List<Int> = getFreeformTasksIdsInDeskInZOrder(deskId).filter { !isMinimizedTask(it) } /** * Returns the count of active non-minimized tasks for [displayId]. * Loading @@ -357,11 +372,15 @@ class DesktopRepository( /** * Returns a list of freeform tasks, ordered from top-bottom (top at index 0). * * TODO: b/389960283 - add explicit [deskId] argument. * TODO: b/389960283 - migrate callers to [getFreeformTasksIdsInDeskInZOrder]. */ @VisibleForTesting fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> = ArrayList(desktopData.getActiveDesk(displayId)?.freeformTasksInZOrder ?: emptyList()) ArrayList(desktopData.getDefaultDesk(displayId)?.freeformTasksInZOrder ?: emptyList()) @VisibleForTesting fun getFreeformTasksIdsInDeskInZOrder(deskId: Int): ArrayList<Int> = ArrayList(desktopData.getDesk(deskId)?.freeformTasksInZOrder ?: emptyList()) /** Returns the tasks inside the given desk. */ fun getActiveTaskIdsInDesk(deskId: Int): Set<Int> = Loading Loading @@ -401,8 +420,8 @@ class DesktopRepository( } val prevCount = getVisibleTaskCount(displayId) if (isVisible) { desktopData.getActiveDesk(displayId)?.visibleTasks?.add(taskId) ?: error("Expected non-null active desk in display $displayId") desktopData.getDefaultDesk(displayId)?.visibleTasks?.add(taskId) ?: error("Expected non-null desk in display $displayId") unminimizeTask(displayId, taskId) } else { desktopData.getActiveDesk(displayId)?.visibleTasks?.remove(taskId) Loading Loading @@ -587,17 +606,15 @@ class DesktopRepository( * TODO: b/389960283 - add explicit [deskId] argument. */ private fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) { val activeDesk = desktopData.getActiveDesk(displayId) ?: error("Expected a desk to be active in display: $displayId") val desk = getDefaultDesk(displayId) ?: error("Expected a desk in display: $displayId") logD( "Add or move task to top: display=%d taskId=%d deskId=%d", taskId, displayId, activeDesk.deskId, desk.deskId, ) desktopData.forAllDesks { _, desk -> desk.freeformTasksInZOrder.remove(taskId) } activeDesk.freeformTasksInZOrder.add(0, taskId) desktopData.forAllDesks { _, desk1 -> desk1.freeformTasksInZOrder.remove(taskId) } desk.freeformTasksInZOrder.add(0, taskId) // Unminimize the task if it is minimized. unminimizeTask(displayId, taskId) if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) { Loading Loading @@ -835,13 +852,8 @@ class DesktopRepository( /** An interface for the desktop hierarchy's data managed by this repository. */ private interface DesktopData { /** * Returns the existing desk or creates a new entry if needed. * * TODO: 389787966 - consider removing this as it cannot be assumed a desk can be created in * all devices / form-factors. */ fun getOrCreateDesk(displayId: Int, deskId: Int): Desk /** Creates a desk record. */ fun createDesk(displayId: Int, deskId: Int) /** Returns the desk with the given id, or null if it does not exist. */ fun getDesk(deskId: Int): Desk? Loading Loading @@ -894,7 +906,8 @@ class DesktopRepository( /** * A [DesktopData] implementation that only supports one desk per display. * * Internally, it reuses the displayId as that display's single desk's id. * Internally, it reuses the displayId as that display's single desk's id. It also never truly * "removes" a desk, it just clears its content. */ private class SingleDesktopData : DesktopData { private val deskByDisplayId = Loading @@ -907,12 +920,13 @@ class DesktopRepository( } } override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk { check(displayId == deskId) return deskByDisplayId.getOrCreate(displayId) override fun createDesk(displayId: Int, deskId: Int) { check(displayId == deskId) { "Display and desk ids must match" } deskByDisplayId.getOrCreate(displayId) } override fun getDesk(deskId: Int): Desk = getOrCreateDesk(deskId, deskId) override fun getDesk(deskId: Int): Desk = checkNotNull(deskByDisplayId[deskId]) { "Expected desk $deskId to exist" } override fun getActiveDesk(displayId: Int): Desk { // TODO: 389787966 - consider migrating to an "active" state instead of checking the Loading @@ -927,7 +941,7 @@ class DesktopRepository( // existence of visible desktop windows, among other factors. } override fun getDefaultDesk(displayId: Int): Desk = getOrCreateDesk(displayId, displayId) override fun getDefaultDesk(displayId: Int): Desk = getDesk(deskId = displayId) override fun getAllActiveDesks(): Set<Desk> = deskByDisplayId.valueIterator().asSequence().toSet() Loading @@ -943,7 +957,7 @@ class DesktopRepository( } override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) { consumer(getOrCreateDesk(displayId, displayId)) consumer(getDesk(deskId = displayId)) } override fun desksSequence(): Sequence<Desk> = deskByDisplayId.valueIterator().asSequence() Loading @@ -962,16 +976,14 @@ class DesktopRepository( private class MultiDesktopData : DesktopData { private val desktopDisplays = SparseArray<DesktopDisplay>() override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk { override fun createDesk(displayId: Int, deskId: Int) { val display = desktopDisplays[displayId] ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it } val desk = display.orderedDesks.find { desk -> desk.deskId == deskId } ?: Desk(deskId = deskId, displayId = displayId).also { display.orderedDesks.add(it) check(display.orderedDesks.none { desk -> desk.deskId == deskId }) { "Attempting to create desk#$deskId that already exists in display#$displayId" } return desk display.orderedDesks.add(Desk(deskId = deskId, displayId = displayId)) } override fun getDesk(deskId: Int): Desk? { Loading Loading @@ -999,7 +1011,8 @@ class DesktopRepository( override fun getDefaultDesk(displayId: Int): Desk? { val display = desktopDisplays[displayId] ?: return null return display.orderedDesks.firstOrNull() return display.orderedDesks.find { it.deskId == display.activeDeskId } ?: display.orderedDesks.firstOrNull() } override fun getAllActiveDesks(): Set<Desk> { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +18 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,8 @@ import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCR import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener 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 Loading Loading @@ -180,6 +182,7 @@ class DesktopTasksController( private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider, private val bubbleController: Optional<BubbleController>, private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver, private val desksOrganizer: DesksOrganizer, ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler, Loading Loading @@ -232,6 +235,9 @@ class DesktopTasksController( // Used to prevent handleRequest from moving the new fullscreen task to freeform. private var dragAndDropFullscreenCookie: Binder? = null // A listener that is invoked after a desk has been remove from the system. */ var onDeskRemovedListener: OnDeskRemovedListener? = null init { desktopMode = DesktopModeImpl() if (DesktopModeStatus.canEnterDesktopMode(context)) { Loading Loading @@ -415,6 +421,18 @@ class DesktopTasksController( return isFreeformDisplay } /** Creates a new desk in the given display. */ fun createDesk(displayId: Int) { if (Flags.enableMultipleDesktopsBackend()) { desksOrganizer.createDesk(displayId) { deskId -> taskRepository.addDesk(displayId = displayId, deskId = deskId) } } else { // In single-desk, the desk reuses the display id. taskRepository.addDesk(displayId = displayId, deskId = displayId) } } /** Moves task to desktop mode if task is running, else launches it in desktop mode. */ @JvmOverloads fun moveTaskToDesktop( Loading