Support for setting stack windowing mode
We currently create a in stack in the windowing mode we want anytime we
want to change the windowing mode of a task which isn't optimal.
We now change the current stack windowing mode for some cases vs.
creating a new stack.
Test: go/wm-smoke
Bug: 64146578
Fixes: 68948655
Fixes: 65739235
Fixes: 29068935
Change-Id: I9fceb7d608e86610c1fb5ba59b15087a96e7401e
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8cc584e..cfb1035 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -95,7 +98,6 @@
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -345,6 +347,7 @@
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
+ private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
@@ -452,8 +455,8 @@
mStackId = stackId;
mCurrentUser = mService.mUserController.getCurrentUserId();
mTmpRect2.setEmpty();
- setWindowingMode(windowingMode);
setActivityType(activityType);
+ setWindowingMode(windowingMode);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -479,6 +482,125 @@
}
@Override
+ public void setWindowingMode(int windowingMode) {
+ setWindowingMode(windowingMode, false /* animate */);
+ }
+
+ void setWindowingMode(int preferredWindowingMode, boolean animate) {
+ final int currentMode = getWindowingMode();
+ final ActivityDisplay display = getDisplay();
+ final TaskRecord topTask = topTask();
+ final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
+ mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
+
+ // Need to make sure windowing mode is supported.
+ int windowingMode = display.resolveWindowingMode(
+ null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());;
+ if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ // Resolution to split-screen secondary for the primary split-screen stack means we want
+ // to go fullscreen.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ }
+
+ // Take any required action due to us not supporting the preferred windowing mode.
+ if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
+ if (display.hasSplitScreenPrimaryStack()
+ && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
+ // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen
+ // and display a warning toast about it.
+ mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ if (currentMode == windowingMode) {
+ // You are already in the window mode silly...
+ return;
+ }
+
+ final WindowManagerService wm = mService.mWindowManager;
+ final ActivityRecord topActivity = getTopActivity();
+
+ if (windowingMode != WINDOWING_MODE_FULLSCREEN && topActivity != null
+ && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) {
+ // Inform the user that they are starting an app that may not work correctly in
+ // multi-window mode.
+ final String packageName = topActivity.appInfo.packageName;
+ mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+ topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
+ }
+
+ wm.deferSurfaceLayout();
+ try {
+ if (!animate && topActivity != null) {
+ mNoAnimActivities.add(topActivity);
+ }
+ super.setWindowingMode(windowingMode);
+
+ if (mWindowContainerController == null) {
+ // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
+ return;
+ }
+
+ if (windowingMode == WINDOWING_MODE_PINNED || currentMode == WINDOWING_MODE_PINNED) {
+ // TODO: Need to remove use of PinnedActivityStack for this to be supported.
+ // NOTE: Need to ASS.scheduleUpdatePictureInPictureModeIfNeeded() in
+ // setWindowModeUnchecked() when this support is added. See TaskRecord.reparent()
+ throw new IllegalArgumentException(
+ "Changing pinned windowing mode not currently supported");
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+ // We already have a split-screen stack in this display, so just move the tasks over.
+ // TODO: Figure-out how to do all the stuff in
+ // AMS.setTaskWindowingModeSplitScreenPrimary
+ throw new IllegalArgumentException("Setting primary split-screen windowing mode"
+ + " while there is already one isn't currently supported");
+ //return;
+ }
+
+ mTmpRect2.setEmpty();
+ if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
+ mWindowContainerController.getRawBounds(mTmpRect2);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (topTask != null) {
+ // TODO: Can we consolidate this and other sites that call this methods?
+ Rect bounds = topTask().getLaunchBounds();
+ if (bounds != null) {
+ mTmpRect2.set(bounds);
+ }
+ }
+ }
+ }
+
+ if (!Objects.equals(mBounds, mTmpRect2)) {
+ resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+ }
+ } finally {
+ if (mDisplayId == DEFAULT_DISPLAY
+ && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Make sure recents stack exist when creating a dock stack as it normally needs to
+ // be on the other side of the docked stack and we make visibility decisions based
+ // on that.
+ // TODO: This is only here to help out with the case where recents stack doesn't
+ // exist yet. For that case the initial size of the split-screen stack will be the
+ // the one where the home stack is visible since recents isn't visible yet, but the
+ // divider will be off. I think we should just make the initial bounds that of home
+ // so that the divider matches and remove this logic.
+ display.getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ // If task moved to docked stack - show recents if needed.
+ mService.mWindowManager.showRecentApps(false /* fromHome */);
+ }
+ wm.continueSurfaceLayout();
+ }
+
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+
+ @Override
public boolean isCompatible(int windowingMode, int activityType) {
// TODO: Should we just move this to ConfigurationContainer?
if (activityType == ACTIVITY_TYPE_UNDEFINED) {
@@ -538,12 +660,6 @@
* either destroyed completely or re-parented.
*/
private void removeFromDisplay() {
- if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // If we removed a docked stack we want to resize it so it resizes all other stacks
- // in the system to fullscreen.
- mStackSupervisor.resizeDockedStackLocked(
- null, null, null, null, null, PRESERVE_WINDOWS);
- }
final ActivityDisplay display = getDisplay();
if (display != null) {
display.removeChild(this);
@@ -730,14 +846,11 @@
return null;
}
- final ActivityRecord topActivity() {
+ ActivityRecord getTopActivity() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (!r.finishing) {
- return r;
- }
+ final ActivityRecord r = mTaskHistory.get(taskNdx).getTopActivity();
+ if (r != null) {
+ return r;
}
}
return null;
@@ -751,7 +864,7 @@
return null;
}
- final TaskRecord bottomTask() {
+ private TaskRecord bottomTask() {
if (mTaskHistory.isEmpty()) {
return null;
}
@@ -1607,8 +1720,6 @@
}
final int otherWindowingMode = other.getWindowingMode();
- // TODO: Can be removed once we are no longer using returnToType for back functionality
- final ActivityStack stackBehind = i > 0 ? display.getChildAt(i - 1) : null;
if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
if (other.isStackTranslucent(starting)) {
@@ -1802,6 +1913,13 @@
return inPinnedWindowingMode();
}
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
+ final TaskRecord topTask = topTask();
+ return super.supportsSplitScreenWindowingMode()
+ && (topTask == null || topTask.supportsSplitScreenWindowingMode());
+ }
+
/** @return True if the resizing of the primary-split-screen stack affects this stack size. */
boolean affectedBySplitScreenResize() {
if (!supportsSplitScreenWindowingMode()) {
@@ -4299,7 +4417,7 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
- final ActivityRecord topActivity = topStack != null ? topStack.topActivity() : null;
+ final ActivityRecord topActivity = topStack != null ? topStack.getTopActivity() : null;
final int numTasks = mTaskHistory.size();
final int index = mTaskHistory.indexOf(tr);
if (numTasks == 0 || index < 0) {
@@ -4502,6 +4620,7 @@
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
if (inFreeformWindowingMode()) {
+ // TODO: Can be removed now since each freeform task is in its own stack.
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.