Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -1373,7 +1373,8 @@ public abstract class WMShellModule { Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController, Optional<DesktopDisplayModeController> desktopDisplayModeController, DesktopRepositoryInitializer desktopRepositoryInitializer DesktopRepositoryInitializer desktopRepositoryInitializer, Optional<DesksTransitionObserver> desksTransitionObserver ) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); Loading @@ -1389,7 +1390,8 @@ public abstract class WMShellModule { desktopRepositoryInitializer, desktopUserRepositories.get(), desktopTasksController.get(), desktopDisplayModeController.get())); desktopDisplayModeController.get(), desksTransitionObserver.get())); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +24 −6 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import com.android.internal.protolog.ProtoLog 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.DesksTransitionObserver import com.android.wm.shell.desktopmode.multidesks.OnDeskDisplayChangeListener 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 Loading @@ -47,7 +49,8 @@ class DesktopDisplayEventHandler( private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, private val desktopDisplayModeController: DesktopDisplayModeController, ) : OnDisplaysChangedListener, OnDeskRemovedListener { private val desksTransitionObserver: DesksTransitionObserver, ) : OnDisplaysChangedListener, OnDeskRemovedListener, OnDeskDisplayChangeListener { init { shellInit.addInitCallback({ onInit() }, this) Loading @@ -67,6 +70,10 @@ class DesktopDisplayEventHandler( } } ) if (DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue) { desksTransitionObserver.deskDisplayChangeListener = this } } } Loading @@ -85,8 +92,7 @@ class DesktopDisplayEventHandler( if (displayId != DEFAULT_DISPLAY) { desktopDisplayModeController.updateDefaultDisplayWindowingMode() } // TODO: b/362720497 - move desks in closing display to the remaining desk. // TODO(b/391652399): store a persisted DesktopDisplay in DesktopRepository } override fun onDesktopModeEligibleChanged(displayId: Int) { Loading Loading @@ -125,15 +131,27 @@ class DesktopDisplayEventHandler( ) } .forEach { displayId -> // TODO: b/393978539 - consider activating the desk on creation when // applicable, such as for connected displays. desktopTasksController.createDesk(displayId, repository.userId) desktopTasksController.createDesk( displayId, repository.userId, isDesktopFirstDisplay(displayId), ) } cancel() } } } override fun onDeskDisplayChange( deskDisplayChanges: Set<OnDeskDisplayChangeListener.DeskDisplayChange> ) { if (!DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue()) return desktopTasksController.onDeskDisconnectTransition(deskDisplayChanges) } // TODO: b/393978539 - implement this private fun isDesktopFirstDisplay(displayId: Int): Boolean = displayId != DEFAULT_DISPLAY // TODO: b/362720497 - connected/projected display considerations. private fun supportsDesks(displayId: Int): Boolean = DesktopModeStatus.canEnterDesktopMode(context) Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +34 −2 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ class DesktopRepository( */ private data class Desk( val deskId: Int, val displayId: Int, var displayId: Int, val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), val minimizedTasks: ArraySet<Int> = ArraySet(), Loading Loading @@ -221,6 +221,19 @@ class DesktopRepository( } } /** Update the data to reflect a desk changing displays. */ fun onDeskDisplayChanged(deskId: Int, newDisplayId: Int) { val desk = desktopData.getDesk(deskId)?.deepCopy() ?: error("Expected to find desk with id: $deskId") desk.displayId = newDisplayId removeDesk(deskId) desktopData.addDesk(newDisplayId, desk) deskChangeListeners.forEach { (listener, executor) -> executor.execute { listener.onDeskAdded(displayId = newDisplayId, deskId = deskId) } } } /** Returns the ids of the existing desks in the given display. */ @VisibleForTesting fun getDeskIds(displayId: Int): Set<Int> = Loading Loading @@ -1178,10 +1191,13 @@ class DesktopRepository( /** Creates a desk record. */ fun createDesk(displayId: Int, deskId: Int) /** Adds an existing desk to a specific display */ fun addDesk(displayId: Int, desk: Desk) /** Returns the desk with the given id, or null if it does not exist. */ fun getDesk(deskId: Int): Desk? /** Returns the active desk in this diplay, or null if none are active. */ /** Returns the active desk in this display, or null if none are active. */ fun getActiveDesk(displayId: Int): Desk? /** Sets the given desk as the active desk in the given display. */ Loading Loading @@ -1254,6 +1270,10 @@ class DesktopRepository( deskByDisplayId.getOrCreate(displayId) } override fun addDesk(displayId: Int, desk: Desk) { // No-op, not supported } override fun getDesk(deskId: Int): Desk = // TODO: b/362720497 - consider enforcing that the desk has been created before trying // to use it. As of now, there are cases where a task may be created faster than a Loading Loading @@ -1327,6 +1347,18 @@ class DesktopRepository( display.orderedDesks.add(Desk(deskId = deskId, displayId = displayId)) } override fun addDesk(displayId: Int, desk: Desk) { val display = desktopDisplays[displayId] ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it } check( display.orderedDesks.none { existingDesk -> desk.deskId == existingDesk.deskId } ) { "Attempting to add desk#${desk.deskId} that already exists in display#$displayId" } display.orderedDesks.add(desk) } override fun getDesk(deskId: Int): Desk? { desktopDisplays.forEach { _, display -> val desk = display.orderedDesks.find { desk -> desk.deskId == deskId } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +68 −1 Original line number Diff line number Diff line Loading @@ -117,6 +117,7 @@ import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler 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.OnDeskDisplayChangeListener import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory Loading Loading @@ -468,7 +469,7 @@ class DesktopTasksController( } /** Adds a new desk to the given display for the given user. */ fun createDesk(displayId: Int, userId: Int = this.userId) { fun createDesk(displayId: Int, userId: Int = this.userId, activateDesk: Boolean = false) { logV("addDesk displayId=%d, userId=%d", displayId, userId) val repository = userRepositories.getProfile(userId) createDesk(displayId, userId) { deskId -> Loading @@ -476,6 +477,9 @@ class DesktopTasksController( logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId) } else { repository.addDesk(displayId = displayId, deskId = deskId) if (activateDesk) { activateDesk(deskId) } } } } Loading Loading @@ -516,6 +520,69 @@ class DesktopTasksController( createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) } } /** * Handle desk operations resulting from a display disconnect transition. Forks to two separate * methods depending on whether the destination display supports desktop mode. * * @param deskDisconnectChanges the individual changes resulting from the disconnect */ fun onDeskDisconnectTransition( deskDisconnectChanges: Set<OnDeskDisplayChangeListener.DeskDisplayChange> ) { val wct = WindowContainerTransaction() // TODO(b/391652399): Remove the wallpaper of the disconnected display. val transitStartRunnables = mutableSetOf<RunOnTransitStart?>() for (change in deskDisconnectChanges) { if ( DesktopModeStatus.isDesktopModeSupportedOnDisplay( context, displayController.getDisplay(change.destinationDisplayId), ) ) { transitStartRunnables.add( updateDesksActivationOnDisconnection( disconnectedDisplayActiveDesk = change.deskId, wct = wct, change.toTop, ) ) } else { // TODO(b/391652399): Implement desktop-not-supported case. } } val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) for (transitStartRunnable in transitStartRunnables) { transitStartRunnable?.invoke(transition) } } /** * Handle desk operations when disconnecting a display and all desks on that display are moving * to a display that supports desks. The previously focused display will determine which desk * will move to the front. * * @param disconnectedDisplayActiveDesk the id of the active desk on the disconnected display * @param toTop whether this desk was reordered to the top */ @VisibleForTesting fun updateDesksActivationOnDisconnection( disconnectedDisplayActiveDesk: Int, wct: WindowContainerTransaction, toTop: Boolean, ): RunOnTransitStart? { val runOnTransitStart = if (toTop) { // The disconnected display's active desk was reparented to the top, activate it // here. addDeskActivationChanges(disconnectedDisplayActiveDesk, wct) } else { // The disconnected display's active desk was reparented to the back, ensure it is // no longer an active launch root. prepareDeskDeactivationIfNeeded(wct, disconnectedDisplayActiveDesk) } return runOnTransitStart } /** Moves task to desktop mode if task is running, else launches it in desktop mode. */ @JvmOverloads fun moveTaskToDefaultDeskAndActivate( Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt +11 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,17 @@ class DesktopUserRepositories( else return profileId } /** Find the repositories that contains the specified desk; returns empty set if none found. */ fun getRepositoriesWithDeskId(deskId: Int): Set<DesktopRepository> { val repositories = mutableSetOf<DesktopRepository>() desktopRepoByUserId.forEach { _, desktopRepository -> if (desktopRepository.getAllDeskIds().contains(deskId)) { repositories.add(desktopRepository) } } return repositories } /** Dumps [DesktopRepository] for each user. */ fun dump(pw: PrintWriter, prefix: String) { val innerPrefix = "$prefix " Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -1373,7 +1373,8 @@ public abstract class WMShellModule { Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController, Optional<DesktopDisplayModeController> desktopDisplayModeController, DesktopRepositoryInitializer desktopRepositoryInitializer DesktopRepositoryInitializer desktopRepositoryInitializer, Optional<DesksTransitionObserver> desksTransitionObserver ) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); Loading @@ -1389,7 +1390,8 @@ public abstract class WMShellModule { desktopRepositoryInitializer, desktopUserRepositories.get(), desktopTasksController.get(), desktopDisplayModeController.get())); desktopDisplayModeController.get(), desksTransitionObserver.get())); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +24 −6 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import com.android.internal.protolog.ProtoLog 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.DesksTransitionObserver import com.android.wm.shell.desktopmode.multidesks.OnDeskDisplayChangeListener 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 Loading @@ -47,7 +49,8 @@ class DesktopDisplayEventHandler( private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, private val desktopDisplayModeController: DesktopDisplayModeController, ) : OnDisplaysChangedListener, OnDeskRemovedListener { private val desksTransitionObserver: DesksTransitionObserver, ) : OnDisplaysChangedListener, OnDeskRemovedListener, OnDeskDisplayChangeListener { init { shellInit.addInitCallback({ onInit() }, this) Loading @@ -67,6 +70,10 @@ class DesktopDisplayEventHandler( } } ) if (DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue) { desksTransitionObserver.deskDisplayChangeListener = this } } } Loading @@ -85,8 +92,7 @@ class DesktopDisplayEventHandler( if (displayId != DEFAULT_DISPLAY) { desktopDisplayModeController.updateDefaultDisplayWindowingMode() } // TODO: b/362720497 - move desks in closing display to the remaining desk. // TODO(b/391652399): store a persisted DesktopDisplay in DesktopRepository } override fun onDesktopModeEligibleChanged(displayId: Int) { Loading Loading @@ -125,15 +131,27 @@ class DesktopDisplayEventHandler( ) } .forEach { displayId -> // TODO: b/393978539 - consider activating the desk on creation when // applicable, such as for connected displays. desktopTasksController.createDesk(displayId, repository.userId) desktopTasksController.createDesk( displayId, repository.userId, isDesktopFirstDisplay(displayId), ) } cancel() } } } override fun onDeskDisplayChange( deskDisplayChanges: Set<OnDeskDisplayChangeListener.DeskDisplayChange> ) { if (!DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue()) return desktopTasksController.onDeskDisconnectTransition(deskDisplayChanges) } // TODO: b/393978539 - implement this private fun isDesktopFirstDisplay(displayId: Int): Boolean = displayId != DEFAULT_DISPLAY // TODO: b/362720497 - connected/projected display considerations. private fun supportsDesks(displayId: Int): Boolean = DesktopModeStatus.canEnterDesktopMode(context) Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +34 −2 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ class DesktopRepository( */ private data class Desk( val deskId: Int, val displayId: Int, var displayId: Int, val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), val minimizedTasks: ArraySet<Int> = ArraySet(), Loading Loading @@ -221,6 +221,19 @@ class DesktopRepository( } } /** Update the data to reflect a desk changing displays. */ fun onDeskDisplayChanged(deskId: Int, newDisplayId: Int) { val desk = desktopData.getDesk(deskId)?.deepCopy() ?: error("Expected to find desk with id: $deskId") desk.displayId = newDisplayId removeDesk(deskId) desktopData.addDesk(newDisplayId, desk) deskChangeListeners.forEach { (listener, executor) -> executor.execute { listener.onDeskAdded(displayId = newDisplayId, deskId = deskId) } } } /** Returns the ids of the existing desks in the given display. */ @VisibleForTesting fun getDeskIds(displayId: Int): Set<Int> = Loading Loading @@ -1178,10 +1191,13 @@ class DesktopRepository( /** Creates a desk record. */ fun createDesk(displayId: Int, deskId: Int) /** Adds an existing desk to a specific display */ fun addDesk(displayId: Int, desk: Desk) /** Returns the desk with the given id, or null if it does not exist. */ fun getDesk(deskId: Int): Desk? /** Returns the active desk in this diplay, or null if none are active. */ /** Returns the active desk in this display, or null if none are active. */ fun getActiveDesk(displayId: Int): Desk? /** Sets the given desk as the active desk in the given display. */ Loading Loading @@ -1254,6 +1270,10 @@ class DesktopRepository( deskByDisplayId.getOrCreate(displayId) } override fun addDesk(displayId: Int, desk: Desk) { // No-op, not supported } override fun getDesk(deskId: Int): Desk = // TODO: b/362720497 - consider enforcing that the desk has been created before trying // to use it. As of now, there are cases where a task may be created faster than a Loading Loading @@ -1327,6 +1347,18 @@ class DesktopRepository( display.orderedDesks.add(Desk(deskId = deskId, displayId = displayId)) } override fun addDesk(displayId: Int, desk: Desk) { val display = desktopDisplays[displayId] ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it } check( display.orderedDesks.none { existingDesk -> desk.deskId == existingDesk.deskId } ) { "Attempting to add desk#${desk.deskId} that already exists in display#$displayId" } display.orderedDesks.add(desk) } override fun getDesk(deskId: Int): Desk? { desktopDisplays.forEach { _, display -> val desk = display.orderedDesks.find { desk -> desk.deskId == deskId } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +68 −1 Original line number Diff line number Diff line Loading @@ -117,6 +117,7 @@ import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler 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.OnDeskDisplayChangeListener import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory Loading Loading @@ -468,7 +469,7 @@ class DesktopTasksController( } /** Adds a new desk to the given display for the given user. */ fun createDesk(displayId: Int, userId: Int = this.userId) { fun createDesk(displayId: Int, userId: Int = this.userId, activateDesk: Boolean = false) { logV("addDesk displayId=%d, userId=%d", displayId, userId) val repository = userRepositories.getProfile(userId) createDesk(displayId, userId) { deskId -> Loading @@ -476,6 +477,9 @@ class DesktopTasksController( logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId) } else { repository.addDesk(displayId = displayId, deskId = deskId) if (activateDesk) { activateDesk(deskId) } } } } Loading Loading @@ -516,6 +520,69 @@ class DesktopTasksController( createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) } } /** * Handle desk operations resulting from a display disconnect transition. Forks to two separate * methods depending on whether the destination display supports desktop mode. * * @param deskDisconnectChanges the individual changes resulting from the disconnect */ fun onDeskDisconnectTransition( deskDisconnectChanges: Set<OnDeskDisplayChangeListener.DeskDisplayChange> ) { val wct = WindowContainerTransaction() // TODO(b/391652399): Remove the wallpaper of the disconnected display. val transitStartRunnables = mutableSetOf<RunOnTransitStart?>() for (change in deskDisconnectChanges) { if ( DesktopModeStatus.isDesktopModeSupportedOnDisplay( context, displayController.getDisplay(change.destinationDisplayId), ) ) { transitStartRunnables.add( updateDesksActivationOnDisconnection( disconnectedDisplayActiveDesk = change.deskId, wct = wct, change.toTop, ) ) } else { // TODO(b/391652399): Implement desktop-not-supported case. } } val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) for (transitStartRunnable in transitStartRunnables) { transitStartRunnable?.invoke(transition) } } /** * Handle desk operations when disconnecting a display and all desks on that display are moving * to a display that supports desks. The previously focused display will determine which desk * will move to the front. * * @param disconnectedDisplayActiveDesk the id of the active desk on the disconnected display * @param toTop whether this desk was reordered to the top */ @VisibleForTesting fun updateDesksActivationOnDisconnection( disconnectedDisplayActiveDesk: Int, wct: WindowContainerTransaction, toTop: Boolean, ): RunOnTransitStart? { val runOnTransitStart = if (toTop) { // The disconnected display's active desk was reparented to the top, activate it // here. addDeskActivationChanges(disconnectedDisplayActiveDesk, wct) } else { // The disconnected display's active desk was reparented to the back, ensure it is // no longer an active launch root. prepareDeskDeactivationIfNeeded(wct, disconnectedDisplayActiveDesk) } return runOnTransitStart } /** Moves task to desktop mode if task is running, else launches it in desktop mode. */ @JvmOverloads fun moveTaskToDefaultDeskAndActivate( Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt +11 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,17 @@ class DesktopUserRepositories( else return profileId } /** Find the repositories that contains the specified desk; returns empty set if none found. */ fun getRepositoriesWithDeskId(deskId: Int): Set<DesktopRepository> { val repositories = mutableSetOf<DesktopRepository>() desktopRepoByUserId.forEach { _, desktopRepository -> if (desktopRepository.getAllDeskIds().contains(deskId)) { repositories.add(desktopRepository) } } return repositories } /** Dumps [DesktopRepository] for each user. */ fun dump(pw: PrintWriter, prefix: String) { val innerPrefix = "$prefix " Loading