import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
import static com.android.server.am.proto.ActivityDisplayProto.ID;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.util.IntArray;
import android.util.Slog;
}
/**
+ * Returns an existing stack compatible with the input params or creates one
+ * if a compatible stack doesn't exist.
+ * @see #getOrCreateStack(int, int, boolean)
+ */
+ <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType,
+ boolean onTop) {
+ final int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
+ return getOrCreateStack(windowingMode, activityType, onTop);
+ }
+
+ /**
* Creates a stack matching the input windowing mode and activity type on this display.
* @param windowingMode The windowing mode the stack should be created in. If
* {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
}
final ActivityManagerService service = mSupervisor.mService;
- if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
+ if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
service.mSupportsPictureInPicture, activityType)) {
throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
}
}
- windowingMode = updateWindowingModeForSplitScreenIfNeeded(windowingMode, activityType);
-
final int stackId = mSupervisor.getNextStackId();
+ return createStackUnchecked(windowingMode, activityType, stackId, onTop);
+ }
- final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
+ @VisibleForTesting
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ }
+ final T stack = (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 need to be on
- // the other side of the docked stack and we make visibility decisions based on that.
+ // 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;
}
- @VisibleForTesting
- <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
- int stackId, boolean onTop) {
- switch (windowingMode) {
- case WINDOWING_MODE_PINNED:
- return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
- default:
- return (T) new ActivityStack(
- this, stackId, mSupervisor, windowingMode, activityType, onTop);
- }
- }
-
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
}
}
+ /**
+ * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+ * @param windowingMode The windowing mode we are checking support for.
+ * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+ * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+ * @param supportsFreeform If we should consider support for freeform multi-window.
+ * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+ * @param activityType The activity type under consideration.
+ * @return true if the windowing mode is supported.
+ */
+ private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+ boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+ int activityType) {
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ return true;
+ }
+ if (!supportsMultiWindow) {
+ return false;
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType);
+ }
+
+ if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+
+ if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return true;
+ }
+
+ int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable TaskRecord task, int activityType) {
+
+ // First preference if the windowing mode in the activity options if set.
+ int windowingMode = (options != null)
+ ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+ // If windowing mode is unset, then next preference is the candidate task, then the
+ // activity record.
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ if (task != null) {
+ windowingMode = task.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
+ windowingMode = r.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ // Use the display's windowing mode.
+ windowingMode = getWindowingMode();
+ }
+ }
+
+ // Make sure the windowing mode we are trying to use makes sense for what is supported.
+ final ActivityManagerService service = mSupervisor.mService;
+ boolean supportsMultiWindow = service.mSupportsMultiWindow;
+ boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
+ boolean supportsPip = service.mSupportsPictureInPicture;
+ if (supportsMultiWindow) {
+ if (task != null) {
+ supportsMultiWindow = task.isResizeable();
+ supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+ // TODO: Do we need to check for freeform and Pip support here?
+ } else if (r != null) {
+ supportsMultiWindow = r.isResizeable();
+ supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+ supportsFreeform = r.supportsFreeform();
+ supportsPip = r.supportsPictureInPicture();
+ }
+ }
+
+ final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
+ if (!inSplitScreenMode
+ && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+ // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
+ // trying to launch in split-screen secondary.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && supportsSplitScreen) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ if (windowingMode != WINDOWING_MODE_UNDEFINED
+ && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ 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) {
}
}
+ /** We are in the process of exiting split-screen mode. */
+ void onExitingSplitScreenMode() {
+ // Remove reference to the primary-split-screen stack so it no longer has any effect on the
+ // display. For example, we want to be able to create fullscreen stack for standard activity
+ // types when exiting split-screen mode.
+ mSplitScreenPrimaryStack = null;
+ }
+
ActivityStack getSplitScreenPrimaryStack() {
return mSplitScreenPrimaryStack;
}
return mPinnedStack != null;
}
- int updateWindowingModeForSplitScreenIfNeeded(int windowingMode, int activityType) {
- final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
- if (!inSplitScreenMode
- && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
- // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
- // trying to launch in split-screen secondary.
- return WINDOWING_MODE_FULLSCREEN;
- } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
- && WindowConfiguration.supportSplitScreenWindowingMode(
- windowingMode, activityType)) {
- return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
- return windowingMode;
- }
-
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
}
// TODO(multi-display): Have the caller pass in the windowing mode and activity type.
final ActivityStack stack = display.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/);
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+ ON_TOP);
return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
}
}
import android.view.IApplicationToken;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
- boolean fullscreen; // covers the full screen?
+ boolean fullscreen; // The activity is opaque and fills the entire space of this task.
+ // TODO: See if it possible to combine this with the fullscreen field.
+ final boolean hasWallpaper; // Has a wallpaper window as a background.
final boolean noDisplay; // activity is not displayed?
private final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window, userId);
- fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array);
- noDisplay = ent != null && ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowNoDisplay, false);
+ if (ent != null) {
+ fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
+ hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
+ noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ } else {
+ hasWallpaper = false;
+ noDisplay = false;
+ }
setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
activityType = ACTIVITY_TYPE_STANDARD;
}
final ActivityDisplay display = getDisplay();
- if (display != null) {
- if (activityType == ACTIVITY_TYPE_STANDARD
+ if (display != null && activityType == ACTIVITY_TYPE_STANDARD
&& windowingMode == WINDOWING_MODE_UNDEFINED) {
- // Standard activity types will mostly take on the windowing mode of the display if
- // one isn't specified, so look-up a compatible stack based on the display's
- // windowing mode.
- windowingMode = display.getWindowingMode();
- }
- windowingMode =
- display.updateWindowingModeForSplitScreenIfNeeded(windowingMode, activityType);
+ // Standard activity types will mostly take on the windowing mode of the display if one
+ // isn't specified, so look-up a compatible stack based on the display's windowing mode.
+ windowingMode = display.getWindowingMode();
}
return super.isCompatible(windowingMode, activityType);
}
/** Adds the stack to specified display and calls WindowManager to do the same. */
void reparent(ActivityDisplay activityDisplay, boolean onTop) {
+ // TODO: We should probably resolve the windowing mode for the stack on the new display here
+ // so that it end up in a compatible mode in the new display. e.g. split-screen secondary.
removeFromDisplay();
mTmpRect2.setEmpty();
postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}
}
- /** Returns true if the stack contains a fullscreen task. */
- private boolean hasFullscreenTask() {
- for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
- final TaskRecord task = mTaskHistory.get(i);
- if (task.mFullscreen) {
- return true;
- }
- }
- return false;
- }
-
/**
* Returns true if the stack is translucent and can have other contents visible behind it if
* needed. A stack is considered translucent if it don't contain a visible or
* starting (about to be visible) activity that is fullscreen (opaque).
* @param starting The currently starting activity or null if there is none.
+ * TODO: Can be removed once we are no longer using returnToType for back functionality
* @param stackBehind The stack directly behind this one.
*/
- private boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
+ @VisibleForTesting
+ boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
+ if (!isAttached() || mForceHidden) {
+ return true;
+ }
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
continue;
}
- if (r.fullscreen) {
+ if (r.fullscreen || r.hasWallpaper) {
// Stack isn't translucent if it has at least one fullscreen activity
// that is visible.
return false;
if (!isAttached() || mForceHidden) {
return false;
}
-
- final ActivityDisplay display = getDisplay();
- if (isTopStackOnDisplay() || mStackSupervisor.isFocusedStack(this)) {
- return true;
- }
-
- final int stackIndex = display.getIndexOf(this);
-
- // Check position and visibility of this stack relative to the front stack on its display.
- final ActivityStack topStack = getDisplay().getTopStack();
- final int windowingMode = getWindowingMode();
- final int activityType = getActivityType();
-
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // If the assistant stack is focused and translucent, then the docked stack is always
- // visible
- if (topStack.isActivityTypeAssistant()) {
- return topStack.isStackTranslucent(starting, this);
- }
+ if (mStackSupervisor.isFocusedStack(this)) {
return true;
}
- // Set home stack to invisible when it is below but not immediately below the docked stack
- // A case would be if recents stack exists but has no tasks and is below the docked stack
- // and home stack is below recents
- if (activityType == ACTIVITY_TYPE_HOME) {
- final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
- int dockedStackIndex = display.getIndexOf(splitScreenStack);
- if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
- return false;
- }
- }
-
- // Find the first stack behind front stack that actually got something visible.
- int stackBehindTopIndex = display.getIndexOf(topStack) - 1;
- while (stackBehindTopIndex >= 0 &&
- display.getChildAt(stackBehindTopIndex).topRunningActivityLocked() == null) {
- stackBehindTopIndex--;
- }
- final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
- ? display.getChildAt(stackBehindTopIndex) : null;
- int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
- int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
- if (stackBehindTop != null) {
- stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
- stackBehindTopActivityType = stackBehindTop.getActivityType();
- }
-
- final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
- if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
- if (this == stackBehindTop) {
- // Stacks directly behind the docked or pinned stack are always visible.
- return true;
- } else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
- // Otherwise, this stack can also be visible if it is directly behind a docked stack
- // or translucent assistant stack behind an always-on-top top-most stack
- if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return true;
- } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
- return stackBehindTop.isStackTranslucent(starting, this);
- }
- }
+ final ActivityRecord top = topRunningActivityLocked();
+ if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
+ // Shouldn't be visible if you don't have any running activities, not starting one, and
+ // not the top stack on display.
+ return false;
}
- if (topStack.isBackdropToTranslucentActivity()
- && topStack.isStackTranslucent(starting, stackBehindTop)) {
- // Stacks behind the fullscreen or assistant stack with a translucent activity are
- // always visible so they can act as a backdrop to the translucent activity.
- // For example, dialog activities
- if (stackIndex == stackBehindTopIndex) {
+ final ActivityDisplay display = getDisplay();
+ boolean gotOpaqueSplitScreenPrimary = false;
+ boolean gotOpaqueSplitScreenSecondary = false;
+ final int windowingMode = getWindowingMode();
+ for (int i = display.getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack other = display.getChildAt(i);
+ if (other == this) {
+ // Should be visible if there is no other stack occluding it.
return true;
}
- if (stackBehindTopIndex >= 0) {
- if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED)
- && stackIndex == (stackBehindTopIndex - 1)) {
- // The stack behind the docked or pinned stack is also visible so we can have a
- // complete backdrop to the translucent activity when the docked stack is up.
- return true;
- }
- }
- }
-
- if (isOnHomeDisplay()) {
- // Visibility of any stack on default display should have been determined by the
- // conditions above.
- return false;
- }
- final int stackCount = display.getChildCount();
- for (int i = stackIndex + 1; i < stackCount; i++) {
- final ActivityStack stack = display.getChildAt(i);
+ 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 (!stack.mFullscreen && !stack.hasFullscreenTask()) {
- continue;
- }
-
- if (!stack.isDynamicStacksVisibleBehindAllowed()) {
- // These stacks can't have any dynamic stacks visible behind them.
+ if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (other.isStackTranslucent(starting, stackBehind)) {
+ // Can be visible behind a translucent fullscreen stack.
+ continue;
+ }
return false;
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && !gotOpaqueSplitScreenPrimary) {
+ gotOpaqueSplitScreenPrimary =
+ !other.isStackTranslucent(starting, stackBehind);
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && gotOpaqueSplitScreenPrimary) {
+ // Can not be visible behind another opaque stack in split-screen-primary mode.
+ return false;
+ }
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && !gotOpaqueSplitScreenSecondary) {
+ gotOpaqueSplitScreenSecondary =
+ !other.isStackTranslucent(starting, stackBehind);
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible behind another opaque stack in split-screen-secondary mode.
+ return false;
+ }
}
-
- if (!stack.isStackTranslucent(starting, null /* stackBehind */)) {
+ if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible if we are in split-screen windowing mode and both halves of
+ // the screen are opaque.
return false;
}
}
+ // Well, nothing is stopping you from being visible...
return true;
}
- private boolean isBackdropToTranslucentActivity() {
- if (isActivityTypeAssistant()) {
- return true;
- }
- final int windowingMode = getWindowingMode();
- return windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- private boolean isDynamicStacksVisibleBehindAllowed() {
- return isActivityTypeAssistant() || getWindowingMode() == WINDOWING_MODE_PINNED;
- }
-
final int rankTaskLayers(int baseLayer) {
int layer = 0;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
public String toString() {
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
+ " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()) + ", "
+ + " mode=" + windowingModeToString(getWindowingMode())
+ + " visible=" + shouldBeVisible(null /* starting */) + ", "
+ mTaskHistory.size() + " tasks}";
}
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
-import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
// at the top of its container (e.g. stack).
static final boolean ON_TOP = true;
- // Used to indicate that an objects (e.g. task) removal from its container
- // (e.g. stack) is due to it moving to another container.
- static final boolean MOVING = true;
-
- // Force the focus to change to the stack we are moving a task to..
- static final boolean FORCE_FOCUS = true;
-
// Don't execute any calls to resume.
static final boolean DEFER_RESUME = true;
* object each time.
*/
private final Rect tempRect = new Rect();
+ private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
return null;
}
- /**
- * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
- * @param windowingMode The windowing mode we are checking support for.
- * @param supportsMultiWindow If we should consider support for multi-window mode in general.
- * @param supportsSplitScreen If we should consider support for split-screen multi-window.
- * @param supportsFreeform If we should consider support for freeform multi-window.
- * @param supportsPip If we should consider support for picture-in-picture mutli-window.
- * @param activityType The activity type under consideration.
- * @return true if the windowing mode is supported.
- */
- boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
- boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
- int activityType) {
-
- if (windowingMode == WINDOWING_MODE_UNDEFINED
- || windowingMode == WINDOWING_MODE_FULLSCREEN) {
- return true;
- }
- if (!supportsMultiWindow) {
- return false;
- }
-
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
- windowingMode, activityType);
- }
-
- if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
- return false;
- }
-
- if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
- return false;
- }
- return true;
- }
-
- private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord task, int activityType) {
-
- // First preference if the windowing mode in the activity options if set.
- int windowingMode = (options != null)
- ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
-
- // If windowing mode is unset, then next preference is the candidate task, then the
- // activity record.
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- if (task != null) {
- windowingMode = task.getWindowingMode();
- }
- if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
- windowingMode = r.getWindowingMode();
- }
- }
-
- // Make sure the windowing mode we are trying to use makes sense for what is supported.
- boolean supportsMultiWindow = mService.mSupportsMultiWindow;
- boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
- boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
- boolean supportsPip = mService.mSupportsPictureInPicture;
- if (supportsMultiWindow) {
- if (task != null) {
- supportsMultiWindow = task.isResizeable();
- supportsSplitScreen = task.supportsSplitScreenWindowingMode();
- // TODO: Do we need to check for freeform and Pip support here?
- } else if (r != null) {
- supportsMultiWindow = r.isResizeable();
- supportsSplitScreen = r.supportsSplitScreenWindowingMode();
- supportsFreeform = r.supportsFreeform();
- supportsPip = r.supportsPictureInPicture();
- }
- }
-
- if (windowingMode != WINDOWING_MODE_UNDEFINED
- && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
- supportsFreeform, supportsPip, activityType)) {
- return windowingMode;
- }
- // Return root/systems windowing mode
- return getWindowingMode();
- }
-
int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable TaskRecord task) {
// Preference is given to the activity type for the activity then the task since the type
}
final int activityType = resolveActivityType(r, options, candidateTask);
- int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
T stack = null;
// Next preference for stack goes to the display Id set in the activity options or the
}
final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
if (display != null) {
- stack = display.getOrCreateStack(windowingMode, activityType, onTop);
+ stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
if (stack != null) {
return stack;
}
stack = r.getStack();
}
if (stack != null) {
- if (stack.isCompatible(windowingMode, activityType)) {
- return stack;
- }
display = stack.getDisplay();
+ if (display != null) {
+ final int windowingMode =
+ display.resolveWindowingMode(r, options, candidateTask, activityType);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ }
}
if (display == null
display = getDefaultDisplay();
}
- return display.getOrCreateStack(windowingMode, activityType, onTop);
+ return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
}
/**
final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Tell the display we are exiting split-screen mode.
+ toDisplay.onExitingSplitScreenMode();
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
if (!tasks.isEmpty()) {
+ mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int size = tasks.size();
- final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
-
- if (onTop) {
- final int returnToType =
- toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
+ for (int i = 0; i < size; ++i) {
+ final TaskRecord task = tasks.get(i);
+ final ActivityStack toStack = toDisplay.getOrCreateStack(
+ null, mTmpOptions, task, task.getActivityType(), onTop);
+
+ if (onTop) {
+ final int returnToType =
+ toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
final boolean isTopTask = i == (size - 1);
if (inPinnedWindowingMode) {
- // Update the return-to to reflect where the pinned stack task was moved
- // from so that we retain the stack that was previously visible if the
- // pinned stack is recreated. See moveActivityToPinnedStackLocked().
+ // Update the return-to to reflect where the pinned stack task was
+ // moved from so that we retain the stack that was previously
+ // visible if the pinned stack is recreated.
+ // See moveActivityToPinnedStackLocked().
task.setTaskToReturnTo(returnToType);
}
// Defer resume until all the tasks have been moved to the fullscreen stack
- task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
isTopTask /* animate */, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - onTop");
- }
- } else {
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
+ } else {
// Position the tasks in the fullscreen stack in order at the bottom of the
// stack. Also defer resume until all the tasks have been moved to the
// fullscreen stack.
- task.reparent(fullscreenStack, i /* position */,
+ task.reparent(toStack, ON_TOP,
REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - NOT_onTop");
+ " task=" + task);
}
- // We don't allow moving a unresizeable task to the docked stack since the docked stack is
- // used for split-screen mode and will cause things like the docked divider to show up. We
- // instead leave the task in its current stack or move it to the fullscreen stack if it
- // isn't currently in a stack.
+ // Leave the task in its current stack or a fullscreen stack if it isn't resizeable and the
+ // preferred stack is in multi-window mode.
if (inMultiWindowMode && !task.isResizeable()) {
- Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
- + " Moving to stackId=" + stackId + " instead.");
- // Temporarily disable resizeablility of the task as we don't want it to be resized if,
- // for example, a docked stack is created which will lead to the stack we are moving
- // from being resized and and its resizeable tasks being resized.
- try {
- task.mTemporarilyUnresizable = true;
- stack = stack.getDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
- } finally {
- task.mTemporarilyUnresizable = false;
+ Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window stack=" + stack
+ + " Moving to a fullscreen stack instead.");
+ if (prevStack != null) {
+ return prevStack;
}
+ stack = stack.getDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
}
return stack;
}
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
final boolean inSplitScreenMode = actualStack != null
- && actualStack.inSplitScreenWindowingMode();
+ && actualStack.getDisplay().hasSplitScreenPrimaryStack();
if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
- && !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) {
+ && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
return;
}
private boolean mSupportsPictureInPicture; // Whether or not this task and its activities
// support PiP. Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag
// of the root activity.
- boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
- // changes on a temporary basis.
-
/** Can't be put in lockTask mode. */
final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
/** Can enter app pinning with user approval. Can never start over existing lockTask task. */
private boolean isResizeable(boolean checkSupportsPip) {
return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
- || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable;
+ || (checkSupportsPip && mSupportsPictureInPicture));
}
boolean isResizeable() {
ComponentName.unflattenFromString("com.foo/.BarActivity2");
private ActivityManagerService mService;
- private ActivityStack mStack;
+ private TestActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@Test
public void testStackCleanupOnClearingTask() throws Exception {
mActivity.setTask(null);
- assertEquals(getActivityRemovedFromStackCount(), 1);
+ assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
}
@Test
public void testStackCleanupOnActivityRemoval() throws Exception {
mTask.removeActivity(mActivity);
- assertEquals(getActivityRemovedFromStackCount(), 1);
+ assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
}
@Test
final TaskRecord newTask =
createTask(mService.mStackSupervisor, testActivityComponent, mStack);
mActivity.reparent(newTask, 0, null /*reason*/);
- assertEquals(getActivityRemovedFromStackCount(), 0);
+ assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
}
@Test
assertEquals(expectedActivityBounds, mActivity.getBounds());
}
- private int getActivityRemovedFromStackCount() {
- if (mStack instanceof ActivityStackReporter) {
- return ((ActivityStackReporter) mStack).onActivityRemovedFromStackInvocationCount();
- }
-
- return -1;
- }
-
-
@Test
public void testCanBeLaunchedOnDisplay() throws Exception {
testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
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;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private static final int TEST_STACK_ID = 100;
private static final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
private static final ComponentName testOverlayComponent =
assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay);
assertNotNull(result.r);
}
+
+ @Test
+ public void testShouldBeVisible_Fullscreen() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+
+ final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
+ // should be visible since it is always on-top.
+ fullscreenStack.setIsTranslucent(false);
+ assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+ assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
+
+ // Home stack should be visible behind a translucent fullscreen stack.
+ fullscreenStack.setIsTranslucent(true);
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+ }
+
+ @Test
+ public void testShouldBeVisible_SplitScreen() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // Home stack shouldn't be visible if both halves of split-screen are opaque.
+ splitScreenPrimary.setIsTranslucent(false);
+ splitScreenSecondary.setIsTranslucent(false);
+ assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+ // Home stack should be visible if one of the halves of split-screen is translucent.
+ splitScreenPrimary.setIsTranslucent(true);
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+ final TestActivityStack splitScreenSecondary2 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // First split-screen secondary shouldn't be visible behind another opaque split-split
+ // secondary.
+ splitScreenSecondary2.setIsTranslucent(false);
+ assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+ // First split-screen secondary should be visible behind another translucent split-split
+ // secondary.
+ splitScreenSecondary2.setIsTranslucent(true);
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+ final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+
+ // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
+ assistantStack.setIsTranslucent(false);
+ assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+ // Split-screen stacks should be visible behind a translucent fullscreen stack.
+ assistantStack.setIsTranslucent(true);
+ assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+ }
+
+ @Test
+ public void testShouldBeVisible_Finishing() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ translucentStack.setIsTranslucent(true);
+
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+
+ final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+ topRunningHomeActivity.finishing = true;
+ final ActivityRecord topRunningTranslucentActivity =
+ translucentStack.topRunningActivityLocked();
+ topRunningTranslucentActivity.finishing = true;
+
+ // Home shouldn't be visible since its activity is marked as finishing and it isn't the top
+ // of the stack list.
+ assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ // Home should be visible if we are starting an activity within it.
+ assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
+ // The translucent stack should be visible since it is the top of the stack list even though
+ // it has its activity marked as finishing.
+ assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+ }
+
+ private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
+ ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
+ final T stack = display.createStack(windowingMode, activityType, onTop);
+ // Create a task and activity in the stack so that it has a top running activity.
+ final TaskRecord task = createTask(mSupervisor, testActivityComponent, stack);
+ final ActivityRecord r = createActivity(mService, testActivityComponent, task, 0);
+ return stack;
+ }
}
return service;
}
- protected interface ActivityStackReporter {
- int onActivityRemovedFromStackInvocationCount();
- }
-
/**
* Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
protected static class TestActivityStack<T extends StackWindowController>
- extends ActivityStack<T> implements ActivityStackReporter {
+ extends ActivityStack<T> {
private int mOnActivityRemovedFromStackCount = 0;
private T mContainerController;
+ static final int IS_TRANSLUCENT_UNSET = 0;
+ static final int IS_TRANSLUCENT_FALSE = 1;
+ static final int IS_TRANSLUCENT_TRUE = 2;
+ private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
+
TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
super(display, stackId, supervisor, windowingMode, activityType, onTop);
}
// Returns the number of times {@link #onActivityRemovedFromStack} has been called
- @Override
- public int onActivityRemovedFromStackInvocationCount() {
+ int onActivityRemovedFromStackInvocationCount() {
return mOnActivityRemovedFromStackCount;
}
T getWindowContainerController() {
return mContainerController;
}
+
+ void setIsTranslucent(boolean isTranslucent) {
+ mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
+ }
+
+ @Override
+ boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
+ switch (mIsTranslucent) {
+ case IS_TRANSLUCENT_TRUE:
+ return true;
+ case IS_TRANSLUCENT_FALSE:
+ return false;
+ case IS_TRANSLUCENT_UNSET:
+ default:
+ return super.isStackTranslucent(starting, stackBehind);
+ }
+ }
}
}