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

Commit 08408b6d authored by Massimo Carli's avatar Massimo Carli Committed by Android (Google) Code Review
Browse files

Merge "[32/n] Fix Insets for Letterbox in Shell" into main

parents a5b1ea9a 4f13d951
Loading
Loading
Loading
Loading
+31 −11
Original line number Diff line number Diff line
@@ -480,6 +480,15 @@ class AppCompatLetterboxPolicy {
        private final Point mLetterboxPosition = new Point();
        private boolean mRunning;

        // The model needs to store the bounds for the multiple surfaces which will be
        // created in Shell anyway.
        private final Rect mLeftBounds = new Rect();
        private final Rect mTopBounds = new Rect();
        private final Rect mRightBounds = new Rect();
        private final Rect mBottomBounds = new Rect();
        private final Rect[] mSurfacesBounds =
                new Rect[]{mLeftBounds, mTopBounds, mRightBounds, mBottomBounds};

        @Override
        public void layoutLetterboxIfNeeded(@NonNull WindowState w) {
            mRunning = true;
@@ -488,6 +497,7 @@ class AppCompatLetterboxPolicy {
            calculateLetterboxInnerBounds(mActivityRecord, w, mInnerBounds);
            mActivityRecord.mAppCompatController.getReachabilityPolicy()
                    .setLetterboxInnerBoundsSupplier(() -> mInnerBounds);
            updateSurfacesBounds();
        }

        @Override
@@ -528,10 +538,10 @@ class AppCompatLetterboxPolicy {
        public Rect getLetterboxInsets() {
            if (isRunning()) {
                return new Rect(
                        Math.max(0, mInnerBounds.left - mOuterBounds.left),
                        Math.max(0, mOuterBounds.top - mInnerBounds.top),
                        Math.max(0, mOuterBounds.right - mInnerBounds.right),
                        Math.max(0, mInnerBounds.bottom - mOuterBounds.bottom)
                        Math.max(0, mLeftBounds.width()),
                        Math.max(0, mTopBounds.height()),
                        Math.max(0, mRightBounds.width()),
                        Math.max(0, mBottomBounds.height())
                );
            }
            return new Rect();
@@ -570,15 +580,25 @@ class AppCompatLetterboxPolicy {
            start(winHint);
        }

        /**
         * @return {@code true} if bar shown within a given rectangle is allowed to be fully
         *          transparent when the current activity is displayed.
         */
        @Override
        public boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
            // TODO(b/374921442) Handle Transparent Activities Letterboxing in Shell.
            // At the moment Shell handles letterbox with a single surface. This would make
            // notIntersectsOrFullyContains() to return false in the existing Letterbox
            // implementation.
            // Note: Previous implementation is
            //       !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect);
            return !isRunning();
            return !isRunning() || AppCompatLetterboxUtils.fullyContainsOrNotIntersects(rect,
                    mSurfacesBounds);
        }

        private void updateSurfacesBounds() {
            mTopBounds.set(mOuterBounds.left, mOuterBounds.top, mOuterBounds.right,
                    mInnerBounds.top);
            mLeftBounds.set(mOuterBounds.left, mOuterBounds.top, mInnerBounds.left,
                    mOuterBounds.bottom);
            mRightBounds.set(mInnerBounds.right, mOuterBounds.top, mOuterBounds.right,
                    mOuterBounds.bottom);
            mBottomBounds.set(mOuterBounds.left, mInnerBounds.bottom, mOuterBounds.right,
                    mOuterBounds.bottom);
        }
    }
}
+27 −0
Original line number Diff line number Diff line
@@ -99,4 +99,31 @@ class AppCompatLetterboxUtils {
        outInnerBounds.set(
                transparentPolicy.isRunning() ? activity.getBounds() : window.getFrame());
    }


    /**
     * Returns {@code true} if the letterbox does not overlap with the bar, or the letterbox can
     * fully cover the window frame.
     *
     * @param rect             The area of the window frame.
     * @param boundsToCheck A Collection of bounds to check.
     */
    static boolean fullyContainsOrNotIntersects(@NonNull Rect rect, @NonNull Rect[] boundsToCheck) {
        // TODO(b/409293223): Make this algorithm simpler and more efficient.
        int emptyCount = 0;
        int noOverlappingCount = 0;
        for (Rect bounds : boundsToCheck) {
            if (bounds.isEmpty()) {
                // empty letterbox
                emptyCount++;
            } else if (!Rect.intersects(bounds, rect)) {
                // no overlapping
                noOverlappingCount++;
            } else if (bounds.contains(rect)) {
                // overlapping and covered
                return true;
            }
        }
        return (emptyCount + noOverlappingCount) == boundsToCheck.length;
    }
}
+5 −16
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ public class Letterbox {
    // for overlaping an app window and letterbox surfaces.
    private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
    private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
    private final Rect[] mTmpSurfacesRect = new Rect[4];

    @NonNull
    private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
@@ -138,23 +139,11 @@ public class Letterbox {
     *
     * @param rect The area of the window frame.
     */
    boolean notIntersectsOrFullyContains(Rect rect) {
        int emptyCount = 0;
        int noOverlappingCount = 0;
        for (LetterboxSurface surface : mSurfaces) {
            final Rect surfaceRect = surface.mLayoutFrameGlobal;
            if (surfaceRect.isEmpty()) {
                // empty letterbox
                emptyCount++;
            } else if (!Rect.intersects(surfaceRect, rect)) {
                // no overlapping
                noOverlappingCount++;
            } else if (surfaceRect.contains(rect)) {
                // overlapping and covered
                return true;
            }
    boolean notIntersectsOrFullyContains(@NonNull Rect rect) {
        for (int i = 0; i < mTmpSurfacesRect.length; i++) {
            mTmpSurfacesRect[i] = mSurfaces[i].mLayoutFrameGlobal;
        }
        return (emptyCount + noOverlappingCount) == mSurfaces.length;
        return AppCompatLetterboxUtils.fullyContainsOrNotIntersects(rect, mTmpSurfacesRect);
    }

    /**
+139 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxInnerBounds;
import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxOuterBounds;
import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxPosition;
import static com.android.server.wm.AppCompatLetterboxUtils.fullyContainsOrNotIntersects;

import static org.mockito.Mockito.mock;

@@ -143,6 +144,123 @@ public class AppCompatLetterboxUtilsTest extends WindowTestsBase {
        });
    }

    @Test
    public void testNoBoundsToCheck() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck();
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testEmptyBoundsToCheck() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(30, 30, 40, 40));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testContainsEmptyRect() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect());
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_NoIntersection() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_FullyContains() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(-5, -5, 15, 15));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_PartiallyIntersects() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(5, 5, 15, 15));
            robot.checkFullyContainsOrNotIntersects(/* expected */ false);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_MultipleBoundsNoIntersection() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(-20, -20, -10, -10));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithOneContaining() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(-5, -5, 15, 15));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithOneIntersecting() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(5, 5, 15, 15));
            robot.checkFullyContainsOrNotIntersects(/* expected */ false);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithEmptyAndNoIntersection() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(), new Rect(10, 10, 20, 20));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_MultipleBoundsWithEmptyAndContaining() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(/* left */ 0, /* top */ 0, /* right */ 10, /* bottom */ 10);
            robot.setBoundsToCheck(new Rect(), new Rect(-5, -5, 15, 15));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_EmptyRectToCheck() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(new Rect());
            robot.setBoundsToCheck(new Rect(10, 10, 20, 20), new Rect(-5, -5, 15, 15));
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    @Test
    public void testCheckFullyContainsOrNotIntersects_EmptyRectToCheckAndEmptyBounds() {
        runTestScenario((robot) -> {
            robot.setWindowFrameArea(new Rect());
            robot.setBoundsToCheck(new Rect());
            robot.checkFullyContainsOrNotIntersects(/* expected */ true);
        });
    }

    /**
     * Runs a test scenario providing a Robot.
     */
