Loading services/core/java/com/android/server/wm/ActivityRecord.java +105 −85 Original line number Original line Diff line number Diff line Loading @@ -702,8 +702,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mInSizeCompatModeForBounds = false; private boolean mInSizeCompatModeForBounds = false; // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio(). // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio(). // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration // but that isn't reflected in this boolean. private boolean mIsAspectRatioApplied = false; private boolean mIsAspectRatioApplied = false; // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed Loading Loading @@ -7504,11 +7502,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * requested orientation. If not, it may be necessary to letterbox the window. * requested orientation. If not, it may be necessary to letterbox the window. * @param parentBounds are the new parent bounds passed down to the activity and should be used * @param parentBounds are the new parent bounds passed down to the activity and should be used * to compute the stable bounds. * to compute the stable bounds. * @param outBounds will store the stable bounds, which are the bounds with insets applied. * @param outStableBounds will store the stable bounds, which are the bounds with insets * These should be used to compute letterboxed bounds if orientation is not * applied, if orientation is not respected when insets are applied. * respected when insets are applied. * Otherwise outStableBounds will be empty. Stable bounds should be used */ * to compute letterboxed bounds if orientation is not respected when private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) { * insets are applied. */ private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outStableBounds) { outStableBounds.setEmpty(); if (mDisplayContent == null) { if (mDisplayContent == null) { return true; return true; } } Loading @@ -7523,17 +7524,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Compute orientation from stable parent bounds (= parent bounds with insets applied) // Compute orientation from stable parent bounds (= parent bounds with insets applied) final Task task = getTask(); final Task task = getTask(); task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */, task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */, outBounds /* outStableBounds */, parentBounds /* bounds */, outStableBounds /* outStableBounds */, parentBounds /* bounds */, mDisplayContent.getDisplayInfo()); mDisplayContent.getDisplayInfo()); final int orientationWithInsets = outBounds.height() >= outBounds.width() final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width() ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; // If orientation does not match the orientation with insets applied, then a // If orientation does not match the orientation with insets applied, then a // display rotation will not be enough to respect orientation. However, even if they do // display rotation will not be enough to respect orientation. However, even if they do // not match but the orientation with insets applied matches the requested orientation, then // not match but the orientation with insets applied matches the requested orientation, then // there is no need to modify the bounds because when insets are applied, the activity will // there is no need to modify the bounds because when insets are applied, the activity will // have the desired orientation. // have the desired orientation. return orientation == orientationWithInsets final boolean orientationRespectedWithInsets = orientation == orientationWithInsets || orientationWithInsets == requestedOrientation; || orientationWithInsets == requestedOrientation; if (orientationRespectedWithInsets) { outStableBounds.setEmpty(); } return orientationRespectedWithInsets; } } /** /** Loading @@ -7551,9 +7556,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A int windowingMode) { int windowingMode) { mLetterboxBoundsForFixedOrientationAndAspectRatio = null; mLetterboxBoundsForFixedOrientationAndAspectRatio = null; final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect containerBounds = new Rect(parentBounds); final Rect stableBounds = new Rect(); // If orientation is respected when insets are applied, then stableBounds will be empty. boolean orientationRespectedWithInsets = boolean orientationRespectedWithInsets = orientationRespectedWithInsets(parentBounds, containerBounds); orientationRespectedWithInsets(parentBounds, stableBounds); if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { // No need to letterbox because of fixed orientation. Display will handle // No need to letterbox because of fixed orientation. Display will handle // fixed-orientation requests and a display rotation is enough to respect requested // fixed-orientation requests and a display rotation is enough to respect requested Loading Loading @@ -7590,71 +7596,68 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; return; } } // TODO(b/182268157) merge aspect ratio logic here and in // TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app // {@link ActivityRecord#applyAspectRatio} // bounds or stable bounds to unify aspect ratio logic. // if no aspect ratio constraints are provided, parent aspect ratio is used final Rect parentBoundsWithInsets = orientationRespectedWithInsets float aspectRatio = computeAspectRatio(parentBounds); ? newParentConfig.windowConfiguration.getAppBounds() : stableBounds; final Rect containingBounds = new Rect(); // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with final Rect containingBoundsWithInsets = new Rect(); // set-fixed-orientation-letterbox-aspect-ratio. // Need to shrink the containing bounds into a square because the parent orientation final float letterboxAspectRatioOverride = // does not match the activity requested orientation. mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); if (forcedOrientation == ORIENTATION_LANDSCAPE) { aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO // Landscape is defined as width > height. Make the container respect landscape ? letterboxAspectRatioOverride : aspectRatio; // orientation by shrinking height to one less than width. Landscape activity will be // vertically centered within parent bounds with insets, so position vertical bounds // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in // within parent bounds with insets to prevent insets from unnecessarily trimming // order to use the extra available space. // vertical bounds. final float maxAspectRatio = info.getMaxAspectRatio(); final int bottom = Math.min(parentBoundsWithInsets.top + parentBounds.width() - 1, final float minAspectRatio = info.getMinAspectRatio(); parentBoundsWithInsets.bottom); if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) { containingBounds.set(parentBounds.left, parentBoundsWithInsets.top, parentBounds.right, aspectRatio = maxAspectRatio; bottom); } else if (aspectRatio < minAspectRatio) { containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top, aspectRatio = minAspectRatio; parentBoundsWithInsets.right, bottom); } else { // Portrait is defined as width <= height. Make the container respect portrait // orientation by shrinking width to match height. Portrait activity will be // horizontally centered within parent bounds with insets, so position horizontal bounds // within parent bounds with insets to prevent insets from unnecessarily trimming // horizontal bounds. final int right = Math.min(parentBoundsWithInsets.left + parentBounds.height(), parentBoundsWithInsets.right); containingBounds.set(parentBoundsWithInsets.left, parentBounds.top, right, parentBounds.bottom); containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top, right, parentBoundsWithInsets.bottom); } } // Store the current bounds to be able to revert to size compat mode values below if needed. // Store the current bounds to be able to revert to size compat mode values below if needed. final Rect prevResolvedBounds = new Rect(resolvedBounds); final Rect prevResolvedBounds = new Rect(resolvedBounds); resolvedBounds.set(containingBounds); // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()}, // set-fixed-orientation-letterbox-aspect-ratio. // to ensure that aspect ratio is respected after insets are applied. final float letterboxAspectRatioOverride = int activityWidth; mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); int activityHeight; final float desiredAspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds); // Apply aspect ratio to resolved bounds mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets, containingBounds, desiredAspectRatio, true); // Vertically center if orientation is landscape. Center within parent bounds with insets // to ensure that insets do not trim height. Bounds will later be horizontally centered in // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation. if (forcedOrientation == ORIENTATION_LANDSCAPE) { if (forcedOrientation == ORIENTATION_LANDSCAPE) { activityWidth = parentBounds.width(); final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY(); // Compute height from stable bounds width to ensure orientation respected after insets. resolvedBounds.offset(0, offsetY); activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio); // Landscape is defined as width > height. To ensure activity is landscape when aspect // ratio is close to 1, reduce the height by one pixel. if (activityWidth == activityHeight) { activityHeight -= 1; } // Center vertically within stable bounds in landscape to ensure insets do not trim // height. final int top = containerBounds.centerY() - activityHeight / 2; resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight); } else { activityHeight = parentBounds.height(); // Compute width from stable bounds height to ensure orientation respected after insets. activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio); // Center horizontally in portrait. For now, align to left and allow // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center // horizontally. Exclude left insets from parent to ensure cutout does not trim width. final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds(); resolvedBounds.set(parentAppBounds.left, parentBounds.top, parentAppBounds.left + activityWidth, parentBounds.bottom); } } if (mCompatDisplayInsets != null) { if (mCompatDisplayInsets != null) { mCompatDisplayInsets.getBoundsByRotation( mCompatDisplayInsets.getBoundsByRotation( mTmpBounds, newParentConfig.windowConfiguration.getRotation()); mTmpBounds, newParentConfig.windowConfiguration.getRotation()); // Insets may differ between different rotations, for example in the case of a display if (resolvedBounds.width() != mTmpBounds.width() // cutout. To ensure consistent bounds across rotations, compare the activity dimensions || resolvedBounds.height() != mTmpBounds.height()) { // minus insets from the rotation the compat bounds were computed in. Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds, mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]); if (activityWidth != mTmpBounds.width() || activityHeight != mTmpBounds.height()) { // The app shouldn't be resized, we only do fixed orientation letterboxing if the // 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, // compat bounds are also from the same fixed orientation letterbox. Otherwise, // clear the fixed orientation bounds to show app in size compat mode. // clear the fixed orientation bounds to show app in size compat mode. Loading Loading @@ -7690,8 +7693,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). if (!mTmpBounds.isEmpty()) { if (!mTmpBounds.isEmpty()) { resolvedBounds.set(mTmpBounds); resolvedBounds.set(mTmpBounds); // Exclude the horizontal decor area. resolvedBounds.left = parentAppBounds.left; } } if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) { if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) { // Compute the configuration based on the resolved bounds. If aspect ratio doesn't // Compute the configuration based on the resolved bounds. If aspect ratio doesn't Loading Loading @@ -7752,13 +7753,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsAspectRatioApplied = mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); } } // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in // the container app bounds. Otherwise the entire container bounds are available. final boolean fillContainer = resolvedBounds.equals(containingBounds); if (!fillContainer) { // The horizontal position should not cover insets. resolvedBounds.left = containingAppBounds.left; } // Use resolvedBounds to compute other override configurations such as appBounds. The bounds // Use resolvedBounds to compute other override configurations such as appBounds. The bounds // are calculated in compat container space. The actual position on screen will be applied // are calculated in compat container space. The actual position on screen will be applied Loading Loading @@ -7825,6 +7819,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition. // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition. // Above coordinates are in "@" space, now place "*" and "#" to screen space. // Above coordinates are in "@" space, now place "*" and "#" to screen space. final boolean fillContainer = resolvedBounds.equals(containingBounds); final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; final int screenPosY = containerBounds.top; final int screenPosY = containerBounds.top; if (screenPosX != 0 || screenPosY != 0) { if (screenPosX != 0 || screenPosY != 0) { Loading Loading @@ -8066,6 +8061,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; return true; } } private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { return applyAspectRatio(outBounds, containingAppBounds, containingBounds, 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */); } /** /** * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is * made to outBounds. * made to outBounds. Loading @@ -8074,17 +8075,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ */ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) { final float maxAspectRatio = info.getMaxAspectRatio(); final float maxAspectRatio = info.getMaxAspectRatio(); final Task rootTask = getRootTask(); final Task rootTask = getRootTask(); final float minAspectRatio = info.getMinAspectRatio(); final float minAspectRatio = info.getMinAspectRatio(); if (task == null || rootTask == null if (task == null || rootTask == null || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()) || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets() || (maxAspectRatio == 0 && minAspectRatio == 0) && !fixedOrientationLetterboxed) || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1) || isInVrUiMode(getConfiguration())) { || isInVrUiMode(getConfiguration())) { // We don't enforce aspect ratio if the activity task is in multiwindow unless it // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in // is in size-compat mode. We also don't set it if we are in VR mode. // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we // are in VR mode. return false; return false; } } Loading @@ -8092,20 +8095,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int containingAppHeight = containingAppBounds.height(); final int containingAppHeight = containingAppBounds.height(); final float containingRatio = computeAspectRatio(containingAppBounds); final float containingRatio = computeAspectRatio(containingAppBounds); if (desiredAspectRatio < 1) { desiredAspectRatio = containingRatio; } if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) { desiredAspectRatio = maxAspectRatio; } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) { desiredAspectRatio = minAspectRatio; } int activityWidth = containingAppWidth; int activityWidth = containingAppWidth; int activityHeight = containingAppHeight; int activityHeight = containingAppHeight; if (containingRatio > maxAspectRatio && maxAspectRatio != 0) { if (containingRatio > desiredAspectRatio) { if (containingAppWidth < containingAppHeight) { if (containingAppWidth < containingAppHeight) { // Width is the shorter side, so we use that to figure-out what the max. height // Width is the shorter side, so we use that to figure-out what the max. height // should be given the aspect ratio. // should be given the aspect ratio. activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f); activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f); } else { } else { // Height is the shorter side, so we use that to figure-out what the max. width // Height is the shorter side, so we use that to figure-out what the max. width // should be given the aspect ratio. // should be given the aspect ratio. activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f); activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f); } } } else if (containingRatio < minAspectRatio) { } else if (containingRatio < desiredAspectRatio) { boolean adjustWidth; boolean adjustWidth; switch (getRequestedConfigurationOrientation()) { switch (getRequestedConfigurationOrientation()) { case ORIENTATION_LANDSCAPE: case ORIENTATION_LANDSCAPE: Loading Loading @@ -8133,9 +8146,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A break; break; } } if (adjustWidth) { if (adjustWidth) { activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f); activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f); } else { } else { activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f); activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f); } } } } Loading @@ -8159,6 +8172,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } outBounds.set(containingBounds.left, containingBounds.top, right, bottom); outBounds.set(containingBounds.left, containingBounds.top, right, bottom); // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the // container app bounds. Otherwise the entire container bounds are available. if (!outBounds.equals(containingBounds)) { // The horizontal position should not cover insets (e.g. display cutout). outBounds.left = containingAppBounds.left; } return true; return true; } } Loading services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +55 −0 Original line number Original line Diff line number Diff line Loading @@ -1986,6 +1986,61 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.areBoundsLetterboxed()); assertTrue(mActivity.areBoundsLetterboxed()); } } /** * Tests that all three paths in which aspect ratio logic can be applied yield the same * result, which is that aspect ratio is respected on app bounds. The three paths are * fixed orientation, no fixed orientation but fixed aspect ratio, and size compat mode. */ @Test public void testAllAspectRatioLogicConsistent() { // Create display that has all stable insets and does not rotate. Make sure that status bar // height is greater than notch height so that stable bounds do not equal app bounds. final int notchHeight = 75; final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600) .setSystemDecorations(true).setNotch(notchHeight) .setStatusBarHeight(notchHeight + 20).setCanRotate(false).build(); // Create task on test display. final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); // Target min aspect ratio must be larger than parent aspect ratio to be applied. final float targetMinAspectRatio = 3.0f; // Create fixed portait activity with min aspect ratio greater than parent aspect ratio. final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm) .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .setMinAspectRatio(targetMinAspectRatio).build(); final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration() .windowConfiguration.getAppBounds()); // Create activity with no fixed orientation and min aspect ratio greater than parent aspect // ratio. final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm).setTask(task) .setMinAspectRatio(targetMinAspectRatio).build(); final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration() .windowConfiguration.getAppBounds()); // Create unresizeable fixed portait activity with min aspect ratio greater than parent // aspect ratio. final ActivityRecord sizeCompatActivity = new ActivityBuilder(mAtm) .setTask(task).setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .setMinAspectRatio(targetMinAspectRatio).build(); // Resize display running unresizeable activity to make it enter size compat mode. resizeDisplay(display, 1800, 1000); final Rect sizeCompatAppBounds = new Rect(sizeCompatActivity.getConfiguration() .windowConfiguration.getAppBounds()); // Check that aspect ratio of app bounds is equal to the min aspect ratio. final float delta = 0.01f; assertEquals(targetMinAspectRatio, ActivityRecord .computeAspectRatio(fixedOrientationAppBounds), delta); assertEquals(targetMinAspectRatio, ActivityRecord .computeAspectRatio(minAspectRatioAppBounds), delta); assertEquals(targetMinAspectRatio, ActivityRecord .computeAspectRatio(sizeCompatAppBounds), delta); } private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity( private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity( float letterboxHorizontalPositionMultiplier) { float letterboxHorizontalPositionMultiplier) { // Set up a display in landscape and ignoring orientation request. // Set up a display in landscape and ignoring orientation request. Loading services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +15 −0 Original line number Original line Diff line number Diff line Loading @@ -19,7 +19,9 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; Loading Loading @@ -77,6 +79,7 @@ class TestDisplayContent extends DisplayContent { private int mPosition = POSITION_BOTTOM; private int mPosition = POSITION_BOTTOM; protected final ActivityTaskManagerService mService; protected final ActivityTaskManagerService mService; private boolean mSystemDecorations = false; private boolean mSystemDecorations = false; private int mStatusBarHeight = 0; Builder(ActivityTaskManagerService service, int width, int height) { Builder(ActivityTaskManagerService service, int width, int height) { mService = service; mService = service; Loading Loading @@ -125,6 +128,10 @@ class TestDisplayContent extends DisplayContent { Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null); Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null); return this; return this; } } Builder setStatusBarHeight(int height) { mStatusBarHeight = height; return this; } Builder setCanRotate(boolean canRotate) { Builder setCanRotate(boolean canRotate) { mCanRotate = canRotate; mCanRotate = canRotate; return this; return this; Loading Loading @@ -158,6 +165,14 @@ class TestDisplayContent extends DisplayContent { doReturn(false).when(displayPolicy).hasStatusBar(); doReturn(false).when(displayPolicy).hasStatusBar(); doReturn(false).when(newDisplay).supportsSystemDecorations(); doReturn(false).when(newDisplay).supportsSystemDecorations(); } } if (mStatusBarHeight > 0) { doReturn(true).when(displayPolicy).hasStatusBar(); doAnswer(invocation -> { Rect inOutInsets = (Rect) invocation.getArgument(0); inOutInsets.top = mStatusBarHeight; return null; }).when(displayPolicy).convertNonDecorInsetsToStableInsets(any(), anyInt()); } Configuration c = new Configuration(); Configuration c = new Configuration(); newDisplay.computeScreenConfiguration(c); newDisplay.computeScreenConfiguration(c); c.windowConfiguration.setWindowingMode(mWindowingMode); c.windowConfiguration.setWindowingMode(mWindowingMode); Loading Loading
services/core/java/com/android/server/wm/ActivityRecord.java +105 −85 Original line number Original line Diff line number Diff line Loading @@ -702,8 +702,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mInSizeCompatModeForBounds = false; private boolean mInSizeCompatModeForBounds = false; // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio(). // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio(). // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration // but that isn't reflected in this boolean. private boolean mIsAspectRatioApplied = false; private boolean mIsAspectRatioApplied = false; // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed Loading Loading @@ -7504,11 +7502,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * requested orientation. If not, it may be necessary to letterbox the window. * requested orientation. If not, it may be necessary to letterbox the window. * @param parentBounds are the new parent bounds passed down to the activity and should be used * @param parentBounds are the new parent bounds passed down to the activity and should be used * to compute the stable bounds. * to compute the stable bounds. * @param outBounds will store the stable bounds, which are the bounds with insets applied. * @param outStableBounds will store the stable bounds, which are the bounds with insets * These should be used to compute letterboxed bounds if orientation is not * applied, if orientation is not respected when insets are applied. * respected when insets are applied. * Otherwise outStableBounds will be empty. Stable bounds should be used */ * to compute letterboxed bounds if orientation is not respected when private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) { * insets are applied. */ private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outStableBounds) { outStableBounds.setEmpty(); if (mDisplayContent == null) { if (mDisplayContent == null) { return true; return true; } } Loading @@ -7523,17 +7524,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Compute orientation from stable parent bounds (= parent bounds with insets applied) // Compute orientation from stable parent bounds (= parent bounds with insets applied) final Task task = getTask(); final Task task = getTask(); task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */, task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */, outBounds /* outStableBounds */, parentBounds /* bounds */, outStableBounds /* outStableBounds */, parentBounds /* bounds */, mDisplayContent.getDisplayInfo()); mDisplayContent.getDisplayInfo()); final int orientationWithInsets = outBounds.height() >= outBounds.width() final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width() ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; // If orientation does not match the orientation with insets applied, then a // If orientation does not match the orientation with insets applied, then a // display rotation will not be enough to respect orientation. However, even if they do // display rotation will not be enough to respect orientation. However, even if they do // not match but the orientation with insets applied matches the requested orientation, then // not match but the orientation with insets applied matches the requested orientation, then // there is no need to modify the bounds because when insets are applied, the activity will // there is no need to modify the bounds because when insets are applied, the activity will // have the desired orientation. // have the desired orientation. return orientation == orientationWithInsets final boolean orientationRespectedWithInsets = orientation == orientationWithInsets || orientationWithInsets == requestedOrientation; || orientationWithInsets == requestedOrientation; if (orientationRespectedWithInsets) { outStableBounds.setEmpty(); } return orientationRespectedWithInsets; } } /** /** Loading @@ -7551,9 +7556,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A int windowingMode) { int windowingMode) { mLetterboxBoundsForFixedOrientationAndAspectRatio = null; mLetterboxBoundsForFixedOrientationAndAspectRatio = null; final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect containerBounds = new Rect(parentBounds); final Rect stableBounds = new Rect(); // If orientation is respected when insets are applied, then stableBounds will be empty. boolean orientationRespectedWithInsets = boolean orientationRespectedWithInsets = orientationRespectedWithInsets(parentBounds, containerBounds); orientationRespectedWithInsets(parentBounds, stableBounds); if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { // No need to letterbox because of fixed orientation. Display will handle // No need to letterbox because of fixed orientation. Display will handle // fixed-orientation requests and a display rotation is enough to respect requested // fixed-orientation requests and a display rotation is enough to respect requested Loading Loading @@ -7590,71 +7596,68 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; return; } } // TODO(b/182268157) merge aspect ratio logic here and in // TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app // {@link ActivityRecord#applyAspectRatio} // bounds or stable bounds to unify aspect ratio logic. // if no aspect ratio constraints are provided, parent aspect ratio is used final Rect parentBoundsWithInsets = orientationRespectedWithInsets float aspectRatio = computeAspectRatio(parentBounds); ? newParentConfig.windowConfiguration.getAppBounds() : stableBounds; final Rect containingBounds = new Rect(); // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with final Rect containingBoundsWithInsets = new Rect(); // set-fixed-orientation-letterbox-aspect-ratio. // Need to shrink the containing bounds into a square because the parent orientation final float letterboxAspectRatioOverride = // does not match the activity requested orientation. mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); if (forcedOrientation == ORIENTATION_LANDSCAPE) { aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO // Landscape is defined as width > height. Make the container respect landscape ? letterboxAspectRatioOverride : aspectRatio; // orientation by shrinking height to one less than width. Landscape activity will be // vertically centered within parent bounds with insets, so position vertical bounds // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in // within parent bounds with insets to prevent insets from unnecessarily trimming // order to use the extra available space. // vertical bounds. final float maxAspectRatio = info.getMaxAspectRatio(); final int bottom = Math.min(parentBoundsWithInsets.top + parentBounds.width() - 1, final float minAspectRatio = info.getMinAspectRatio(); parentBoundsWithInsets.bottom); if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) { containingBounds.set(parentBounds.left, parentBoundsWithInsets.top, parentBounds.right, aspectRatio = maxAspectRatio; bottom); } else if (aspectRatio < minAspectRatio) { containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top, aspectRatio = minAspectRatio; parentBoundsWithInsets.right, bottom); } else { // Portrait is defined as width <= height. Make the container respect portrait // orientation by shrinking width to match height. Portrait activity will be // horizontally centered within parent bounds with insets, so position horizontal bounds // within parent bounds with insets to prevent insets from unnecessarily trimming // horizontal bounds. final int right = Math.min(parentBoundsWithInsets.left + parentBounds.height(), parentBoundsWithInsets.right); containingBounds.set(parentBoundsWithInsets.left, parentBounds.top, right, parentBounds.bottom); containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top, right, parentBoundsWithInsets.bottom); } } // Store the current bounds to be able to revert to size compat mode values below if needed. // Store the current bounds to be able to revert to size compat mode values below if needed. final Rect prevResolvedBounds = new Rect(resolvedBounds); final Rect prevResolvedBounds = new Rect(resolvedBounds); resolvedBounds.set(containingBounds); // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()}, // set-fixed-orientation-letterbox-aspect-ratio. // to ensure that aspect ratio is respected after insets are applied. final float letterboxAspectRatioOverride = int activityWidth; mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); int activityHeight; final float desiredAspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds); // Apply aspect ratio to resolved bounds mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets, containingBounds, desiredAspectRatio, true); // Vertically center if orientation is landscape. Center within parent bounds with insets // to ensure that insets do not trim height. Bounds will later be horizontally centered in // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation. if (forcedOrientation == ORIENTATION_LANDSCAPE) { if (forcedOrientation == ORIENTATION_LANDSCAPE) { activityWidth = parentBounds.width(); final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY(); // Compute height from stable bounds width to ensure orientation respected after insets. resolvedBounds.offset(0, offsetY); activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio); // Landscape is defined as width > height. To ensure activity is landscape when aspect // ratio is close to 1, reduce the height by one pixel. if (activityWidth == activityHeight) { activityHeight -= 1; } // Center vertically within stable bounds in landscape to ensure insets do not trim // height. final int top = containerBounds.centerY() - activityHeight / 2; resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight); } else { activityHeight = parentBounds.height(); // Compute width from stable bounds height to ensure orientation respected after insets. activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio); // Center horizontally in portrait. For now, align to left and allow // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center // horizontally. Exclude left insets from parent to ensure cutout does not trim width. final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds(); resolvedBounds.set(parentAppBounds.left, parentBounds.top, parentAppBounds.left + activityWidth, parentBounds.bottom); } } if (mCompatDisplayInsets != null) { if (mCompatDisplayInsets != null) { mCompatDisplayInsets.getBoundsByRotation( mCompatDisplayInsets.getBoundsByRotation( mTmpBounds, newParentConfig.windowConfiguration.getRotation()); mTmpBounds, newParentConfig.windowConfiguration.getRotation()); // Insets may differ between different rotations, for example in the case of a display if (resolvedBounds.width() != mTmpBounds.width() // cutout. To ensure consistent bounds across rotations, compare the activity dimensions || resolvedBounds.height() != mTmpBounds.height()) { // minus insets from the rotation the compat bounds were computed in. Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds, mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]); if (activityWidth != mTmpBounds.width() || activityHeight != mTmpBounds.height()) { // The app shouldn't be resized, we only do fixed orientation letterboxing if the // 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, // compat bounds are also from the same fixed orientation letterbox. Otherwise, // clear the fixed orientation bounds to show app in size compat mode. // clear the fixed orientation bounds to show app in size compat mode. Loading Loading @@ -7690,8 +7693,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). if (!mTmpBounds.isEmpty()) { if (!mTmpBounds.isEmpty()) { resolvedBounds.set(mTmpBounds); resolvedBounds.set(mTmpBounds); // Exclude the horizontal decor area. resolvedBounds.left = parentAppBounds.left; } } if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) { if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) { // Compute the configuration based on the resolved bounds. If aspect ratio doesn't // Compute the configuration based on the resolved bounds. If aspect ratio doesn't Loading Loading @@ -7752,13 +7753,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsAspectRatioApplied = mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); } } // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in // the container app bounds. Otherwise the entire container bounds are available. final boolean fillContainer = resolvedBounds.equals(containingBounds); if (!fillContainer) { // The horizontal position should not cover insets. resolvedBounds.left = containingAppBounds.left; } // Use resolvedBounds to compute other override configurations such as appBounds. The bounds // Use resolvedBounds to compute other override configurations such as appBounds. The bounds // are calculated in compat container space. The actual position on screen will be applied // are calculated in compat container space. The actual position on screen will be applied Loading Loading @@ -7825,6 +7819,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition. // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition. // Above coordinates are in "@" space, now place "*" and "#" to screen space. // Above coordinates are in "@" space, now place "*" and "#" to screen space. final boolean fillContainer = resolvedBounds.equals(containingBounds); final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; final int screenPosY = containerBounds.top; final int screenPosY = containerBounds.top; if (screenPosX != 0 || screenPosY != 0) { if (screenPosX != 0 || screenPosY != 0) { Loading Loading @@ -8066,6 +8061,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; return true; } } private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { return applyAspectRatio(outBounds, containingAppBounds, containingBounds, 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */); } /** /** * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is * made to outBounds. * made to outBounds. Loading @@ -8074,17 +8075,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ */ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) { final float maxAspectRatio = info.getMaxAspectRatio(); final float maxAspectRatio = info.getMaxAspectRatio(); final Task rootTask = getRootTask(); final Task rootTask = getRootTask(); final float minAspectRatio = info.getMinAspectRatio(); final float minAspectRatio = info.getMinAspectRatio(); if (task == null || rootTask == null if (task == null || rootTask == null || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()) || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets() || (maxAspectRatio == 0 && minAspectRatio == 0) && !fixedOrientationLetterboxed) || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1) || isInVrUiMode(getConfiguration())) { || isInVrUiMode(getConfiguration())) { // We don't enforce aspect ratio if the activity task is in multiwindow unless it // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in // is in size-compat mode. We also don't set it if we are in VR mode. // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we // are in VR mode. return false; return false; } } Loading @@ -8092,20 +8095,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int containingAppHeight = containingAppBounds.height(); final int containingAppHeight = containingAppBounds.height(); final float containingRatio = computeAspectRatio(containingAppBounds); final float containingRatio = computeAspectRatio(containingAppBounds); if (desiredAspectRatio < 1) { desiredAspectRatio = containingRatio; } if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) { desiredAspectRatio = maxAspectRatio; } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) { desiredAspectRatio = minAspectRatio; } int activityWidth = containingAppWidth; int activityWidth = containingAppWidth; int activityHeight = containingAppHeight; int activityHeight = containingAppHeight; if (containingRatio > maxAspectRatio && maxAspectRatio != 0) { if (containingRatio > desiredAspectRatio) { if (containingAppWidth < containingAppHeight) { if (containingAppWidth < containingAppHeight) { // Width is the shorter side, so we use that to figure-out what the max. height // Width is the shorter side, so we use that to figure-out what the max. height // should be given the aspect ratio. // should be given the aspect ratio. activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f); activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f); } else { } else { // Height is the shorter side, so we use that to figure-out what the max. width // Height is the shorter side, so we use that to figure-out what the max. width // should be given the aspect ratio. // should be given the aspect ratio. activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f); activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f); } } } else if (containingRatio < minAspectRatio) { } else if (containingRatio < desiredAspectRatio) { boolean adjustWidth; boolean adjustWidth; switch (getRequestedConfigurationOrientation()) { switch (getRequestedConfigurationOrientation()) { case ORIENTATION_LANDSCAPE: case ORIENTATION_LANDSCAPE: Loading Loading @@ -8133,9 +8146,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A break; break; } } if (adjustWidth) { if (adjustWidth) { activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f); activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f); } else { } else { activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f); activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f); } } } } Loading @@ -8159,6 +8172,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } outBounds.set(containingBounds.left, containingBounds.top, right, bottom); outBounds.set(containingBounds.left, containingBounds.top, right, bottom); // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the // container app bounds. Otherwise the entire container bounds are available. if (!outBounds.equals(containingBounds)) { // The horizontal position should not cover insets (e.g. display cutout). outBounds.left = containingAppBounds.left; } return true; return true; } } Loading
services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +55 −0 Original line number Original line Diff line number Diff line Loading @@ -1986,6 +1986,61 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.areBoundsLetterboxed()); assertTrue(mActivity.areBoundsLetterboxed()); } } /** * Tests that all three paths in which aspect ratio logic can be applied yield the same * result, which is that aspect ratio is respected on app bounds. The three paths are * fixed orientation, no fixed orientation but fixed aspect ratio, and size compat mode. */ @Test public void testAllAspectRatioLogicConsistent() { // Create display that has all stable insets and does not rotate. Make sure that status bar // height is greater than notch height so that stable bounds do not equal app bounds. final int notchHeight = 75; final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600) .setSystemDecorations(true).setNotch(notchHeight) .setStatusBarHeight(notchHeight + 20).setCanRotate(false).build(); // Create task on test display. final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); // Target min aspect ratio must be larger than parent aspect ratio to be applied. final float targetMinAspectRatio = 3.0f; // Create fixed portait activity with min aspect ratio greater than parent aspect ratio. final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm) .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .setMinAspectRatio(targetMinAspectRatio).build(); final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration() .windowConfiguration.getAppBounds()); // Create activity with no fixed orientation and min aspect ratio greater than parent aspect // ratio. final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm).setTask(task) .setMinAspectRatio(targetMinAspectRatio).build(); final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration() .windowConfiguration.getAppBounds()); // Create unresizeable fixed portait activity with min aspect ratio greater than parent // aspect ratio. final ActivityRecord sizeCompatActivity = new ActivityBuilder(mAtm) .setTask(task).setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .setMinAspectRatio(targetMinAspectRatio).build(); // Resize display running unresizeable activity to make it enter size compat mode. resizeDisplay(display, 1800, 1000); final Rect sizeCompatAppBounds = new Rect(sizeCompatActivity.getConfiguration() .windowConfiguration.getAppBounds()); // Check that aspect ratio of app bounds is equal to the min aspect ratio. final float delta = 0.01f; assertEquals(targetMinAspectRatio, ActivityRecord .computeAspectRatio(fixedOrientationAppBounds), delta); assertEquals(targetMinAspectRatio, ActivityRecord .computeAspectRatio(minAspectRatioAppBounds), delta); assertEquals(targetMinAspectRatio, ActivityRecord .computeAspectRatio(sizeCompatAppBounds), delta); } private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity( private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity( float letterboxHorizontalPositionMultiplier) { float letterboxHorizontalPositionMultiplier) { // Set up a display in landscape and ignoring orientation request. // Set up a display in landscape and ignoring orientation request. Loading
services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +15 −0 Original line number Original line Diff line number Diff line Loading @@ -19,7 +19,9 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; Loading Loading @@ -77,6 +79,7 @@ class TestDisplayContent extends DisplayContent { private int mPosition = POSITION_BOTTOM; private int mPosition = POSITION_BOTTOM; protected final ActivityTaskManagerService mService; protected final ActivityTaskManagerService mService; private boolean mSystemDecorations = false; private boolean mSystemDecorations = false; private int mStatusBarHeight = 0; Builder(ActivityTaskManagerService service, int width, int height) { Builder(ActivityTaskManagerService service, int width, int height) { mService = service; mService = service; Loading Loading @@ -125,6 +128,10 @@ class TestDisplayContent extends DisplayContent { Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null); Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null); return this; return this; } } Builder setStatusBarHeight(int height) { mStatusBarHeight = height; return this; } Builder setCanRotate(boolean canRotate) { Builder setCanRotate(boolean canRotate) { mCanRotate = canRotate; mCanRotate = canRotate; return this; return this; Loading Loading @@ -158,6 +165,14 @@ class TestDisplayContent extends DisplayContent { doReturn(false).when(displayPolicy).hasStatusBar(); doReturn(false).when(displayPolicy).hasStatusBar(); doReturn(false).when(newDisplay).supportsSystemDecorations(); doReturn(false).when(newDisplay).supportsSystemDecorations(); } } if (mStatusBarHeight > 0) { doReturn(true).when(displayPolicy).hasStatusBar(); doAnswer(invocation -> { Rect inOutInsets = (Rect) invocation.getArgument(0); inOutInsets.top = mStatusBarHeight; return null; }).when(displayPolicy).convertNonDecorInsetsToStableInsets(any(), anyInt()); } Configuration c = new Configuration(); Configuration c = new Configuration(); newDisplay.computeScreenConfiguration(c); newDisplay.computeScreenConfiguration(c); c.windowConfiguration.setWindowingMode(mWindowingMode); c.windowConfiguration.setWindowingMode(mWindowingMode); Loading