Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2584fae4 authored by mattsziklay's avatar mattsziklay Committed by Matt Sziklay
Browse files

[1/N] Reparent desks on display disconnect.

When a display containing a desk is disconnected, start a transition to reparent the desk to
the display area of its destination display. TaskDisplayArea will set the
desk root task to top if it was on top of the focused display or bottom if not.

On a destination display that supports desks, ensure the reparented desk is activated if it was on top of the focused display. Otherwise, move it to the display below
whatever is currently active. Whichever desk moves to the bottom will
then be deactivated.

Bug: 391652399
Test: Manual
Flag: com.android.window.flags.enable_display_disconnect_interaction
Change-Id: I6ec4b9d305a6bd9497450328dad36bc53179ee7c
parent 56172bf3
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1370,7 +1370,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();
@@ -1386,7 +1387,8 @@ public abstract class WMShellModule {
                        desktopRepositoryInitializer,
                        desktopUserRepositories.get(),
                        desktopTasksController.get(),
                        desktopDisplayModeController.get()));
                        desktopDisplayModeController.get(),
                        desksTransitionObserver.get()));
    }

    @WMSingleton
+24 −6
Original line number Diff line number Diff line
@@ -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
@@ -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)
@@ -67,6 +70,10 @@ class DesktopDisplayEventHandler(
                    }
                }
            )

            if (DesktopExperienceFlags.ENABLE_DISPLAY_DISCONNECT_INTERACTION.isTrue) {
                desksTransitionObserver.deskDisplayChangeListener = this
            }
        }
    }

@@ -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) {
@@ -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)
+34 −2
Original line number Diff line number Diff line
@@ -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(),
@@ -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> =
@@ -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. */
@@ -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
@@ -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 }
+68 −1
Original line number Diff line number Diff line
@@ -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
@@ -464,7 +465,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 ->
@@ -472,6 +473,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)
                }
            }
        }
    }
@@ -512,6 +516,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(
+11 −0
Original line number Diff line number Diff line
@@ -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