diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e73c928ef925f..c4545fa23cc8a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1351,11 +1351,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLetterbox.attachInput(w); } getPosition(mTmpPoint); - // Get the bounds of the "space-to-fill". In multi-window mode, the task-level - // represents this. In fullscreen-mode, the stack does (since the orientation letterbox - // is also applied to the task). - Rect spaceToFill = (inMultiWindowMode() || getStack() == null) - ? task.getDisplayedBounds() : getStack().getDisplayedBounds(); + // Get the bounds of the "space-to-fill". The transformed bounds have the highest + // priority because the activity is launched in a rotated environment. In multi-window + // mode, the task-level represents this. In fullscreen-mode, the task container does + // (since the orientation letterbox is also applied to the task). + final Rect transformedBounds = getFixedRotationTransformDisplayBounds(); + final Rect spaceToFill = transformedBounds != null + ? transformedBounds + : inMultiWindowMode() + ? task.getDisplayedBounds() + : getRootTask().getParent().getDisplayedBounds(); mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint); } else if (mLetterbox != null) { mLetterbox.hide(); @@ -6362,33 +6367,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mCompatDisplayInsets != null) { resolveSizeCompatModeConfiguration(newParentConfiguration); } else { - // We ignore activities' requested orientation in multi-window modes. Task level may - // take them into consideration when calculating bounds. 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; - } - final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); - final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); - // Use tmp bounds to calculate aspect ratio so we can know whether the activity should - // use restricted size (resolvedBounds may be the requested override bounds). - mTmpBounds.setEmpty(); - applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds); - // If the out bounds is not empty, it means the activity cannot fill parent's app - // bounds, then the relative configuration (e.g. screen size, layout) needs to be - // resolved according to the bounds. - if (!mTmpBounds.isEmpty()) { - final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); - resolvedBounds.set(mTmpBounds); - // Exclude the horizontal decor area because the activity will be centered - // horizontally in parent's app bounds to balance the visual appearance. - resolvedBounds.left = parentAppBounds.left; - task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - getFixedRotationTransformDisplayInfo()); - final int offsetX = getHorizontalCenterOffset( - parentAppBounds.width(), resolvedBounds.width()); - if (offsetX > 0) { - offsetBounds(resolvedConfig, offsetX - resolvedBounds.left, 0 /* offsetY */); + // If the activity has requested override bounds, the configuration needs to be + // computed accordingly. + if (!matchParentBounds()) { + task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); } + } else { + resolveFullscreenConfiguration(newParentConfiguration); } } @@ -6399,6 +6388,43 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A getResolvedOverrideConfiguration().seq = mConfigurationSeq; } + /** + * 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 + * the visual appearance. The policy of aspect ratio has higher priority than the requested + * override bounds. + */ + private void resolveFullscreenConfiguration(Configuration newParentConfiguration) { + final Configuration resolvedConfig = getResolvedOverrideConfiguration(); + final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); + final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); + final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); + // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use + // restricted size (resolved bounds may be the requested override bounds). + mTmpBounds.setEmpty(); + applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds); + // If the out bounds is not empty, it means the activity cannot fill parent's app bounds, + // then there is space to be centered. + final boolean needToBeCentered = !mTmpBounds.isEmpty(); + if (needToBeCentered) { + resolvedBounds.set(mTmpBounds); + // Exclude the horizontal decor area. + resolvedBounds.left = parentAppBounds.left; + } + if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) { + // Compute the configuration based on the resolved bounds. If aspect ratio doesn't + // restrict, the bounds should be the requested override bounds. + task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, + getFixedRotationTransformDisplayInfo()); + } + if (needToBeCentered) { + // Offset to center relative to parent's app bounds. + final int offsetX = getHorizontalCenterOffset( + parentAppBounds.width(), resolvedBounds.width()); + offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */); + } + } + /** * Resolves consistent screen configuration for orientation and rotation changes without * inheriting the parent bounds. @@ -6507,7 +6533,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Above coordinates are in "@" space, now place "*" and "#" to screen space. final int screenPosX = parentAppBounds.left + offsetX; final int screenPosY = parentBounds.top; - if (screenPosX > 0 || screenPosY > 0) { + if (screenPosX != 0 || screenPosY != 0) { if (mSizeCompatBounds != null) { mSizeCompatBounds.offset(screenPosX, screenPosY); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 5ab5fbcf693da..92ea913630d37 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2127,14 +2127,26 @@ class Task extends WindowContainer { intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets); } + /** + * Forces the app bounds related configuration can be computed by + * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo, + * ActivityRecord.CompatDisplayInsets)}. + */ + private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) { + final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds(); + if (appBounds != null) { + appBounds.setEmpty(); + } + inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED; + inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED; + } + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) { if (overrideDisplayInfo != null) { // Make sure the screen related configs can be computed by the provided display info. - inOutConfig.windowConfiguration.setAppBounds(null); inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; - inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED; - inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED; + invalidateAppBoundsConfig(inOutConfig); } computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo, null /* compatInsets */); @@ -2149,6 +2161,10 @@ class Task extends WindowContainer { void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig, @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + if (compatInsets != null) { + // Make sure the app bounds can be computed by the compat insets. + invalidateAppBoundsConfig(inOutConfig); + } computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, compatInsets); } 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 ba4a82fc2d54e..edf81ea32ad36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -107,10 +107,16 @@ public class SizeCompatTests extends ActivityTestsBase { setUpApp(display); // Put app window into freeform and then make it a compat app. - mTask.setBounds(100, 100, 400, 600); + final Rect bounds = new Rect(100, 100, 400, 600); + mTask.setBounds(bounds); prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); + assertEquals(bounds, mActivity.getBounds()); + + // The activity should be able to accept negative x position [-150, 100 - 150, 600]. + final int dx = bounds.left + bounds.width() / 2; + mTask.setBounds(bounds.left - dx, bounds.top, bounds.right - dx, bounds.bottom); + assertEquals(mTask.getBounds(), mActivity.getBounds()); - final Rect bounds = new Rect(mActivity.getBounds()); final int density = mActivity.getConfiguration().densityDpi; // change display configuration to fullscreen @@ -231,11 +237,7 @@ public class SizeCompatTests extends ActivityTestsBase { // The position should be horizontal centered. assertEquals((displayWidth - bounds.width()) / 2, bounds.left); - final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState( - mService.mWindowManager, mock(Session.class), new TestIWindow(), - new WindowManager.LayoutParams(), mActivity); - mActivity.addWindow(w); - mActivity.mDisplayContent.mInputMethodTarget = w; + mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity); // Make sure IME cannot attach to the app, otherwise IME window will also be shifted. assertFalse(mActivity.mDisplayContent.isImeAttachedToApp()); } @@ -475,6 +477,25 @@ public class SizeCompatTests extends ActivityTestsBase { assertEquals((int) (dw * maxAspect), mActivity.getBounds().width()); // The bounds should be horizontal centered: (2500-1900)/2=350. assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left); + + // The letterbox needs a main window to layout. + addWindowToActivity(mActivity); + // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}. + mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */); + // The letterbox insets should be [350, 0 - 350, 0]. + assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0), + mActivity.getLetterboxInsets()); + } + + private WindowState addWindowToActivity(ActivityRecord activity) { + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState( + mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity); + WindowTestsBase.makeWindowVisible(w); + w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; + mActivity.addWindow(w); + return w; } /**