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

Commit 00557fbf authored by Hongwei Wang's avatar Hongwei Wang
Browse files

Ignore minWidth and minHeight for PiP if too large

PiP acknowledges the minWidth and minHeight app sets on their Activity.
When those dimensions are too large though, the PiP window would exceed
the display bounds.

Given PiP permits such override to allow a even smaller PiP size, we
address the issue by

- Ignore minWidth and minHeight if any one of them is larger than the
  default size
- Skip the check on WM core side when setting the bounds, if it's a
  pinned Task

Flag: EXEMPT bugfix
Bug: 394769616
Test: atest WMShellUnitTests:PipBoundsStateTest \
            WMShellUnitTests:PipTaskOrganizerTest \
            WMShellUnitTests:PipBoundsAlgorithmTest
Test: manual test with PoC app
Change-Id: I48e3b97851553fac0344d11644cc6722e3690336
parent 9456c277
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -377,6 +377,16 @@ public class PipBoundsState {

    /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
    public void setOverrideMinSize(@Nullable Size overrideMinSize) {
        if (overrideMinSize != null) {
            final Size defaultSize = mSizeSpecSource.getDefaultSize(getAspectRatio());
            if (overrideMinSize.getWidth() > defaultSize.getWidth()
                    || overrideMinSize.getHeight() > defaultSize.getHeight()) {
                ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                        "Ignore override min size(%s): larger than default size (%s)",
                        overrideMinSize, defaultSize);
                return;
            }
        }
        final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize());
        mSizeSpecSource.setOverrideMinSize(overrideMinSize);
        if (changed && mOnMinimalSizeChangeCallback != null) {
+20 −12
Original line number Diff line number Diff line
@@ -147,7 +147,9 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
    public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() {
        overrideDefaultAspectRatio(1.0f);
        // The min size's aspect ratio is greater than the default aspect ratio.
        final Size overrideMinSize = new Size(150, 120);
        final Size widerSize = mSizeSpecSource.getDefaultSize(1.5f);
        final Size overrideMinSize = new Size(
                widerSize.getWidth() / 2, widerSize.getHeight() / 2);

        mPipBoundsState.setOverrideMinSize(overrideMinSize);
        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
@@ -163,7 +165,9 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
    public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() {
        overrideDefaultAspectRatio(1.0f);
        // The min size's aspect ratio is greater than the default aspect ratio.
        final Size overrideMinSize = new Size(120, 150);
        final Size tallerSize = mSizeSpecSource.getDefaultSize(0.5f);
        final Size overrideMinSize = new Size(
                tallerSize.getWidth() / 2, tallerSize.getHeight() / 2);

        mPipBoundsState.setOverrideMinSize(overrideMinSize);
        final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
@@ -306,10 +310,12 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
                DEFAULT_ASPECT_RATIO,
                (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
        };
        final Size defaultSize = mSizeSpecSource.getDefaultSize(1.0f);
        final int defaultMinHeight = defaultSize.getHeight() / 2;
        final Size[] minimalSizes = new Size[] {
                new Size((int) (200 * aspectRatios[0]), 200),
                new Size((int) (200 * aspectRatios[1]), 200),
                new Size((int) (200 * aspectRatios[2]), 200)
                new Size((int) (defaultMinHeight * aspectRatios[0]), defaultMinHeight),
                new Size((int) (defaultMinHeight * aspectRatios[1]), defaultMinHeight),
                new Size((int) (defaultMinHeight * aspectRatios[2]), defaultMinHeight)
        };
        for (int i = 0; i < aspectRatios.length; i++) {
            final float aspectRatio = aspectRatios[i];
@@ -331,15 +337,17 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {

    @Test
    public void getAdjustedDestinationBounds_ignoreMinBounds() {
        final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
        final Rect currentBounds = new Rect(0, 0, 0, 100);
        currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
        final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);

        mPipBoundsState.setAspectRatio(aspectRatio);
        final float oldAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
        final float newAspectRatio = (DEFAULT_ASPECT_RATIO + MIN_ASPECT_RATIO) / 2;
        final Size defaultSize = mSizeSpecSource.getDefaultSize(oldAspectRatio);
        final Size minSize = new Size(
                defaultSize.getWidth() / 2, defaultSize.getHeight() / 2);
        final Rect currentBounds = new Rect(0, 0, defaultSize.getWidth(), defaultSize.getHeight());

        mPipBoundsState.setAspectRatio(newAspectRatio);
        mPipBoundsState.setOverrideMinSize(minSize);
        final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds(
                currentBounds, aspectRatio);
                currentBounds, newAspectRatio);

        assertTrue("Destination bounds ignores minimal size",
                destinationBounds.width() > minSize.getWidth()
+35 −9
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import java.util.function.Consumer;
@SmallTest
public class PipBoundsStateTest extends ShellTestCase {

    private static final Size DEFAULT_SIZE = new Size(10, 10);
    private static final float DEFAULT_SNAP_FRACTION = 1.0f;

    /** The minimum possible size of the override min size's width or height */
@@ -184,10 +183,14 @@ public class PipBoundsStateTest extends ShellTestCase {
    @Test
    public void testSetOverrideMinSize_changed_callbackInvoked() {
        final Runnable callback = mock(Runnable.class);
        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
        mPipBoundsState.setAspectRatio(2f);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
        mPipBoundsState.setOverrideMinSize(
                new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2));
        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);

        mPipBoundsState.setOverrideMinSize(new Size(10, 10));
        mPipBoundsState.setOverrideMinSize(
                new Size(defaultSize.getWidth() / 4, defaultSize.getHeight() / 4));

        verify(callback).run();
    }
@@ -195,24 +198,47 @@ public class PipBoundsStateTest extends ShellTestCase {
    @Test
    public void testSetOverrideMinSize_notChanged_callbackNotInvoked() {
        final Runnable callback = mock(Runnable.class);
        mPipBoundsState.setOverrideMinSize(new Size(100, 150));
        mPipBoundsState.setAspectRatio(2f);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
        mPipBoundsState.setOverrideMinSize(
                new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2));
        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);

        mPipBoundsState.setOverrideMinSize(new Size(100, 150));
        mPipBoundsState.setOverrideMinSize(
                new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2));

        verify(callback, never()).run();
    }

    @Test
    public void testSetOverrideMinSize_tooLarge_ignored() {
        final Runnable callback = mock(Runnable.class);
        mPipBoundsState.setAspectRatio(2f);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
        final Size halfSize = new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2);
        final Size doubleSize = new Size(defaultSize.getWidth() * 2, defaultSize.getHeight() * 2);
        mPipBoundsState.setOverrideMinSize(halfSize);
        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);

        mPipBoundsState.setOverrideMinSize(doubleSize);

        assertEquals("Override min size should be ignored",
                mPipBoundsState.getOverrideMinSize(), halfSize);
        verify(callback, never()).run();
    }

    @Test
    public void testGetOverrideMinEdgeSize() {
        mPipBoundsState.setOverrideMinSize(null);
        assertEquals(0, mPipBoundsState.getOverrideMinEdgeSize());

        mPipBoundsState.setOverrideMinSize(new Size(100, 110));
        assertEquals(100, mPipBoundsState.getOverrideMinEdgeSize());
        mPipBoundsState.setAspectRatio(2f);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(mPipBoundsState.getAspectRatio());
        final Size halfSize = new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2);
        mPipBoundsState.setOverrideMinSize(halfSize);

        mPipBoundsState.setOverrideMinSize(new Size(150, 200));
        assertEquals(150, mPipBoundsState.getOverrideMinEdgeSize());
        assertEquals(Math.min(halfSize.getWidth(), halfSize.getHeight()),
                mPipBoundsState.getOverrideMinEdgeSize());
    }

    @Test
