Merge "Letterbox positioning (1/4): Consolidate activity and task letterboxing" into sc-dev

This commit is contained in:
Mariia Sandrikova
2021-02-26 17:25:37 +00:00
committed by Android (Google) Code Review
8 changed files with 373 additions and 367 deletions

View File

@@ -40,8 +40,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.app.servertransaction.TransferSplashScreenViewStateItem.ATTACH_TO;
import static android.app.servertransaction.TransferSplashScreenViewStateItem.HANDOVER_TO;
import static android.content.Intent.ACTION_MAIN;
@@ -209,6 +212,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
@@ -557,7 +561,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* The precomputed display insets for resolving configuration. It will be non-null if
* {@link #shouldUseSizeCompatMode} returns {@code true}.
* {@link #shouldCreateCompatDisplayInsets} returns {@code true}.
*/
private CompatDisplayInsets mCompatDisplayInsets;
@@ -648,6 +652,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private Rect mSizeCompatBounds;
// Whether this activity is in size compatibility mode because its bounds don't fit in parent
// naturally.
private boolean mInSizeCompatModeForBounds = false;
// Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
// orientation then aspect ratio restrictions are also already respected.
// This happens when an activity has fixed orientation which doesn't match orientation of the
// parent because a display is ignoring orientation request or fixed to user rotation.
// See WindowManagerService#getIgnoreOrientationRequest and
// WindowManagerService#getFixedToUserRotation for more context.
private boolean mIsLetterboxedForFixedOrientationAndAspectRatio = false;
// activity is not displayed?
// TODO: rename to mNoDisplay
@VisibleForTesting
@@ -1296,9 +1312,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/36505427): Maybe this call should be moved inside
// updateOverrideConfiguration()
newTask.updateOverrideConfigurationFromLaunchBounds();
// Make sure override configuration is up-to-date before using to create window
// controller.
updateSizeCompatMode();
// When an activity is started directly into a split-screen fullscreen root task, we
// need to update the initial multi-window modes so that the callbacks are scheduled
// correctly when the user leaves that mode.
@@ -6718,12 +6731,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (onDescendantOrientationChanged(this)) {
// The app is just becoming visible, and the parent Task has updated with the
// orientation request. Update the size compat mode.
updateSizeCompatMode();
// WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure
// that WM Shell is called when an activity becomes visible. Without this, WM Core
// will handle positioning instead of WM Shell when an app is reopened.
// WM Shell can show additional UI elements, e.g. a restart button for size compat mode
// so ensure that WM Shell is called when an activity becomes visible.
task.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
}
}
@@ -6788,7 +6797,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* density than its parent or its bounds don't fit in parent naturally.
*/
boolean inSizeCompatMode() {
if (mCompatDisplayInsets == null || !shouldUseSizeCompatMode()
if (mInSizeCompatModeForBounds) {
return true;
}
if (mCompatDisplayInsets == null || !shouldCreateCompatDisplayInsets()
// The orientation is different from parent when transforming.
|| isFixedRotationTransforming()) {
return false;
@@ -6798,70 +6810,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The app bounds hasn't been computed yet.
return false;
}
final Configuration parentConfig = getParent().getConfiguration();
// Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
// fields should be changed with density and bounds, so here only compares the most
// significant field.
if (parentConfig.densityDpi != getConfiguration().densityDpi) {
return true;
}
final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
final int appWidth = appBounds.width();
final int appHeight = appBounds.height();
final int parentAppWidth = parentAppBounds.width();
final int parentAppHeight = parentAppBounds.height();
if (parentAppWidth == appWidth && parentAppHeight == appHeight) {
// Matched the parent bounds.
return false;
}
if (parentAppWidth > appWidth && parentAppHeight > appHeight) {
// Both sides are smaller than the parent.
return true;
}
if (parentAppWidth < appWidth || parentAppHeight < appHeight) {
// One side is larger than the parent.
return true;
}
// The rest of the condition is that only one side is smaller than the parent, but it still
// needs to exclude the cases where the size is limited by the fixed aspect ratio.
if (info.maxAspectRatio > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
if (aspectRatio >= info.maxAspectRatio) {
// The current size has reached the max aspect ratio.
return false;
}
}
if (info.minAspectRatio > 0) {
// The activity should have at least the min aspect ratio, so this checks if the parent
// still has available space to provide larger aspect ratio.
final float parentAspectRatio = (0.5f + Math.max(parentAppWidth, parentAppHeight))
/ Math.min(parentAppWidth, parentAppHeight);
if (parentAspectRatio <= info.minAspectRatio) {
// The long side has reached the parent.
return false;
}
}
return true;
return parentConfig.densityDpi != getConfiguration().densityDpi;
}
/**
* Indicates the activity will keep the bounds and screen configuration when it was first
* launched, no matter how its parent changes.
*
* <p>If {@true}, then {@link CompatDisplayInsets} will be created in {@link
* #resolveOverrideConfiguration} to "freeze" activity bounds and insets.
*
* @return {@code true} if this activity is declared as non-resizable and fixed orientation or
* aspect ratio.
*/
boolean shouldUseSizeCompatMode() {
boolean shouldCreateCompatDisplayInsets() {
if (info.supportsSizeChanges() != ActivityInfo.SIZE_CHANGES_UNSUPPORTED) {
return false;
}
if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
final ActivityRecord root = task != null ? task.getRootActivity() : null;
if (root != null && root != this && !root.shouldUseSizeCompatMode()) {
if (root != null && root != this && !root.shouldCreateCompatDisplayInsets()) {
// If the root activity doesn't use size compatibility mode, the activities above
// are forced to be the same for consistent visual appearance.
return false;
@@ -6883,25 +6855,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void updateSizeCompatMode() {
if (mCompatDisplayInsets != null || !shouldUseSizeCompatMode()) {
private void updateCompatDisplayInsets(@Nullable Rect fixedOrientationBounds) {
if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
// The override configuration is set only once in size compatibility mode.
return;
}
final Configuration parentConfig = getParent().getConfiguration();
if (!hasProcess() && !isConfigurationCompatible(parentConfig)) {
// Don't compute when launching in fullscreen and the fixed orientation is not the
// current orientation. It is more accurately to compute the override bounds from
// the updated configuration after the fixed orientation is applied.
return;
}
if (task == null || (!handlesOrientationChangeFromDescendant()
&& task.getLastTaskBoundsComputeActivity() != this)) {
// Don't compute when Task hasn't computed its bounds for this app, because the Task can
// be letterboxed, and its bounds may not be accurate until then.
return;
}
Configuration overrideConfig = getRequestedOverrideConfiguration();
final Configuration fullConfig = getConfiguration();
@@ -6924,17 +6882,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// The role of CompatDisplayInsets is like the override bounds.
mCompatDisplayInsets = new CompatDisplayInsets(mDisplayContent, this);
mCompatDisplayInsets =
new CompatDisplayInsets(mDisplayContent, this, fixedOrientationBounds);
}
@VisibleForTesting
void clearSizeCompatMode() {
mInSizeCompatModeForBounds = false;
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
// Recompute from Task because letterbox can also happen on Task level.
task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration());
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
}
@Override
@@ -6969,23 +6928,41 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mTmpConfig.updateFrom(resolvedConfig);
newParentConfiguration = mTmpConfig;
}
final int windowingMode = getWindowingMode();
// TODO(b/181207944): Consider removing the if condition and always run
// resolveFixedOrientationConfiguration() since this should be applied for all cases.
if (isSplitScreenWindowingMode(windowingMode)
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW
|| windowingMode == WINDOWING_MODE_FULLSCREEN) {
resolveFixedOrientationConfiguration(newParentConfiguration);
}
final Rect fixedOrientationBounds = isLetterboxedForFixedOrientationAndAspectRatio()
? new Rect(resolvedConfig.windowConfiguration.getBounds()) : null;
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
} else {
if (inMultiWindowMode()) {
// We ignore activities' requested orientation in multi-window modes. Task level may
// take them into consideration when calculating bounds.
resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
// If the activity has requested override bounds, the configuration needs to be
// computed accordingly.
if (!matchParentBounds()) {
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
} else {
resolveFullscreenConfiguration(newParentConfiguration);
} else if (inMultiWindowMode()) {
// We ignore activities' requested orientation in multi-window modes. They may be
// taken into consideration in resolveFixedOrientationConfiguration call above.
resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
// If the activity has requested override bounds, the configuration needs to be
// computed accordingly.
if (!matchParentBounds()) {
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
} else if (!isLetterboxedForFixedOrientationAndAspectRatio()) {
resolveFullscreenConfiguration(newParentConfiguration);
}
if (mVisibleRequested) {
updateCompatDisplayInsets(fixedOrientationBounds);
}
// TODO(b/175212232): Consolidate position logic from each "resolve" method above here.
// Assign configuration sequence number into hierarchy because there is a different way than
// ensureActivityConfiguration() in this class that uses configuration in WindowState during
// layout traversals.
@@ -6993,6 +6970,109 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
}
/**
* Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
* orientation then aspect ratio restrictions are also already respected.
*
* <p>This happens when an activity has fixed orientation which doesn't match orientation of the
* parent because a display setting 'ignoreOrientationRequest' is set to true. See {@link
* WindowManagerService#getIgnoreOrientationRequest} for more context.
*/
boolean isLetterboxedForFixedOrientationAndAspectRatio() {
return mIsLetterboxedForFixedOrientationAndAspectRatio;
}
/**
* Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
* change and the requested orientation is different from the parent.
*
* <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
* in this methiod.
*/
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
mIsLetterboxedForFixedOrientationAndAspectRatio = false;
if (handlesOrientationChangeFromDescendant()) {
// No need to letterbox because of fixed orientation. Display will handle
// fixed-orientation requests.
return;
}
final Rect resolvedBounds =
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
final int parentOrientation = newParentConfig.orientation;
// If the activity requires a different orientation (either by override or activityInfo),
// make it fit the available bounds by scaling down its bounds.
final int forcedOrientation = getRequestedConfigurationOrientation();
if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
return;
}
if (mCompatDisplayInsets != null && !mCompatDisplayInsets.mIsInFixedOrientationLetterbox) {
// App prefers to keep its original size.
// If the size compat is from previous fixed orientation letterboxing, we may want to
// have fixed orientation letterbox again, otherwise it will show the size compat
// restart button even if the restart bounds will be the same.
return;
}
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final int parentWidth = parentBounds.width();
final int parentHeight = parentBounds.height();
float aspect = Math.max(parentWidth, parentHeight)
/ (float) Math.min(parentWidth, parentHeight);
// Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
// order to use the extra available space.
final float maxAspectRatio = info.maxAspectRatio;
final float minAspectRatio = info.minAspectRatio;
if (aspect > maxAspectRatio && maxAspectRatio != 0) {
aspect = maxAspectRatio;
} else if (aspect < minAspectRatio) {
aspect = minAspectRatio;
}
// Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio.
// TODO(b/175212232): Rename getTaskLetterboxAspectRatio and all related methods since fixed
// orientation letterbox is on the activity level now.
final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio();
// Activity min/max aspect ratio restrictions will be respected by the activity-level
// letterboxing (size-compat mode). Therefore this override can control the maximum screen
// area that can be occupied by the app in the letterbox mode.
aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : aspect;
// Store the current bounds to be able to revert to size compat mode values below if needed.
Rect mTmpFullBounds = new Rect(resolvedBounds);
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
final int height = (int) Math.rint(parentWidth / aspect);
final int top = parentBounds.centerY() - height / 2;
resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height);
} else {
final int width = (int) Math.rint(parentHeight / aspect);
final int left = parentBounds.centerX() - width / 2;
resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
}
if (mCompatDisplayInsets != null) {
mCompatDisplayInsets.getBoundsByRotation(
mTmpBounds, newParentConfig.windowConfiguration.getRotation());
if (resolvedBounds.width() != mTmpBounds.width()
|| resolvedBounds.height() != mTmpBounds.height()) {
// The app shouldn't be resized, we only do fixed orientation letterboxing if the
// compat bounds are also from the same fixed orientation letterbox. Otherwise,
// clear the fixed orientation bounds to show app in size compat mode.
resolvedBounds.set(mTmpFullBounds);
return;
}
}
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
mIsLetterboxedForFixedOrientationAndAspectRatio = true;
}
/**
* Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by
* aspect ratio, the position will be centered horizontally in parent's app bounds to balance
@@ -7037,6 +7117,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
// When an activity needs to be letterboxed because of fixed orientation, use fixed
// orientation bounds (stored in resolved bounds) instead of parent bounds since the
// activity will be displayed within them even if it is in size compat mode. They should be
// saved here before resolved bounds are overridden below.
final Rect containerBounds = isLetterboxedForFixedOrientationAndAspectRatio()
? new Rect(resolvedBounds)
: newParentConfiguration.windowConfiguration.getBounds();
final Rect containerAppBounds = isLetterboxedForFixedOrientationAndAspectRatio()
? new Rect(getResolvedOverrideConfiguration().windowConfiguration.getAppBounds())
: newParentConfiguration.windowConfiguration.getAppBounds();
final int requestedOrientation = getRequestedConfigurationOrientation();
final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
final int orientation = orientationRequested
@@ -7095,7 +7187,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Below figure is an example that puts an activity which was launched in a larger container
// into a smaller container.
// The outermost rectangle is the real display bounds.
// "@" is the parent app bounds.
// "@" is the container app bounds (parent bounds or fixed orientation bouds)
// "#" is the {@code resolvedBounds} that applies to application.
// "*" is the {@code mSizeCompatBounds} that used to show on screen if scaled.
// ------------------------------
@@ -7111,19 +7203,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The application is still layouted in "#" since it was launched, and it will be visually
// scaled and positioned to "*".
final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
// Calculates the scale and offset to horizontal center the size compatibility bounds into
// the region which is available to application.
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
final int contentW = resolvedAppBounds.width();
final int contentH = resolvedAppBounds.height();
final int viewportW = parentAppBounds.width();
final int viewportH = parentAppBounds.height();
final int viewportW = containerAppBounds.width();
final int viewportH = containerAppBounds.height();
// Only allow to scale down.
mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
final int screenTopInset = parentAppBounds.top - parentBounds.top;
final int screenTopInset = containerAppBounds.top - containerBounds.top;
final boolean topNotAligned = screenTopInset != resolvedAppBounds.top - resolvedBounds.top;
if (mSizeCompatScale != 1f || topNotAligned) {
if (mSizeCompatBounds == null) {
@@ -7143,8 +7234,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int offsetX = getHorizontalCenterOffset(
(int) viewportW, (int) (contentW * mSizeCompatScale));
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final int screenPosX = (fillContainer ? parentBounds.left : parentAppBounds.left) + offsetX;
final int screenPosY = parentBounds.top;
final int screenPosX = (fillContainer
? containerBounds.left : containerAppBounds.left) + offsetX;
final int screenPosY = containerBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
mSizeCompatBounds.offset(screenPosX, screenPosY);
@@ -7154,6 +7246,52 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int dy = screenPosY - resolvedBounds.top;
offsetBounds(resolvedConfig, dx, dy);
}
mInSizeCompatModeForBounds =
isInSizeCompatModeForBounds(resolvedAppBounds, containerAppBounds);
}
private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
final int appWidth = appBounds.width();
final int appHeight = appBounds.height();
final int containerAppWidth = containerBounds.width();
final int containerAppHeight = containerBounds.height();
if (containerAppWidth == appWidth && containerAppHeight == appHeight) {
// Matched the container bounds.
return false;
}
if (containerAppWidth > appWidth && containerAppHeight > appHeight) {
// Both sides are smaller than the container.
return true;
}
if (containerAppWidth < appWidth || containerAppHeight < appHeight) {
// One side is larger than the container.
return true;
}
// The rest of the condition is that only one side is smaller than the container, but it
// still needs to exclude the cases where the size is limited by the fixed aspect ratio.
if (info.maxAspectRatio > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
if (aspectRatio >= info.maxAspectRatio) {
// The current size has reached the max aspect ratio.
return false;
}
}
if (info.minAspectRatio > 0) {
// The activity should have at least the min aspect ratio, so this checks if the
// container still has available space to provide larger aspect ratio.
final float containerAspectRatio =
(0.5f + Math.max(containerAppWidth, containerAppHeight))
/ Math.min(containerAppWidth, containerAppHeight);
if (containerAspectRatio <= info.minAspectRatio) {
// The long side has reached the parent.
return false;
}
}
return true;
}
/** @return The horizontal offset of putting the content in the center of viewport. */
@@ -7305,7 +7443,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Task rootTask = getRootTask();
final float minAspectRatio = info.minAspectRatio;
if (task == null || rootTask == null || (inMultiWindowMode() && !shouldUseSizeCompatMode())
if (task == null || rootTask == null
|| (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
|| (maxAspectRatio == 0 && minAspectRatio == 0)
|| isInVrUiMode(getConfiguration())) {
// We don't enforce aspect ratio if the activity task is in multiwindow unless it
@@ -7443,8 +7582,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (displayChanged) {
mLastReportedDisplayId = newDisplayId;
}
// TODO(b/36505427): Is there a better place to do this?
updateSizeCompatMode();
// Short circuit: if the two full configurations are equal (the common case), then there is
// nothing to do. We test the full configuration instead of the global and merged override
@@ -7723,11 +7860,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Reset the existing override configuration so it can be updated according to the latest
// configuration.
clearSizeCompatMode();
if (mVisibleRequested) {
// Configuration will be ensured when becoming visible, so if it is already visible,
// then the manual update is needed.
updateSizeCompatMode();
}
if (!attachedToProcess()) {
return;
@@ -8134,8 +8266,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private final int mHeight;
/** Whether the {@link Task} windowingMode represents a floating window*/
final boolean mIsFloating;
/** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */
final boolean mIsTaskLetterboxed;
/**
* Whether is letterboxed because of fixed orientation when the unresizable activity is
* first shown.
*/
final boolean mIsInFixedOrientationLetterbox;
/**
* The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
* is used to compute the appBounds.
@@ -8149,7 +8284,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect[] mStableInsets = new Rect[4];
/** Constructs the environment to simulate the bounds behavior of the given container. */
CompatDisplayInsets(DisplayContent display, ActivityRecord container) {
CompatDisplayInsets(DisplayContent display, ActivityRecord container,
@Nullable Rect fixedOrientationBounds) {
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
if (mIsFloating) {
final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -8162,24 +8298,34 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mNonDecorInsets[rotation] = emptyRect;
mStableInsets[rotation] = emptyRect;
}
mIsTaskLetterboxed = false;
mIsInFixedOrientationLetterbox = false;
return;
}
final Task task = container.getTask();
mIsTaskLetterboxed = task != null && task.isTaskLetterboxed();
mIsInFixedOrientationLetterbox = fixedOrientationBounds != null;
// Store the bounds of the Task for the non-resizable activity to use in size compat
// mode so that the activity will not be resized regardless the windowing mode it is
// currently in.
final WindowContainer filledContainer = task != null ? task : display;
final Point dimensions = getRotationZeroDimensions(filledContainer);
// When an activity needs to be letterboxed because of fixed orientation, use fixed
// orientation bounds instead of task bounds since the activity will be displayed
// within these even if it is in size compat mode.
final Rect filledContainerBounds = mIsInFixedOrientationLetterbox
? fixedOrientationBounds
: task != null ? task.getBounds() : display.getBounds();
final int filledContainerRotation = task != null
? task.getConfiguration().windowConfiguration.getRotation()
: display.getConfiguration().windowConfiguration.getRotation();
final Point dimensions = getRotationZeroDimensions(
filledContainerBounds, filledContainerRotation);
mWidth = dimensions.x;
mHeight = dimensions.y;
// Bounds of the filled container if it doesn't fill the display.
final Rect unfilledContainerBounds =
filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect();
filledContainerBounds.equals(display.getBounds()) ? null : new Rect();
final DisplayPolicy policy = display.getDisplayPolicy();
for (int rotation = 0; rotation < 4; rotation++) {
mNonDecorInsets[rotation] = new Rect();
@@ -8199,9 +8345,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The insets is based on the display, but the container may be smaller than the
// display, so update the insets to exclude parts that are not intersected with the
// container.
unfilledContainerBounds.set(filledContainer.getBounds());
unfilledContainerBounds.set(filledContainerBounds);
display.rotateBounds(
filledContainer.getConfiguration().windowConfiguration.getRotation(),
filledContainerRotation,
rotation,
unfilledContainerBounds);
updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
@@ -8214,9 +8360,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* the display is rotated, we can calculate the bounds by rotating the dimensions.
* @see #getBoundsByRotation
*/
private static Point getRotationZeroDimensions(WindowContainer container) {
final Rect bounds = container.getBounds();
final int rotation = container.getConfiguration().windowConfiguration.getRotation();
private static Point getRotationZeroDimensions(final Rect bounds, int rotation) {
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int width = bounds.width();
final int height = bounds.height();

View File

@@ -35,7 +35,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -148,7 +147,6 @@ import static com.android.server.wm.WindowContainerChildProto.TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -597,10 +595,6 @@ class Task extends WindowContainer<WindowContainer> {
@Nullable
private ActivityRecord mResumedActivity = null;
/** Last activity that is used to compute the Task bounds. */
@Nullable
private ActivityRecord mLastTaskBoundsComputeActivity;
private boolean mForceShowForAllUsers;
/** When set, will force the task to report as invisible. */
@@ -1496,11 +1490,6 @@ class Task extends WindowContainer<WindowContainer> {
}
void cleanUpActivityReferences(ActivityRecord r) {
// mLastTaskBoundsComputeActivity is set at leaf Task
if (mLastTaskBoundsComputeActivity == r) {
mLastTaskBoundsComputeActivity = null;
}
// mPausingActivity is set at leaf task
if (mPausingActivity != null && mPausingActivity == r) {
mPausingActivity = null;
@@ -2863,7 +2852,6 @@ class Task extends WindowContainer<WindowContainer> {
private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
Rect previousBounds) {
mLastTaskBoundsComputeActivity = getTopNonFinishingActivity(false /* includeOverlays */);
int windowingMode =
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
@@ -2877,7 +2865,8 @@ class Task extends WindowContainer<WindowContainer> {
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
computeFullscreenBounds(outOverrideBounds, newParentConfig);
// Use empty bounds to indicate "fill parent".
outOverrideBounds.setEmpty();
// The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
// the parent or display is smaller than the size, the content may be cropped.
return;
@@ -2888,21 +2877,6 @@ class Task extends WindowContainer<WindowContainer> {
computeFreeformBounds(outOverrideBounds, newParentConfig);
return;
}
if (isSplitScreenWindowingMode(windowingMode)
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
// This is to compute whether the task should be letterboxed to handle non-resizable app
// in multi window. There is no split screen only logic.
computeLetterboxBounds(outOverrideBounds, newParentConfig);
}
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */
@VisibleForTesting
void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
@@ -2934,94 +2908,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
/**
* Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
* change and the requested orientation is different from the parent.
*/
private void computeLetterboxBounds(@NonNull Rect outBounds,
@NonNull Configuration newParentConfig) {
if (handlesOrientationChangeFromDescendant()) {
// No need to letterbox at task level. Display will handle fixed-orientation requests.
return;
}
final int parentOrientation = newParentConfig.orientation;
// Use the top activity as the reference of orientation. Don't include overlays because
// it is usually not the actual content or just temporarily shown.
// E.g. ForcedResizableInfoActivity.
final ActivityRecord refActivity = getTopNonFinishingActivity(false /* includeOverlays */);
// If the task or the reference activity requires a different orientation (either by
// override or activityInfo), make it fit the available bounds by scaling down its bounds.
final int overrideOrientation = getRequestedOverrideConfiguration().orientation;
final int forcedOrientation =
(overrideOrientation != ORIENTATION_UNDEFINED || refActivity == null)
? overrideOrientation : refActivity.getRequestedConfigurationOrientation();
if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
return;
}
final ActivityRecord.CompatDisplayInsets compatDisplayInsets =
refActivity == null ? null : refActivity.getCompatDisplayInsets();
if (compatDisplayInsets != null && !compatDisplayInsets.mIsTaskLetterboxed) {
// App prefers to keep its original size.
// If the size compat is from previous task letterboxing, we may want to have task
// letterbox again, otherwise it will show the size compat restart button even if the
// restart bounds will be the same.
return;
}
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final int parentWidth = parentBounds.width();
final int parentHeight = parentBounds.height();
float aspect = Math.max(parentWidth, parentHeight)
/ (float) Math.min(parentWidth, parentHeight);
// Adjust the Task letterbox bounds to fit the app request aspect ratio in order to use the
// extra available space.
if (refActivity != null) {
final float maxAspectRatio = refActivity.info.maxAspectRatio;
final float minAspectRatio = refActivity.info.minAspectRatio;
if (aspect > maxAspectRatio && maxAspectRatio != 0) {
aspect = maxAspectRatio;
} else if (aspect < minAspectRatio) {
aspect = minAspectRatio;
}
}
// Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio.
final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio();
// Activity min/max aspect ratio restrictions will be respected by the activity-level
// letterboxing (size-compat mode). Therefore this override can control the maximum screen
// area that can be occupied by the app in the letterbox mode.
aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : aspect;
// Store the current bounds to be able to revert to size compat mode values below if needed.
mTmpFullBounds.set(outBounds);
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
final int height = (int) Math.rint(parentWidth / aspect);
final int top = parentBounds.centerY() - height / 2;
outBounds.set(parentBounds.left, top, parentBounds.right, top + height);
} else {
final int width = (int) Math.rint(parentHeight / aspect);
final int left = parentBounds.centerX() - width / 2;
outBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
}
if (compatDisplayInsets != null) {
compatDisplayInsets.getBoundsByRotation(
mTmpBounds, newParentConfig.windowConfiguration.getRotation());
if (outBounds.width() != mTmpBounds.width()
|| outBounds.height() != mTmpBounds.height()) {
// The app shouldn't be resized, we only do task letterboxing if the compat bounds
// is also from the same task letterbox. Otherwise, clear the task bounds to show
// app in size compat mode.
outBounds.set(mTmpFullBounds);
}
}
}
Rect updateOverrideConfigurationFromLaunchBounds() {
// If the task is controlled by another organized task, do not set override
// configurations and let its parent (organized task) to control it;
@@ -3036,11 +2922,6 @@ class Task extends WindowContainer<WindowContainer> {
return bounds;
}
@Nullable
ActivityRecord getLastTaskBoundsComputeActivity() {
return mLastTaskBoundsComputeActivity;
}
/** Updates the task's bounds and override configuration to match what is expected for the
* input root task. */
void updateOverrideConfigurationForRootTask(Task inRootTask) {
@@ -3939,12 +3820,6 @@ class Task extends WindowContainer<WindowContainer> {
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
boolean isTaskLetterboxed() {
// No letterbox for multi window root task
return !matchParentBounds()
&& (getWindowingMode() == WINDOWING_MODE_FULLSCREEN || !isRootTask());
}
@Override
boolean fillsParent() {
// From the perspective of policy, we still want to report that this task fills parent

View File

@@ -3827,13 +3827,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** @return true when the window should be letterboxed. */
boolean isLetterboxedAppWindow() {
// Fullscreen mode but doesn't fill display area.
return (!inMultiWindowMode() && !matchesDisplayAreaBounds())
// Activity in size compat.
|| (mActivityRecord != null && mActivityRecord.inSizeCompatMode())
// Task letterboxed.
|| (getTask() != null && getTask().isTaskLetterboxed())
// Letterboxed for display cutout.
|| isLetterboxedForDisplayCutout();
if (!inMultiWindowMode() && !matchesDisplayAreaBounds()) {
return true;
}
if (mActivityRecord != null) {
// Activity in size compat.
if (mActivityRecord.inSizeCompatMode()) {
return true;
}
// Letterbox for fixed orientation.
if (mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio()) {
return true;
}
}
// Letterboxed for display cutout.
return isLetterboxedForDisplayCutout();
}
/** Returns {@code true} if the window is letterboxed for the display cutout. */

View File

@@ -555,9 +555,10 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.setRequestedOrientation(
isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE);
// Asserts it has orientation derived from bounds.
assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT,
// Asserts it has orientation derived requested orientation (fixed orientation letterbox).
assertEquals(isScreenPortrait ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE,
activity.getConfiguration().orientation);
assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio());
}
@Test

View File

@@ -171,7 +171,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
final Rect activityBounds = new Rect(mFirstActivity.getBounds());
// DAG is portrait (860x1200), so Task and Activity fill DAG.
assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
assertThat(taskBounds).isEqualTo(dagBounds);
assertThat(activityBounds).isEqualTo(taskBounds);
@@ -194,8 +194,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
final Rect activityConfigBounds =
new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds());
// DAG is landscape (1200x860), Task fills parent
assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
// DAG is landscape (1200x860), no fixed orientation letterbox
assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
assertThat(newDagBounds.height()).isEqualTo(dagBounds.width());
@@ -211,7 +211,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
public void testLaunchLandscapeApp_taskIsLetterboxInDisplayAreaGroup() {
public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() {
mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
@@ -221,17 +221,18 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
final Rect taskBounds = new Rect(mFirstTask.getBounds());
final Rect activityBounds = new Rect(mFirstActivity.getBounds());
// DAG is portrait (860x1200), so Task is letterbox (860x[860x860/1200=616])
assertThat(mFirstTask.isTaskLetterboxed()).isTrue();
// DAG is portrait (860x1200), and activity is letterboxed for fixed orientation
// (860x[860x860/1200=616]). Task fills DAG.
assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
assertThat(taskBounds.width()).isEqualTo(dagBounds.width());
assertThat(taskBounds.height())
assertThat(taskBounds).isEqualTo(dagBounds);
assertThat(activityBounds.width()).isEqualTo(dagBounds.width());
assertThat(activityBounds.height())
.isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height());
assertThat(activityBounds).isEqualTo(taskBounds);
}
@Test
public void testLaunchLandscapeApp_taskLetterboxBecomesActivityLetterboxAfterRotation() {
public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() {
mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
@@ -245,9 +246,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
final Rect newTaskBounds = new Rect(mFirstTask.getBounds());
final Rect newActivityBounds = new Rect(mFirstActivity.getBounds());
// DAG is landscape (1200x860), Task fills parent
// Task letterbox size
assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
// DAG is landscape (1200x860), no fixed orientation letterbox
assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
assertThat(newDagBounds.height()).isEqualTo(dagBounds.width());
@@ -311,7 +311,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
public void testResizableFixedOrientationApp_taskLevelLetterboxing() {
public void testResizableFixedOrientationApp_fixedOrientationLetterboxing() {
mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
@@ -324,7 +324,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
// Launch portrait on second DAG
@@ -336,13 +336,13 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
assertThat(mSecondTask.isTaskLetterboxed()).isFalse();
assertThat(mSecondActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
assertThat(mSecondActivity.inSizeCompatMode()).isFalse();
// First activity is letterboxed in portrait as requested.
assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
assertThat(mFirstTask.isTaskLetterboxed()).isTrue();
assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
}

View File

@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -43,7 +44,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.same;
@@ -126,6 +126,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Put app window into freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertEquals(bounds, mActivity.getBounds());
@@ -194,12 +195,7 @@ public class SizeCompatTests extends WindowTestsBase {
new TestDisplayContent.Builder(mAtm, 1000, 2000)
.setDensityDpi(200).build();
mActivity = new ActivityBuilder(mAtm)
.setTask(mTask)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setMaxAspectRatio(1.5f)
.build();
mActivity.mVisibleRequested = true;
prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalBounds = new Rect(mActivity.getBounds());
final int originalDpi = mActivity.getConfiguration().densityDpi;
@@ -566,18 +562,18 @@ public class SizeCompatTests extends WindowTestsBase {
.setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.build();
assertTrue(activity.shouldUseSizeCompatMode());
assertTrue(activity.shouldCreateCompatDisplayInsets());
// The non-resizable activity should not be size compat because it is on a resizable task
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
assertFalse(activity.shouldCreateCompatDisplayInsets());
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
assertFalse(activity.shouldCreateCompatDisplayInsets());
}
@Test
@@ -597,7 +593,7 @@ public class SizeCompatTests extends WindowTestsBase {
SizeCompatTests.class.getName()))
.setUid(android.os.Process.myUid())
.build();
assertFalse(activity.shouldUseSizeCompatMode());
assertFalse(activity.shouldCreateCompatDisplayInsets());
}
@Test
@@ -653,7 +649,7 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedInTaskLetterbox() {
public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedLetterbox() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -662,7 +658,6 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect activityBounds = new Rect(mActivity.getBounds());
// Display shouldn't be rotated.
@@ -670,19 +665,19 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mDisplayContent.getLastOrientation());
assertTrue(displayBounds.width() > displayBounds.height());
// App should launch in task level letterboxing.
assertTrue(mTask.isTaskLetterboxed());
// App should launch in fixed orientation letterbox.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
// Activity bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), activityBounds.height());
assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
taskBounds.width());
activityBounds.width());
}
@Test
public void testDisplayIgnoreOrientationRequest_taskLetterboxBecameSizeCompatAfterRotate() {
public void
testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -700,7 +695,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() < displayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
@@ -719,7 +714,7 @@ public class SizeCompatTests extends WindowTestsBase {
Rect activityBounds = new Rect(mActivity.getBounds());
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(displayBounds, activityBounds);
@@ -731,7 +726,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(displayBounds.width() > displayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
// App bounds should be 700x1400 with the ratio as the display.
@@ -741,7 +736,7 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInTaskLetterbox() {
public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInLetterbox() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
final DisplayContent display = mActivity.mDisplayContent;
@@ -750,7 +745,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
assertTrue(mTask.isTaskLetterboxed());
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
// Launch another portrait fixed app.
@@ -765,19 +760,19 @@ public class SizeCompatTests extends WindowTestsBase {
// Update with new activity requested orientation and recompute bounds with no previous
// size compat cache.
verify(mTask).onDescendantOrientationChanged(same(newActivity));
verify(mTask).computeFullscreenBounds(any(), any());
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
// Task and display bounds should be equal while activity should be letterboxed and
// has 700x1400 bounds with the ratio as the display.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals(taskBounds, displayBounds);
assertEquals(displayBounds.height(), newActivityBounds.height());
assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
taskBounds.width());
newActivityBounds.width());
}
@Test
@@ -790,7 +785,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
assertTrue(mTask.isTaskLetterboxed());
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
// Launch another portrait fixed app with max aspect ratio as 1.3.
@@ -806,21 +801,20 @@ public class SizeCompatTests extends WindowTestsBase {
// Update with new activity requested orientation and recompute bounds with no previous
// size compat cache.
verify(mTask).onDescendantOrientationChanged(same(newActivity));
verify(mTask).computeFullscreenBounds(any(), any());
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
// Task bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
assertTrue(mTask.isTaskLetterboxed());
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
// Task bounds should fill parent bounds.
assertEquals(displayBounds, taskBounds);
// App bounds should be fullscreen in Task bounds.
// Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), newActivityBounds.height());
assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio),
newActivityBounds.width());
}
@Test
@@ -834,26 +828,23 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
clearInvocations(mActivity);
assertTrue(mTask.isTaskLetterboxed());
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
// Rotate display to portrait.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
// App still in size compat, and the bounds don't change.
verify(mActivity, never()).clearSizeCompatMode();
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
}
@@ -867,22 +858,22 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
}
@@ -898,22 +889,22 @@ public class SizeCompatTests extends WindowTestsBase {
// Landscape fixed app.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertScaled();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
// In fixed orientation letterbox
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(mActivity.inSizeCompatMode());
}
@@ -972,21 +963,21 @@ public class SizeCompatTests extends WindowTestsBase {
addWindowToActivity(mActivity);
mActivity.mRootWindowContainer.performSurfacePlacement();
// Split screen is also in portrait [1000,1400], so Task should be in letterbox, and
// activity fills task.
assertEquals(ORIENTATION_LANDSCAPE, mTask.getConfiguration().orientation);
// Split screen is also in portrait [1000,1400], so activty should be in fixed orientation
// letterbox.
assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
assertFitted();
assertTrue(mTask.isTaskLetterboxed());
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
// Letterbox should fill the gap between the split screen and the letterboxed task.
// Letterbox should fill the gap between the split screen and the letterboxed activity.
final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
final Rect letterboxedTaskBounds = new Rect(mTask.getBounds());
assertTrue(primarySplitBounds.contains(letterboxedTaskBounds));
assertEquals(new Rect(letterboxedTaskBounds.left - primarySplitBounds.left,
letterboxedTaskBounds.top - primarySplitBounds.top,
primarySplitBounds.right - letterboxedTaskBounds.right,
primarySplitBounds.bottom - letterboxedTaskBounds.bottom),
final Rect letterboxedBounds = new Rect(mActivity.getBounds());
assertTrue(primarySplitBounds.contains(letterboxedBounds));
assertEquals(new Rect(letterboxedBounds.left - primarySplitBounds.left,
letterboxedBounds.top - primarySplitBounds.top,
primarySplitBounds.right - letterboxedBounds.right,
primarySplitBounds.bottom - letterboxedBounds.bottom),
mActivity.getLetterboxInsets());
}

View File

@@ -266,8 +266,9 @@ public class TaskRecordTests extends WindowTestsBase {
root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(root, task.getRootActivity());
assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
assertEquals(fullScreenBounds.height(), task.getBounds().height());
// Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
assertEquals(fullScreenBounds, task.getBounds());
// Top activity gets used
final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(stack)
@@ -286,8 +287,11 @@ public class TaskRecordTests extends WindowTestsBase {
// Fix the display orientation to portrait which is 90 degrees for the test display.
dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
// Fixed orientation request should be resolved on activity level. Task fills display
// bounds.
assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
assertEquals(fullScreenBoundsPort, task.getBounds());
// in FREEFORM, no constraint
final Rect freeformBounds = new Rect(display.getBounds());
@@ -297,10 +301,11 @@ public class TaskRecordTests extends WindowTestsBase {
task.setBounds(freeformBounds);
assertEquals(freeformBounds, task.getBounds());
// FULLSCREEN letterboxes bounds
// FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
assertEquals(fullScreenBoundsPort, task.getBounds());
// FREEFORM restores bounds as before
stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -327,9 +332,10 @@ public class TaskRecordTests extends WindowTestsBase {
assertEquals(fullScreenBounds, task.getBounds());
// Setting app to fixed portrait fits within parent
// Setting app to fixed portrait fits within parent on activity level. Task fills parent.
root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
assertEquals(task.getBounds(), fullScreenBounds);
assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
}
@@ -424,7 +430,8 @@ public class TaskRecordTests extends WindowTestsBase {
// to the input bounds.
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
final ActivityRecord.CompatDisplayInsets compatIntsets =
new ActivityRecord.CompatDisplayInsets(display, activity);
new ActivityRecord.CompatDisplayInsets(
display, activity, /* fixedOrientationBounds= */ null);
task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());

View File

@@ -257,24 +257,4 @@ public class TaskTests extends WindowTestsBase {
task.resolveOverrideConfiguration(parentConfig);
assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
}
@Test
public void testCleanUpActivityReferences_clearLastTaskBoundsComputeActivity() {
final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
final Task leafTask = createTaskInStack(rootTask, 0 /* userId */);
final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask);
final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask);
activity1.finishing = false;
leafTask.resolveOverrideConfiguration(rootTask.getConfiguration());
assertEquals(activity1, leafTask.getLastTaskBoundsComputeActivity());
leafTask.cleanUpActivityReferences(activity2);
assertNotNull(leafTask.getLastTaskBoundsComputeActivity());
leafTask.cleanUpActivityReferences(activity1);
assertNull(leafTask.getLastTaskBoundsComputeActivity());
}
}