Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +25 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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( Loading @@ -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() Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +29 −26 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) } /** Loading @@ -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 = Loading @@ -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 } Loading Loading @@ -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 ) } } } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +74 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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() { Loading Loading @@ -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 = Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +49 −11 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) Loading Loading @@ -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, )) Loading Loading @@ -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) Loading Loading @@ -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, )) Loading Loading @@ -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, )) Loading @@ -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, )) Loading @@ -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, )) Loading @@ -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, )) Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +25 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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( Loading @@ -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() Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +29 −26 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) } /** Loading @@ -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 = Loading @@ -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 } Loading Loading @@ -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 ) } } } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +74 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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() { Loading Loading @@ -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 = Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +49 −11 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) Loading Loading @@ -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, )) Loading Loading @@ -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) Loading Loading @@ -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, )) Loading Loading @@ -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, )) Loading @@ -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, )) Loading @@ -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, )) Loading @@ -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, )) Loading