Clean up the logic of selecting launching stack
- Consolidate the logic of get or create the launching stack.
- A launch stack was created and abandon (sometimes leaked)
due to the created stack was not applicable. Now we return
a valid launch stack where the activity can actually be landed.
- Defer resuming activity while brought the target stack to front.
- Remove some outdated code.
Bug: 139449647
Test: atest WmTests CtsWindowManagerDeviceTestCases
Change-Id: I2c07f8d5dfbc025b7a18f6c0307c361865a601f4
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 842edb3..bf6a81e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -70,6 +70,7 @@
import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
@@ -1834,6 +1835,15 @@
&& (topTask == null || topTask.supportsSplitScreenWindowingMode());
}
+ /**
+ * Returns {@code true} if this is the top-most split-screen-primary or
+ * split-screen-secondary stack, {@code false} otherwise.
+ */
+ boolean isTopSplitScreenStack() {
+ return inSplitScreenWindowingMode()
+ && this == getDisplay().getTopStackInWindowingMode(getWindowingMode());
+ }
+
/** @return True if the resizing of the primary-split-screen stack affects this stack size. */
boolean affectedBySplitScreenResize() {
if (!supportsSplitScreenWindowingMode()) {
@@ -3003,6 +3013,11 @@
final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
+ moveTaskToFrontLocked(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
+ }
+
+ final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+ AppTimeTracker timeTracker, boolean deferResume, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
@@ -3074,7 +3089,9 @@
topActivity.supportsEnterPipOnTaskSwitch = true;
}
- mRootWindowContainer.resumeFocusedStacksTopActivities();
+ if (!deferResume) {
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ }
EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId);
mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.getTaskInfo());
} finally {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c688612..61ba15c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -29,8 +29,6 @@
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -65,10 +63,8 @@
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
@@ -1488,11 +1484,6 @@
mIntent.setFlags(mLaunchFlags);
final Task reusedTask = getReusableTask();
- mSupervisor.getLaunchParamsController().calculate(reusedTask != null ? reusedTask : mInTask,
- r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams);
- mPreferredDisplayId =
- mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
// If requested, freeze the task list
if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -1506,6 +1497,8 @@
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
+ computeLaunchParams(r, sourceRecord, targetTask);
+
// Check if starting activity on given task or on a new task is allowed.
int startResult = isAllowedToStart(r, newTask, targetTask);
if (startResult != START_SUCCESS) {
@@ -1533,7 +1526,7 @@
}
if (mTargetStack == null) {
- mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
+ mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, targetTask, mOptions);
}
if (newTask) {
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
@@ -1623,15 +1616,49 @@
} else if (mInTask != null) {
return mInTask;
} else {
- final ActivityRecord top = computeStackFocus(mStartActivity, false /* newTask */,
- mLaunchFlags, mOptions).getTopNonFinishingActivity();
+ final ActivityStack stack = getLaunchStack(mStartActivity, mLaunchFlags,
+ null /* task */, mOptions);
+ final ActivityRecord top = stack.getTopNonFinishingActivity();
if (top != null) {
return top.getTask();
+ } else {
+ // Remove the stack if no activity in the stack.
+ stack.removeIfPossible();
}
}
return null;
}
+ private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
+ Task targetTask) {
+ final ActivityStack sourceStack = mSourceStack != null ? mSourceStack
+ : mRootWindowContainer.getTopDisplayFocusedStack();
+ if (sourceStack != null && sourceStack.inSplitScreenWindowingMode()
+ && (mOptions == null
+ || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) {
+ int windowingMode =
+ targetTask != null ? targetTask.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+ if (sourceStack.inSplitScreenPrimaryWindowingMode()) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ } else if (sourceStack.inSplitScreenSecondaryWindowingMode()) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ }
+ }
+
+ if (mOptions == null) {
+ mOptions = ActivityOptions.makeBasic();
+ }
+ mOptions.setLaunchWindowingMode(windowingMode);
+ }
+
+ mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
+ sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams);
+ mPreferredDisplayId =
+ mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
+ : DEFAULT_DISPLAY;
+ }
+
private int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
if (mStartActivity.packageName == null) {
if (mStartActivity.resultTo != null) {
@@ -1870,8 +1897,8 @@
if (targetTask.getStack() == null) {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
- mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
- mLaunchFlags, mOptions);
+ mTargetStack =
+ getLaunchStack(mStartActivity, mLaunchFlags, null /* task */, mOptions);
mTargetStack.addChild(targetTask, !mLaunchTaskBehind /* toTop */,
(mStartActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
}
@@ -2320,57 +2347,25 @@
final ActivityStack launchStack =
getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
if (launchStack == null || launchStack == mTargetStack) {
+ // Do not set mMovedToFront to true below for split-screen-top stack, or
+ // START_TASK_TO_FRONT will be returned and trigger unexpected animations when a
+ // new intent has delivered.
+ final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
+
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
// task on top there.
+ // Defer resuming the top activity while moving task to top, since the
+ // current task-top activity may not be the activity that should be resumed.
mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
- mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
- mMovedToFront = true;
- } else if (launchStack.inSplitScreenWindowingMode()) {
- if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
- // If we want to launch adjacent and mTargetStack is not the computed
- // launch stack - move task to top of computed stack.
- intentTask.reparent(launchStack, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
- "launchToSide");
- } else {
- // TODO: This should be reevaluated in MW v2.
- // We choose to move task to front instead of launching it adjacent
- // when specific stack was requested explicitly and it appeared to be
- // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
- mTargetStack.moveTaskToFrontLocked(intentTask,
- mNoAnimation, mOptions, mStartActivity.appTimeTracker,
- "bringToFrontInsteadOfAdjacentLaunch");
- }
- mMovedToFront = launchStack != launchStack.getDisplay()
- .getTopStackInWindowingMode(launchStack.getWindowingMode());
- } else if (launchStack.getDisplayId() != mTargetStack.getDisplayId()) {
- // Target and computed stacks are on different displays and we've
- // found a matching task - move the existing instance to that display and
- // move it to front.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
- "reparentToDisplay");
- mMovedToFront = true;
- } else if (launchStack.isActivityTypeHome()
- && !mTargetStack.isActivityTypeHome()) {
- // It is possible for the home activity to be in another stack initially.
- // For example, the activity may have been initially started with an intent
- // which placed it in the fullscreen stack. To ensure the proper handling of
- // the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
- REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
- "reparentingHome");
+ mStartActivity.appTimeTracker, DEFER_RESUME,
+ "bringingFoundTaskToFront");
+ mMovedToFront = !isSplitScreenTopStack;
+ } else {
+ intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
+ DEFER_RESUME, "reparentToTargetStack");
mMovedToFront = true;
}
-
- if (launchStack != null && launchStack.getTopMostTask() == null) {
- // The task does not need to be reparented to the launch stack. Remove the
- // launch stack if there is no activity in it.
- Slog.w(TAG, "Removing an empty stack: " + launchStack);
- launchStack.removeIfPossible();
- }
-
mOptions = null;
}
}
@@ -2470,90 +2465,6 @@
return launchFlags;
}
- private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
- ActivityOptions aOptions) {
- final Task task = r.getTask();
- ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
- if (stack != null) {
- return stack;
- }
-
- final ActivityStack currentStack = task != null ? task.getStack() : null;
- final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
- if (currentStack != null) {
- if (focusedStack != currentStack) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting " + "focused stack to r=" + r
- + " task=" + task);
- } else {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already=" + focusedStack);
- }
- return currentStack;
- }
-
- if (canLaunchIntoFocusedStack(r, newTask)) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Have a focused stack=" + focusedStack);
- return focusedStack;
- }
-
- if (mPreferredDisplayId != DEFAULT_DISPLAY) {
- // Try to put the activity in a stack on a secondary display.
- stack = mRootWindowContainer.getValidLaunchStackOnDisplay(
- mPreferredDisplayId, r, aOptions, mLaunchParams);
- if (stack == null) {
- // If source display is not suitable - look for topmost valid stack in the system.
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Can't launch on mPreferredDisplayId="
- + mPreferredDisplayId + ", looking on all displays.");
- stack = mRootWindowContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
- }
- }
- if (stack == null) {
- stack = mRootWindowContainer.getLaunchStack(r, aOptions, task, ON_TOP);
- }
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
- + r + " stackId=" + stack.mStackId);
- return stack;
- }
-
- /** Check if provided activity record can launch in currently focused stack. */
- // TODO: This method can probably be consolidated into getLaunchStack() below.
- private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
- final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
- final boolean canUseFocusedStack;
- if (focusedStack.isActivityTypeAssistant()) {
- canUseFocusedStack = r.isActivityTypeAssistant();
- } else {
- switch (focusedStack.getWindowingMode()) {
- case WINDOWING_MODE_FULLSCREEN:
- // The fullscreen stack can contain any task regardless of if the task is
- // resizeable or not. So, we let the task go in the fullscreen task if it is the
- // focus stack.
- canUseFocusedStack = true;
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- // Any activity which supports split screen can go in the docked stack.
- canUseFocusedStack = r.supportsSplitScreenWindowingMode();
- break;
- case WINDOWING_MODE_FREEFORM:
- // Any activity which supports freeform can go in the freeform stack.
- canUseFocusedStack = r.supportsFreeform();
- break;
- default:
- // Dynamic stacks behave similarly to the fullscreen stack and can contain any
- // resizeable task.
- canUseFocusedStack = !focusedStack.isOnHomeDisplay()
- && r.canBeLaunchedOnDisplay(focusedStack.getDisplayId());
- }
- }
- return canUseFocusedStack && !newTask
- // Using the focus stack isn't important enough to override the preferred display.
- && (mPreferredDisplayId == focusedStack.getDisplayId());
- }
-
private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, Task task,
ActivityOptions aOptions) {
// We are reusing a task, keep the stack!
@@ -2561,53 +2472,10 @@
return mReuseTask.getStack();
}
- if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
- || mPreferredDisplayId != DEFAULT_DISPLAY) {
- final boolean onTop =
- (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
- final ActivityStack stack =
- mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
- mRequest.realCallingPid, mRequest.realCallingUid);
- return stack;
- }
- // Otherwise handle adjacent launch.
-
- final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
- // The parent activity doesn't want to launch the activity on top of itself, but
- // instead tries to put it onto other side in side-by-side mode.
- final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
-
- if (parentStack != focusedStack) {
- // If task's parent stack is not focused - use it during adjacent launch.
- return parentStack;
- } else {
- if (focusedStack != null && task == focusedStack.getTopMostTask()) {
- // If task is already on top of focused stack - use it. We don't want to move the
- // existing focused task to adjacent stack, just deliver new intent in this case.
- return focusedStack;
- }
-
- if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
- // If parent was in docked stack, the natural place to launch another activity
- // will be fullscreen, so it can appear alongside the docked window.
- final int activityType =
- mRootWindowContainer.resolveActivityType(r, mOptions, task);
- return parentStack.getDisplay().getOrCreateStack(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
- } else {
- // If the parent is not in the docked stack, we check if there is docked window
- // and if yes, we will launch into that stack. If not, we just put the new
- // activity into parent's stack, because we can't find a better place.
- final ActivityStack dockedStack =
- mRootWindowContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
- if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
- // There is a docked stack, but it isn't visible, so we can't launch into that.
- return mRootWindowContainer.getLaunchStack(r, aOptions, task, ON_TOP);
- } else {
- return dockedStack;
- }
- }
- }
+ final boolean onTop =
+ (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
+ return mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
+ mRequest.realCallingPid, mRequest.realCallingUid);
}
private boolean isLaunchModeOneOf(int mode1, int mode2) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 93362b9..704ab67 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2805,7 +2805,6 @@
int realCallingUid) {
int taskId = INVALID_TASK_ID;
int displayId = INVALID_DISPLAY;
- //Rect bounds = null;
// We give preference to the launch preference in activity options.
if (options != null) {
@@ -2827,7 +2826,7 @@
}
final int activityType = resolveActivityType(r, options, candidateTask);
- ActivityStack stack;
+ ActivityStack stack = null;
// Next preference for stack goes to the display Id set the candidate display.
if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
@@ -2860,7 +2859,6 @@
// Give preference to the stack and display of the input task and activity if they match the
// mode we want to launch into.
- stack = null;
DisplayContent display = null;
if (candidateTask != null) {
stack = candidateTask.getStack();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b1132f6..9e54f40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -33,8 +33,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
@@ -433,32 +435,28 @@
}
/**
- * This test ensures that if the intent is being delivered to a
+ * This test ensures that if the intent is being delivered to a split-screen unfocused task
+ * while it already on top, reports it as delivering to top.
*/
@Test
public void testSplitScreenDeliverToTop() {
- final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
- final ActivityRecord focusActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
-
- focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
- final ActivityRecord reusableActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
-
- // Create reusable activity after entering split-screen so that it is the top secondary
- // stack.
- reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ final ActivityStarter starter = prepareStarter(
+ FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+ final ActivityRecord splitPrimaryFocusActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord splitSecondReusableActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ splitPrimaryFocusActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ splitSecondReusableActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
// Set focus back to primary.
- final ActivityStack focusStack = focusActivity.getActivityStack();
- focusStack.moveToFront("testSplitScreenDeliverToTop");
+ splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenDeliverToTop");
- doReturn(reusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
-
+ // Start activity and delivered new intent.
+ starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
+ doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -471,26 +469,30 @@
*/
@Test
public void testSplitScreenTaskToFront() {
- final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ final ActivityStarter starter = prepareStarter(
+ FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+ final ActivityRecord splitSecondReusableActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord splitSecondTopActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord splitPrimaryFocusActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ splitPrimaryFocusActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ splitSecondReusableActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ splitSecondTopActivity.getActivityStack()
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- // Create reusable activity here first. Setting the windowing mode of the primary stack
- // will move the existing standard full screen stack to secondary, putting this one on the
- // bottom.
- final ActivityRecord reusableActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
+ // Make it on top of split-screen-secondary.
+ splitSecondTopActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
- reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ // Let primary stack has focus.
+ splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
- final ActivityRecord focusActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .build();
-
- // Enter split-screen. Primary stack should have focus.
- focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
- doReturn(reusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
-
+ // Start activity and delivered new intent.
+ starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
+ doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
// Ensure result is moving task to front.
@@ -979,4 +981,29 @@
assertThat(result == START_SUCCESS).isTrue();
assertThat(starter.mAddingToTask).isTrue();
}
+
+ @Test
+ public void testTargetStackInSplitScreen() {
+ final ActivityStarter starter =
+ prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetLaunchStack */);
+ final ActivityRecord top = new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ final ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ // Activity must not land on split-screen stack if currently not in split-screen mode.
+ starter.setActivityOptions(options.toBundle())
+ .setReason("testWindowingModeOptionsLaunchAdjacent")
+ .setOutActivity(outActivity).execute();
+ assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
+
+ // Move activity to split-screen-primary stack and make sure it has the focus.
+ top.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ top.getActivityStack().moveToFront("testWindowingModeOptionsLaunchAdjacent");
+
+ // Activity must landed on split-screen-secondary when launch adjacent.
+ starter.setActivityOptions(options.toBundle())
+ .setReason("testWindowingModeOptionsLaunchAdjacent")
+ .setOutActivity(outActivity).execute();
+ assertThat(outActivity[0].inSplitScreenSecondaryWindowingMode()).isTrue();
+ }
}