Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −0 Original line number Diff line number Diff line Loading @@ -703,6 +703,7 @@ public abstract class WMShellModule { Transitions transitions, KeyguardManager keyguardManager, ReturnToDragStartAnimator returnToDragStartAnimator, Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler, EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler, ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler, DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler, Loading Loading @@ -736,6 +737,7 @@ public abstract class WMShellModule { transitions, keyguardManager, returnToDragStartAnimator, desktopMixedTransitionHandler.get(), enterDesktopTransitionHandler, exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler, Loading Loading @@ -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)) { Loading @@ -972,6 +975,7 @@ public abstract class WMShellModule { desktopRepository, freeformTaskTransitionHandler, closeDesktopTaskTransitionHandler, desktopImmersiveController.get(), interactionJankMonitor, handler)); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +42 −17 Original line number Diff line number Diff line Loading @@ -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) } /** Loading @@ -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) } ) } /** Loading @@ -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 -> return ExitResult.Exit( exitingTask = taskInfo.taskId, runOnTransitionStart = { transition -> addPendingImmersiveExit( taskId = taskInfo.taskId, displayId = taskInfo.displayId, transition = transition ) } ) } return null return ExitResult.NoExit } Loading Loading @@ -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 } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +202 −4 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -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, Loading @@ -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. */ Loading @@ -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)) { Loading @@ -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. Loading @@ -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 Loading @@ -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" } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +46 −30 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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, Loading @@ -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 } Loading @@ -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, Loading @@ -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) } /** Loading Loading @@ -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) } } Loading Loading @@ -507,7 +509,8 @@ class DesktopTasksController( taskId ) ) return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit() ?.runOnTransitionStart } fun minimizeTask(taskInfo: RunningTaskInfo) { Loading @@ -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) Loading @@ -531,7 +534,7 @@ class DesktopTasksController( taskId = taskId ) } runOnTransit?.invoke(transition) exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } /** Move a task with given `taskId` to fullscreen */ Loading Loading @@ -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, Loading @@ -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) } /** Loading @@ -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 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, ) runOnTransit?.invoke(transition) 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 } Loading Loading @@ -1373,14 +1388,15 @@ class DesktopTasksController( wct.startTask(requestedTaskId, options.toBundle()) val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( callingTask.displayId, wct, requestedTaskId) val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( 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, Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt +26 −11 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −0 Original line number Diff line number Diff line Loading @@ -703,6 +703,7 @@ public abstract class WMShellModule { Transitions transitions, KeyguardManager keyguardManager, ReturnToDragStartAnimator returnToDragStartAnimator, Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler, EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler, ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler, DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler, Loading Loading @@ -736,6 +737,7 @@ public abstract class WMShellModule { transitions, keyguardManager, returnToDragStartAnimator, desktopMixedTransitionHandler.get(), enterDesktopTransitionHandler, exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler, Loading Loading @@ -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)) { Loading @@ -972,6 +975,7 @@ public abstract class WMShellModule { desktopRepository, freeformTaskTransitionHandler, closeDesktopTaskTransitionHandler, desktopImmersiveController.get(), interactionJankMonitor, handler)); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +42 −17 Original line number Diff line number Diff line Loading @@ -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) } /** Loading @@ -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) } ) } /** Loading @@ -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 -> return ExitResult.Exit( exitingTask = taskInfo.taskId, runOnTransitionStart = { transition -> addPendingImmersiveExit( taskId = taskInfo.taskId, displayId = taskInfo.displayId, transition = transition ) } ) } return null return ExitResult.NoExit } Loading Loading @@ -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 } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +202 −4 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -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, Loading @@ -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. */ Loading @@ -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)) { Loading @@ -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. Loading @@ -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 Loading @@ -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" } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +46 −30 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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, Loading @@ -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 } Loading @@ -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, Loading @@ -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) } /** Loading Loading @@ -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) } } Loading Loading @@ -507,7 +509,8 @@ class DesktopTasksController( taskId ) ) return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit() ?.runOnTransitionStart } fun minimizeTask(taskInfo: RunningTaskInfo) { Loading @@ -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) Loading @@ -531,7 +534,7 @@ class DesktopTasksController( taskId = taskId ) } runOnTransit?.invoke(transition) exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } /** Move a task with given `taskId` to fullscreen */ Loading Loading @@ -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, Loading @@ -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) } /** Loading @@ -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 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, ) runOnTransit?.invoke(transition) 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 } Loading Loading @@ -1373,14 +1388,15 @@ class DesktopTasksController( wct.startTask(requestedTaskId, options.toBundle()) val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( callingTask.displayId, wct, requestedTaskId) val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( 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, Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt +26 −11 File changed.Preview size limit exceeded, changes collapsed. Show changes