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

Commit 41eff682 authored by Mariia Sandrikova's avatar Mariia Sandrikova
Browse files

Letterbox: show activity under insets when possible or needed

Activity handles insets on its own (e.g. under status bar or navigation bar). After task's surface is repositioned, intersection between an activity and insets can change but if it doesn't, the activity should be shown under insets to maximize visible area.

Also, an activity can use area under insets and insets shouldn't be cropped in this case regardless of a position on the screen.

Test: atest LetterboxTaskListenerTest
Fix: 174086576
Change-Id: I84cd28520ead3fb0099f15cc672167871c7d78a1
parent b3bf04dc
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -194,6 +194,14 @@ public class TaskInfo {
    @Nullable
    public Rect letterboxActivityBounds;

    /**
     * Activity insets if this task or its top activity is presented in letterbox mode and
     * {@code null} otherwise.
     * @hide
     */
    @Nullable
    public Rect letterboxActivityInsets;

    /**
     * Relative position of the task's top left corner in the parent container.
     * @hide
@@ -328,7 +336,8 @@ public class TaskInfo {
                && Objects.equals(
                        getConfiguration().windowConfiguration.getMaxBounds(),
                        that.getConfiguration().windowConfiguration.getMaxBounds())
                && Objects.equals(parentBounds, that.parentBounds);
                && Objects.equals(parentBounds, that.parentBounds)
                && Objects.equals(letterboxActivityInsets, that.letterboxActivityInsets);
    }

    /**
@@ -365,6 +374,7 @@ public class TaskInfo {
        parentBounds = source.readTypedObject(Rect.CREATOR);
        isFocused = source.readBoolean();
        isVisible = source.readBoolean();
        letterboxActivityInsets = source.readTypedObject(Rect.CREATOR);
    }

    /**
@@ -402,6 +412,7 @@ public class TaskInfo {
        dest.writeTypedObject(parentBounds, flags);
        dest.writeBoolean(isFocused);
        dest.writeBoolean(isVisible);
        dest.writeTypedObject(letterboxActivityInsets, flags);
    }

    @Override
@@ -428,6 +439,7 @@ public class TaskInfo {
                + " parentBounds=" + parentBounds
                + " isFocused=" + isFocused
                + " isVisible=" + isVisible
                + " letterboxActivityInsets=" + letterboxActivityInsets
                + "}";
    }
}
+47 −4
Original line number Diff line number Diff line
@@ -164,8 +164,6 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
            switch (gravity) {
                case Gravity.TOP:
                    positionInParent.y += taskBoundsWithInsets.top - activityBoundsWithInsets.top;
                    // Showing status bar decor view.
                    crop.top -= activityBoundsWithInsets.top - activityBounds.top;
                    break;
                case Gravity.CENTER:
                    positionInParent.y +=
@@ -187,8 +185,6 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
            final int gravity = mLetterboxConfigController.getLandscapeGravity();
            // Align activity to the top.
            positionInParent.y += taskBoundsWithInsets.top - activityBoundsWithInsets.top;
            // Showing status bar decor view.
            crop.top -= activityBoundsWithInsets.top - activityBounds.top;
            switch (gravity) {
                case Gravity.LEFT:
                    positionInParent.x += taskBoundsWithInsets.left - activityBoundsWithInsets.left;
@@ -209,6 +205,53 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
                            + " for task: #" + taskInfo.taskId);
            }
        }

        // New bounds of the activity after it's repositioned with required gravity.
        Rect newActivityBounds = new Rect(activityBounds);
        // Task's surfce will be repositioned to positionInParent together with the activity
        // inside it so the new activity bounds are the original activity bounds offset by
        // the task's offset.
        newActivityBounds.offset(
                positionInParent.x - taskBounds.left, positionInParent.y - taskBounds.top);
        Rect newActivityBoundsWithInsets = new Rect(newActivityBounds);
        newActivityBoundsWithInsets.intersect(displayBoundsWithInsets);
        // Activity handles insets on its own (e.g. under status bar or navigation bar).
        // crop that is calculated above crops all insets from an activity and below insets that
        // can be shown are added back to the crop bounds  (e.g. if activity is still shown at the
        // top of the display then the top inset won't be cropped).
        // After task's surface is repositioned, intersection between an activity and insets can
        // change but if it doesn't, the activity should be shown under insets to maximize visible
        // area.
        // Also, an activity can use area under insets and insets shouldn't be cropped in this case
        // regardless of a position on the screen.
        final Rect activityInsetsFromCore = taskInfo.letterboxActivityInsets;
        if (newActivityBounds.top - newActivityBoundsWithInsets.top
                == activityBounds.top - activityBoundsWithInsets.top
                // Check whether an activity is shown under inset. If it is, then the inset from
                // WM Core and the inset computed here will be different because local insets
                // doesn't take into account visibility of insets requested by the activity.
                ||  activityBoundsWithInsets.top - activityBounds.top
                        != activityInsetsFromCore.top) {
            crop.top -= activityBoundsWithInsets.top - activityBounds.top;
        }
        if (newActivityBounds.bottom - newActivityBoundsWithInsets.bottom
                == activityBounds.bottom - activityBoundsWithInsets.bottom
                || activityBounds.bottom - activityBoundsWithInsets.bottom
                        != activityInsetsFromCore.bottom) {
            crop.bottom += activityBounds.bottom - activityBoundsWithInsets.bottom;
        }
        if (newActivityBounds.left - newActivityBoundsWithInsets.left
                == activityBounds.left - activityBoundsWithInsets.left
                || activityBoundsWithInsets.left - activityBounds.left
                        != activityInsetsFromCore.left) {
            crop.left -= activityBoundsWithInsets.left - activityBounds.left;
        }
        if (newActivityBounds.right - newActivityBoundsWithInsets.right
                == activityBounds.right - activityBoundsWithInsets.right
                || activityBounds.right - activityBoundsWithInsets.right
                        != activityInsetsFromCore.right) {
            crop.right += activityBounds.right - activityBoundsWithInsets.right;
        }
    }

    private Insets getInsets() {
+50 −15
Original line number Diff line number Diff line
@@ -99,7 +99,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 200, 100),
                        /* activityBounds */ new Rect(75, 0, 125, 75),
                        /* taskBounds */ new Rect(50, 0, 125, 100)),
                        /* taskBounds */ new Rect(50, 0, 125, 100),
                        /* activityInsets */ new Rect(0, 0, 0, 0)),
                mLeash);

        // Task doesn't need to repositioned
