From: Wale Ogunwale Date: Wed, 18 Oct 2017 22:19:59 +0000 (-0700) Subject: Simplified ActivityStack.shouldBeVisible() X-Git-Tag: android-x86-9.0-r1~347^2~24^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=7e1f5f56f8f3453f62183fd36df843a1a4e2ee12;p=android-x86%2Fframeworks-base.git Simplified ActivityStack.shouldBeVisible() The method can now be simplified since we have a stack per task so if a stack should be visible is now a question of if it is occluded by another stack. Also, - Fixed an issue where the windowing mode of the primary split-screen stack was changing to split-screen-secondary instead of fullscreen when we are exiting split-screen mode because we are not allowed to to create fullscreen stack when there is a primary split-screen stack. We now clear the reference to the primary split-screen stack when exiting split-screen mode. - Re-worked windowing mode resolution to be inside ActivityDisplay object since the determination of the windowing mode is dependant on the display. Test: bit FrameworksServicesTests:com.android.server.am.ActivityStackTests Test: Existing tests pass. Test: go/wm-smoke Bug: 64146578 Fixes: 67914671 Change-Id: I7e8cfe49fbf6a5836ded022bb11adcde58ae689c --- diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 089db871aa2f..29073cb68c04 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -38,7 +38,9 @@ import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CON 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; @@ -206,6 +208,18 @@ class ActivityDisplay extends ConfigurationContainer { } /** + * 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 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 @@ -235,7 +249,7 @@ class ActivityDisplay extends ConfigurationContainer { } 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=" @@ -252,35 +266,29 @@ class ActivityDisplay extends ConfigurationContainer { } } - 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 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 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 @@ -372,6 +380,105 @@ class ActivityDisplay extends ConfigurationContainer { } } + /** + * 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) { @@ -408,6 +515,14 @@ class ActivityDisplay extends ConfigurationContainer { } } + /** 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; } @@ -424,21 +539,6 @@ class ActivityDisplay extends ConfigurationContainer { 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() + "}"; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e1e53b3b0b31..eed1e6bc110e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -31,6 +31,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 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; @@ -10318,7 +10319,8 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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; } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2c72a4db635a..b885eabae57c 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -165,6 +165,7 @@ import android.view.IAppTransitionAnimationSpecsFuture; 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; @@ -232,7 +233,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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? @@ -883,9 +886,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 941c371b717d..f05243b1563a 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -489,22 +489,19 @@ class ActivityStack extends ConfigurationContai 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); @@ -1496,25 +1493,19 @@ class ActivityStack extends ConfigurationContai } } - /** 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 activities = task.mActivities; @@ -1533,7 +1524,7 @@ class ActivityStack extends ConfigurationContai 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; @@ -1572,129 +1563,68 @@ class ActivityStack extends ConfigurationContai 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) { @@ -5263,7 +5193,8 @@ class ActivityStack extends ConfigurationContai 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}"; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c15b5e2e9bbd..7a4a0d4f23b3 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -110,7 +110,6 @@ import android.app.AppOpsManager; 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; @@ -223,13 +222,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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; @@ -405,6 +397,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * 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. @@ -2186,89 +2179,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ -2329,7 +2239,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 @@ -2347,7 +2256,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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; } @@ -2365,10 +2274,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ -2379,7 +2292,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D display = getDefaultDisplay(); } - return display.getOrCreateStack(windowingMode, activityType, onTop); + return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); } /** @@ -2596,6 +2509,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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. @@ -2625,35 +2540,34 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ArrayList 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"); @@ -3080,23 +2994,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D + " 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; } @@ -4287,9 +4194,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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; } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index c4512353db94..4080c27b4ecc 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -231,9 +231,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi 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. */ @@ -1457,7 +1454,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi private boolean isResizeable(boolean checkSupportsPip) { return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode) - || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable; + || (checkSupportsPip && mSupportsPictureInPicture)); } boolean isResizeable() { diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index 679be1d40b60..9e4a9e9fdae9 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -57,7 +57,7 @@ public class ActivityRecordTests extends ActivityTestsBase { ComponentName.unflattenFromString("com.foo/.BarActivity2"); private ActivityManagerService mService; - private ActivityStack mStack; + private TestActivityStack mStack; private TaskRecord mTask; private ActivityRecord mActivity; @@ -76,13 +76,13 @@ public class ActivityRecordTests extends ActivityTestsBase { @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 @@ -97,7 +97,7 @@ public class ActivityRecordTests extends ActivityTestsBase { final TaskRecord newTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack); mActivity.reparent(newTask, 0, null /*reason*/); - assertEquals(getActivityRemovedFromStackCount(), 0); + assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0); } @Test @@ -129,15 +129,6 @@ public class ActivityRecordTests extends ActivityTestsBase { 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*/, 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 4ee1f47d02e0..e17e51bb9da3 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -16,13 +16,19 @@ 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; @@ -45,7 +51,6 @@ import org.junit.Test; @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 = @@ -127,4 +132,122 @@ public class ActivityStackTests extends ActivityTestsBase { 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 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; + } } 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 d36f9d3505b2..f5cdf216f04a 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -276,20 +276,21 @@ public class ActivityTestsBase { 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 - extends ActivityStack implements ActivityStackReporter { + extends ActivityStack { 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); @@ -302,8 +303,7 @@ public class ActivityTestsBase { } // Returns the number of times {@link #onActivityRemovedFromStack} has been called - @Override - public int onActivityRemovedFromStackInvocationCount() { + int onActivityRemovedFromStackInvocationCount() { return mOnActivityRemovedFromStackCount; } @@ -317,5 +317,22 @@ public class ActivityTestsBase { 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); + } + } } }