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

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

Merge "Exit immersive on new instance launches" into main

parents 10ad8cc2 2034639b
Loading
Loading
Loading
Loading
+25 −8
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.Handler
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_OPEN
import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
@@ -95,7 +96,7 @@ class DesktopMixedTransitionHandler(
    fun startLaunchTransition(
        @WindowManager.TransitionType transitionType: Int,
        wct: WindowContainerTransaction,
        taskId: Int,
        taskId: Int?,
        minimizingTaskId: Int? = null,
        exitingImmersiveTask: Int? = null,
    ): IBinder {
@@ -216,12 +217,12 @@ class DesktopMixedTransitionHandler(
    ): Boolean {
        // Check if there's also an immersive change during this launch.
        val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask ->
            findDesktopTaskChange(info, exitingTask)
            findTaskChange(info, exitingTask)
        }
        val minimizeChange = pending.minimizingTask?.let { minimizingTask ->
            findDesktopTaskChange(info, minimizingTask)
            findTaskChange(info, minimizingTask)
        }
        val launchChange = findDesktopTaskChange(info, pending.launchingTask)
        val launchChange = findDesktopTaskLaunchChange(info, pending.launchingTask)
        if (launchChange == null) {
            check(minimizeChange == null)
            check(immersiveExitChange == null)
@@ -291,7 +292,7 @@ class DesktopMixedTransitionHandler(
    ): Boolean {
        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue) return false

        val minimizeChange = findDesktopTaskChange(info, pending.minimizingTask)
        val minimizeChange = findTaskChange(info, pending.minimizingTask)
        if (minimizeChange == null) {
            logW("Should have minimizing desktop task")
            return false
@@ -417,8 +418,24 @@ class DesktopMixedTransitionHandler(
        }
    }

    private fun findDesktopTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? {
        return info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId }
    private fun findTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? =
        info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId }

    private fun findDesktopTaskLaunchChange(
        info: TransitionInfo,
        launchTaskId: Int?
    ): TransitionInfo.Change? {
        return if (launchTaskId != null) {
            // Launching a known task (probably from background or moving to front), so
            // specifically look for it.
            findTaskChange(info, launchTaskId)
        } else {
            // Launching a new task, so the first opening freeform task.
            info.changes.firstOrNull { change ->
                change.mode == TRANSIT_OPEN
                        && change.taskInfo != null && change.taskInfo!!.isFreeform
            }
        }
    }

    private fun WindowContainerTransaction?.merge(
@@ -441,7 +458,7 @@ class DesktopMixedTransitionHandler(
        /** A task is opening or moving to front. */
        data class Launch(
            override val transition: IBinder,
            val launchingTask: Int,
            val launchingTask: Int?,
            val minimizingTask: Int?,
            val exitingImmersiveTask: Int?,
        ) : PendingMixedTransition()
+29 −26
Original line number Diff line number Diff line
@@ -89,7 +89,6 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener
@@ -618,25 +617,18 @@ class DesktopTasksController(
    private fun moveBackgroundTaskToFront(taskId: Int, remoteTransition: RemoteTransition?) {
        logV("moveBackgroundTaskToFront taskId=%s", taskId)
        val wct = WindowContainerTransaction()
        // TODO: b/342378842 - Instead of using default display, support multiple displays
        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
            wct = wct,
            displayId = DEFAULT_DISPLAY,
            excludeTaskId = taskId,
        )
        wct.startTask(
            taskId,
            ActivityOptions.makeBasic().apply {
                launchWindowingMode = WINDOWING_MODE_FREEFORM
            }.toBundle(),
        )
        val transition = startLaunchTransition(
        startLaunchTransition(
            TRANSIT_OPEN,
            wct,
            taskId,
            remoteTransition = remoteTransition
        )
        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
    }

    /**
@@ -655,47 +647,53 @@ class DesktopTasksController(
        }
        val wct = WindowContainerTransaction()
        wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
        val result = desktopImmersiveController.exitImmersiveIfApplicable(
            wct = wct,
            displayId = taskInfo.displayId,
            excludeTaskId = taskInfo.taskId,
        )
        val exitResult = if (result is ExitResult.Exit) { result } else { null }
        val transition = startLaunchTransition(
        startLaunchTransition(
            transitionType = TRANSIT_TO_FRONT,
            wct = wct,
            taskId = taskInfo.taskId,
            exitingImmersiveTask = exitResult?.exitingTask,
            launchingTaskId = taskInfo.taskId,
            remoteTransition = remoteTransition,
            displayId = taskInfo.displayId,
        )
        exitResult?.runOnTransitionStart?.invoke(transition)
    }

    private fun startLaunchTransition(
        transitionType: Int,
        wct: WindowContainerTransaction,
        taskId: Int,
        exitingImmersiveTask: Int? = null,
        launchingTaskId: Int?,
        remoteTransition: RemoteTransition? = null,
        displayId: Int = DEFAULT_DISPLAY,
    ): IBinder {
        val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId)
        val taskIdToMinimize = if (launchingTaskId != null) {
            addAndGetMinimizeChanges(displayId, wct, newTaskId = launchingTaskId)
        } else {
            logW("Starting desktop task launch without checking the task-limit")
            // TODO(b/378920066): This currently does not respect the desktop window limit.
            //  It's possible that |launchingTaskId| is null when launching using an intent, and
            //  the task-limit should be respected then too.
            null
        }
        val exitImmersiveResult = desktopImmersiveController.exitImmersiveIfApplicable(
            wct = wct,
            displayId = displayId,
            excludeTaskId = launchingTaskId,
        )
        if (remoteTransition == null) {
            val t = desktopMixedTransitionHandler.startLaunchTransition(
                transitionType = transitionType,
                wct = wct,
                taskId = taskId,
                taskId = launchingTaskId,
                minimizingTaskId = taskIdToMinimize,
                exitingImmersiveTask = exitingImmersiveTask,
                exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
            )
            taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
            exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
            return t
        }
        if (taskIdToMinimize == null) {
            val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
            val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
            remoteTransitionHandler.setTransition(t)
            exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
            return t
        }
        val remoteTransitionHandler =
@@ -704,6 +702,7 @@ class DesktopTasksController(
        val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
        remoteTransitionHandler.setTransition(t)
        taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
        exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
        return t
    }

@@ -1472,10 +1471,14 @@ class DesktopTasksController(
                )
            }
            WINDOWING_MODE_FREEFORM -> {
                // TODO(b/336289597): This currently does not respect the desktop window limit.
                val wct = WindowContainerTransaction()
                wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
                transitions.startTransition(TRANSIT_OPEN, wct, null)
                startLaunchTransition(
                    transitionType = TRANSIT_OPEN,
                    wct = wct,
                    launchingTaskId = null,
                    displayId = callingTaskInfo.displayId
                )
            }
        }
    }
+74 −1
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TransitionType
@@ -47,6 +49,7 @@ import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler.PendingMix
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.StubTransaction
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
@@ -490,6 +493,72 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
        assertFalse("Should not start animation without launching desktop task", started)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
    fun startLaunchTransition_unknownLaunchingTask_animates() {
        val wct = WindowContainerTransaction()
        val task = createTask(WINDOWING_MODE_FREEFORM)
        val transition = Binder()
        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
            .thenReturn(transition)
        whenever(transitions.dispatchTransition(eq(transition), any(), any(), any(), any(), any()))
            .thenReturn(mock())

        mixedHandler.startLaunchTransition(
            transitionType = TRANSIT_OPEN,
            wct = wct,
            taskId = null,
        )

        val started = mixedHandler.startAnimation(
            transition,
            createTransitionInfo(
                TRANSIT_OPEN,
                listOf(createChange(task, mode = TRANSIT_OPEN))
            ),
            StubTransaction(),
            StubTransaction(),
        ) { }

        assertThat(started).isEqualTo(true)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
    fun startLaunchTransition_unknownLaunchingTaskOverImmersive_animatesImmersiveChange() {
        val wct = WindowContainerTransaction()
        val immersiveTask = createTask(WINDOWING_MODE_FREEFORM)
        val openingTask = createTask(WINDOWING_MODE_FREEFORM)
        val transition = Binder()
        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
            .thenReturn(transition)
        whenever(transitions.dispatchTransition(eq(transition), any(), any(), any(), any(), any()))
            .thenReturn(mock())

        mixedHandler.startLaunchTransition(
            transitionType = TRANSIT_OPEN,
            wct = wct,
            taskId = null,
            exitingImmersiveTask = immersiveTask.taskId,
        )

        val immersiveChange = createChange(immersiveTask, mode = TRANSIT_CHANGE)
        val openingChange = createChange(openingTask, mode = TRANSIT_OPEN)
        val started = mixedHandler.startAnimation(
            transition,
            createTransitionInfo(
                TRANSIT_OPEN,
                listOf(immersiveChange, openingChange)
            ),
            StubTransaction(),
            StubTransaction(),
        ) { }

        assertThat(started).isEqualTo(true)
        verify(desktopImmersiveController)
            .animateResizeChange(eq(immersiveChange), any(), any(), any())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
    fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
@@ -712,9 +781,13 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
        changes.forEach { change -> addChange(change) }
    }

    private fun createChange(task: RunningTaskInfo): TransitionInfo.Change =
    private fun createChange(
        task: RunningTaskInfo,
        @TransitionInfo.TransitionMode mode: Int = TRANSIT_NONE
    ): TransitionInfo.Change =
        TransitionInfo.Change(task.token, SurfaceControl()).apply {
            taskInfo = task
            setMode(mode)
        }

    private fun createTask(@WindowingMode windowingMode: Int): RunningTaskInfo =
+49 −11
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ import com.android.wm.shell.common.LaunchAdjacentController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener
@@ -167,6 +168,7 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.kotlin.eq
@@ -294,10 +296,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
    whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), any<RunningTaskInfo>()))
      .thenReturn(DesktopImmersiveController.ExitResult.NoExit)
      .thenReturn(ExitResult.NoExit)
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
      .thenReturn(DesktopImmersiveController.ExitResult.NoExit)
      .thenReturn(ExitResult.NoExit)

    controller = createController()
    controller.setSplitScreenController(splitScreenController)
@@ -1833,7 +1835,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
      .thenReturn(transition)
    whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task)))
      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
      .thenReturn(
        ExitResult.Exit(
        exitingTask = task.taskId,
        runOnTransitionStart = runOnTransit,
      ))
@@ -3214,13 +3217,43 @@ class DesktopTasksControllerTest : ShellTestCase() {
  fun newWindow_fromFreeformAddsNewWindow() {
    setUpLandscapeDisplay()
    val task = setUpFreeformTask()
    val wctCaptor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
    val wctCaptor = argumentCaptor<WindowContainerTransaction>()
    val transition = Binder()
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
      .thenReturn(ExitResult.NoExit)
    whenever(desktopMixedTransitionHandler
      .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
      .thenReturn(transition)

    runOpenNewWindow(task)
    verify(transitions).startTransition(anyInt(), wctCaptor.capture(), anyOrNull())
    assertThat(ActivityOptions.fromBundle(wctCaptor.value.hierarchyOps[0].launchOptions)

    verify(desktopMixedTransitionHandler)
      .startLaunchTransition(anyInt(), wctCaptor.capture(), anyOrNull(), anyOrNull(), anyOrNull())
    assertThat(ActivityOptions.fromBundle(wctCaptor.firstValue.hierarchyOps[0].launchOptions)
      .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
  }

  @Test
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
  fun newWindow_fromFreeform_exitsImmersiveIfNeeded() {
    setUpLandscapeDisplay()
    val immersiveTask = setUpFreeformTask()
    val task = setUpFreeformTask()
    val runOnStart = RunOnStartTransitionCallback()
    val transition = Binder()
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
      .thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
    whenever(desktopMixedTransitionHandler
      .startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
      .thenReturn(transition)

    runOpenNewWindow(task)

    runOnStart.assertOnlyInvocation(transition)
  }

  private fun runOpenNewWindow(task: RunningTaskInfo) {
    markTaskVisible(task)
    task.baseActivity = mock(ComponentName::class.java)
@@ -3314,7 +3347,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
      .thenReturn(transition)
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)))
      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
      .thenReturn(
        ExitResult.Exit(
        exitingTask = immersiveTask.taskId,
        runOnTransitionStart = runOnStartTransit,
      ))
@@ -3719,7 +3753,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    val transition = Binder()
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
      .thenReturn(
        ExitResult.Exit(
        exitingTask = 5,
        runOnTransitionStart = runOnStartTransit,
      ))
@@ -3740,7 +3775,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    val transition = Binder()
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
      .thenReturn(
        ExitResult.Exit(
        exitingTask = 5,
        runOnTransitionStart = runOnStartTransit,
      ))
@@ -3760,7 +3796,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    val transition = Binder()
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
      .thenReturn(
        ExitResult.Exit(
        exitingTask = 5,
        runOnTransitionStart = runOnStartTransit,
      ))
@@ -3782,7 +3819,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    val transition = Binder()
    whenever(mMockDesktopImmersiveController
      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
      .thenReturn(DesktopImmersiveController.ExitResult.Exit(
      .thenReturn(
        ExitResult.Exit(
        exitingTask = 5,
        runOnTransitionStart = runOnStartTransit,
      ))