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

Commit d2d72887 authored by Jorge Gil's avatar Jorge Gil Committed by Android (Google) Code Review
Browse files

Merge "[5/N] Desks: Add DesksOrganizer and create default desks" into main

parents 9cf8d0f7 ba157ead
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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,
@@ -775,7 +788,8 @@ public abstract class WMShellModule {
                desktopTilingDecorViewModel,
                desktopWallpaperActivityTokenProvider,
                bubbleController,
                overviewToDesktopTransitionObserver);
                overviewToDesktopTransitionObserver,
                desksOrganizer);
    }

    @WMSingleton
@@ -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(
@@ -1196,7 +1211,9 @@ public abstract class WMShellModule {
                        transitions,
                        displayController,
                        rootTaskDisplayAreaOrganizer,
                        windowManager));
                        windowManager,
                        desktopUserRepositories.get(),
                        desktopTasksController.get()));
    }

    @WMSingleton
+48 −6
Original line number Diff line number Diff line
@@ -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

@@ -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)
@@ -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 !=
@@ -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"
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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 {
+61 −48
Original line number Diff line number Diff line
@@ -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
@@ -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) {
@@ -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)
        }
    }
@@ -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,
            )
        }
    }
@@ -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> =
@@ -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].
     *
@@ -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> =
@@ -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)
@@ -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()) {
@@ -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?
@@ -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 =
@@ -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
@@ -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()
@@ -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()
@@ -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? {
@@ -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> {
+18 −0
Original line number Diff line number Diff line
@@ -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
@@ -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,
@@ -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)) {
@@ -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