@@ -114,7 +115,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* parentBounds */ new Rect(0, 0, 200, 100),
                        // Activity is offset by 25 to the left
                        /* activityBounds */ new Rect(50, 0, 100, 75),
                        /* taskBounds */ new Rect(50, 0, 125, 100)));
                        /* taskBounds */ new Rect(50, 0, 125, 100),
                        /* activityInsets */ new Rect(0, 0, 0, 0)));

        // Task needs to be repositioned by 25 to the left
        verifySetPosition(75, 0);
@@ -135,7 +137,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 200, 100),
                        /* activityBounds */ new Rect(150, 0, 200, 75),
                        /* taskBounds */ new Rect(125, 0, 200, 100)),
                        /* taskBounds */ new Rect(125, 0, 200, 100),
                        /* activityInsets */ new Rect(0, 10, 10, 0)),
                mLeash);

        verifySetPosition(-15, 0);
@@ -156,7 +159,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 200, 100),
                        /* activityBounds */ new Rect(150, 0, 200, 75),
                        /* taskBounds */ new Rect(125, 0, 200, 100)),
                        /* taskBounds */ new Rect(125, 0, 200, 100),
                        /* activityInsets */ new Rect(0, 10, 10, 0)),
                mLeash);

        verifySetPosition(55, 0);
@@ -177,7 +181,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 200, 100),
                        /* activityBounds */ new Rect(50, 0, 100, 75),
                        /* taskBounds */ new Rect(25, 0, 100, 100)),
                        /* taskBounds */ new Rect(25, 0, 100, 100),
                        /* activityInsets */ new Rect(0, 10, 10, 0)),
                mLeash);

        verifySetPosition(115, 0);
