diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index d138b4b414505..229e74b262b8b 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.KeyguardManager; +import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -678,9 +679,9 @@ public final class Display { @UnsupportedAppUsage public DisplayAdjustments getDisplayAdjustments() { if (mResources != null) { - final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments(); - if (!mDisplayAdjustments.equals(currentAdjustements)) { - mDisplayAdjustments = new DisplayAdjustments(currentAdjustements); + final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments(); + if (!mDisplayAdjustments.equals(currentAdjustments)) { + mDisplayAdjustments = new DisplayAdjustments(currentAdjustments); } } @@ -1278,6 +1279,18 @@ public final class Display { public void getRealSize(Point outSize) { synchronized (this) { updateDisplayInfoLocked(); + if (shouldReportMaxBounds()) { + final Rect bounds = mResources.getConfiguration() + .windowConfiguration.getMaxBounds(); + outSize.x = bounds.width(); + outSize.y = bounds.height(); + if (DEBUG) { + Log.d(TAG, "getRealSize determined from max bounds: " + outSize); + } + // Skip adjusting by fixed rotation, since if it is necessary, the configuration + // should already reflect the expected rotation. + return; + } outSize.x = mDisplayInfo.logicalWidth; outSize.y = mDisplayInfo.logicalHeight; if (mMayAdjustByFixedRotation) { @@ -1336,6 +1349,17 @@ public final class Display { public void getRealMetrics(DisplayMetrics outMetrics) { synchronized (this) { updateDisplayInfoLocked(); + if (shouldReportMaxBounds()) { + mDisplayInfo.getMaxBoundsMetrics(outMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, + mResources.getConfiguration()); + if (DEBUG) { + Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics); + } + // Skip adjusting by fixed rotation, since if it is necessary, the configuration + // should already reflect the expected rotation. + return; + } mDisplayInfo.getLogicalMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); if (mMayAdjustByFixedRotation) { @@ -1344,6 +1368,24 @@ public final class Display { } } + /** + * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the + * display dimensions. The max bounds field may be smaller than the logical dimensions + * when apps need to be sandboxed. + * + * Depends upon {@link WindowConfiguration#getMaxBounds()} being set in + * {@link com.android.server.wm.ConfigurationContainer#providesMaxBounds()}. In most cases, this + * value reflects the size of the current DisplayArea. + * @return {@code true} when max bounds should be applied. + */ + private boolean shouldReportMaxBounds() { + if (mResources == null) { + return false; + } + final Configuration config = mResources.getConfiguration(); + return config != null && !config.windowConfiguration.getMaxBounds().isEmpty(); + } + /** * Gets the state of the display, such as whether it is on or off. * diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 655f42308a1f8..a8aaeb7846a21 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -24,6 +24,7 @@ import static android.view.DisplayInfoProto.LOGICAL_WIDTH; import static android.view.DisplayInfoProto.NAME; import android.annotation.Nullable; +import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -615,11 +616,29 @@ public final class DisplayInfo implements Parcelable { getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight); } + /** + * Populates {@code outMetrics} with details of the logical display. Bounds are limited + * by the logical size of the display. + * + * @param outMetrics the {@link DisplayMetrics} to be populated + * @param compatInfo the {@link CompatibilityInfo} to be applied + * @param configuration the {@link Configuration} + */ public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo, Configuration configuration) { getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight); } + /** + * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from + * {@link WindowConfiguration#getMaxBounds()} + */ + public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo, + Configuration configuration) { + Rect bounds = configuration.windowConfiguration.getMaxBounds(); + getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height()); + } + public int getNaturalWidth() { return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ? logicalWidth : logicalHeight; diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java new file mode 100644 index 0000000000000..678f21fe1211b --- /dev/null +++ b/core/tests/mockingcoretests/src/android/view/DisplayTests.java @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_90; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.platform.test.annotations.Presubmit; +import android.util.DisplayMetrics; +import android.view.DisplayAdjustments.FixedRotationAdjustments; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.quality.Strictness; + +import java.util.function.Consumer; + +/** + * Tests for {@link Display}. + * + *
Build/Install/Run:
+ *
+ * atest FrameworksMockingCoreTests:android.view.DisplayTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayTests {
+
+ private static final int APP_WIDTH = 272;
+ private static final int APP_HEIGHT = 700;
+ // Tablet size device, ROTATION_0 corresponds to portrait.
+ private static final int LOGICAL_WIDTH = 700;
+ private static final int LOGICAL_HEIGHT = 1800;
+
+ // Bounds of the app when the device is in portrait mode.
+ private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
+ private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+ private StaticMockitoSession mMockitoSession;
+
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ private Context mApplicationContext;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ @Before
+ public void setupTests() {
+ mMockitoSession = mockitoSession()
+ .mockStatic(DisplayManagerGlobal.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // Ensure no adjustments are set before each test.
+ mApplicationContext = ApplicationProvider.getApplicationContext();
+ DisplayAdjustments displayAdjustments =
+ mApplicationContext.getResources().getDisplayAdjustments();
+ displayAdjustments.setFixedRotationAdjustments(null);
+ mApplicationContext.getResources().overrideDisplayAdjustments(null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
+ null);
+ mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
+ null);
+ mDisplayInfo.rotation = ROTATION_0;
+
+ mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
+ doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
+ }
+
+ @After
+ public void teardownTests() {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ Mockito.framework().clearInlineMocks();
+ }
+
+ @Test
+ public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testConstructor_defaultResources_matchesDisplayInfo() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ assertThat(display.getDisplayAdjustments()).isEqualTo(
+ mApplicationContext.getResources().getDisplayAdjustments());
+ DisplayInfo actualDisplayInfo = new DisplayInfo();
+ display.getDisplayInfo(actualDisplayInfo);
+ verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
+ }
+
+ @Test
+ public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ // GIVEN display is constructed with display adjustments.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ displayAdjustments);
+ // THEN rotation is not adjusted since no override was set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, but no override is set.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is not adjusted since no override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_0);
+ }
+
+ @Test
+ public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN rotation is adjusted since an override is set.
+ assertThat(display.getRotation()).isEqualTo(ROTATION_90);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches display orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated, and an override is set.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app orientation.
+ verifyRealSizeIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real size matches app bounds.
+ verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches display orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated with an override.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsPortrait(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN fixed rotation adjustments are rotated.
+ setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
+ // GIVEN display is constructed with default resources.
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app orientation.
+ verifyRealMetricsIsLandscape(display);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
+ // GIVEN display is not rotated.
+ setDisplayInfoPortrait(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsPortrait);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
+ }
+
+ @Test
+ public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+ // GIVEN display is rotated.
+ setDisplayInfoLandscape(mDisplayInfo);
+ // GIVEN app is letterboxed.
+ setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
+ sAppBoundsLandscape);
+ final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+ mApplicationContext.getResources());
+ // THEN real metrics matches app bounds.
+ verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
+ }
+
+ // Given rotated display dimensions, calculate the letterboxed app bounds.
+ private static Rect buildAppBounds(int displayWidth, int displayHeight) {
+ final int midWidth = displayWidth / 2;
+ final int left = midWidth - (APP_WIDTH / 2);
+ final int right = midWidth + (APP_WIDTH / 2);
+ final int midHeight = displayHeight / 2;
+ // Coordinate system starts at top left.
+ final int top = midHeight - (APP_HEIGHT / 2);
+ final int bottom = midHeight + (APP_HEIGHT / 2);
+ return new Rect(left, top, right, bottom);
+ }
+
+ private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_90;
+ // Flip width & height assignment since the device is rotated.
+ displayInfo.logicalWidth = LOGICAL_HEIGHT;
+ displayInfo.logicalHeight = LOGICAL_WIDTH;
+ }
+
+ private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
+ displayInfo.rotation = ROTATION_0;
+ displayInfo.logicalWidth = LOGICAL_WIDTH;
+ displayInfo.logicalHeight = LOGICAL_HEIGHT;
+ }
+
+ /**
+ * Set max bounds to be sandboxed to the app bounds, indicating the app is in
+ * size compat mode or letterbox.
+ */
+ private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
+ resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
+ }
+
+ /**
+ * Do not compare entire display info, since it is updated to match display the test is run on.
+ */
+ private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+ assertThat(actual.displayId).isEqualTo(expected.displayId);
+ assertThat(actual.rotation).isEqualTo(expected.rotation);
+ assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
+ assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeIsLandscape(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ // Flip the width and height check since the device is rotated.
+ assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
+ }
+
+ private static void verifyRealMetricsIsLandscape(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ // Flip the width and height check since the device is rotated.
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
+ }
+
+ private static void verifyRealSizeIsPortrait(Display display) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
+ }
+
+ private static void verifyRealMetricsIsPortrait(Display display) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
+ assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
+ }
+
+ private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
+ Point size = new Point();
+ display.getRealSize(size);
+ assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
+ }
+
+ private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
+ assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
+ }
+
+ private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
+ Resources resources, @Surface.Rotation int rotation) {
+ FixedRotationAdjustments fixedRotationAdjustments =
+ setFixedRotationAdjustments(resources, rotation);
+ resources.overrideDisplayAdjustments(
+ buildOverrideRotationAdjustments(fixedRotationAdjustments));
+ return fixedRotationAdjustments;
+ }
+
+ private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
+ @Surface.Rotation int rotation) {
+ final FixedRotationAdjustments fixedRotationAdjustments =
+ new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
+ DisplayCutout.NO_CUTOUT);
+ resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
+ return fixedRotationAdjustments;
+ }
+
+ private static Consumer
- * The maximum bounds is how large a window can be expanded. Currently only
- * {@link DisplayContent} and {@link DisplayArea} effect this property.
+ * The maximum bounds is how large a window can be expanded.
*