diff options
7 files changed, 582 insertions, 75 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 8d25a9a7fe2d..c6cd3204451b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -703,6 +703,7 @@ public abstract class WMShellModule { Transitions transitions, KeyguardManager keyguardManager, ReturnToDragStartAnimator returnToDragStartAnimator, + Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler, EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler, ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler, DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler, @@ -736,6 +737,7 @@ public abstract class WMShellModule { transitions, keyguardManager, returnToDragStartAnimator, + desktopMixedTransitionHandler.get(), enterDesktopTransitionHandler, exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler, @@ -960,6 +962,7 @@ public abstract class WMShellModule { @DynamicOverride DesktopRepository desktopRepository, FreeformTaskTransitionHandler freeformTaskTransitionHandler, CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, InteractionJankMonitor interactionJankMonitor, @ShellMainThread Handler handler) { if (!DesktopModeStatus.canEnterDesktopMode(context)) { @@ -972,6 +975,7 @@ public abstract class WMShellModule { desktopRepository, freeformTaskTransitionHandler, closeDesktopTaskTransitionHandler, + desktopImmersiveController.get(), interactionJankMonitor, handler)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt index d0bc5f0955f7..e741892bf6fc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt @@ -130,7 +130,8 @@ class DesktopImmersiveController( displayId: Int ) { if (!Flags.enableFullyImmersiveInDesktop()) return - exitImmersiveIfApplicable(wct, displayId)?.invoke(transition) + val result = exitImmersiveIfApplicable(wct, displayId) + result.asExit()?.runOnTransitionStart?.invoke(transition) } /** @@ -145,16 +146,23 @@ class DesktopImmersiveController( wct: WindowContainerTransaction, displayId: Int, excludeTaskId: Int? = null, - ): ((IBinder) -> Unit)? { - if (!Flags.enableFullyImmersiveInDesktop()) return null - val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null + ): ExitResult { + if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit + val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) + ?: return ExitResult.NoExit if (immersiveTask == excludeTaskId) { - return null + return ExitResult.NoExit } - val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null + val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) + ?: return ExitResult.NoExit logV("Appending immersive exit for task: $immersiveTask in display: $displayId") wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) - return { transition -> addPendingImmersiveExit(immersiveTask, displayId, transition) } + return ExitResult.Exit( + exitingTask = immersiveTask, + runOnTransitionStart = { transition -> + addPendingImmersiveExit(immersiveTask, displayId, transition) + } + ) } /** @@ -167,22 +175,25 @@ class DesktopImmersiveController( fun exitImmersiveIfApplicable( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo - ): ((IBinder) -> Unit)? { - if (!Flags.enableFullyImmersiveInDesktop()) return null + ): ExitResult { + if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit if (desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId)) { // A full immersive task is being minimized, make sure the immersive state is broken // (i.e. resize back to max bounds). wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) logV("Appending immersive exit for task: ${taskInfo.taskId}") - return { transition -> - addPendingImmersiveExit( - taskId = taskInfo.taskId, - displayId = taskInfo.displayId, - transition = transition - ) - } + return ExitResult.Exit( + exitingTask = taskInfo.taskId, + runOnTransitionStart = { transition -> + addPendingImmersiveExit( + taskId = taskInfo.taskId, + displayId = taskInfo.displayId, + transition = transition + ) + } + ) } - return null + return ExitResult.NoExit } @@ -461,6 +472,20 @@ class DesktopImmersiveController( var transition: IBinder, ) + /** The result of an external exit request. */ + sealed class ExitResult { + /** An immersive task exit (meaning, resize) was appended to the request. */ + data class Exit( + val exitingTask: Int, + val runOnTransitionStart: ((IBinder) -> Unit) + ) : ExitResult() + /** There was no exit appended to the request. */ + data object NoExit : ExitResult() + + /** Returns the result as an [Exit] or null if it isn't of that type. */ + fun asExit(): Exit? = if (this is Exit) this else null + } + private enum class Direction { ENTER, EXIT } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt index 54a07f20fd84..0bc571f4782c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt @@ -27,15 +27,18 @@ import android.window.DesktopModeFlags import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction +import androidx.annotation.VisibleForTesting import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE import com.android.internal.jank.InteractionJankMonitor import com.android.internal.protolog.ProtoLog +import com.android.window.flags.Flags 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 import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.transition.MixedTransitionHandler import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TransitionFinishCallback /** The [Transitions.TransitionHandler] coordinates transition handlers in desktop windowing. */ class DesktopMixedTransitionHandler( @@ -44,10 +47,14 @@ class DesktopMixedTransitionHandler( private val desktopRepository: DesktopRepository, private val freeformTaskTransitionHandler: FreeformTaskTransitionHandler, private val closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler, + private val desktopImmersiveController: DesktopImmersiveController, private val interactionJankMonitor: InteractionJankMonitor, @ShellMainThread private val handler: Handler, ) : MixedTransitionHandler, FreeformTaskTransitionStarter { + @VisibleForTesting + val pendingMixedTransitions = mutableListOf<PendingMixedTransition>() + /** Delegates starting transition to [FreeformTaskTransitionHandler]. */ override fun startWindowingModeTransition( targetWindowingMode: Int, @@ -65,6 +72,40 @@ class DesktopMixedTransitionHandler( } requireNotNull(wct) return transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this) + .also { transition -> + pendingMixedTransitions.add(PendingMixedTransition.Close(transition)) + } + } + + /** + * Starts a launch transition for [taskId], with an optional [exitingImmersiveTask] if it was + * included in the [wct] and is expected to be animated by this handler. + */ + fun startLaunchTransition( + @WindowManager.TransitionType transitionType: Int, + wct: WindowContainerTransaction, + taskId: Int, + exitingImmersiveTask: Int? = null, + ): IBinder { + if (!Flags.enableFullyImmersiveInDesktop()) { + return transitions.startTransition(transitionType, wct, /* handler= */ null) + } + if (exitingImmersiveTask == null) { + logV("Starting mixed launch transition for task#%d", taskId) + } else { + logV( + "Starting mixed launch transition for task#%d with immersive exit of task#%d", + taskId, exitingImmersiveTask + ) + } + return transitions.startTransition(transitionType, wct, /* handler= */ this) + .also { transition -> + pendingMixedTransitions.add(PendingMixedTransition.Launch( + transition = transition, + launchingTask = taskId, + exitingImmersiveTask = exitingImmersiveTask + )) + } } /** Returns null, as it only handles transitions started from Shell. */ @@ -78,11 +119,43 @@ class DesktopMixedTransitionHandler( info: TransitionInfo, startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction, - finishCallback: Transitions.TransitionFinishCallback, + finishCallback: TransitionFinishCallback, + ): Boolean { + val pending = pendingMixedTransitions.find { pending -> pending.transition == transition } + ?: return false.also { + logW("Should have pending desktop transition") + } + pendingMixedTransitions.remove(pending) + logV("Animating pending mixed transition: %s", pending) + return when (pending) { + is PendingMixedTransition.Close -> animateCloseTransition( + transition, + info, + startTransaction, + finishTransaction, + finishCallback + ) + is PendingMixedTransition.Launch -> animateLaunchTransition( + pending, + transition, + info, + startTransaction, + finishTransaction, + finishCallback + ) + } + } + + private fun animateCloseTransition( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: TransitionFinishCallback, ): Boolean { val closeChange = findCloseDesktopTaskChange(info) if (closeChange == null) { - ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: Should have closing desktop task", TAG) + logW("Should have closing desktop task") return false } if (isLastDesktopTask(closeChange)) { @@ -106,6 +179,74 @@ class DesktopMixedTransitionHandler( ) } + private fun animateLaunchTransition( + pending: PendingMixedTransition.Launch, + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: TransitionFinishCallback, + ): Boolean { + // Check if there's also an immersive change during this launch. + val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask -> + findDesktopTaskChange(info, exitingTask) + } + val launchChange = findDesktopTaskChange(info, pending.launchingTask) + ?: error("Should have pending launching task change") + + var subAnimationCount = -1 + var combinedWct: WindowContainerTransaction? = null + val finishCb = TransitionFinishCallback { wct -> + --subAnimationCount + combinedWct = combinedWct.merge(wct) + if (subAnimationCount > 0) return@TransitionFinishCallback + finishCallback.onTransitionFinished(combinedWct) + } + + logV( + "Animating pending mixed launch transition task#%d immersiveExitTask#%s", + launchChange.taskInfo!!.taskId, immersiveExitChange?.taskInfo?.taskId + ) + if (immersiveExitChange != null) { + subAnimationCount = 2 + // Animate the immersive exit change separately. + info.changes.remove(immersiveExitChange) + desktopImmersiveController.animateResizeChange( + immersiveExitChange, + startTransaction, + finishTransaction, + finishCb + ) + // Let the leftover/default handler animate the remaining changes. + return dispatchToLeftoverHandler( + transition, + info, + startTransaction, + finishTransaction, + finishCb + ) + } + // There's nothing to animate separately, so let the left over handler animate + // the entire transition. + subAnimationCount = 1 + return dispatchToLeftoverHandler( + transition, + info, + startTransaction, + finishTransaction, + finishCb + ) + } + + override fun onTransitionConsumed( + transition: IBinder, + aborted: Boolean, + finishTransaction: SurfaceControl.Transaction? + ) { + pendingMixedTransitions.removeAll { pending -> pending.transition == transition } + super.onTransitionConsumed(transition, aborted, finishTransaction) + } + /** * Dispatch close desktop task animation to the default transition handlers. Allows delegating * it to Launcher to animate in sync with show Home transition. @@ -126,14 +267,34 @@ class DesktopMixedTransitionHandler( CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE, ) // Dispatch the last desktop task closing animation. + return dispatchToLeftoverHandler( + transition = transition, + info = info, + startTransaction = startTransaction, + finishTransaction = finishTransaction, + finishCallback = finishCallback, + doOnFinishCallback = { + // Finish the jank trace when closing the last window in desktop mode. + interactionJankMonitor.end(CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE) + } + ) + } + + private fun dispatchToLeftoverHandler( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: TransitionFinishCallback, + doOnFinishCallback: (() -> Unit)? = null, + ): Boolean { return transitions.dispatchTransition( transition, info, startTransaction, finishTransaction, { wct -> - // Finish the jank trace when closing the last window in desktop mode. - interactionJankMonitor.end(CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE) + doOnFinishCallback?.invoke() finishCallback.onTransitionFinished(wct) }, /* skip= */ this @@ -155,6 +316,43 @@ class DesktopMixedTransitionHandler( } } + private fun findDesktopTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? { + return info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId } + } + + private fun WindowContainerTransaction?.merge( + wct: WindowContainerTransaction? + ): WindowContainerTransaction? { + if (wct == null) return this + if (this == null) return wct + return this.merge(wct) + } + + /** A scheduled transition that will potentially be animated by more than one handler */ + sealed class PendingMixedTransition { + abstract val transition: IBinder + + /** A task is closing. */ + data class Close( + override val transition: IBinder, + ) : PendingMixedTransition() + + /** A task is opening or moving to front. */ + data class Launch( + override val transition: IBinder, + val launchingTask: Int, + val exitingImmersiveTask: Int?, + ) : PendingMixedTransition() + } + + private fun logV(msg: String, vararg arguments: Any?) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private fun logW(msg: String, vararg arguments: Any?) { + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + companion object { private const val TAG = "DesktopMixedTransitionHandler" } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 38a4c71b046f..3d3c072727be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -87,6 +87,7 @@ 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 @@ -146,6 +147,7 @@ class DesktopTasksController( private val transitions: Transitions, private val keyguardManager: KeyguardManager, private val returnToDragStartAnimator: ReturnToDragStartAnimator, + private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler, private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler, private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler, private val desktopModeDragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler, @@ -379,7 +381,7 @@ class DesktopTasksController( // TODO(342378842): Instead of using default display, support multiple displays val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( DEFAULT_DISPLAY, wct, taskId) - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, displayId = DEFAULT_DISPLAY, excludeTaskId = taskId, @@ -393,7 +395,7 @@ class DesktopTasksController( // TODO(343149901): Add DPI changes for task launch val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } - runOnTransit?.invoke(transition) + exitResult.asExit()?.runOnTransitionStart?.invoke(transition) return true } @@ -410,7 +412,7 @@ class DesktopTasksController( } logV("moveRunningTaskToDesktop taskId=%d", task.taskId) exitSplitIfApplicable(wct, task) - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, displayId = task.displayId, excludeTaskId = task.taskId, @@ -422,7 +424,7 @@ class DesktopTasksController( val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } - runOnTransit?.invoke(transition) + exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } /** @@ -459,12 +461,12 @@ class DesktopTasksController( val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) addMoveToDesktopChanges(wct, taskInfo) - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct, taskInfo.displayId) val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) transition?.let { taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) } - runOnTransit?.invoke(transition) + exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } } @@ -507,7 +509,8 @@ class DesktopTasksController( taskId ) ) - return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) + return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit() + ?.runOnTransitionStart } fun minimizeTask(taskInfo: RunningTaskInfo) { @@ -520,7 +523,7 @@ class DesktopTasksController( removeWallpaperActivity(wct) } // Notify immersive handler as it might need to exit immersive state. - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) + val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) wct.reorder(taskInfo.token, false) val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct) @@ -531,7 +534,7 @@ class DesktopTasksController( taskId = taskId ) } - runOnTransit?.invoke(transition) + exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } /** Move a task with given `taskId` to fullscreen */ @@ -627,7 +630,7 @@ class DesktopTasksController( logV("moveBackgroundTaskToFront taskId=%s", taskId) val wct = WindowContainerTransaction() // TODO: b/342378842 - Instead of using default display, support multiple displays - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, displayId = DEFAULT_DISPLAY, excludeTaskId = taskId, @@ -638,8 +641,13 @@ class DesktopTasksController( launchWindowingMode = WINDOWING_MODE_FREEFORM }.toBundle(), ) - val transition = startLaunchTransition(TRANSIT_OPEN, wct, taskId, remoteTransition) - runOnTransit?.invoke(transition) + val transition = startLaunchTransition( + TRANSIT_OPEN, + wct, + taskId, + remoteTransition = remoteTransition + ) + exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } /** @@ -658,32 +666,39 @@ class DesktopTasksController( } val wct = WindowContainerTransaction() wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */) - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + val result = desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, displayId = taskInfo.displayId, excludeTaskId = taskInfo.taskId, ) - val transition = - startLaunchTransition( - TRANSIT_TO_FRONT, - wct, - taskInfo.taskId, - remoteTransition, - taskInfo.displayId - ) - runOnTransit?.invoke(transition) + val exitResult = if (result is ExitResult.Exit) { result } else { null } + val transition = startLaunchTransition( + transitionType = TRANSIT_TO_FRONT, + wct = wct, + taskId = taskInfo.taskId, + exitingImmersiveTask = exitResult?.exitingTask, + remoteTransition = remoteTransition, + displayId = taskInfo.displayId, + ) + exitResult?.runOnTransitionStart?.invoke(transition) } private fun startLaunchTransition( transitionType: Int, wct: WindowContainerTransaction, taskId: Int, - remoteTransition: RemoteTransition?, + exitingImmersiveTask: Int? = null, + remoteTransition: RemoteTransition? = null, displayId: Int = DEFAULT_DISPLAY, ): IBinder { val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId) if (remoteTransition == null) { - val t = transitions.startTransition(transitionType, wct, null /* handler */) + val t = desktopMixedTransitionHandler.startLaunchTransition( + transitionType = transitionType, + wct = wct, + taskId = taskId, + exitingImmersiveTask = exitingImmersiveTask, + ) taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) } return t } @@ -1373,14 +1388,15 @@ class DesktopTasksController( wct.startTask(requestedTaskId, options.toBundle()) val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( callingTask.displayId, wct, requestedTaskId) - val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( - wct = wct, - displayId = callingTask.displayId, - excludeTaskId = requestedTaskId, - ) + val exitResult = desktopImmersiveController + .exitImmersiveIfApplicable( + wct = wct, + displayId = callingTask.displayId, + excludeTaskId = requestedTaskId, + ) val transition = transitions.startTransition(TRANSIT_OPEN, wct, null) taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } - runOnTransit?.invoke(transition) + exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } else { val splitPosition = splitScreenController.determineNewInstancePosition(callingTask) splitScreenController.startTask(requestedTaskId, splitPosition, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt index e83f5c7a79a1..1c4b9bfe2fd7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt @@ -347,7 +347,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() { wct = wct, displayId = DEFAULT_DISPLAY, excludeTaskId = task.taskId - )?.invoke(transition) + ).asExit()?.runOnTransitionStart?.invoke(transition) assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY @@ -402,7 +402,8 @@ class DesktopImmersiveControllerTest : ShellTestCase() { immersive = true ) - controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition) + controller.exitImmersiveIfApplicable(wct, task) + .asExit()?.runOnTransitionStart?.invoke(transition) assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY @@ -412,23 +413,36 @@ class DesktopImmersiveControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun exitImmersiveIfApplicable_byTask_notInImmersive_doesNotAddPendingExitOnRun() { + fun exitImmersiveIfApplicable_byTask_notInImmersive_doesNotExit() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) val wct = WindowContainerTransaction() - val transition = Binder() desktopRepository.setTaskInFullImmersiveState( - displayId = DEFAULT_DISPLAY, + displayId = task.displayId, taskId = task.taskId, immersive = false ) - controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition) + val result = controller.exitImmersiveIfApplicable(wct, task) - assertThat(controller.pendingExternalExitTransitions.any { exit -> - exit.transition == transition && exit.displayId == DEFAULT_DISPLAY - && exit.taskId == task.taskId - }).isFalse() + assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun exitImmersiveIfApplicable_byDisplay_notInImmersive_doesNotExit() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = false + ) + + val result = controller.exitImmersiveIfApplicable(wct, task.displayId) + + assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit) } @Test @@ -631,7 +645,8 @@ class DesktopImmersiveControllerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - controller.exitImmersiveIfApplicable(wct, task)?.invoke(Binder()) + controller.exitImmersiveIfApplicable(wct, task) + .asExit()?.runOnTransitionStart?.invoke(Binder()) controller.moveTaskToNonImmersive(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt index be0663cbd70d..868883d12ac0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt @@ -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_OPEN +import android.view.WindowManager.TransitionType import android.window.TransitionInfo import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest @@ -41,6 +43,7 @@ import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.freeform.FreeformTaskTransitionHandler import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertTrue @@ -51,6 +54,8 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.argThat import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -71,6 +76,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { @Mock lateinit var desktopRepository: DesktopRepository @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler @Mock lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler + @Mock lateinit var desktopImmersiveController: DesktopImmersiveController @Mock lateinit var interactionJankMonitor: InteractionJankMonitor @Mock lateinit var mockHandler: Handler @Mock lateinit var closingTaskLeash: SurfaceControl @@ -86,6 +92,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { desktopRepository, freeformTaskTransitionHandler, closeDesktopTaskTransitionHandler, + desktopImmersiveController, interactionJankMonitor, mockHandler ) @@ -146,7 +153,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { val transition = mock<IBinder>() val transitionInfo = createTransitionInfo( - changeMode = WindowManager.TRANSIT_OPEN, + changeMode = TRANSIT_OPEN, task = createTask(WINDOWING_MODE_FREEFORM) ) whenever(freeformTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any())) @@ -164,7 +171,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS) fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() { + val wct = WindowContainerTransaction() val transition = mock<IBinder>() val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)) whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2) @@ -172,6 +181,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { closeDesktopTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any()) ) .thenReturn(true) + whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler)) + .thenReturn(transition) + mixedHandler.startRemoveTransition(wct) val started = mixedHandler.startAnimation( transition = transition, @@ -187,12 +199,17 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS) fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() { + val wct = WindowContainerTransaction() val transition = mock<IBinder>() val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)) whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(1) whenever(transitions.dispatchTransition(any(), any(), any(), any(), any(), any())) .thenReturn(mock()) + whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler)) + .thenReturn(transition) + mixedHandler.startRemoveTransition(wct) mixedHandler.startAnimation( transition = transition, @@ -220,6 +237,176 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { ) } + @Test + @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startLaunchTransition_immersiveMixDisabled_doesNotUseMixedHandler() { + val wct = WindowContainerTransaction() + val task = createTask(WINDOWING_MODE_FREEFORM) + whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull())) + .thenReturn(Binder()) + + mixedHandler.startLaunchTransition( + transitionType = TRANSIT_OPEN, + wct = wct, + taskId = task.taskId, + exitingImmersiveTask = null + ) + + verify(transitions).startTransition(TRANSIT_OPEN, wct, /* handler= */ null) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startLaunchTransition_immersiveMixEnabled_usesMixedHandler() { + val wct = WindowContainerTransaction() + val task = createTask(WINDOWING_MODE_FREEFORM) + whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull())) + .thenReturn(Binder()) + + mixedHandler.startLaunchTransition( + transitionType = TRANSIT_OPEN, + wct = wct, + taskId = task.taskId, + exitingImmersiveTask = null + ) + + verify(transitions).startTransition(TRANSIT_OPEN, wct, mixedHandler) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startAndAnimateLaunchTransition_withoutImmersiveChange_dispatchesAllChangesToLeftOver() { + val wct = WindowContainerTransaction() + val launchingTask = createTask(WINDOWING_MODE_FREEFORM) + val transition = Binder() + whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull())) + .thenReturn(transition) + + mixedHandler.startLaunchTransition( + transitionType = TRANSIT_OPEN, + wct = wct, + taskId = launchingTask.taskId, + exitingImmersiveTask = null, + ) + val launchTaskChange = createChange(launchingTask) + val otherChange = createChange(createTask(WINDOWING_MODE_FREEFORM)) + mixedHandler.startAnimation( + transition, + createTransitionInfo( + TRANSIT_OPEN, + listOf(launchTaskChange, otherChange) + ), + SurfaceControl.Transaction(), + SurfaceControl.Transaction(), + ) { } + + verify(transitions).dispatchTransition( + eq(transition), + argThat { info -> + info.changes.contains(launchTaskChange) && info.changes.contains(otherChange) + }, + any(), + any(), + any(), + eq(mixedHandler), + ) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startAndAnimateLaunchTransition_withImmersiveChange_mixesAnimations() { + val wct = WindowContainerTransaction() + val launchingTask = createTask(WINDOWING_MODE_FREEFORM) + val immersiveTask = createTask(WINDOWING_MODE_FREEFORM) + val transition = Binder() + whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull())) + .thenReturn(transition) + + mixedHandler.startLaunchTransition( + transitionType = TRANSIT_OPEN, + wct = wct, + taskId = launchingTask.taskId, + exitingImmersiveTask = immersiveTask.taskId, + ) + val launchTaskChange = createChange(launchingTask) + val immersiveChange = createChange(immersiveTask) + mixedHandler.startAnimation( + transition, + createTransitionInfo( + TRANSIT_OPEN, + listOf(launchTaskChange, immersiveChange) + ), + SurfaceControl.Transaction(), + SurfaceControl.Transaction(), + ) { } + + verify(desktopImmersiveController) + .animateResizeChange(eq(immersiveChange), any(), any(), any()) + verify(transitions).dispatchTransition( + eq(transition), + argThat { info -> + info.changes.contains(launchTaskChange) && !info.changes.contains(immersiveChange) + }, + any(), + any(), + any(), + eq(mixedHandler), + ) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startAndAnimateLaunchTransition_removesPendingMixedTransition() { + val wct = WindowContainerTransaction() + val launchingTask = createTask(WINDOWING_MODE_FREEFORM) + val transition = Binder() + whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull())) + .thenReturn(transition) + + mixedHandler.startLaunchTransition( + transitionType = TRANSIT_OPEN, + wct = wct, + taskId = launchingTask.taskId, + exitingImmersiveTask = null, + ) + val launchTaskChange = createChange(launchingTask) + mixedHandler.startAnimation( + transition, + createTransitionInfo( + TRANSIT_OPEN, + listOf(launchTaskChange) + ), + SurfaceControl.Transaction(), + SurfaceControl.Transaction(), + ) { } + + assertThat(mixedHandler.pendingMixedTransitions).isEmpty() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startAndAnimateLaunchTransition_aborted_removesPendingMixedTransition() { + val wct = WindowContainerTransaction() + val launchingTask = createTask(WINDOWING_MODE_FREEFORM) + val transition = Binder() + whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull())) + .thenReturn(transition) + + mixedHandler.startLaunchTransition( + transitionType = TRANSIT_OPEN, + wct = wct, + taskId = launchingTask.taskId, + exitingImmersiveTask = null, + ) + mixedHandler.onTransitionConsumed( + transition = transition, + aborted = true, + finishTransaction = SurfaceControl.Transaction() + ) + + assertThat(mixedHandler.pendingMixedTransitions).isEmpty() + } + private fun createTransitionInfo( type: Int = WindowManager.TRANSIT_CLOSE, changeMode: Int = WindowManager.TRANSIT_CLOSE, @@ -235,6 +422,18 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { ) } + private fun createTransitionInfo( + @TransitionType type: Int, + changes: List<TransitionInfo.Change> = emptyList() + ): TransitionInfo = TransitionInfo(type, /* flags= */ 0).apply { + changes.forEach { change -> addChange(change) } + } + + private fun createChange(task: RunningTaskInfo): TransitionInfo.Change = + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + private fun createTask(@WindowingMode windowingMode: Int): RunningTaskInfo = TestRunningTaskInfoBuilder() .setActivityType(ACTIVITY_TYPE_STANDARD) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index b358686224a0..62d2c987ac5d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -196,6 +196,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var transitions: Transitions @Mock lateinit var keyguardManager: KeyguardManager @Mock lateinit var mReturnToDragStartAnimator: ReturnToDragStartAnimator + @Mock lateinit var desktopMixedTransitionHandler: DesktopMixedTransitionHandler @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler @Mock lateinit var dragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler @@ -288,6 +289,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), any<RunningTaskInfo>())) + .thenReturn(DesktopImmersiveController.ExitResult.NoExit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull())) + .thenReturn(DesktopImmersiveController.ExitResult.NoExit) controller = createController() controller.setSplitScreenController(splitScreenController) @@ -323,6 +330,7 @@ class DesktopTasksControllerTest : ShellTestCase() { transitions, keyguardManager, mReturnToDragStartAnimator, + desktopMixedTransitionHandler, enterDesktopTransitionHandler, exitDesktopTransitionHandler, dragAndDropTransitionHandler, @@ -1389,7 +1397,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveTaskToFront(task1, remoteTransition = null) - val wct = getLatestWct(type = TRANSIT_TO_FRONT) + val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT) assertThat(wct.hierarchyOps).hasSize(1) wct.assertReorderAt(index = 0, task1) } @@ -1398,10 +1406,16 @@ class DesktopTasksControllerTest : ShellTestCase() { fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() { setUpHomeTask() val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() } + whenever(desktopMixedTransitionHandler.startLaunchTransition( + eq(TRANSIT_TO_FRONT), + any(), + eq(freeformTasks[0].taskId), + anyOrNull() + )).thenReturn(Binder()) controller.moveTaskToFront(freeformTasks[0], remoteTransition = null) - val wct = getLatestWct(type = TRANSIT_TO_FRONT) + val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT) assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize wct.assertReorderAt(0, freeformTasks[0], toTop = true) wct.assertReorderAt(1, freeformTasks[1], toTop = false) @@ -1443,7 +1457,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.moveTaskToFront(task.taskId, remoteTransition = null) - val wct = getLatestWct(type = TRANSIT_OPEN) + val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN) assertThat(wct.hierarchyOps).hasSize(1) wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM) } @@ -1453,10 +1467,13 @@ class DesktopTasksControllerTest : ShellTestCase() { val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } val task = createTaskInfo(1001) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null) + whenever(desktopMixedTransitionHandler + .startLaunchTransition(eq(TRANSIT_OPEN), any(), eq(task.taskId), anyOrNull())) + .thenReturn(Binder()) controller.moveTaskToFront(task.taskId, remoteTransition = null) - val wct = getLatestWct(type = TRANSIT_OPEN) + val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN) assertThat(wct.hierarchyOps.size).isEqualTo(2) // launch + minimize wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM) wct.assertReorderAt(1, freeformTasks[0], toTop = false) @@ -1791,7 +1808,10 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task))) - .thenReturn(runOnTransit) + .thenReturn(DesktopImmersiveController.ExitResult.Exit( + exitingTask = task.taskId, + runOnTransitionStart = runOnTransit, + )) controller.minimizeTask(task) @@ -3101,7 +3121,10 @@ class DesktopTasksControllerTest : ShellTestCase() { .thenReturn(transition) whenever(mMockDesktopImmersiveController .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId))) - .thenReturn(runOnStartTransit) + .thenReturn(DesktopImmersiveController.ExitResult.Exit( + exitingTask = immersiveTask.taskId, + runOnTransitionStart = runOnStartTransit, + )) runOpenInstance(immersiveTask, freeformTask.taskId) @@ -3502,7 +3525,11 @@ class DesktopTasksControllerTest : ShellTestCase() { val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() whenever(mMockDesktopImmersiveController - .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit) + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)) + .thenReturn(DesktopImmersiveController.ExitResult.Exit( + exitingTask = 5, + runOnTransitionStart = runOnStartTransit, + )) whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition) controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN) @@ -3519,7 +3546,11 @@ class DesktopTasksControllerTest : ShellTestCase() { val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() whenever(mMockDesktopImmersiveController - .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit) + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)) + .thenReturn(DesktopImmersiveController.ExitResult.Exit( + exitingTask = 5, + runOnTransitionStart = runOnStartTransit, + )) whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition) controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN) @@ -3536,8 +3567,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val transition = Binder() whenever(mMockDesktopImmersiveController .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))) - .thenReturn(runOnStartTransit) - whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) + .thenReturn(DesktopImmersiveController.ExitResult.Exit( + exitingTask = 5, + runOnTransitionStart = runOnStartTransit, + )) + whenever(desktopMixedTransitionHandler + .startLaunchTransition(any(), any(), anyInt(), anyOrNull())).thenReturn(transition) controller.moveTaskToFront(task.taskId, remoteTransition = null) @@ -3553,8 +3588,13 @@ class DesktopTasksControllerTest : ShellTestCase() { val transition = Binder() whenever(mMockDesktopImmersiveController .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))) - .thenReturn(runOnStartTransit) - whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) + .thenReturn(DesktopImmersiveController.ExitResult.Exit( + exitingTask = 5, + runOnTransitionStart = runOnStartTransit, + )) + whenever(desktopMixedTransitionHandler + .startLaunchTransition(any(), any(), eq(task.taskId), anyOrNull())) + .thenReturn(transition) controller.moveTaskToFront(task.taskId, remoteTransition = null) @@ -3936,6 +3976,16 @@ class DesktopTasksControllerTest : ShellTestCase() { return arg.value } + private fun getLatestDesktopMixedTaskWct( + @WindowManager.TransitionType type: Int = TRANSIT_OPEN, + ): WindowContainerTransaction { + val arg: ArgumentCaptor<WindowContainerTransaction> = + ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + verify(desktopMixedTransitionHandler) + .startLaunchTransition(eq(type), capture(arg), anyInt(), anyOrNull()) + return arg.value + } + private fun getLatestEnterDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any()) |