@@ -198,7 +203,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 100, 150),
                        /* activityBounds */ new Rect(0, 75, 50, 125),
                        /* taskBounds */ new Rect(0, 50, 100, 125)),
                        /* taskBounds */ new Rect(0, 50, 100, 125),
                        /* activityInsets */ new Rect(10, 0, 0, 0)),
                mLeash);

        verifySetPosition(20, -15);
@@ -219,7 +225,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 100, 150),
                        /* activityBounds */ new Rect(0, 75, 50, 125),
                        /* taskBounds */ new Rect(0, 50, 100, 125)),
                        /* taskBounds */ new Rect(0, 50, 100, 125),
                        /* activityInsets */ new Rect(10, 0, 0, 0)),
                mLeash);

        verifySetPosition(20, 20);
@@ -227,6 +234,29 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
        verifySetWindowCrop(new Rect(10, 25, 50, 75));
    }

    @Test
    public void testOnTaskInfoAppeared_portraitWithCenterGravity_visibleLeftInset() {
        mLetterboxConfigController.setPortraitGravity(Gravity.CENTER);
        setWindowBoundsAndInsets(
                /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20));

        mLetterboxTaskListener.onTaskAppeared(
                createTaskInfo(
                        /* taskId */ 1,
                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 100, 150),
                        /* activityBounds */ new Rect(0, 75, 50, 125),
                        /* taskBounds */ new Rect(0, 50, 100, 125),
                        // Activity is drawn under the left inset.
                        /* activityInsets */ new Rect(0, 0, 0, 0)),
                mLeash);

        verifySetPosition(20, 20);
        // Should return activity coordinates offset by task coordinates
        verifySetWindowCrop(new Rect(0, 25, 50, 75));
    }

    @Test
    public void testOnTaskInfoAppeared_portraitWithBottomGravity() {
        mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM);
@@ -240,7 +270,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 100, 150),
                        /* activityBounds */ new Rect(0, 75, 50, 125),
                        /* taskBounds */ new Rect(0, 50, 100, 125)),
                        /* taskBounds */ new Rect(0, 50, 100, 125),
                        /* activityInsets */ new Rect(10, 0, 0, 0)),
                mLeash);

        verifySetPosition(20, 55);
@@ -261,14 +292,14 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds
                        /* parentBounds */ new Rect(0, 0, 200, 125),
                        /* activityBounds */ new Rect(15, 0, 175, 120),
                        /* taskBounds */ new Rect(0, 0, 100, 125)), // equal to parent bounds
                        /* taskBounds */ new Rect(0, 0, 200, 125),
                        /* activityInsets */ new Rect(10, 25, 10, 10)), // equal to parent bounds
                mLeash);

        // Activity fully covers parent bounds with insets so doesn't need to be moved.
        verifySetPosition(0, 0);
        // Should return activity coordinates offset by task coordinates minus all insets
        // except top one (keep status bar decor visible).
        verifySetWindowCrop(new Rect(25, 0, 165, 110));
        // Should return activity coordinates offset by task coordinates
        verifySetWindowCrop(new Rect(15, 0, 175, 120));
    }

    @Test
@@ -284,7 +315,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                        /* maxBounds= */ new Rect(0, 0, 100, 150),
                        /* parentBounds */ new Rect(0, 75, 100, 225),
                        /* activityBounds */ new Rect(25, 75, 75, 125),
                        /* taskBounds */ new Rect(0, 75, 100, 125)),
                        /* taskBounds */ new Rect(0, 75, 100, 125),
                        /* activityInsets */ new Rect(10, 0, 0, 0)),
                mLeash);

        verifySetPosition(0, 0);
@@ -295,7 +327,8 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
    public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
        setWindowBoundsAndInsets(new Rect(),  Insets.NONE);
        RunningTaskInfo taskInfo =
                createTaskInfo(/* taskId */ 1, new Rect(),  new Rect(), new Rect(), new Rect());
                createTaskInfo(/* taskId */ 1, new Rect(),  new Rect(), new Rect(), new Rect(),
                new Rect());
        mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
        mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
    }
