diff options
14 files changed, 369 insertions, 185 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 0b816b5ff820..d2dfbbf5d9d0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -736,7 +736,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // However, the window bounds include the insets, so we need to subtract them here to make // them identical. if (ssp.hasDockedTask()) { - windowRect.bottom -= systemInsets.bottom; + if (systemInsets.bottom < windowRect.height()) { + // Only apply inset if it isn't going to cause the rect height to go negative. + windowRect.bottom -= systemInsets.bottom; + } systemInsets.bottom = 0; } calculateWindowStableInsets(systemInsets, windowRect, displayRect); diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index ea7b443f0c46..2289f8572b93 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -276,17 +276,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { if (windowingMode == WINDOWING_MODE_PINNED) { return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop); } - final T stack = (T) new ActivityStack( + return (T) new ActivityStack( this, stackId, mSupervisor, windowingMode, activityType, onTop); - - 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: Not sure if this is needed after we change to calculate visibility based on - // stack z-order vs. id. - getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop); - } - return stack; } /** @@ -365,6 +356,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { + " already exist on display=" + this + " stack=" + stack); } mSplitScreenPrimaryStack = stack; + onSplitScreenModeActivated(); } } @@ -377,6 +369,42 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { mPinnedStack = null; } else if (stack == mSplitScreenPrimaryStack) { mSplitScreenPrimaryStack = null; + // Inform the reset of the system that split-screen mode was dismissed so things like + // resizing all the other stacks can take place. + onSplitScreenModeDismissed(); + } + } + + private void onSplitScreenModeDismissed() { + mSupervisor.mWindowManager.deferSurfaceLayout(); + try { + // Adjust the windowing mode of any stack in secondary split-screen to fullscreen. + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack otherStack = mStacks.get(i); + if (!otherStack.inSplitScreenSecondaryWindowingMode()) { + continue; + } + otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } + } finally { + mSupervisor.mWindowManager.continueSurfaceLayout(); + } + } + + private void onSplitScreenModeActivated() { + mSupervisor.mWindowManager.deferSurfaceLayout(); + try { + // Adjust the windowing mode of any affected by split-screen to split-screen secondary. + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack otherStack = mStacks.get(i); + if (otherStack == mSplitScreenPrimaryStack + || !otherStack.affectedBySplitScreenResize()) { + continue; + } + otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } + } finally { + mSupervisor.mWindowManager.continueSurfaceLayout(); } } @@ -475,22 +503,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { supportsFreeform, supportsPip, activityType)) { return windowingMode; } - // Return the display's windowing mode - return getWindowingMode(); - } - - /** Returns the top visible stack activity type that isn't in the exclude windowing mode. */ - int getTopVisibleStackActivityType(int excludeWindowingMode) { - for (int i = mStacks.size() - 1; i >= 0; --i) { - final ActivityStack stack = mStacks.get(i); - if (stack.getWindowingMode() == excludeWindowingMode) { - continue; - } - if (stack.shouldBeVisible(null /* starting */)) { - return stack.getActivityType(); - } - } - return ACTIVITY_TYPE_UNDEFINED; + // Try to use the display's windowing mode otherwise fallback to fullscreen. + windowingMode = getWindowingMode(); + return windowingMode != WINDOWING_MODE_UNDEFINED + ? windowingMode : WINDOWING_MODE_FULLSCREEN; } /** @@ -599,7 +615,20 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { } public void dump(PrintWriter pw, String prefix) { - pw.println(prefix + "displayId=" + mDisplayId + " mStacks=" + mStacks); + pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()); + final String myPrefix = prefix + " "; + if (mHomeStack != null) { + pw.println(myPrefix + "mHomeStack=" + mHomeStack); + } + if (mRecentsStack != null) { + pw.println(myPrefix + "mRecentsStack=" + mRecentsStack); + } + if (mPinnedStack != null) { + pw.println(myPrefix + "mPinnedStack=" + mPinnedStack); + } + if (mSplitScreenPrimaryStack != null) { + pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack); + } } public void writeToProto(ProtoOutputStream proto, long fieldId) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 02ba6a086d57..0ee1e9a8bbcc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4755,7 +4755,7 @@ public class ActivityManagerService extends IActivityManager.Stub throws RemoteException { Slog.i(TAG, "Activity tried to startVoiceInteraction"); synchronized (this) { - ActivityRecord activity = getFocusedStack().topActivity(); + ActivityRecord activity = getFocusedStack().getTopActivity(); if (ActivityRecord.forTokenLocked(callingActivity) != activity) { throw new SecurityException("Only focused activity can call startVoiceInteraction"); } @@ -10431,13 +10431,7 @@ public class ActivityManagerService extends IActivityManager.Stub "exitFreeformMode: You can only go fullscreen from freeform."); } - final ActivityStack fullscreenStack = stack.getDisplay().getOrCreateStack( - WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP); - - if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r); - // TODO: Should just change windowing mode vs. re-parenting... - r.getTask().reparent(fullscreenStack, ON_TOP, - REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode"); + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); } finally { Binder.restoreCallingIdentity(ident); } @@ -10446,6 +10440,11 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, + toTop, ANIMATE, null /* initialBounds */); + return; + } enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); @@ -10458,56 +10457,16 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId + " to windowingMode=" + windowingMode + " toTop=" + toTop); - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - mWindowManager.setDockedStackCreateState(SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, - null /* initialBounds */); - } if (!task.isActivityTypeStandardOrUndefined()) { - throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task " - + taskId + " to non-standard windowin mode=" + windowingMode); + throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move" + + " non-standard task " + taskId + " to windowing mode=" + + windowingMode); } final ActivityDisplay display = task.getStack().getDisplay(); final ActivityStack stack = display.getOrCreateStack(windowingMode, task.getStack().getActivityType(), toTop); - // TODO: We should just change the windowing mode for the task vs. creating and - // moving it to a stack. - task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, - "moveTaskToStack"); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - @Override - public void moveTaskToStack(int taskId, int stackId, boolean toTop) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); - synchronized (this) { - long ident = Binder.clearCallingIdentity(); - try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); - if (task == null) { - Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); - return; - } - - if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId - + " to stackId=" + stackId + " toTop=" + toTop); - - final ActivityStack stack = mStackSupervisor.getStack(stackId); - if (stack == null) { - throw new IllegalStateException( - "moveTaskToStack: No stack for stackId=" + stackId); - } - if (!stack.isActivityTypeStandardOrUndefined()) { - throw new IllegalArgumentException("moveTaskToStack: Attempt to move task " - + taskId + " to stack " + stackId); - } - if (stack.inSplitScreenPrimaryWindowingMode()) { - mWindowManager.setDockedStackCreateState( - SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); - } + // TODO: Use ActivityStack.setWindowingMode instead of re-parenting. task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack"); } finally { @@ -10517,7 +10476,7 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Moves the input task to the primary-split-screen stack. + * Moves the specified task to the primary-split-screen stack. * * @param taskId Id of task to move. * @param createMode The mode the primary split screen stack should be created in if it doesn't @@ -10526,7 +10485,7 @@ public class ActivityManagerService extends IActivityManager.Stub * and * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT} * @param toTop If the task and stack should be moved to the top. - * @param animate Whether we should play an animation for the moving the task + * @param animate Whether we should play an animation for the moving the task. * @param initialBounds If the primary stack gets created, it will use these bounds for the * stack. Pass {@code null} to use default bounds. */ @@ -10546,22 +10505,55 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId + " to createMode=" + createMode + " toTop=" + toTop); + if (!task.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move" + + " non-standard task " + taskId + " to split-screen windowing mode"); + } + mWindowManager.setDockedStackCreateState(createMode, initialBounds); + final int windowingMode = task.getWindowingMode(); + final ActivityStack stack = task.getStack(); + if (toTop) { + stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task); + } + stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate); + return windowingMode != task.getWindowingMode(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } - final ActivityDisplay display = task.getStack().getDisplay(); - final ActivityStack stack = display.getOrCreateStack( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, task.getStack().getActivityType(), - toTop); - - // Defer resuming until we move the home stack to the front below - // TODO: Should just change windowing mode vs. re-parenting... - final boolean moved = task.reparent(stack, toTop, - REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME, - "setTaskWindowingModeSplitScreenPrimary"); - if (moved) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + @Override + public void moveTaskToStack(int taskId, int stackId, boolean toTop) { + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); + synchronized (this) { + long ident = Binder.clearCallingIdentity(); + try { + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + if (task == null) { + Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); + return; } - return moved; + + if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId + + " to stackId=" + stackId + " toTop=" + toTop); + + final ActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack == null) { + throw new IllegalStateException( + "moveTaskToStack: No stack for stackId=" + stackId); + } + if (!stack.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException("moveTaskToStack: Attempt to move task " + + taskId + " to stack " + stackId); + } + if (stack.inSplitScreenPrimaryWindowingMode()) { + mWindowManager.setDockedStackCreateState( + SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); + } + task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, + "moveTaskToStack"); } finally { Binder.restoreCallingIdentity(ident); } @@ -10584,14 +10576,11 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found."); return; } + if (toTop) { - mStackSupervisor.resizeStackLocked(stack, null /* destBounds */, - null /* tempTaskBounds */, null /* tempTaskInsetBounds */, - true /* preserveWindows */, true /* allowResizeInDockedMode */, - !DEFER_RESUME); - } else { - mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */); + stack.moveToFront("dismissSplitScreenMode"); } + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); } } finally { Binder.restoreCallingIdentity(ident); @@ -12947,7 +12936,7 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } - final ActivityRecord activity = focusedStack.topActivity(); + final ActivityRecord activity = focusedStack.getTopActivity(); if (activity == null) { return false; } @@ -12964,7 +12953,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { synchronized (this) { ActivityRecord caller = ActivityRecord.forTokenLocked(token); - ActivityRecord top = getFocusedStack().topActivity(); + ActivityRecord top = getFocusedStack().getTopActivity(); if (top != caller) { Slog.w(TAG, "showAssistFromActivity failed: caller " + caller + " is not current top " + top); @@ -13007,7 +12996,7 @@ public class ActivityManagerService extends IActivityManager.Stub "enqueueAssistContext()"); synchronized (this) { - ActivityRecord activity = getFocusedStack().topActivity(); + ActivityRecord activity = getFocusedStack().getTopActivity(); if (activity == null) { Slog.w(TAG, "getAssistContextExtras failed: no top activity"); return null; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 8cc584e44d19..cfb10350baa3 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 static java.lang.Integer.MAX_VALUE; 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } @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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return null; } - final TaskRecord bottomTask() { + private TaskRecord bottomTask() { if (mTaskHistory.isEmpty()) { return null; } @@ -1607,8 +1720,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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. diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 062083c47b26..41695faa22af 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2945,6 +2945,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * Returns the reparent target stack, creating the stack if necessary. This call also enforces * the various checks on tasks that are going to be reparented from one stack to another. */ + // TODO: Look into changing users to this method to ActivityDisplay.resolveWindowingMode() ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) { final ActivityStack prevStack = task.getStack(); final int stackId = stack.mStackId; @@ -2971,8 +2972,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D + " reparent task=" + task + " to stackId=" + stackId); } - // Ensure that we aren't trying to move into a freeform stack without freeform - // support + // Ensure that we aren't trying to move into a freeform stack without freeform support if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM && !mService.mSupportsFreeformWindowManagement) { throw new IllegalArgumentException("Device doesn't support freeform, can not reparent" @@ -3376,7 +3376,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // When launching tasks behind, update the last active time of the top task after the new // task has been shown briefly - final ActivityRecord top = stack.topActivity(); + final ActivityRecord top = stack.getTopActivity(); if (top != null) { top.getTask().touchActiveTime(); } @@ -4200,10 +4200,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - final ActivityRecord topActivity = task.getTopActivity(); if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) { - // Display a warning toast that we tried to put a non-dockable task in the docked - // stack. + // Display a warning toast that we tried to put an app that doesn't support split-screen + // in split-screen. mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack(); // Dismiss docked stack. If task appeared to be in docked stack but is not resizable - @@ -4217,6 +4216,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } + final ActivityRecord topActivity = task.getTopActivity(); if (topActivity != null && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) { final String packageName = topActivity.appInfo.packageName; @@ -4552,7 +4552,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityStack stack = display.getChildAt(j); // Get top activity from a visible stack and add it to the list. if (stack.shouldBeVisible(null /* starting */)) { - final ActivityRecord top = stack.topActivity(); + final ActivityRecord top = stack.getTopActivity(); if (top != null) { if (stack == mFocusedStack) { topActivityTokens.add(0, top.appToken); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 1c8028270f97..32d5ba8a9380 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1119,7 +1119,7 @@ class ActivityStarter { // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. final ActivityStack topStack = mSupervisor.mFocusedStack; - final ActivityRecord topFocused = topStack.topActivity(); + final ActivityRecord topFocused = topStack.getTopActivity(); final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); final boolean dontStart = top != null && mStartActivity.resultTo == null && top.realActivity.equals(mStartActivity.realActivity) @@ -1565,8 +1565,8 @@ class ActivityStarter { && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()) && !mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); - if (mSourceRecord == null || (mSourceStack.topActivity() != null && - mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) { + if (mSourceRecord == null || (mSourceStack.getTopActivity() != null && + mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) { // We really do want to push this one into the user's face, right now. if (mLaunchTaskBehind && mSourceRecord != null) { intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); @@ -1956,7 +1956,7 @@ class ActivityStarter { if (mDoResume) { mTargetStack.moveToFront("addingToTopTask"); } - final ActivityRecord prev = mTargetStack.topActivity(); + final ActivityRecord prev = mTargetStack.getTopActivity(); final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info, mIntent, null, null, true); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 40b1cac70883..949f51fe1b09 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -606,9 +606,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi final int toStackWindowingMode = toStack.getWindowingMode(); final ActivityRecord topActivity = getTopActivity(); - final boolean mightReplaceWindow = - replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode) - && topActivity != null; + final boolean mightReplaceWindow = topActivity != null + && replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode); if (mightReplaceWindow) { // We are about to relaunch the activity because its configuration changed due to // being maximized, i.e. size change. The activity will first remove the old window @@ -722,7 +721,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } // TODO: Handle incorrect request to move before the actual move, not after. - final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenPrimaryStack(); supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(), DEFAULT_DISPLAY, toStack); @@ -735,10 +733,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } /** - * Returns true if the windows of tasks being moved to the target stack from the source - * stack should be replaced, meaning that window manager will keep the old window around - * until the new is ready. - * @hide + * @return True if the windows of tasks being moved to the target stack from the source stack + * should be replaced, meaning that window manager will keep the old window around until the new + * is ready. */ private static boolean replaceWindowsOnTaskMove( int sourceWindowingMode, int targetWindowingMode) { @@ -2062,11 +2059,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } static Rect validateBounds(Rect bounds) { - if (bounds != null && bounds.isEmpty()) { - Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable()); - return null; - } - return bounds; + // TODO: Not needed once we have bounds in WindowConfiguration. + return (bounds != null && bounds.isEmpty()) ? null : bounds; } /** Updates the task's bounds and override configuration to match what is expected for the @@ -2095,7 +2089,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } /** Returns the bounds that should be used to launch this task. */ - private Rect getLaunchBounds() { + Rect getLaunchBounds() { if (mStack == null) { return null; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c4b810f7d30b..4d839d009c56 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; @@ -1488,7 +1489,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * Returns the topmost stack on the display that is compatible with the input windowing mode. * Null is no compatible stack on the display. */ - TaskStack getStack(int windowingMode) { + TaskStack getTopStackInWindowingMode(int windowingMode) { return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED); } @@ -1764,10 +1765,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final TaskStack stack = new TaskStack(mService, stackId, controller); mTaskStackContainers.addStackToDisplay(stack, onTop); - - if (stack.inSplitScreenPrimaryWindowingMode()) { - mDividerControllerLocked.notifyDockedStackExistsChanged(true); - } return stack; } @@ -2274,7 +2271,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Returns true if the stack in the windowing mode is visible. */ boolean isStackVisible(int windowingMode) { - final TaskStack stack = getStack(windowingMode); + final TaskStack stack = getTopStackInWindowingMode(windowingMode); return stack != null && stack.isVisible(); } @@ -3391,6 +3388,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) { final TaskStack stack = mTaskStackContainers.getChildAt(i); + if (activityType == ACTIVITY_TYPE_UNDEFINED + && windowingMode == stack.getWindowingMode()) { + // Passing in undefined type means we want to match the topmost stack with the + // windowing mode. + return stack; + } if (stack.isCompatible(windowingMode, activityType)) { return stack; } @@ -3461,6 +3464,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo + " already exist on display=" + this + " stack=" + stack); } mSplitScreenPrimaryStack = stack; + mDividerControllerLocked.notifyDockedStackExistsChanged(true); } } @@ -3471,6 +3475,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mPinnedStack = null; } else if (stack == mSplitScreenPrimaryStack) { mSplitScreenPrimaryStack = null; + // Re-set the split-screen create mode whenever the split-screen stack is removed. + mService.setDockedStackCreateStateLocked( + SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); + mDividerControllerLocked.notifyDockedStackExistsChanged(false); } } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index e693e5af46a1..d79ba897150b 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -559,7 +559,7 @@ public class DockedStackDividerController implements DimLayerUser { void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { // TODO: Maybe only allow split-screen windowing modes? final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED - ? mDisplayContent.getStack(targetWindowingMode) + ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode) : null; final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack(); boolean visibleAndValid = visible && stack != null && dockedStack != null; @@ -663,11 +663,15 @@ public class DockedStackDividerController implements DimLayerUser { if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) { return; } - final TaskStack fullscreenStack = mDisplayContent.getStack( + final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode( WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - final boolean homeVisible = homeTask.getTopVisibleAppToken() != null; - final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible(); - setMinimizedDockedStack(homeVisible && !homeBehind, animate); + boolean homeVisible = homeTask.getTopVisibleAppToken() != null; + if (homeVisible && topSecondaryStack != null) { + // Home should only be considered visible if it is greater or equal to the top secondary + // stack in terms of z-order. + homeVisible = homeStack.compareTo(topSecondaryStack) >= 0; + } + setMinimizedDockedStack(homeVisible, animate); } private boolean isWithinDisplay(Task task) { diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java index aff1bc62563f..95c1d536123f 100644 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ b/services/core/java/com/android/server/wm/StackWindowController.java @@ -225,11 +225,13 @@ public class StackWindowController } } - private void getRawBounds(Rect outBounds) { - if (mContainer.getRawFullscreen()) { - outBounds.setEmpty(); - } else { - mContainer.getRawBounds(outBounds); + public void getRawBounds(Rect outBounds) { + synchronized (mWindowMap) { + if (mContainer.getRawFullscreen()) { + outBounds.setEmpty(); + } else { + mContainer.getRawBounds(outBounds); + } } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index dde79462e39f..053fb47023e1 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -685,9 +685,12 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye public void onConfigurationChanged(Configuration newParentConfig) { final int prevWindowingMode = getWindowingMode(); super.onConfigurationChanged(newParentConfig); - if (mDisplayContent != null && prevWindowingMode != getWindowingMode()) { - mDisplayContent.onStackWindowingModeChanged(this); + final int windowingMode = getWindowingMode(); + if (mDisplayContent == null || prevWindowingMode == windowingMode) { + return; } + mDisplayContent.onStackWindowingModeChanged(this); + updateBoundsForWindowModeChange(); } @Override @@ -699,39 +702,41 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye mDisplayContent = dc; mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(), "animation background stackId=" + mStackId); + updateBoundsForWindowModeChange(); + super.onDisplayChanged(dc); + } + private void updateBoundsForWindowModeChange() { Rect bounds = null; - final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility(); - if (inSplitScreenPrimaryWindowingMode() - || (dockedStack != null && inSplitScreenSecondaryWindowingMode() - && !dockedStack.fillsParent())) { + final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode(); + final TaskStack splitScreenStack = + mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); + if (inSplitScreenPrimary || (splitScreenStack != null + && inSplitScreenSecondaryWindowingMode() && !splitScreenStack.fillsParent())) { // The existence of a docked stack affects the size of other static stack created since // the docked stack occupies a dedicated region on screen, but only if the dock stack is // not fullscreen. If it's fullscreen, it means that we are in the transition of // dismissing it, so we must not resize this stack. bounds = new Rect(); - dc.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getLogicalDisplayRect(mTmpRect); mTmpRect2.setEmpty(); - if (dockedStack != null) { - dockedStack.getRawBounds(mTmpRect2); + if (splitScreenStack != null) { + splitScreenStack.getRawBounds(mTmpRect2); } final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2, - mDisplayContent.mDividerControllerLocked.getContentWidth(), - dockedOnTopOrLeft); + mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); } else if (inPinnedWindowingMode()) { // Update the bounds based on any changes to the display info getAnimationOrCurrentBounds(mTmpRect2); - boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged( - mTmpRect2, mTmpRect3); - if (updated) { + if (mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged( + mTmpRect2, mTmpRect3)) { bounds = new Rect(mTmpRect3); } } updateDisplayInfo(bounds); - super.onDisplayChanged(dc); } /** @@ -922,10 +927,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye mAnimationBackgroundSurface = null; } - if (inSplitScreenPrimaryWindowingMode()) { - mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false); - } - mDisplayContent = null; mService.mWindowPlacerLocked.requestTraversal(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index df51be18936d..723b888e0d14 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6860,11 +6860,16 @@ public class WindowManagerService extends IWindowManager.Stub public void setWillReplaceWindow(IBinder token, boolean animate) { synchronized (mWindowMap) { final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token); - if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) { + if (appWindowToken == null) { Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token); return; } + if (!appWindowToken.hasContentToDisplay()) { + Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content" + + token); + return; + } appWindowToken.setWillReplaceWindows(animate); } } @@ -6884,11 +6889,16 @@ public class WindowManagerService extends IWindowManager.Stub void setWillReplaceWindows(IBinder token, boolean childrenOnly) { synchronized (mWindowMap) { final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token); - if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) { + if (appWindowToken == null) { Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token); return; } + if (!appWindowToken.hasContentToDisplay()) { + Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content" + + token); + return; + } if (childrenOnly) { appWindowToken.setWillReplaceChildWindows(); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 480b210c17b8..6b09363cff0b 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -166,6 +166,8 @@ public class ActivityStackTests extends ActivityTestsBase { final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); + // Home stack should always be fullscreen for this test. + homeStack.setSupportsSplitScreen(false); final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display, diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 198cc6ded558..482d5f52815f 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -396,6 +396,11 @@ public class ActivityTestsBase { static final int IS_TRANSLUCENT_TRUE = 2; private int mIsTranslucent = IS_TRANSLUCENT_UNSET; + static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0; + static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1; + static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2; + private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET; + TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor, int windowingMode, int activityType, boolean onTop) { super(display, stackId, supervisor, windowingMode, activityType, onTop); @@ -439,5 +444,23 @@ public class ActivityTestsBase { return super.isStackTranslucent(starting); } } + + void setSupportsSplitScreen(boolean supportsSplitScreen) { + mSupportsSplitScreen = supportsSplitScreen + ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE; + } + + @Override + public boolean supportsSplitScreenWindowingMode() { + switch (mSupportsSplitScreen) { + case SUPPORTS_SPLIT_SCREEN_TRUE: + return true; + case SUPPORTS_SPLIT_SCREEN_FALSE: + return false; + case SUPPORTS_SPLIT_SCREEN_UNSET: + default: + return super.supportsSplitScreenWindowingMode(); + } + } } } |