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

Commit dc648413 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I25eb4365,I2b51e3ab into main

* changes:
  [15/N] Desks: Deactivate desk when last window is minimized
  [14/N] Desks: Deactivate desk when last window is closed
parents 54007d84 c6e124f4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -821,7 +821,7 @@ class DesktopRepository(

    /** Removes the given task from the given desk. */
    fun removeTaskFromDesk(deskId: Int, taskId: Int) {
        logD("removeTaskFromDesk: deskId=%d, taskId=%d", taskId, deskId)
        logD("removeTaskFromDesk: deskId=%d, taskId=%d", deskId, taskId)
        // TODO: b/362720497 - consider not clearing bounds on any removal, such as when moving
        //  it between desks. It might be better to allow restoring to the previous bounds as long
        //  as they're valid (probably valid if in the same display).
+102 −38
Original line number Diff line number Diff line
@@ -153,6 +153,16 @@ import java.util.concurrent.TimeUnit
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull

/**
 * A callback to be invoked when a transition is started via |Transitions.startTransition| with the
 * transition binder token that it produces.
 *
 * Useful when multiple components are appending WCT operations to a single transition that is
 * started outside of their control, and each of them wants to track the transition lifecycle
 * independently by cross-referencing the transition token with future ready-transitions.
 */
typealias RunOnTransitStart = (IBinder) -> Unit

/** Handles moving tasks in and out of desktop */
class DesktopTasksController(
    private val context: Context,
@@ -481,7 +491,7 @@ class DesktopTasksController(
    ): Boolean {
        val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId)
        if (runningTask != null) {
            moveRunningTaskToDesk(
            return moveRunningTaskToDesk(
                task = runningTask,
                deskId = deskId,
                wct = wct,
@@ -563,10 +573,10 @@ class DesktopTasksController(
        transitionSource: DesktopModeTransitionSource,
        remoteTransition: RemoteTransition? = null,
        callback: IMoveToDesktopCallback? = null,
    ) {
    ): Boolean {
        if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)) {
            logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
            return
            return false
        }
        val displayId = taskRepository.getDisplayForDesk(deskId)
        logV(
@@ -621,6 +631,7 @@ class DesktopTasksController(
        } else {
            taskRepository.setActiveDesk(displayId = displayId, deskId = deskId)
        }
        return true
    }

    /**
@@ -789,17 +800,33 @@ class DesktopTasksController(
        wct: WindowContainerTransaction,
        displayId: Int,
        taskInfo: RunningTaskInfo,
    ): ((IBinder) -> Unit)? {
    ): ((IBinder) -> Unit) {
        val taskId = taskInfo.taskId
        val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
        snapEventHandler.removeTaskIfTiled(displayId, taskId)
        // TODO: b/394268248 - desk needs to be deactivated when closing the last task and going
        //  home.
        performDesktopExitCleanupIfNeeded(taskId, displayId, wct, forceToFullscreen = false)
        val shouldExitDesktop =
            willExitDesktop(
                triggerTaskId = taskInfo.taskId,
                displayId = displayId,
                forceToFullscreen = false,
            )
        taskRepository.setPipShouldKeepDesktopActive(displayId, keepActive = true)
        val desktopExitRunnable =
            performDesktopExitCleanUp(
                wct = wct,
                deskId = deskId,
                displayId = displayId,
                willExitDesktop = shouldExitDesktop,
                shouldEndUpAtHome = true,
            )

        taskRepository.addClosingTask(displayId, taskId)
        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
            doesAnyTaskRequireTaskbarRounding(displayId, taskId)
        )
        return desktopImmersiveController

        val immersiveRunnable =
            desktopImmersiveController
                .exitImmersiveIfApplicable(
                    wct = wct,
                    taskInfo = taskInfo,
@@ -807,6 +834,10 @@ class DesktopTasksController(
                )
                .asExit()
                ?.runOnTransitionStart
        return { transitionToken ->
            immersiveRunnable?.invoke(transitionToken)
            desktopExitRunnable?.invoke(transitionToken)
        }
    }

    fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
@@ -840,12 +871,20 @@ class DesktopTasksController(

    private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
        val taskId = taskInfo.taskId
        val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
        val displayId = taskInfo.displayId
        val wct = WindowContainerTransaction()

        snapEventHandler.removeTaskIfTiled(displayId, taskId)
        // TODO: b/394268248 - desk needs to be deactivated when minimizing the last task and going
        //  home.
        performDesktopExitCleanupIfNeeded(taskId, displayId, wct, forceToFullscreen = false)
        taskRepository.setPipShouldKeepDesktopActive(displayId, keepActive = true)
        val willExitDesktop = willExitDesktop(taskId, displayId, forceToFullscreen = false)
        val desktopExitRunnable =
            performDesktopExitCleanUp(
                wct = wct,
                deskId = deskId,
                displayId = displayId,
                willExitDesktop = willExitDesktop,
            )
        // Notify immersive handler as it might need to exit immersive state.
        val exitResult =
            desktopImmersiveController.exitImmersiveIfApplicable(
@@ -867,6 +906,7 @@ class DesktopTasksController(
            )
        }
        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
        desktopExitRunnable?.invoke(transition)
    }

    /** Move a task with given `taskId` to fullscreen */
@@ -915,7 +955,7 @@ class DesktopTasksController(
        logV("moveToFullscreenWithAnimation taskId=%d", task.taskId)
        val wct = WindowContainerTransaction()
        val willExitDesktop = willExitDesktop(task.taskId, task.displayId, forceToFullscreen = true)
        val deactivatingDeskId = addMoveToFullscreenChanges(wct, task, willExitDesktop)
        val deactivationRunnable = addMoveToFullscreenChanges(wct, task, willExitDesktop)

        // We are moving a freeform task to fullscreen, put the home task under the fullscreen task.
        if (!forceEnterDesktop(task.displayId)) {
@@ -930,11 +970,7 @@ class DesktopTasksController(
                position,
                mOnAnimationFinishedCallback,
            )
        if (deactivatingDeskId != null) {
            desksTransitionObserver.addPendingTransition(
                DeskTransition.DeactivateDesk(token = transition, deskId = deactivatingDeskId)
            )
        }
        deactivationRunnable?.invoke(transition)

        // handles case where we are moving to full screen without closing all DW tasks.
        if (!taskRepository.isOnlyVisibleNonClosingTask(task.taskId)) {
@@ -1770,19 +1806,30 @@ class DesktopTasksController(
        wct: WindowContainerTransaction,
        forceToFullscreen: Boolean,
        shouldEndUpAtHome: Boolean = true,
    ) {
    ): RunOnTransitStart? {
        taskRepository.setPipShouldKeepDesktopActive(displayId, keepActive = !forceToFullscreen)
        if (!willExitDesktop(taskId, displayId, forceToFullscreen)) {
            return
            return null
        }
        performDesktopExitCleanUp(wct, displayId, shouldEndUpAtHome)
        // TODO: b/394268248 - update remaining callers to pass in a |deskId| and apply the
        //  |RunOnTransitStart| when the transition is started.
        return performDesktopExitCleanUp(
            wct = wct,
            deskId = null,
            displayId = displayId,
            willExitDesktop = true,
            shouldEndUpAtHome = shouldEndUpAtHome,
        )
    }

    private fun performDesktopExitCleanUp(
        wct: WindowContainerTransaction,
        deskId: Int?,
        displayId: Int,
        willExitDesktop: Boolean,
        shouldEndUpAtHome: Boolean = true,
    ) {
    ): RunOnTransitStart? {
        if (!willExitDesktop) return null
        desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
            FULLSCREEN_ANIMATION_DURATION
        )
@@ -1792,6 +1839,7 @@ class DesktopTasksController(
            // intent.
            addLaunchHomePendingIntent(wct, displayId)
        }
        return prepareDeskDeactivationIfNeeded(wct, deskId)
    }

    fun releaseVisualIndicator() {
@@ -2473,7 +2521,7 @@ class DesktopTasksController(
        wct: WindowContainerTransaction,
        taskInfo: RunningTaskInfo,
        willExitDesktop: Boolean,
    ): Int? {
    ): RunOnTransitStart? {
        val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
        val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
        val targetWindowingMode =
@@ -2492,15 +2540,14 @@ class DesktopTasksController(
            wct.reparent(taskInfo.token, tdaInfo.token, /* onTop= */ true)
        }
        taskRepository.setPipShouldKeepDesktopActive(taskInfo.displayId, keepActive = false)
        if (willExitDesktop) {
            performDesktopExitCleanUp(wct, taskInfo.displayId, shouldEndUpAtHome = false)
        val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
            if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue && deskId != null) {
                desksOrganizer.deactivateDesk(wct, deskId)
                return deskId
            }
        }
        return null
        return performDesktopExitCleanUp(
            wct = wct,
            deskId = deskId,
            displayId = taskInfo.displayId,
            willExitDesktop = willExitDesktop,
            shouldEndUpAtHome = false,
        )
    }

    private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
@@ -2661,6 +2708,23 @@ class DesktopTasksController(
        )
    }

    /**
     * TODO: b/393978539 - Deactivation should not happen in desktop-first devices when going home.
     */
    private fun prepareDeskDeactivationIfNeeded(
        wct: WindowContainerTransaction,
        deskId: Int?,
    ): RunOnTransitStart? {
        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return null
        if (deskId == null) return null
        desksOrganizer.deactivateDesk(wct, deskId)
        return { transition ->
            desksTransitionObserver.addPendingTransition(
                DeskTransition.DeactivateDesk(token = transition, deskId = deskId)
            )
        }
    }

    /** Removes the default desk in the given display. */
    @Deprecated("Deprecated with multi-desks.", ReplaceWith("removeDesk()"))
    fun removeDefaultDeskInDisplay(displayId: Int) {
+10 −1
Original line number Diff line number Diff line
@@ -92,10 +92,11 @@ class DesksTransitionObserver(
                }
            }
            is DeskTransition.DeactivateDesk -> {
                var visibleDeactivation = false
                for (change in info.changes) {
                    val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
                    if (isDeskChange) {
                        desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
                        visibleDeactivation = true
                        continue
                    }
                    val taskId = change.taskInfo?.taskId ?: continue
@@ -109,6 +110,14 @@ class DesksTransitionObserver(
                        )
                    }
                }
                // Always deactivate even if there's no change that confirms the desk was
                // deactivated. Some interactions, such as the desk deactivating because it's
                // occluded by a fullscreen task result in a transition change, but others, such
                // as transitioning from an empty desk to home may not.
                if (!visibleDeactivation) {
                    logD("Deactivating desk without transition change")
                }
                desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
            }
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -984,7 +984,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                            mDesktopTasksController.onDesktopWindowClose(
                                    wct, mDisplayId, decoration.mTaskInfo);
                    final IBinder transition = mTaskOperations.closeTask(mTaskToken, wct);
                    if (transition != null && runOnTransitionStart != null) {
                    if (transition != null) {
                        runOnTransitionStart.invoke(transition);
                    }
                }
+68 −0
Original line number Diff line number Diff line
@@ -2921,6 +2921,32 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onDesktopWindowClose_lastWindow_deactivatesDesk() {
        val task = setUpFreeformTask()
        val wct = WindowContainerTransaction()

        controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)

        verify(desksOrganizer).deactivateDesk(wct, deskId = 0)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onDesktopWindowClose_lastWindow_addsPendingDeactivateTransition() {
        val task = setUpFreeformTask()
        val wct = WindowContainerTransaction()

        val transition = Binder()
        val runOnTransitStart =
            controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
        runOnTransitStart(transition)

        verify(desksTransitionsObserver)
            .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId = 0))
    }

    @Test
    fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() {
        val task = setUpFreeformTask(active = false)
@@ -2944,6 +2970,48 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onDesktopWindowMinimize_lastWindow_deactivatesDesk() {
        val task = setUpFreeformTask()
        val transition = Binder()
        whenever(
                freeformTaskTransitionStarter.startMinimizedModeTransition(
                    any(),
                    anyInt(),
                    anyBoolean(),
                )
            )
            .thenReturn(transition)

        controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)

        val captor = argumentCaptor<WindowContainerTransaction>()
        verify(freeformTaskTransitionStarter)
            .startMinimizedModeTransition(captor.capture(), eq(task.taskId), eq(true))
        verify(desksOrganizer).deactivateDesk(captor.firstValue, deskId = 0)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    fun onDesktopWindowMinimize_lastWindow_addsPendingDeactivateTransition() {
        val task = setUpFreeformTask()
        val transition = Binder()
        whenever(
                freeformTaskTransitionStarter.startMinimizedModeTransition(
                    any(),
                    anyInt(),
                    anyBoolean(),
                )
            )
            .thenReturn(transition)

        controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)

        verify(desksTransitionsObserver)
            .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
    }

    @Test
    fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
        val task = setUpPipTask(autoEnterEnabled = true)
Loading