Merge "Fix app in size compat when it should not"
This commit is contained in:
@@ -6492,8 +6492,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
mLastReportedConfiguration.setConfiguration(global, override);
|
||||
}
|
||||
|
||||
boolean hasCompatDisplayInsets() {
|
||||
return mCompatDisplayInsets != null;
|
||||
@Nullable
|
||||
CompatDisplayInsets getCompatDisplayInsets() {
|
||||
return mCompatDisplayInsets;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7816,13 +7817,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
/**
|
||||
* The precomputed insets of the display in each rotation. This is used to make the size
|
||||
* compatibility mode activity compute the configuration without relying on its current display.
|
||||
* This currently only supports fullscreen and freeform windowing mode.
|
||||
*/
|
||||
static class CompatDisplayInsets {
|
||||
/** The container width on rotation 0. */
|
||||
private final int mWidth;
|
||||
/** The container height on rotation 0. */
|
||||
private final int mHeight;
|
||||
/** Whether the {@link Task} windowingMode represents a floating window*/
|
||||
final boolean mIsFloating;
|
||||
|
||||
/** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */
|
||||
final boolean mIsTaskLetterboxed;
|
||||
/**
|
||||
* The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
|
||||
* is used to compute the appBounds.
|
||||
@@ -7849,27 +7853,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
mNonDecorInsets[rotation] = emptyRect;
|
||||
mStableInsets[rotation] = emptyRect;
|
||||
}
|
||||
mIsTaskLetterboxed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
final Task task = container.getTask();
|
||||
if (task != null && task.isTaskLetterboxed()) {
|
||||
// For apps in Task letterbox, it should fill the task bounds.
|
||||
final Point dimensions = getRotationZeroDimensions(task);
|
||||
mWidth = dimensions.x;
|
||||
mHeight = dimensions.y;
|
||||
} else {
|
||||
// If the activity is not floating nor letterboxed, assume it fills the root.
|
||||
final RootDisplayArea root = container.getRootDisplayArea();
|
||||
if (root == null || root == display) {
|
||||
mWidth = display.mBaseDisplayWidth;
|
||||
mHeight = display.mBaseDisplayHeight;
|
||||
} else {
|
||||
final Point dimensions = getRotationZeroDimensions(root);
|
||||
mWidth = dimensions.x;
|
||||
mHeight = dimensions.y;
|
||||
}
|
||||
}
|
||||
mIsTaskLetterboxed = task != null && task.isTaskLetterboxed();
|
||||
|
||||
// Store the bounds of the Task for the non-resizable activity to use in size compat
|
||||
// mode so that the activity will not be resized regardless the windowing mode it is
|
||||
// currently in.
|
||||
final WindowContainer filledContainer = task != null ? task : display;
|
||||
final Point dimensions = getRotationZeroDimensions(filledContainer);
|
||||
mWidth = dimensions.x;
|
||||
mHeight = dimensions.y;
|
||||
|
||||
// Bounds of the filled container if it doesn't fill the display.
|
||||
final Rect unfilledContainerBounds =
|
||||
filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect();
|
||||
final DisplayPolicy policy = display.getDisplayPolicy();
|
||||
for (int rotation = 0; rotation < 4; rotation++) {
|
||||
mNonDecorInsets[rotation] = new Rect();
|
||||
@@ -7882,6 +7883,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
|
||||
mStableInsets[rotation].set(mNonDecorInsets[rotation]);
|
||||
policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
|
||||
|
||||
if (unfilledContainerBounds == null) {
|
||||
continue;
|
||||
}
|
||||
// The insets is based on the display, but the container may be smaller than the
|
||||
// display, so update the insets to exclude parts that are not intersected with the
|
||||
// container.
|
||||
unfilledContainerBounds.set(filledContainer.getBounds());
|
||||
display.rotateBounds(
|
||||
filledContainer.getConfiguration().windowConfiguration.getRotation(),
|
||||
rotation,
|
||||
unfilledContainerBounds);
|
||||
updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
|
||||
updateInsetsForBounds(unfilledContainerBounds, dw, dh, mStableInsets[rotation]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7899,6 +7914,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return rotated ? new Point(height, width) : new Point(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display insets to exclude the parts that are not intersected with the given
|
||||
* bounds.
|
||||
*/
|
||||
private static void updateInsetsForBounds(Rect bounds, int displayWidth, int displayHeight,
|
||||
Rect inset) {
|
||||
inset.left = Math.max(0, inset.left - bounds.left);
|
||||
inset.top = Math.max(0, inset.top - bounds.top);
|
||||
inset.right = Math.max(0, bounds.right - displayWidth + inset.right);
|
||||
inset.bottom = Math.max(0, bounds.bottom - displayHeight + inset.bottom);
|
||||
}
|
||||
|
||||
void getBoundsByRotation(Rect outBounds, int rotation) {
|
||||
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
|
||||
final int dw = rotated ? mHeight : mWidth;
|
||||
|
||||
@@ -2825,9 +2825,7 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
|
||||
|
||||
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
|
||||
computeFullscreenBounds(outOverrideBounds, null /* refActivity */,
|
||||
newParentConfig.windowConfiguration.getBounds(),
|
||||
newParentConfig.orientation);
|
||||
computeFullscreenBounds(outOverrideBounds, newParentConfig);
|
||||
// The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
|
||||
// the parent or display is smaller than the size, the content may be cropped.
|
||||
return;
|
||||
@@ -2867,19 +2865,19 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
* {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN} when the parent doesn't handle the
|
||||
* orientation change and the requested orientation is different from the parent.
|
||||
*/
|
||||
void computeFullscreenBounds(@NonNull Rect outBounds, @Nullable ActivityRecord refActivity,
|
||||
@NonNull Rect parentBounds, int parentOrientation) {
|
||||
void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
|
||||
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
|
||||
outBounds.setEmpty();
|
||||
if (handlesOrientationChangeFromDescendant()) {
|
||||
// No need to letterbox at task level. Display will handle fixed-orientation requests.
|
||||
return;
|
||||
}
|
||||
if (refActivity == null) {
|
||||
// Use the top activity as the reference of orientation. Don't include overlays because
|
||||
// it is usually not the actual content or just temporarily shown.
|
||||
// E.g. ForcedResizableInfoActivity.
|
||||
refActivity = getTopNonFinishingActivity(false /* includeOverlays */);
|
||||
}
|
||||
|
||||
final int parentOrientation = newParentConfig.orientation;
|
||||
// Use the top activity as the reference of orientation. Don't include overlays because
|
||||
// it is usually not the actual content or just temporarily shown.
|
||||
// E.g. ForcedResizableInfoActivity.
|
||||
final ActivityRecord refActivity = getTopNonFinishingActivity(false /* includeOverlays */);
|
||||
|
||||
// If the task or the reference activity requires a different orientation (either by
|
||||
// override or activityInfo), make it fit the available bounds by scaling down its bounds.
|
||||
@@ -2891,11 +2889,17 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (refActivity != null && refActivity.hasCompatDisplayInsets()) {
|
||||
final ActivityRecord.CompatDisplayInsets compatDisplayInsets =
|
||||
refActivity == null ? null : refActivity.getCompatDisplayInsets();
|
||||
if (compatDisplayInsets != null && !compatDisplayInsets.mIsTaskLetterboxed) {
|
||||
// App prefers to keep its original size.
|
||||
// If the size compat is from previous task letterboxing, we may want to have task
|
||||
// letterbox again, otherwise it will show the size compat restart button even if the
|
||||
// restart bounds will be the same.
|
||||
return;
|
||||
}
|
||||
|
||||
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
|
||||
final int parentWidth = parentBounds.width();
|
||||
final int parentHeight = parentBounds.height();
|
||||
float aspect = Math.max(parentWidth, parentHeight)
|
||||
@@ -2930,6 +2934,18 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
final int left = parentBounds.centerX() - width / 2;
|
||||
outBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
|
||||
}
|
||||
|
||||
if (compatDisplayInsets != null) {
|
||||
compatDisplayInsets.getBoundsByRotation(
|
||||
mTmpBounds, newParentConfig.windowConfiguration.getRotation());
|
||||
if (outBounds.width() != mTmpBounds.width()
|
||||
|| outBounds.height() != mTmpBounds.height()) {
|
||||
// The app shouldn't be resized, we only do task letterboxing if the compat bounds
|
||||
// is also from the same task letterbox. Otherwise, clear the task bounds to show
|
||||
// app in size compat mode.
|
||||
outBounds.setEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect updateOverrideConfigurationFromLaunchBounds() {
|
||||
|
||||
@@ -20,8 +20,10 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
import static android.view.SurfaceProto.ROTATION_180;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
@@ -39,7 +41,6 @@ import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
@@ -243,7 +244,7 @@ public class SizeCompatTests extends WindowTestsBase {
|
||||
// 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(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.
|
||||
@@ -729,7 +730,7 @@ public class SizeCompatTests extends WindowTestsBase {
|
||||
// Update with new activity requested orientation and recompute bounds with no previous
|
||||
// size compat cache.
|
||||
verify(mTask).onDescendantOrientationChanged(same(newActivity));
|
||||
verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
|
||||
verify(mTask).computeFullscreenBounds(any(), any());
|
||||
|
||||
final Rect displayBounds = new Rect(display.getBounds());
|
||||
final Rect taskBounds = new Rect(mTask.getBounds());
|
||||
@@ -770,7 +771,7 @@ public class SizeCompatTests extends WindowTestsBase {
|
||||
// Update with new activity requested orientation and recompute bounds with no previous
|
||||
// size compat cache.
|
||||
verify(mTask).onDescendantOrientationChanged(same(newActivity));
|
||||
verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
|
||||
verify(mTask).computeFullscreenBounds(any(), any());
|
||||
|
||||
final Rect displayBounds = new Rect(display.getBounds());
|
||||
final Rect taskBounds = new Rect(mTask.getBounds());
|
||||
@@ -821,6 +822,88 @@ public class SizeCompatTests extends WindowTestsBase {
|
||||
assertEquals(activityBounds, mActivity.getBounds());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisplayIgnoreOrientationRequest_rotated180_notInSizeCompat() {
|
||||
// Set up a display in landscape and ignoring orientation request.
|
||||
setUpDisplaySizeWithApp(2800, 1400);
|
||||
final DisplayContent display = mActivity.mDisplayContent;
|
||||
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
|
||||
|
||||
// Portrait fixed app.
|
||||
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
|
||||
|
||||
// In Task letterbox
|
||||
assertTrue(mTask.isTaskLetterboxed());
|
||||
assertFalse(mActivity.inSizeCompatMode());
|
||||
|
||||
// Rotate display to portrait.
|
||||
rotateDisplay(display, ROTATION_90);
|
||||
|
||||
// App should be in size compat.
|
||||
assertFalse(mTask.isTaskLetterboxed());
|
||||
assertScaled();
|
||||
|
||||
// Rotate display to landscape.
|
||||
rotateDisplay(display, ROTATION_180);
|
||||
|
||||
// In Task letterbox
|
||||
assertTrue(mTask.isTaskLetterboxed());
|
||||
assertFalse(mActivity.inSizeCompatMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisplayIgnoreOrientationRequestWithInsets_rotated180_notInSizeCompat() {
|
||||
// Set up a display in portrait with display cutout and ignoring orientation request.
|
||||
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 2800)
|
||||
.setNotch(75)
|
||||
.build();
|
||||
setUpApp(display);
|
||||
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
|
||||
|
||||
// Landscape fixed app.
|
||||
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
|
||||
|
||||
// In Task letterbox
|
||||
assertTrue(mTask.isTaskLetterboxed());
|
||||
assertFalse(mActivity.inSizeCompatMode());
|
||||
|
||||
// Rotate display to portrait.
|
||||
rotateDisplay(display, ROTATION_90);
|
||||
|
||||
// App should be in size compat.
|
||||
assertFalse(mTask.isTaskLetterboxed());
|
||||
assertScaled();
|
||||
|
||||
// Rotate display to landscape.
|
||||
rotateDisplay(display, ROTATION_180);
|
||||
|
||||
// In Task letterbox
|
||||
assertTrue(mTask.isTaskLetterboxed());
|
||||
assertFalse(mActivity.inSizeCompatMode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskDisplayAreaNotFillDisplay() {
|
||||
setUpDisplaySizeWithApp(1400, 2800);
|
||||
final DisplayContent display = mActivity.mDisplayContent;
|
||||
final TaskDisplayArea taskDisplayArea = mActivity.getDisplayArea();
|
||||
taskDisplayArea.setBounds(0, 0, 1000, 2400);
|
||||
|
||||
// Portrait fixed app.
|
||||
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
|
||||
|
||||
final Rect displayBounds = new Rect(display.getBounds());
|
||||
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
|
||||
assertEquals(2800, displayBounds.width());
|
||||
assertEquals(1400, displayBounds.height());
|
||||
taskDisplayArea.setBounds(0, 0, 2400, 1000);
|
||||
|
||||
final Rect activityBounds = new Rect(mActivity.getBounds());
|
||||
assertFalse(mActivity.inSizeCompatMode());
|
||||
assertEquals(2400, activityBounds.width());
|
||||
assertEquals(1000, activityBounds.height());
|
||||
}
|
||||
|
||||
private static WindowState addWindowToActivity(ActivityRecord activity) {
|
||||
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
|
||||
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||
|
||||
Reference in New Issue
Block a user