summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java41
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt53
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt62
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java172
9 files changed, 372 insertions, 98 deletions
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 1acde73e68dc..4723eb273988 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
@@ -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",
@@ -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,
@@ -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)
}
@@ -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)
@@ -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,
@@ -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 ->
@@ -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)
}
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 a0bdd9fad510..eb930b0d9e00 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
@@ -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
@@ -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(
@@ -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.
@@ -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)
+ )
}
}
)
@@ -381,6 +389,7 @@ class DesktopTasksController(
wct = wct,
displayId = DEFAULT_DISPLAY,
excludeTaskId = taskId,
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
wct.startTask(
taskId,
@@ -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 =
@@ -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()
@@ -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) {
@@ -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)
@@ -675,6 +696,7 @@ class DesktopTasksController(
wct = wct,
displayId = displayId,
excludeTaskId = launchingTaskId,
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
if (remoteTransition == null) {
val t = desktopMixedTransitionHandler.startLaunchTransition(
@@ -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)
}
@@ -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)
}
/**
@@ -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)
@@ -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) }
@@ -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)
@@ -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)) {
@@ -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
+ )
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 6da4f510ab77..d917f937b16c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -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));
+ });
}
});
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 1c58dbbf71fd..032dac9ff3a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -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;
@@ -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;
}
/**
@@ -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);
}
}
@@ -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);
@@ -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);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
index 95874c8193c9..ea7cfd374f71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionStateListener.java
@@ -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";
+ };
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index f935ac76bbeb..9c31b46a80e5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -45,6 +45,7 @@ public final class TestRunningTaskInfoBuilder {
private Intent mBaseIntent = new Intent();
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ private @WindowConfiguration.ActivityType int mTopActivityType = ACTIVITY_TYPE_STANDARD;
private int mDisplayId = Display.DEFAULT_DISPLAY;
private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
private final Point mPositionInParent = new Point();
@@ -102,6 +103,12 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setTopActivityType(
+ @WindowConfiguration.ActivityType int activityType) {
+ mTopActivityType = activityType;
+ return this;
+ }
+
public TestRunningTaskInfoBuilder setWindowingMode(
@WindowConfiguration.WindowingMode int windowingMode) {
mWindowingMode = windowingMode;
@@ -154,6 +161,7 @@ public final class TestRunningTaskInfoBuilder {
info.configuration.windowConfiguration.setBounds(mBounds);
info.configuration.windowConfiguration.setActivityType(mActivityType);
info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
+ info.topActivityType = mTopActivityType;
info.token = mToken;
info.isResizeable = true;
info.supportsMultiWindow = true;
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 a4f4d05d2079..4666276c2fae 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
@@ -42,6 +42,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitReason.USER_INTERACTION
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -168,7 +169,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.moveTaskToNonImmersive(task)
+ controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.onTransitionReady(
transition = mockBinder,
info = createTransitionInfo(
@@ -195,7 +196,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
)
desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
- controller.moveTaskToNonImmersive(task)
+ controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.onTransitionReady(
transition = mockBinder,
info = createTransitionInfo(
@@ -252,8 +253,8 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)))
.thenReturn(mockBinder)
- controller.moveTaskToNonImmersive(task)
- controller.moveTaskToNonImmersive(task)
+ controller.moveTaskToNonImmersive(task, USER_INTERACTION)
+ controller.moveTaskToNonImmersive(task, USER_INTERACTION)
verify(mockTransitions, times(1))
.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))
@@ -272,7 +273,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
assertThat(controller.pendingExternalExitTransitions.any { exit ->
exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
@@ -293,7 +294,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = false
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
assertThat(controller.pendingExternalExitTransitions.any { exit ->
exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
@@ -314,7 +315,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
assertThat(wct.hasBoundsChange(task.token)).isTrue()
}
@@ -332,7 +333,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = false
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
assertThat(wct.hasBoundsChange(task.token)).isFalse()
}
@@ -353,7 +354,8 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
controller.exitImmersiveIfApplicable(
wct = wct,
displayId = DEFAULT_DISPLAY,
- excludeTaskId = task.taskId
+ excludeTaskId = task.taskId,
+ reason = USER_INTERACTION,
).asExit()?.runOnTransitionStart?.invoke(transition)
assertThat(controller.pendingExternalExitTransitions.any { exit ->
@@ -374,7 +376,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+ controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
assertThat(wct.hasBoundsChange(task.token)).isTrue()
}
@@ -391,7 +393,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = false
)
- controller.exitImmersiveIfApplicable(wct, task)
+ controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
assertThat(wct.hasBoundsChange(task.token)).isFalse()
}
@@ -409,7 +411,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(wct, task)
+ controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
.asExit()?.runOnTransitionStart?.invoke(transition)
assertThat(controller.pendingExternalExitTransitions.any { exit ->
@@ -430,7 +432,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = false
)
- val result = controller.exitImmersiveIfApplicable(wct, task)
+ val result = controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit)
}
@@ -447,7 +449,8 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = false
)
- val result = controller.exitImmersiveIfApplicable(wct, task.displayId)
+ val result = controller.exitImmersiveIfApplicable(
+ wct, task.displayId, excludeTaskId = null, USER_INTERACTION)
assertThat(result).isEqualTo(DesktopImmersiveController.ExitResult.NoExit)
}
@@ -464,7 +467,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
taskId = task.taskId,
immersive = true
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
@@ -495,7 +498,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
taskId = task.taskId,
immersive = true
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
@@ -530,7 +533,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
taskId = task.taskId,
immersive = true
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
@@ -560,7 +563,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
controller.onTransitionReady(
transition = transition,
@@ -587,7 +590,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+ controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
assertThat(
wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task))
@@ -611,7 +614,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
val preImmersiveBounds = Rect(100, 100, 500, 500)
desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds)
- controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+ controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
assertThat(
wct.hasBoundsChange(task.token, preImmersiveBounds)
@@ -634,7 +637,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+ controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task, reason = USER_INTERACTION)
assertThat(
wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task))
@@ -652,10 +655,10 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
taskId = task.taskId,
immersive = true
)
- controller.exitImmersiveIfApplicable(wct, task)
+ controller.exitImmersiveIfApplicable(wct, task, USER_INTERACTION)
.asExit()?.runOnTransitionStart?.invoke(Binder())
- controller.moveTaskToNonImmersive(task)
+ controller.moveTaskToNonImmersive(task, USER_INTERACTION)
verify(mockTransitions, never()).startTransition(any(), any(), any())
}
@@ -674,7 +677,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+ controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY, USER_INTERACTION)
assertThat(controller.isImmersiveChange(transition, change)).isTrue()
}
@@ -692,7 +695,7 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
immersive = true
)
- controller.moveTaskToNonImmersive(task)
+ controller.moveTaskToNonImmersive(task, USER_INTERACTION)
controller.animateResizeChange(
change = TransitionInfo.Change(task.token, SurfaceControl()).apply {
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 5df395754c7a..490e6b9a226b 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
@@ -113,6 +113,8 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.recents.RecentTasksController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING
+import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
import com.android.wm.shell.shared.split.SplitScreenConstants
@@ -291,10 +293,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), any<RunningTaskInfo>()))
+ .exitImmersiveIfApplicable(any(), any<RunningTaskInfo>(), any()))
.thenReturn(ExitResult.NoExit)
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
+ .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
.thenReturn(ExitResult.NoExit)
controller = createController()
@@ -1793,7 +1795,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.minimizeTask(task)
- verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task))
+ verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
}
@Test
@@ -1803,7 +1805,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val runOnTransit = RunOnStartTransitionCallback()
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task)))
+ whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = task.taskId,
@@ -2192,7 +2194,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskVisible(freeformTask)
// Mark recents animation running
- recentsTransitionStateListener.onAnimationStateChanged(true)
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
// Open a fullscreen task, check that it does not result in a WCT with changes to it
val fullscreenTask = createFullscreenTask()
@@ -2206,7 +2208,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskVisible(freeformTask)
// Mark recents animation running
- recentsTransitionStateListener.onAnimationStateChanged(true)
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)
// Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
val result = controller.handleRequest(Binder(), createTransition(freeformTask))
@@ -3189,7 +3191,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val wctCaptor = argumentCaptor<WindowContainerTransaction>()
val transition = Binder()
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
+ .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
.thenReturn(ExitResult.NoExit)
whenever(desktopMixedTransitionHandler
.startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
@@ -3212,7 +3214,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val runOnStart = RunOnStartTransitionCallback()
val transition = Binder()
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull()))
+ .exitImmersiveIfApplicable(any(), anyInt(), anyOrNull(), any()))
.thenReturn(ExitResult.Exit(immersiveTask.taskId, runOnStart))
whenever(desktopMixedTransitionHandler
.startLaunchTransition(anyInt(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
@@ -3315,7 +3317,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
.thenReturn(transition)
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)))
+ .exitImmersiveIfApplicable(
+ any(), eq(immersiveTask.displayId), eq(freeformTask.taskId), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = immersiveTask.taskId,
@@ -3325,7 +3328,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
runOpenInstance(immersiveTask, freeformTask.taskId)
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId))
+ .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId), any())
runOnStartTransit.assertOnlyInvocation(transition)
}
@@ -3740,7 +3743,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.toggleDesktopTaskFullImmersiveState(task)
- verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(task)
+ verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any())
}
@Test
@@ -3752,7 +3755,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
task.requestedVisibleTypes = WindowInsets.Type.statusBars()
controller.onTaskInfoChanged(task)
- verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(task)
+ verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any())
}
@Test
@@ -3764,7 +3767,20 @@ class DesktopTasksControllerTest : ShellTestCase() {
task.requestedVisibleTypes = WindowInsets.Type.statusBars()
controller.onTaskInfoChanged(task)
- verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(task)
+ verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_inRecentsTransition_noExit() {
+ val task = setUpFreeformTask(DEFAULT_DISPLAY)
+ taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+ recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED)
+
+ task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+ controller.onTaskInfoChanged(task)
+
+ verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(eq(task), any())
}
@Test
@@ -3774,7 +3790,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val runOnStartTransit = RunOnStartTransitionCallback()
val transition = Binder()
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = 5,
@@ -3785,7 +3801,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
runOnStartTransit.assertOnlyInvocation(transition)
}
@@ -3796,7 +3812,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val runOnStartTransit = RunOnStartTransitionCallback()
val transition = Binder()
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(wct, task.displayId, task.taskId))
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = 5,
@@ -3807,7 +3823,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)
+ .exitImmersiveIfApplicable(eq(wct), eq(task.displayId), eq(task.taskId), any())
runOnStartTransit.assertOnlyInvocation(transition)
}
@@ -3817,7 +3833,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val runOnStartTransit = RunOnStartTransitionCallback()
val transition = Binder()
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = 5,
@@ -3830,7 +3846,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveTaskToFront(task.taskId, remoteTransition = null)
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
runOnStartTransit.assertOnlyInvocation(transition)
}
@@ -3840,7 +3856,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val runOnStartTransit = RunOnStartTransitionCallback()
val transition = Binder()
whenever(mMockDesktopImmersiveController
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any()))
.thenReturn(
ExitResult.Exit(
exitingTask = 5,
@@ -3853,7 +3869,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveTaskToFront(task.taskId, remoteTransition = null)
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))
+ .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId), any())
runOnStartTransit.assertOnlyInvocation(transition)
}
@@ -3867,7 +3883,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.handleRequest(binder, createTransition(task))
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId))
+ .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
}
@Test
@@ -3879,7 +3895,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.handleRequest(binder, createTransition(task))
verify(mMockDesktopImmersiveController)
- .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId))
+ .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId), any())
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index 6087763b4978..f0f5fe159069 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -16,7 +16,16 @@
package com.android.wm.shell.recents;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+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.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION;
+
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -27,6 +36,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.KeyguardManager;
@@ -38,7 +48,10 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -47,6 +60,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.IResultReceiver;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -56,7 +70,9 @@ import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.HomeTransitionObserver;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.StubTransaction;
import org.junit.After;
import org.junit.Before;
@@ -93,6 +109,8 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
private IRecentTasksListener mRecentTasksListener;
@Mock
private TaskStackTransitionObserver mTaskStackTransitionObserver;
+ @Mock
+ private Transitions mTransitions;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -129,10 +147,9 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
- final Transitions transitions = mock(Transitions.class);
- doReturn(mMainExecutor).when(transitions).getMainExecutor();
+ doReturn(mMainExecutor).when(mTransitions).getMainExecutor();
mRecentsTransitionHandler = new RecentsTransitionHandler(mShellInit, mShellTaskOrganizer,
- transitions, mRecentTasksController, mock(HomeTransitionObserver.class));
+ mTransitions, mRecentTasksController, mock(HomeTransitionObserver.class));
mShellInit.init();
}
@@ -146,12 +163,8 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
public void testStartSyntheticRecentsTransition_callsOnAnimationStartAndFinishCallback() throws Exception {
final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class);
final IResultReceiver finishCallback = mock(IResultReceiver.class);
- doReturn(new Binder()).when(runner).asBinder();
- Bundle options = new Bundle();
- options.putBoolean("is_synthetic_recents_transition", true);
- IBinder transition = mRecentsTransitionHandler.startRecentsTransition(
- mock(PendingIntent.class), new Intent(), options, mock(IApplicationThread.class),
- runner);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
// Finish and verify no transition remains and that the provided finish callback is called
@@ -165,12 +178,8 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
@Test
public void testStartSyntheticRecentsTransition_callsOnAnimationCancel() throws Exception {
final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class);
- doReturn(new Binder()).when(runner).asBinder();
- Bundle options = new Bundle();
- options.putBoolean("is_synthetic_recents_transition", true);
- IBinder transition = mRecentsTransitionHandler.startRecentsTransition(
- mock(PendingIntent.class), new Intent(), options, mock(IApplicationThread.class),
- runner);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ true, runner);
verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any());
mRecentsTransitionHandler.findController(transition).cancel("test");
@@ -178,4 +187,137 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
verify(runner).onAnimationCanceled(any(), any());
assertNull(mRecentsTransitionHandler.findController(transition));
}
+
+ @Test
+ public void testStartTransition_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ startRecentsTransition(/* synthetic= */ false);
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_REQUESTED);
+ }
+
+ @Test
+ public void testStartAnimation_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false);
+ mRecentsTransitionHandler.startAnimation(
+ transition, createTransitionInfo(), new StubTransaction(), new StubTransaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_ANIMATING);
+ }
+
+ @Test
+ public void testFinishTransition_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false);
+ mRecentsTransitionHandler.startAnimation(
+ transition, createTransitionInfo(), new StubTransaction(), new StubTransaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ mRecentsTransitionHandler.findController(transition).finish(true /* toHome */,
+ false /* sendUserLeaveHint */, mock(IResultReceiver.class));
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_NOT_RUNNING);
+ }
+
+ @Test
+ public void testCancelTransition_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false);
+ mRecentsTransitionHandler.findController(transition).cancel("test");
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_NOT_RUNNING);
+ }
+
+ @Test
+ public void testStartAnimation_synthetic_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ startRecentsTransition(/* synthetic= */ true);
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_ANIMATING);
+ }
+
+ @Test
+ public void testFinishTransition_synthetic_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ true);
+ mRecentsTransitionHandler.findController(transition).finish(true /* toHome */,
+ false /* sendUserLeaveHint */, mock(IResultReceiver.class));
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_NOT_RUNNING);
+ }
+
+ @Test
+ public void testCancelTransition_synthetic_updatesStateListeners() {
+ final TestTransitionStateListener listener = new TestTransitionStateListener();
+ mRecentsTransitionHandler.addTransitionStateListener(listener);
+
+ final IBinder transition = startRecentsTransition(/* synthetic= */ true);
+ mRecentsTransitionHandler.findController(transition).cancel("test");
+ mMainExecutor.flushAll();
+
+ assertThat(listener.getState()).isEqualTo(TRANSITION_STATE_NOT_RUNNING);
+ }
+
+ private IBinder startRecentsTransition(boolean synthetic) {
+ return startRecentsTransition(synthetic, mock(IRecentsAnimationRunner.class));
+ }
+
+ private IBinder startRecentsTransition(boolean synthetic,
+ @NonNull IRecentsAnimationRunner runner) {
+ doReturn(new Binder()).when(runner).asBinder();
+ final Bundle options = new Bundle();
+ options.putBoolean("is_synthetic_recents_transition", synthetic);
+ final IBinder transition = new Binder();
+ when(mTransitions.startTransition(anyInt(), any(), any())).thenReturn(transition);
+ return mRecentsTransitionHandler.startRecentsTransition(
+ mock(PendingIntent.class), new Intent(), options, mock(IApplicationThread.class),
+ runner);
+ }
+
+ private TransitionInfo createTransitionInfo() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+ .setTopActivityType(ACTIVITY_TYPE_HOME)
+ .build();
+ final TransitionInfo.Change homeChange = new TransitionInfo.Change(
+ task.token, new SurfaceControl());
+ homeChange.setMode(TRANSIT_TO_FRONT);
+ homeChange.setTaskInfo(task);
+ return new TransitionInfoBuilder(TRANSIT_START_RECENTS_TRANSITION)
+ .addChange(homeChange)
+ .build();
+ }
+
+ private static class TestTransitionStateListener implements RecentsTransitionStateListener {
+ @RecentsTransitionState
+ private int mState = TRANSITION_STATE_NOT_RUNNING;
+
+ @Override
+ public void onTransitionStateChanged(int state) {
+ mState = state;
+ }
+
+ @RecentsTransitionState
+ int getState() {
+ return mState;
+ }
+ }
}