Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 870f472e authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

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

parents 3ac7938f 07658877
Loading
Loading
Loading
Loading
+48 −21
Original line number Diff line number Diff line
@@ -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);
            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;
+28 −12
Original line number Diff line number Diff line
@@ -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) {

        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.
            refActivity = getTopNonFinishingActivity(false /* includeOverlays */);
        }
        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() {
+87 −4
Original line number Diff line number Diff line
@@ -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;