Merge "Fix app in size compat when it should not"

This commit is contained in:
Chris Li
2021-01-16 01:09:53 +00:00
committed by Android (Google) Code Review
3 changed files with 163 additions and 37 deletions

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;