@@ -157,6 +275,10 @@ public class AppCompatLetterboxUtilsTest extends WindowTestsBase {
        private final Rect mInnerBound = new Rect();
        private final Rect mOuterBound = new Rect();

        private final Rect mWindowFrameArea = new Rect();

        private Rect[] mBoundsToCheck;

        @NonNull
        private final WindowState mWindowState;

@@ -188,6 +310,18 @@ public class AppCompatLetterboxUtilsTest extends WindowTestsBase {
            doReturn(frame).when(mWindowState).getFrame();
        }

        void setWindowFrameArea(int left, int top, int right, int bottom) {
            mWindowFrameArea.set(left, top, right, bottom);
        }

        void setWindowFrameArea(@NonNull Rect windowFrameArea) {
            mWindowFrameArea.set(windowFrameArea);
        }

        void setBoundsToCheck(@NonNull Rect... boundsToCheck) {
            mBoundsToCheck = boundsToCheck;
        }

        void getLetterboxPosition() {
            calculateLetterboxPosition(activity().top(), mPosition);
        }
@@ -235,5 +369,10 @@ public class AppCompatLetterboxUtilsTest extends WindowTestsBase {
            Assert.assertEquals(mInnerBound, activity().top().getBounds());
        }

        void checkFullyContainsOrNotIntersects(boolean expected) {
            Assert.assertEquals(expected,
                    fullyContainsOrNotIntersects(mWindowFrameArea, mBoundsToCheck));
        }

    }
}