Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +23 −7 Original line number Diff line number Diff line Loading @@ -119,7 +119,8 @@ class DesktopImmersiveController( ) } fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo) { /** Starts a transition to move an immersive task out of immersive. */ fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo, reason: ExitReason) { if (inProgress) { logV( "Cannot start exit because transition(s) already in progress: %s", Loading @@ -131,7 +132,7 @@ class DesktopImmersiveController( val wct = WindowContainerTransaction().apply { setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) } logV("Moving task ${taskInfo.taskId} out of immersive mode") logV("Moving task %d out of immersive mode, reason: %s", taskInfo.taskId, reason) val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this) state = TransitionState( transition = transition, Loading @@ -151,10 +152,11 @@ class DesktopImmersiveController( fun exitImmersiveIfApplicable( transition: IBinder, wct: WindowContainerTransaction, displayId: Int displayId: Int, reason: ExitReason, ) { if (!Flags.enableFullyImmersiveInDesktop()) return val result = exitImmersiveIfApplicable(wct, displayId) val result = exitImmersiveIfApplicable(wct, displayId, excludeTaskId = null, reason) result.asExit()?.runOnTransitionStart?.invoke(transition) } Loading @@ -170,6 +172,7 @@ class DesktopImmersiveController( wct: WindowContainerTransaction, displayId: Int, excludeTaskId: Int? = null, reason: ExitReason, ): ExitResult { if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) Loading @@ -179,7 +182,10 @@ class DesktopImmersiveController( } val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return ExitResult.NoExit logV("Appending immersive exit for task: $immersiveTask in display: $displayId") logV( "Appending immersive exit for task: %d in display: %d for reason: %s", immersiveTask, displayId, reason ) wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) return ExitResult.Exit( exitingTask = immersiveTask, Loading @@ -198,14 +204,15 @@ class DesktopImmersiveController( */ fun exitImmersiveIfApplicable( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo taskInfo: RunningTaskInfo, reason: ExitReason, ): 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}") logV("Appending immersive exit for task: %d for reason: %s", taskInfo.taskId, reason) return ExitResult.Exit( exitingTask = taskInfo.taskId, runOnTransitionStart = { transition -> Loading Loading @@ -550,6 +557,15 @@ class DesktopImmersiveController( ENTER, EXIT } /** The reason for moving the task out of desktop immersive mode. */ enum class ExitReason { APP_NOT_IMMERSIVE, // The app stopped requesting immersive treatment. USER_INTERACTION, // Explicit user intent request, e.g. a button click. TASK_LAUNCH, // A task launched/moved on top of the immersive task. MINIMIZED, // The immersive task was minimized. CLOSED, // The immersive task was closed. } private fun logV(msg: String, vararg arguments: Any?) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +60 −17 Original line number Diff line number Diff line Loading @@ -70,7 +70,6 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut import com.android.wm.shell.Flags.enableFlexibleSplit import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer Loading Loading @@ -133,6 +132,8 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeT import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING /** Handles moving tasks in and out of desktop */ class DesktopTasksController( Loading Loading @@ -209,7 +210,9 @@ class DesktopTasksController( val draggingTaskId get() = dragToDesktopTransitionHandler.draggingTaskId private var recentsAnimationRunning = false @RecentsTransitionState private var recentsTransitionState = TRANSITION_STATE_NOT_RUNNING private lateinit var splitScreenController: SplitScreenController lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter // Launch cookie used to identify a drag and drop transition to fullscreen after it has begun. Loading Loading @@ -238,10 +241,15 @@ class DesktopTasksController( dragToDesktopTransitionHandler.dragToDesktopStateListener = dragToDesktopStateListener recentsTransitionHandler.addTransitionStateListener( object : RecentsTransitionStateListener { override fun onAnimationStateChanged(running: Boolean) { logV("Recents animation state changed running=%b", running) recentsAnimationRunning = running desktopTilingDecorViewModel.onOverviewAnimationStateChange(running) override fun onTransitionStateChanged(@RecentsTransitionState state: Int) { logV( "Recents transition state changed: %s", RecentsTransitionStateListener.stateToString(state) ) recentsTransitionState = state desktopTilingDecorViewModel.onOverviewAnimationStateChange( RecentsTransitionStateListener.isAnimating(state) ) } } ) Loading Loading @@ -381,6 +389,7 @@ class DesktopTasksController( wct = wct, displayId = DEFAULT_DISPLAY, excludeTaskId = taskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) wct.startTask( taskId, Loading Loading @@ -413,6 +422,7 @@ class DesktopTasksController( wct = wct, displayId = task.displayId, excludeTaskId = task.taskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) // Bring other apps to front first val taskIdToMinimize = Loading Loading @@ -460,7 +470,11 @@ class DesktopTasksController( bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) addMoveToDesktopChanges(wct, taskInfo) val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct, taskInfo.displayId) wct = wct, displayId = taskInfo.displayId, excludeTaskId = null, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH ) val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted( DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt() Loading Loading @@ -508,8 +522,11 @@ class DesktopTasksController( taskId ) ) return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit() ?.runOnTransitionStart return desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, taskInfo = taskInfo, reason = DesktopImmersiveController.ExitReason.CLOSED ).asExit()?.runOnTransitionStart } fun minimizeTask(taskInfo: RunningTaskInfo) { Loading @@ -518,7 +535,11 @@ class DesktopTasksController( val wct = WindowContainerTransaction() performDesktopExitCleanupIfNeeded(taskId, wct) // Notify immersive handler as it might need to exit immersive state. val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, taskInfo = taskInfo, reason = DesktopImmersiveController.ExitReason.MINIMIZED ) wct.reorder(taskInfo.token, false) val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct) Loading Loading @@ -675,6 +696,7 @@ class DesktopTasksController( wct = wct, displayId = displayId, excludeTaskId = launchingTaskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) if (remoteTransition == null) { val t = desktopMixedTransitionHandler.startLaunchTransition( Loading Loading @@ -766,7 +788,10 @@ class DesktopTasksController( /** Moves a task in/out of full immersive state within the desktop. */ fun toggleDesktopTaskFullImmersiveState(taskInfo: RunningTaskInfo) { if (taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)) { exitDesktopTaskFromFullImmersive(taskInfo) exitDesktopTaskFromFullImmersive( taskInfo, DesktopImmersiveController.ExitReason.USER_INTERACTION, ) } else { moveDesktopTaskToFullImmersive(taskInfo) } Loading @@ -777,9 +802,12 @@ class DesktopTasksController( desktopImmersiveController.moveTaskToImmersive(taskInfo) } private fun exitDesktopTaskFromFullImmersive(taskInfo: RunningTaskInfo) { private fun exitDesktopTaskFromFullImmersive( taskInfo: RunningTaskInfo, reason: DesktopImmersiveController.ExitReason, ) { check(taskInfo.isFreeform) { "Task must already be in freeform" } desktopImmersiveController.moveTaskToNonImmersive(taskInfo) desktopImmersiveController.moveTaskToNonImmersive(taskInfo, reason) } /** Loading Loading @@ -1292,6 +1320,8 @@ class DesktopTasksController( // Check if we should skip handling this transition var reason = "" val triggerTask = request.triggerTask val recentsAnimationRunning = RecentsTransitionStateListener.isAnimating(recentsTransitionState) var shouldHandleMidRecentsFreeformLaunch = recentsAnimationRunning && isFreeformRelaunch(triggerTask, request) val isDragAndDropFullscreenTransition = taskContainsDragAndDropCookie(triggerTask) Loading Loading @@ -1479,6 +1509,7 @@ class DesktopTasksController( wct = wct, displayId = callingTask.displayId, excludeTaskId = requestedTaskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) val transition = transitions.startTransition(TRANSIT_OPEN, wct, null) taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } Loading Loading @@ -1628,7 +1659,12 @@ class DesktopTasksController( } // Desktop Mode is showing and we're launching a new Task: // 1) Exit immersive if needed. desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId) desktopImmersiveController.exitImmersiveIfApplicable( transition = transition, wct = wct, displayId = task.displayId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) // 2) minimize a Task if needed. val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize) Loading Loading @@ -1665,7 +1701,10 @@ class DesktopTasksController( taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize) desktopImmersiveController.exitImmersiveIfApplicable( transition, wct, task.displayId transition, wct, task.displayId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH ) } } else if (taskRepository.isActiveTask(task.taskId)) { Loading Loading @@ -2282,9 +2321,13 @@ class DesktopTasksController( if (!Flags.enableFullyImmersiveInDesktop()) return val inImmersive = taskRepository.isTaskInFullImmersiveState(taskInfo.taskId) val requestingImmersive = taskInfo.requestingImmersive if (inImmersive && !requestingImmersive) { if (inImmersive && !requestingImmersive && !RecentsTransitionStateListener.isRunning(recentsTransitionState)) { // Exit immersive if the app is no longer requesting it. exitDesktopTaskFromFullImmersive(taskInfo) exitDesktopTaskFromFullImmersive( taskInfo, DesktopImmersiveController.ExitReason.APP_NOT_IMMERSIVE ) } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +4 −2 Original line number Diff line number Diff line Loading @@ -665,8 +665,10 @@ public class RecentTasksController implements TaskStackListenerCallback, } mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() { @Override public void onAnimationStateChanged(boolean running) { executor.execute(() -> listener.accept(running)); public void onTransitionStateChanged(@RecentsTransitionState int state) { executor.execute(() -> { listener.accept(RecentsTransitionStateListener.isAnimating(state)); }); } }); }); Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +15 −6 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING; import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING; import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION; import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION; Loading Loading @@ -166,13 +169,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // only care about latest one. mAnimApp = appThread; for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_REQUESTED); } // TODO(b/366021931): Formalize this later final boolean isSyntheticRequest = options.containsKey("is_synthetic_recents_transition"); final boolean isSyntheticRequest = options.getBoolean( "is_synthetic_recents_transition", /* defaultValue= */ false); final IBinder transition; if (isSyntheticRequest) { return startSyntheticRecentsTransition(listener); transition = startSyntheticRecentsTransition(listener); } else { return startRealRecentsTransition(intent, fillIn, options, listener); transition = startRealRecentsTransition(intent, fillIn, options, listener); } return transition; } /** Loading Loading @@ -542,7 +551,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, mPendingFinishTransition = null; mControllers.remove(this); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(false); mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_NOT_RUNNING); } } Loading Loading @@ -578,7 +587,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, new RemoteAnimationTarget[0], new Rect(0, 0, 0, 0), new Rect(), new Bundle()); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(true); mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING); } } catch (RemoteException e) { Slog.e(TAG, "Error starting recents animation", e); Loading Loading @@ -809,7 +818,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), new Rect(0, 0, 0, 0), new Rect(), b); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(true); mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING); } } catch (RemoteException e) { Slog.e(TAG, "Error starting recents animation", e); Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java +38 −3 Original line number Diff line number Diff line Loading @@ -16,12 +16,47 @@ package com.android.wm.shell.recents; import android.os.IBinder; import android.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** The listener for the events from {@link RecentsTransitionHandler}. */ public interface RecentsTransitionStateListener { /** Notifies whether the recents animation is running. */ default void onAnimationStateChanged(boolean running) { @IntDef(prefix = { "TRANSITION_STATE_" }, value = { TRANSITION_STATE_NOT_RUNNING, TRANSITION_STATE_REQUESTED, TRANSITION_STATE_ANIMATING, }) @Retention(RetentionPolicy.SOURCE) @interface RecentsTransitionState {} int TRANSITION_STATE_NOT_RUNNING = 1; int TRANSITION_STATE_REQUESTED = 2; int TRANSITION_STATE_ANIMATING = 3; /** Notifies whether the recents transition state changes. */ default void onTransitionStateChanged(@RecentsTransitionState int state) { } /** Returns whether the recents transition is running. */ static boolean isRunning(@RecentsTransitionState int state) { return state >= TRANSITION_STATE_REQUESTED; } /** Returns whether the recents transition is animating. */ static boolean isAnimating(@RecentsTransitionState int state) { return state >= TRANSITION_STATE_ANIMATING; } /** Returns a string representation of the given state. */ static String stateToString(@RecentsTransitionState int state) { return switch (state) { case TRANSITION_STATE_NOT_RUNNING -> "TRANSITION_STATE_NOT_RUNNING"; case TRANSITION_STATE_REQUESTED -> "TRANSITION_STATE_REQUESTED"; case TRANSITION_STATE_ANIMATING -> "TRANSITION_STATE_ANIMATING"; default -> "UNKNOWN"; }; } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +23 −7 Original line number Diff line number Diff line Loading @@ -119,7 +119,8 @@ class DesktopImmersiveController( ) } fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo) { /** Starts a transition to move an immersive task out of immersive. */ fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo, reason: ExitReason) { if (inProgress) { logV( "Cannot start exit because transition(s) already in progress: %s", Loading @@ -131,7 +132,7 @@ class DesktopImmersiveController( val wct = WindowContainerTransaction().apply { setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) } logV("Moving task ${taskInfo.taskId} out of immersive mode") logV("Moving task %d out of immersive mode, reason: %s", taskInfo.taskId, reason) val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this) state = TransitionState( transition = transition, Loading @@ -151,10 +152,11 @@ class DesktopImmersiveController( fun exitImmersiveIfApplicable( transition: IBinder, wct: WindowContainerTransaction, displayId: Int displayId: Int, reason: ExitReason, ) { if (!Flags.enableFullyImmersiveInDesktop()) return val result = exitImmersiveIfApplicable(wct, displayId) val result = exitImmersiveIfApplicable(wct, displayId, excludeTaskId = null, reason) result.asExit()?.runOnTransitionStart?.invoke(transition) } Loading @@ -170,6 +172,7 @@ class DesktopImmersiveController( wct: WindowContainerTransaction, displayId: Int, excludeTaskId: Int? = null, reason: ExitReason, ): ExitResult { if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) Loading @@ -179,7 +182,10 @@ class DesktopImmersiveController( } val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return ExitResult.NoExit logV("Appending immersive exit for task: $immersiveTask in display: $displayId") logV( "Appending immersive exit for task: %d in display: %d for reason: %s", immersiveTask, displayId, reason ) wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) return ExitResult.Exit( exitingTask = immersiveTask, Loading @@ -198,14 +204,15 @@ class DesktopImmersiveController( */ fun exitImmersiveIfApplicable( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo taskInfo: RunningTaskInfo, reason: ExitReason, ): 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}") logV("Appending immersive exit for task: %d for reason: %s", taskInfo.taskId, reason) return ExitResult.Exit( exitingTask = taskInfo.taskId, runOnTransitionStart = { transition -> Loading Loading @@ -550,6 +557,15 @@ class DesktopImmersiveController( ENTER, EXIT } /** The reason for moving the task out of desktop immersive mode. */ enum class ExitReason { APP_NOT_IMMERSIVE, // The app stopped requesting immersive treatment. USER_INTERACTION, // Explicit user intent request, e.g. a button click. TASK_LAUNCH, // A task launched/moved on top of the immersive task. MINIMIZED, // The immersive task was minimized. CLOSED, // The immersive task was closed. } private fun logV(msg: String, vararg arguments: Any?) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +60 −17 Original line number Diff line number Diff line Loading @@ -70,7 +70,6 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut import com.android.wm.shell.Flags.enableFlexibleSplit import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer Loading Loading @@ -133,6 +132,8 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeT import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING /** Handles moving tasks in and out of desktop */ class DesktopTasksController( Loading Loading @@ -209,7 +210,9 @@ class DesktopTasksController( val draggingTaskId get() = dragToDesktopTransitionHandler.draggingTaskId private var recentsAnimationRunning = false @RecentsTransitionState private var recentsTransitionState = TRANSITION_STATE_NOT_RUNNING private lateinit var splitScreenController: SplitScreenController lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter // Launch cookie used to identify a drag and drop transition to fullscreen after it has begun. Loading Loading @@ -238,10 +241,15 @@ class DesktopTasksController( dragToDesktopTransitionHandler.dragToDesktopStateListener = dragToDesktopStateListener recentsTransitionHandler.addTransitionStateListener( object : RecentsTransitionStateListener { override fun onAnimationStateChanged(running: Boolean) { logV("Recents animation state changed running=%b", running) recentsAnimationRunning = running desktopTilingDecorViewModel.onOverviewAnimationStateChange(running) override fun onTransitionStateChanged(@RecentsTransitionState state: Int) { logV( "Recents transition state changed: %s", RecentsTransitionStateListener.stateToString(state) ) recentsTransitionState = state desktopTilingDecorViewModel.onOverviewAnimationStateChange( RecentsTransitionStateListener.isAnimating(state) ) } } ) Loading Loading @@ -381,6 +389,7 @@ class DesktopTasksController( wct = wct, displayId = DEFAULT_DISPLAY, excludeTaskId = taskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) wct.startTask( taskId, Loading Loading @@ -413,6 +422,7 @@ class DesktopTasksController( wct = wct, displayId = task.displayId, excludeTaskId = task.taskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) // Bring other apps to front first val taskIdToMinimize = Loading Loading @@ -460,7 +470,11 @@ class DesktopTasksController( bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) addMoveToDesktopChanges(wct, taskInfo) val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct, taskInfo.displayId) wct = wct, displayId = taskInfo.displayId, excludeTaskId = null, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH ) val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted( DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt() Loading Loading @@ -508,8 +522,11 @@ class DesktopTasksController( taskId ) ) return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit() ?.runOnTransitionStart return desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, taskInfo = taskInfo, reason = DesktopImmersiveController.ExitReason.CLOSED ).asExit()?.runOnTransitionStart } fun minimizeTask(taskInfo: RunningTaskInfo) { Loading @@ -518,7 +535,11 @@ class DesktopTasksController( val wct = WindowContainerTransaction() performDesktopExitCleanupIfNeeded(taskId, wct) // Notify immersive handler as it might need to exit immersive state. val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) val exitResult = desktopImmersiveController.exitImmersiveIfApplicable( wct = wct, taskInfo = taskInfo, reason = DesktopImmersiveController.ExitReason.MINIMIZED ) wct.reorder(taskInfo.token, false) val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct) Loading Loading @@ -675,6 +696,7 @@ class DesktopTasksController( wct = wct, displayId = displayId, excludeTaskId = launchingTaskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) if (remoteTransition == null) { val t = desktopMixedTransitionHandler.startLaunchTransition( Loading Loading @@ -766,7 +788,10 @@ class DesktopTasksController( /** Moves a task in/out of full immersive state within the desktop. */ fun toggleDesktopTaskFullImmersiveState(taskInfo: RunningTaskInfo) { if (taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)) { exitDesktopTaskFromFullImmersive(taskInfo) exitDesktopTaskFromFullImmersive( taskInfo, DesktopImmersiveController.ExitReason.USER_INTERACTION, ) } else { moveDesktopTaskToFullImmersive(taskInfo) } Loading @@ -777,9 +802,12 @@ class DesktopTasksController( desktopImmersiveController.moveTaskToImmersive(taskInfo) } private fun exitDesktopTaskFromFullImmersive(taskInfo: RunningTaskInfo) { private fun exitDesktopTaskFromFullImmersive( taskInfo: RunningTaskInfo, reason: DesktopImmersiveController.ExitReason, ) { check(taskInfo.isFreeform) { "Task must already be in freeform" } desktopImmersiveController.moveTaskToNonImmersive(taskInfo) desktopImmersiveController.moveTaskToNonImmersive(taskInfo, reason) } /** Loading Loading @@ -1292,6 +1320,8 @@ class DesktopTasksController( // Check if we should skip handling this transition var reason = "" val triggerTask = request.triggerTask val recentsAnimationRunning = RecentsTransitionStateListener.isAnimating(recentsTransitionState) var shouldHandleMidRecentsFreeformLaunch = recentsAnimationRunning && isFreeformRelaunch(triggerTask, request) val isDragAndDropFullscreenTransition = taskContainsDragAndDropCookie(triggerTask) Loading Loading @@ -1479,6 +1509,7 @@ class DesktopTasksController( wct = wct, displayId = callingTask.displayId, excludeTaskId = requestedTaskId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) val transition = transitions.startTransition(TRANSIT_OPEN, wct, null) taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } Loading Loading @@ -1628,7 +1659,12 @@ class DesktopTasksController( } // Desktop Mode is showing and we're launching a new Task: // 1) Exit immersive if needed. desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId) desktopImmersiveController.exitImmersiveIfApplicable( transition = transition, wct = wct, displayId = task.displayId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH, ) // 2) minimize a Task if needed. val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize) Loading Loading @@ -1665,7 +1701,10 @@ class DesktopTasksController( taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize) desktopImmersiveController.exitImmersiveIfApplicable( transition, wct, task.displayId transition, wct, task.displayId, reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH ) } } else if (taskRepository.isActiveTask(task.taskId)) { Loading Loading @@ -2282,9 +2321,13 @@ class DesktopTasksController( if (!Flags.enableFullyImmersiveInDesktop()) return val inImmersive = taskRepository.isTaskInFullImmersiveState(taskInfo.taskId) val requestingImmersive = taskInfo.requestingImmersive if (inImmersive && !requestingImmersive) { if (inImmersive && !requestingImmersive && !RecentsTransitionStateListener.isRunning(recentsTransitionState)) { // Exit immersive if the app is no longer requesting it. exitDesktopTaskFromFullImmersive(taskInfo) exitDesktopTaskFromFullImmersive( taskInfo, DesktopImmersiveController.ExitReason.APP_NOT_IMMERSIVE ) } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +4 −2 Original line number Diff line number Diff line Loading @@ -665,8 +665,10 @@ public class RecentTasksController implements TaskStackListenerCallback, } mTransitionHandler.addTransitionStateListener(new RecentsTransitionStateListener() { @Override public void onAnimationStateChanged(boolean running) { executor.execute(() -> listener.accept(running)); public void onTransitionStateChanged(@RecentsTransitionState int state) { executor.execute(() -> { listener.accept(RecentsTransitionStateListener.isAnimating(state)); }); } }); }); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +15 −6 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING; import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING; import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION; import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION; Loading Loading @@ -166,13 +169,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // only care about latest one. mAnimApp = appThread; for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_REQUESTED); } // TODO(b/366021931): Formalize this later final boolean isSyntheticRequest = options.containsKey("is_synthetic_recents_transition"); final boolean isSyntheticRequest = options.getBoolean( "is_synthetic_recents_transition", /* defaultValue= */ false); final IBinder transition; if (isSyntheticRequest) { return startSyntheticRecentsTransition(listener); transition = startSyntheticRecentsTransition(listener); } else { return startRealRecentsTransition(intent, fillIn, options, listener); transition = startRealRecentsTransition(intent, fillIn, options, listener); } return transition; } /** Loading Loading @@ -542,7 +551,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, mPendingFinishTransition = null; mControllers.remove(this); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(false); mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_NOT_RUNNING); } } Loading Loading @@ -578,7 +587,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, new RemoteAnimationTarget[0], new Rect(0, 0, 0, 0), new Rect(), new Bundle()); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(true); mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING); } } catch (RemoteException e) { Slog.e(TAG, "Error starting recents animation", e); Loading Loading @@ -809,7 +818,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), new Rect(0, 0, 0, 0), new Rect(), b); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(true); mStateListeners.get(i).onTransitionStateChanged(TRANSITION_STATE_ANIMATING); } } catch (RemoteException e) { Slog.e(TAG, "Error starting recents animation", e); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java +38 −3 Original line number Diff line number Diff line Loading @@ -16,12 +16,47 @@ package com.android.wm.shell.recents; import android.os.IBinder; import android.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** The listener for the events from {@link RecentsTransitionHandler}. */ public interface RecentsTransitionStateListener { /** Notifies whether the recents animation is running. */ default void onAnimationStateChanged(boolean running) { @IntDef(prefix = { "TRANSITION_STATE_" }, value = { TRANSITION_STATE_NOT_RUNNING, TRANSITION_STATE_REQUESTED, TRANSITION_STATE_ANIMATING, }) @Retention(RetentionPolicy.SOURCE) @interface RecentsTransitionState {} int TRANSITION_STATE_NOT_RUNNING = 1; int TRANSITION_STATE_REQUESTED = 2; int TRANSITION_STATE_ANIMATING = 3; /** Notifies whether the recents transition state changes. */ default void onTransitionStateChanged(@RecentsTransitionState int state) { } /** Returns whether the recents transition is running. */ static boolean isRunning(@RecentsTransitionState int state) { return state >= TRANSITION_STATE_REQUESTED; } /** Returns whether the recents transition is animating. */ static boolean isAnimating(@RecentsTransitionState int state) { return state >= TRANSITION_STATE_ANIMATING; } /** Returns a string representation of the given state. */ static String stateToString(@RecentsTransitionState int state) { return switch (state) { case TRANSITION_STATE_NOT_RUNNING -> "TRANSITION_STATE_NOT_RUNNING"; case TRANSITION_STATE_REQUESTED -> "TRANSITION_STATE_REQUESTED"; case TRANSITION_STATE_ANIMATING -> "TRANSITION_STATE_ANIMATING"; default -> "UNKNOWN"; }; } }