@@ -319,13 +352,15 @@ public final class LetterboxTaskListenerTest extends ShellTestCase {
                final Rect maxBounds,
                final Rect parentBounds,
                final Rect activityBounds,
                final Rect taskBounds) {
                final Rect taskBounds,
                final Rect activityInsets) {
        RunningTaskInfo taskInfo = new RunningTaskInfo();
        taskInfo.taskId = taskId;
        taskInfo.configuration.windowConfiguration.setMaxBounds(maxBounds);
        taskInfo.parentBounds = parentBounds;
        taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
        taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
        taskInfo.letterboxActivityInsets = Rect.copyOrNull(activityInsets);

        return taskInfo;
    }
+13 −7
Original line number Diff line number Diff line
@@ -1366,7 +1366,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        } else if (mLetterbox != null) {
            mLetterbox.hide();
        }
        task.maybeUpdateLetterboxBounds(this, getLetterboxParams(w));
        maybeUpdateLetterboxInTaskOrganizer(w);
    }

    private void maybeUpdateLetterboxInTaskOrganizer(WindowState w) {
        boolean isLetterboxed = w.isLetterboxedAppWindow() && fillsParent();
        if (!isLetterboxed) {
            task.maybeUpdateLetterboxInTaskOrganizer(
                    this, /* activityBounds= */ null, /* activityInsets= */ null);
            return;
        }
        final Rect insets = w.getInsetsStateWithVisibilityOverride().calculateInsets(
                getBounds(), Type.systemBars(), false /* ignoreVisibility */);
        task.maybeUpdateLetterboxInTaskOrganizer(this, getBounds(), insets);
    }

    void updateLetterboxSurface(WindowState winHint) {
@@ -1380,12 +1392,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
    }

    @Nullable
    private Rect getLetterboxParams(WindowState w) {
        boolean isLetterboxed = w.isLetterboxedAppWindow() && fillsParent();
        return isLetterboxed ? getBounds() : null;
    }

    Rect getLetterboxInsets() {
        if (mLetterbox != null) {
            return mLetterbox.getInsets();
+14 −6
Original line number Diff line number Diff line
@@ -519,6 +519,11 @@ class Task extends WindowContainer<WindowContainer> {
    @Nullable
    private Rect mLetterboxActivityBounds;

    // Activity insets if this task or its top activity is presented in letterbox mode and
    // {@code null} otherwise.
    @Nullable
    private Rect mLetterboxActivityInsets;

    // Whether the task is currently being drag-resized
    private boolean mDragResizing;
    private int mDragResizeMode;
@@ -4083,6 +4088,7 @@ class Task extends WindowContainer<WindowContainer> {
        // bounds, e.g. fullscreen bounds instead of letterboxed bounds. To work around this,
        // assigning bounds from ActivityRecord#layoutLetterbox when they are ready.
        info.letterboxActivityBounds = Rect.copyOrNull(mLetterboxActivityBounds);
        info.letterboxActivityInsets = Rect.copyOrNull(mLetterboxActivityInsets);
        info.positionInParent = getRelativePosition();
        info.parentBounds = getParentBounds();

@@ -4110,15 +4116,17 @@ class Task extends WindowContainer<WindowContainer> {
                ? null : new PictureInPictureParams(rootActivity.pictureInPictureArgs);
    }

    void maybeUpdateLetterboxBounds(
                ActivityRecord activityRecord, @Nullable Rect letterboxActivityBounds) {
    void maybeUpdateLetterboxInTaskOrganizer(
                ActivityRecord activityRecord,
                @Nullable Rect activityBounds,
                @Nullable Rect activityInsets) {
        if (isOrganized()
                && mReuseActivitiesReport.top == activityRecord
                // Want to force update only if letterbox bounds have changed.
                && !Objects.equals(
                    mLetterboxActivityBounds,
                    letterboxActivityBounds)) {
            mLetterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds);
                && (!Objects.equals(mLetterboxActivityBounds, activityBounds)
                        || !Objects.equals(mLetterboxActivityInsets, activityInsets))) {
            mLetterboxActivityBounds = Rect.copyOrNull(activityBounds);
            mLetterboxActivityInsets = Rect.copyOrNull(activityInsets);
            // Forcing update to reduce visual jank during the transition.
            dispatchTaskInfoChangedIfNeeded(true /* force */);
        }