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

Commit b0cd6828 authored by Eghosa Ewansiha-Vlachavas's avatar Eghosa Ewansiha-Vlachavas
Browse files

[1/n] Remove top transparent fullscreen task on new task opening

When a new task is opening or getting brought to front, remove the top
transparent fullscreen task in the current wct to forcefully close it.
The closing of this top transparent task will be handle and animated by
the SystemModalTransitionHandler.

Flag: com.android.window.flags.force_close_top_transparent_fullscreen_task
Bug: 395041610
Test: atest WMShellUnitTests:DesktopTasksTransitionObserverTest,
      atest WMShellUnitTests:DesktopTasksControllerTest,
      atest WMShellUnitTests:DesktopRepositoryTest,
      atest WMShellUnitTests:DesktopMixedTransitionHandlerTest
Change-Id: I792901711ebb57a63f6d1f4fdf091f8d0c8a5a00
parent 0ec6b1d6
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -192,18 +192,19 @@ import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationToolt
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
import com.android.wm.shell.windowdecor.viewholder.AppHandleNotifier;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;

import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.MainCoroutineDispatcher;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only accessible
 * from components within the WM subcomponent (can be explicitly exposed to the SysUIComponent, see
@@ -1403,6 +1404,7 @@ public abstract class WMShellModule {
            Optional<DesktopImmersiveController> desktopImmersiveController,
            DesktopMinimizationTransitionHandler desktopMinimizationTransitionHandler,
            DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler,
            Optional<SystemModalsTransitionHandler> systemModalsTransitionHandler,
            InteractionJankMonitor interactionJankMonitor,
            @ShellMainThread Handler handler,
            ShellInit shellInit,
@@ -1423,6 +1425,7 @@ public abstract class WMShellModule {
                        desktopImmersiveController.get(),
                        desktopMinimizationTransitionHandler,
                        desktopModeDragAndDropTransitionHandler,
                        systemModalsTransitionHandler,
                        interactionJankMonitor,
                        handler,
                        shellInit,
+34 −3
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TASK_LIMIT_MINIMIZE
import com.android.wm.shell.desktopmode.compatui.SystemModalsTransitionHandler
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -45,6 +46,7 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.MixedTransitionHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
import java.util.Optional

/** The [Transitions.TransitionHandler] coordinates transition handlers in desktop windowing. */
class DesktopMixedTransitionHandler(
@@ -56,6 +58,7 @@ class DesktopMixedTransitionHandler(
    private val desktopImmersiveController: DesktopImmersiveController,
    private val desktopMinimizationTransitionHandler: DesktopMinimizationTransitionHandler,
    private val desktopModeDragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler,
    private val systemModalsTransitionHandler: Optional<SystemModalsTransitionHandler>,
    private val interactionJankMonitor: InteractionJankMonitor,
    @ShellMainThread private val handler: Handler,
    shellInit: ShellInit,
@@ -137,6 +140,7 @@ class DesktopMixedTransitionHandler(
        wct: WindowContainerTransaction,
        taskId: Int?,
        minimizingTaskId: Int? = null,
        closingTopTransparentTaskId: Int? = null,
        exitingImmersiveTask: Int? = null,
        dragEvent: DragEvent? = null,
    ): IBinder {
@@ -163,6 +167,7 @@ class DesktopMixedTransitionHandler(
                    transition = transition,
                    launchingTask = taskId,
                    minimizingTask = minimizingTaskId,
                    closingTopTransparentTask = closingTopTransparentTaskId,
                    exitingImmersiveTask = exitingImmersiveTask,
                    dragEvent = dragEvent,
                )
@@ -274,6 +279,10 @@ class DesktopMixedTransitionHandler(
            pending.exitingImmersiveTask?.let { exitingTask -> findTaskChange(info, exitingTask) }
        val minimizeChange =
            pending.minimizingTask?.let { minimizingTask -> findTaskChange(info, minimizingTask) }
        val closeTopTransparentFullscreenTaskChange =
            pending.closingTopTransparentTask?.let { closingTopTransparentTask ->
                findTaskChange(info, closingTopTransparentTask)
            }
        val launchChange = findDesktopTaskLaunchChange(info, pending.launchingTask)
        if (launchChange == null) {
            check(immersiveExitChange == null)
@@ -281,6 +290,7 @@ class DesktopMixedTransitionHandler(
            return false
        }

        var topTransparentAnimationCount = 0
        var subAnimationCount = -1
        var combinedWct: WindowContainerTransaction? = null
        val finishCb = TransitionFinishCallback { wct ->
@@ -291,9 +301,11 @@ class DesktopMixedTransitionHandler(
        }

        logV(
            "Animating mixed launch transition task#%d, minimizingTask#%s immersiveExitTask#%s",
            "Animating mixed launch transition task#%d, minimizingTask#%s " +
                "closingTopTransparentTask#%s immersiveExitTask#%s",
            launchChange.taskInfo!!.taskId,
            minimizeChange?.taskInfo?.taskId,
            closeTopTransparentFullscreenTaskChange?.taskInfo?.taskId,
            immersiveExitChange?.taskInfo?.taskId,
        )
        if (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue) {
@@ -314,8 +326,26 @@ class DesktopMixedTransitionHandler(
                finishCallback,
            )
        }
        if (closeTopTransparentFullscreenTaskChange != null) {
            systemModalsTransitionHandler.ifPresent { handler ->
                logV(
                    "Animating system modal close: taskId=%d",
                    closeTopTransparentFullscreenTaskChange.taskInfo?.taskId,
                )
                topTransparentAnimationCount = 1
                // Animate the modal closure separately.
                info.changes.remove(closeTopTransparentFullscreenTaskChange)
                handler.animateSystemModal(
                    closeTopTransparentFullscreenTaskChange.leash,
                    startTransaction,
                    finishTransaction,
                    finishCb,
                    /* toShow= */ false,
                )
            }
        }
        if (immersiveExitChange != null) {
            subAnimationCount = 2
            subAnimationCount = 2 + topTransparentAnimationCount
            // Animate the immersive exit change separately.
            info.changes.remove(immersiveExitChange)
            desktopImmersiveController.animateResizeChange(
@@ -335,7 +365,7 @@ class DesktopMixedTransitionHandler(
        }
        // There's nothing to animate separately, so let the left over handler animate
        // the entire transition.
        subAnimationCount = 1
        subAnimationCount = 1 + topTransparentAnimationCount
        return dispatchToLeftoverHandler(
            transition,
            info,
@@ -536,6 +566,7 @@ class DesktopMixedTransitionHandler(
            override val transition: IBinder,
            val launchingTask: Int?,
            val minimizingTask: Int?,
            val closingTopTransparentTask: Int?,
            val exitingImmersiveTask: Int?,
            val dragEvent: DragEvent? = null,
        ) : PendingMixedTransition()
+28 −30
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.desktopmode

import android.app.ActivityManager
import android.graphics.Rect
import android.graphics.Region
import android.util.ArrayMap
@@ -24,6 +25,7 @@ import android.util.SparseArray
import android.view.Display.INVALID_DISPLAY
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import android.window.WindowContainerToken
import androidx.core.util.forEach
import androidx.core.util.valueIterator
import com.android.internal.annotations.VisibleForTesting
@@ -55,6 +57,9 @@ class DesktopRepository(
        var activeDeskId: Int? = null
    }

    /** Specific [TaskInfo] data related to top transparent fullscreen task handling. */
    data class TopTransparentFullscreenTaskData(val taskId: Int, val token: WindowContainerToken)

    /**
     * Task data tracked per desk.
     *
@@ -82,7 +87,7 @@ class DesktopRepository(
        val closingTasks: ArraySet<Int> = ArraySet(),
        val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
        var fullImmersiveTaskId: Int? = null,
        var topTransparentFullscreenTaskId: Int? = null,
        var topTransparentFullscreenTaskData: TopTransparentFullscreenTaskData? = null,
        var leftTiledTaskId: Int? = null,
        var rightTiledTaskId: Int? = null,
    ) {
@@ -96,7 +101,7 @@ class DesktopRepository(
                closingTasks = ArraySet(closingTasks),
                freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
                fullImmersiveTaskId = fullImmersiveTaskId,
                topTransparentFullscreenTaskId = topTransparentFullscreenTaskId,
                topTransparentFullscreenTaskData = topTransparentFullscreenTaskData,
                leftTiledTaskId = leftTiledTaskId,
                rightTiledTaskId = rightTiledTaskId,
            )
@@ -110,7 +115,7 @@ class DesktopRepository(
            closingTasks.clear()
            freeformTasksInZOrder.clear()
            fullImmersiveTaskId = null
            topTransparentFullscreenTaskId = null
            topTransparentFullscreenTaskData = null
            leftTiledTaskId = null
            rightTiledTaskId = null
        }
@@ -789,36 +794,29 @@ class DesktopRepository(
    fun getTaskInFullImmersiveState(displayId: Int): Int? =
        desktopData.getActiveDesk(displayId)?.fullImmersiveTaskId

    /** Sets the top transparent fullscreen task id for a given display's active desk. */
    @Deprecated("Deprecated with multiple desks")
    fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
    /** Sets the top transparent fullscreen task data for a given desk. */
    fun setTopTransparentFullscreenTaskData(deskId: Int, task: ActivityManager.RunningTaskInfo) {
        logD(
            "Top transparent fullscreen task set for display: taskId=%d, displayId=%d",
            taskId,
            displayId,
            "Top transparent fullscreen task set for desk: taskId=%d, deskId=%d",
            task.taskId,
            deskId,
        )
        desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = taskId
        desktopData.getDesk(deskId)?.topTransparentFullscreenTaskData =
            TopTransparentFullscreenTaskData(task.taskId, task.token)
    }

    /** Returns the top transparent fullscreen task id for a given display, or null. */
    @Deprecated("Deprecated with multiple desks")
    fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
        desktopData
            .desksSequence(displayId)
            .mapNotNull { it.topTransparentFullscreenTaskId }
            .firstOrNull()

    /** Clears the top transparent fullscreen task id info for a given display's active desk. */
    @Deprecated("Deprecated with multiple desks")
    fun clearTopTransparentFullscreenTaskId(displayId: Int) {
        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
    /** Returns the top transparent fullscreen task data for a given desk, or null. */
    fun getTopTransparentFullscreenTaskData(deskId: Int): TopTransparentFullscreenTaskData? =
        desktopData.getDesk(deskId)?.topTransparentFullscreenTaskData

    /** Clears the top transparent fullscreen task data for a given desk. */
    fun clearTopTransparentFullscreenTaskData(deskId: Int) {
        logD(
            "Top transparent fullscreen task cleared for display: taskId=%d, displayId=%d",
            desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId,
            displayId,
            "Top transparent fullscreen task cleared for desk: taskId=%d, deskId=%d",
            desktopData.getDesk(deskId)?.topTransparentFullscreenTaskData?.taskId,
            deskId,
        )
        desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = null
        desktopData.getDesk(deskId)?.topTransparentFullscreenTaskData = null
    }

    @VisibleForTesting
@@ -838,7 +836,7 @@ class DesktopRepository(
            }
            val hasVisibleTasks = desk.visibleTasks.isNotEmpty()
            val hasTopTransparentFullscreenTask =
                getTopTransparentFullscreenTaskId(displayId) != null
                getTopTransparentFullscreenTaskData(desk.deskId) != null
            if (
                DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                    .isTrue && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue
@@ -1184,8 +1182,8 @@ class DesktopRepository(
                    pw.println(desk.minimizedTasks.toDumpString())
                    pw.print("$desksPrefix  fullImmersiveTaskId=")
                    pw.println(desk.fullImmersiveTaskId)
                    pw.print("$desksPrefix  topTransparentFullscreenTaskId=")
                    pw.println(desk.topTransparentFullscreenTaskId)
                    pw.print("$desksPrefix  topTransparentFullscreenTaskData=")
                    pw.println(desk.topTransparentFullscreenTaskData)
                }
            }
    }
+65 −6
Original line number Diff line number Diff line
@@ -1506,6 +1506,8 @@ class DesktopTasksController(
                    launchingNewIntent = launchingTaskId == null,
                )
            }
        val closingTopTransparentTaskId =
            deskId?.let { taskRepository.getTopTransparentFullscreenTaskData(it)?.taskId }
        val exitImmersiveResult =
            desktopImmersiveController.exitImmersiveIfApplicable(
                wct = launchTransaction,
@@ -1543,6 +1545,8 @@ class DesktopTasksController(
                )
            }
        }
        // Remove top transparent fullscreen task if needed.
        deskId?.let { closeTopTransparentFullscreenTask(launchTransaction, it) }
        val t =
            if (remoteTransition == null) {
                logV("startLaunchTransition -- no remoteTransition -- wct = $launchTransaction")
@@ -1551,10 +1555,13 @@ class DesktopTasksController(
                    wct = launchTransaction,
                    taskId = launchingTaskId,
                    minimizingTaskId = taskIdToMinimize,
                    closingTopTransparentTaskId = closingTopTransparentTaskId,
                    exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
                    dragEvent = dragEvent,
                )
            } else if (taskIdToMinimize == null) {
                // TODO(b/412761429): Move OneShotRemoteHandler call to within
                //  DesktopMixedTransitionHandler.
                val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
                transitions
                    .startTransition(transitionType, launchTransaction, remoteTransitionHandler)
@@ -2935,7 +2942,16 @@ class DesktopTasksController(
        // 2) minimize a Task if needed.
        // TODO: b/32994943 - remove dead code when cleaning up task_limit_separate_transition flag
        val taskIdToMinimize = addAndGetMinimizeChanges(deskId, wct, task.taskId)
        addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
        // 3) Remove top transparent fullscreen task if needed.
        val closingTopTransparentTaskId =
            taskRepository.getTopTransparentFullscreenTaskData(deskId)?.taskId
        closeTopTransparentFullscreenTask(wct, deskId)
        addPendingAppLaunchTransition(
            transition,
            task.taskId,
            taskIdToMinimize,
            closingTopTransparentTaskId,
        )
        if (taskIdToMinimize != null) {
            addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
            return wct
@@ -2992,8 +3008,17 @@ class DesktopTasksController(
                                )
                            }
                            addPendingTaskLimitTransition(transition, deskId, task.taskId)
                            // Remove top transparent fullscreen task if needed.
                            val closingTopTransparentTaskId =
                                taskRepository.getTopTransparentFullscreenTaskData(deskId)?.taskId
                            closeTopTransparentFullscreenTask(wct, deskId)
                            // Also track the pending launching task.
                            addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
                            addPendingAppLaunchTransition(
                                transition,
                                task.taskId,
                                taskIdToMinimize,
                                closingTopTransparentTaskId,
                            )
                        }
                    }
                runOnTransitStart?.invoke(transition)
@@ -3057,11 +3082,15 @@ class DesktopTasksController(
        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
            if (!inDesktop && !forceEnterDesktop(displayId)) return null
            if (
                DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                    .isTrue && isTransparentTask
                isTransparentTask &&
                    (DesktopModeFlags.FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK.isTrue ||
                        DesktopModeFlags
                            .INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                            .isTrue)
            ) {
                // Only update task repository for transparent task.
                taskRepository.setTopTransparentFullscreenTaskId(displayId, taskId)
                val deskId = taskRepository.getActiveDeskId(displayId)
                deskId?.let { taskRepository.setTopTransparentFullscreenTaskData(it, task) }
            }
            // Already fullscreen, no-op.
            if (task.isFullscreen) return null
@@ -3084,6 +3113,16 @@ class DesktopTasksController(
            logD("handleIncompatibleTaskLaunch not in desktop, not a freeform task, nothing to do")
            return null
        }
        if (
            isTransparentTask &&
                (DesktopModeFlags.FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK.isTrue ||
                    DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                        .isTrue)
        ) {
            // Only update task repository for transparent task.
            val deskId = taskRepository.getActiveDeskId(displayId)
            deskId?.let { taskRepository.setTopTransparentFullscreenTaskData(it, task) }
        }
        // Both opaque and transparent incompatible tasks need to be forced to fullscreen, but
        // opaque ones force-exit the desktop while transparent ones are just shown on top of the
        // desktop while keeping it active.
@@ -3445,6 +3484,7 @@ class DesktopTasksController(
        transition: IBinder,
        launchTaskId: Int,
        minimizeTaskId: Int?,
        closingTopTransparentTaskId: Int?,
    ) {
        if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue) {
            return
@@ -3455,6 +3495,7 @@ class DesktopTasksController(
                transition,
                launchTaskId,
                minimizeTaskId,
                closingTopTransparentTaskId,
                /* exitingImmersiveTask= */ null,
            )
        )
@@ -3509,7 +3550,16 @@ class DesktopTasksController(
                    launchTaskId = newTask?.taskId,
                )
                if (newTask != null && addPendingLaunchTransition) {
                    addPendingAppLaunchTransition(transition, newTask.taskId, taskIdToMinimize)
                    // Remove top transparent fullscreen task if needed.
                    val closingTopTransparentTaskId =
                        taskRepository.getTopTransparentFullscreenTaskData(deskId)?.taskId
                    closeTopTransparentFullscreenTask(wct, deskId)
                    addPendingAppLaunchTransition(
                        transition,
                        newTask.taskId,
                        taskIdToMinimize,
                        closingTopTransparentTaskId,
                    )
                }
            }
        }
@@ -3571,6 +3621,15 @@ class DesktopTasksController(
        }
    }

    private fun closeTopTransparentFullscreenTask(wct: WindowContainerTransaction, deskId: Int) {
        if (!DesktopModeFlags.FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK.isTrue) return
        val data = taskRepository.getTopTransparentFullscreenTaskData(deskId)
        if (data != null) {
            logD("closeTopTransparentFullscreenTask: taskId=%d, deskId=%d", data.taskId, deskId)
            wct.removeTask(data.token)
        }
    }

    /** Activates the desk at the given index if it exists. */
    fun activatePreviousDesk(displayId: Int) {
        if (
+6 −5
Original line number Diff line number Diff line
@@ -79,9 +79,9 @@ class DesktopTasksTransitionObserver(
    ) {
        // TODO: b/332682201 Update repository state
        if (
            DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC.isTrue &&
            DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
                !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
                (DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                    .isTrue || DesktopModeFlags.FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK.isTrue)
        ) {
            updateTopTransparentFullscreenTaskId(info)
        }
@@ -213,8 +213,9 @@ class DesktopTasksTransitionObserver(
                change.taskInfo?.let { task ->
                    val desktopRepository = desktopUserRepositories.getProfile(task.userId)
                    val displayId = task.displayId
                    val deskId = desktopRepository.getActiveDeskId(displayId) ?: return@forEachLoop
                    val transparentTaskId =
                        desktopRepository.getTopTransparentFullscreenTaskId(displayId)
                        desktopRepository.getTopTransparentFullscreenTaskData(deskId)?.taskId
                    if (transparentTaskId == null) return@forEachLoop
                    val changeMode = change.mode
                    val taskId = task.taskId
@@ -228,7 +229,7 @@ class DesktopTasksTransitionObserver(
                        isTopTransparentFullscreenTaskClosing ||
                            isNonTopTransparentFullscreenTaskOpening
                    ) {
                        desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
                        desktopRepository.clearTopTransparentFullscreenTaskData(deskId)
                        return@forEachLoop
                    }
                }
Loading