+13 −7
Original line number Diff line number Diff line
@@ -164,10 +164,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {

    @Test
    public void startSwipePipToHome_updatesOverrideMinSize() {
        final Size minSize = new Size(400, 320);
        final Rational aspectRatio = new Rational(2, 1);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(aspectRatio.floatValue());
        final Size minSize = new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2);

        mPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
                createPipParams(null));
                createPipParams(aspectRatio));

        assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
    }
@@ -192,10 +194,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {

    @Test
    public void onTaskAppeared_updatesOverrideMinSize() {
        final Size minSize = new Size(400, 320);
        final Rational aspectRatio = new Rational(2, 1);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(aspectRatio.floatValue());
        final Size minSize = new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2);

        mPipTaskOrganizer.onTaskAppeared(
                createTaskInfo(mComponent1, createPipParams(null), minSize),
                createTaskInfo(mComponent1, createPipParams(aspectRatio), minSize),
                mock(SurfaceControl.class));

        assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -248,13 +252,15 @@ public class PipTaskOrganizerTest extends ShellTestCase {

    @Test
    public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
        final Rational aspectRatio = new Rational(2, 1);
        final Size defaultSize = mSizeSpecSource.getDefaultSize(aspectRatio.floatValue());
        final Size minSize = new Size(defaultSize.getWidth() / 2, defaultSize.getHeight() / 2);
        mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                createPipParams(null)), mock(SurfaceControl.class));
                createPipParams(aspectRatio)), mock(SurfaceControl.class));
        sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);

        final Size minSize = new Size(400, 320);
        mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
                createPipParams(null), minSize));
                createPipParams(aspectRatio), minSize));

        assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
    }
+18 −14
Original line number Diff line number Diff line
@@ -2170,12 +2170,16 @@ class Task extends TaskFragment {

    void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
            @NonNull Configuration parentConfig) {
        int minWidth = mMinWidth;
        int minHeight = mMinHeight;
        // If the task has no requested minimal size, we'd like to enforce a minimal size
        // so that the user can not render the task fragment too small to manipulate. We don't need
        // to do this for the root pinned task as the bounds are controlled by the system.
        if (!inPinnedWindowingMode()) {
        if (inPinnedWindowingMode()) {
            Slog.i(TAG, "Skip adjustForMinimalTaskDimensions for pip task");
            return;
        }

        int minWidth = mMinWidth;
        int minHeight = mMinHeight;
        // Use Display specific min sizes when there is one associated with this Task.
        final int defaultMinSizeDp = mDisplayContent == null
                ? DEFAULT_MIN_TASK_SIZE_DP : mDisplayContent.mMinSizeOfResizeableTaskDp;
@@ -2188,7 +2192,7 @@ class Task extends TaskFragment {
        if (minHeight == INVALID_MIN_SIZE) {
            minHeight = defaultMinSize;
        }
        }

        if (bounds.isEmpty()) {
            // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
            // do, we can just skip.