From e3424aefa51d3e8587a90240099a937996c105ef Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Tue, 1 Sep 2020 18:30:45 +0800 Subject: [PATCH] Use orientation to determine size compat bounds If a display is resized to different orientation, the mapping between rotation and orientation is also inverted. So it may be inaccurate to calculate compat bounds according to current rotation. While the given orientation is better because it is relative to the width and height. That makes sure either the requested orientation takes effect or just inherit parent's configuration. Also consider the following cases: - Display rotation changed but activity orientation is not changed. The display is forced rotated and the activity is fixed orientation. - Display orientation changed but rotation is not changed. Resize fixed rotation display between portrait and landscape. Fixes: 166822610 Test: SizeCompatTests#testFixedScreenBoundsWhenDisplaySizeChanged Change-Id: I329325c134f37e08ecdb6311bff5cd331e3bc7c3 --- .../com/android/server/wm/ActivityRecord.java | 58 +++++++++---------- .../android/server/wm/SizeCompatTests.java | 36 +++++++++++- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index aea0a58c9acba..9dfe568b9ccd7 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7784,42 +7784,40 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Gets the horizontal centered container bounds for size compatibility mode. */ void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation, boolean orientationRequested, boolean canChangeOrientation) { + getFrameByOrientation(outBounds, orientation); if (mIsFloating) { - getFrameByOrientation(outBounds, orientation); outAppBounds.set(outBounds); return; } - if (canChangeOrientation) { - getBoundsByRotation(outBounds, rotation); - if (orientationRequested) { - getFrameByOrientation(outAppBounds, orientation); - } else { - outAppBounds.set(outBounds); - } - } else { - if (orientationRequested) { - getFrameByOrientation(outBounds, orientation); - if ((outBounds.width() > outBounds.height()) != (mWidth > mHeight)) { - // The orientation is mismatched but the display cannot rotate. The bounds - // will fit to the short side of display. - if (orientation == ORIENTATION_LANDSCAPE) { - outBounds.bottom = (int) ((float) mWidth * mWidth / mHeight); - outBounds.right = mWidth; - } else { - outBounds.bottom = mHeight; - outBounds.right = (int) ((float) mHeight * mHeight / mWidth); - } - outBounds.offset( - getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); - } - } else { - outBounds.set(0, 0, mWidth, mHeight); - } - outAppBounds.set(outBounds); - } + getBoundsByRotation(outAppBounds, rotation); + final int dW = outAppBounds.width(); + final int dH = outAppBounds.height(); + final boolean isOrientationMismatched = + ((outBounds.width() > outBounds.height()) != (dW > dH)); - if (rotation != ROTATION_UNDEFINED) { + if (isOrientationMismatched && !canChangeOrientation && orientationRequested) { + // The orientation is mismatched but the display cannot rotate. The bounds will fit + // to the short side of container. + if (orientation == ORIENTATION_LANDSCAPE) { + outBounds.bottom = (int) ((float) dW * dW / dH); + outBounds.right = dW; + } else { + outBounds.bottom = dH; + outBounds.right = (int) ((float) dH * dH / dW); + } + outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); + } + outAppBounds.set(outBounds); + + if (isOrientationMismatched) { + // One side of container is smaller than the requested size, then it will be scaled + // and the final position will be calculated according to the parent container and + // scale, so the original size shouldn't be shrunk by insets. + final Rect insets = mNonDecorInsets[rotation]; + outBounds.offset(insets.left, insets.top); + outAppBounds.offset(insets.left, insets.top); + } else if (rotation != ROTATION_UNDEFINED) { // Ensure the app bounds won't overlap with insets. Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 6c648a8948217..468b2c35ffc2e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -35,7 +35,9 @@ 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.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doCallRealMethod; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -216,22 +218,50 @@ public class SizeCompatTests extends WindowTestsBase { final Rect origBounds = new Rect(mActivity.getBounds()); final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); + final DisplayContent display = mActivity.mDisplayContent; // Change the size of current display. - resizeDisplay(mStack.mDisplayContent, 1000, 2000); - + resizeDisplay(display, 1000, 2000); + // The bounds should be [100, 0 - 1100, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); assertScaled(); + // The scale is 2000/2500=0.8. The horizontal centered offset is (1000-(1000*0.8))/2=100. + final float scale = (float) display.mBaseDisplayHeight / currentBounds.height(); + final int offsetX = (int) (display.mBaseDisplayWidth - (origBounds.width() * scale)) / 2; + assertEquals(offsetX, currentBounds.left); + // The position of configuration bounds should be the same as compat bounds. assertEquals(mActivity.getBounds().left, currentBounds.left); assertEquals(mActivity.getBounds().top, currentBounds.top); // Change display size to a different orientation - resizeDisplay(mStack.mDisplayContent, 2000, 1000); + resizeDisplay(display, 2000, 1000); + // The bounds should be [800, 0 - 1800, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); + + // The previous resize operation doesn't consider the rotation change after size changed. + // These setups apply the requested orientation to rotation as real case that the top fixed + // portrait activity will determine the display rotation. + final DisplayRotation displayRotation = display.getDisplayRotation(); + doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean()); + // Skip unrelated layout procedures. + mAtm.deferWindowLayout(); + display.reconfigureDisplayLocked(); + displayRotation.updateOrientation(display.getOrientation(), true /* forceUpdate */); + display.sendNewConfiguration(); + + assertEquals(Configuration.ORIENTATION_PORTRAIT, display.getConfiguration().orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); + // The size should still be in portrait [100, 0 - 1100, 2500] = 1000x2500. + assertEquals(origBounds.width(), currentBounds.width()); + assertEquals(origBounds.height(), currentBounds.height()); + assertEquals(offsetX, currentBounds.left); + assertScaled(); } @Test