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

Commit 140a6597 authored by Ikram Gabiyev's avatar Ikram Gabiyev
Browse files

Resize PiP proportionally upon fold/unfold

Make sure to resize pip proportionally upon folding
or unfolding while keeping it withing the allowed
bounds provided by the pip size spec.

Bug: 302205188
Bug: 302394468
Bug: 301414095
Test: repro the steps in the bug
Test: atest WMShellUnitTests:PipBoundsStateTest
Change-Id: I9eb0b45c84f847dca40b71d4747b9a7ac9711c43
parent d029e237
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -126,12 +126,22 @@ public class PipBoundsState {
    private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
    private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();

    // the size of the current bounds relative to the max size spec
    private float mBoundsScale;

    public PipBoundsState(@NonNull Context context, @NonNull SizeSpecSource sizeSpecSource,
            @NonNull PipDisplayLayoutState pipDisplayLayoutState) {
        mContext = context;
        reloadResources();
        mSizeSpecSource = sizeSpecSource;
        mPipDisplayLayoutState = pipDisplayLayoutState;

        // Update the relative proportion of the bounds compared to max possible size. Max size
        // spec takes the aspect ratio of the bounds into account, so both width and height
        // scale by the same factor.
        addPipExclusionBoundsChangeCallback((bounds) -> {
            mBoundsScale = Math.min((float) bounds.width() / mMaxSize.x, 1.0f);
        });
    }

    /** Reloads the resources. */
@@ -160,6 +170,15 @@ public class PipBoundsState {
        return new Rect(mBounds);
    }

    /**
     * Get the scale of the current bounds relative to the maximum size possible.
     *
     * @return 1.0 if {@link PipBoundsState#getBounds()} equals {@link PipBoundsState#getMaxSize()}.
     */
    public float getBoundsScale() {
        return mBoundsScale;
    }

    /** Returns the current movement bounds. */
    @NonNull
    public Rect getMovementBounds() {
@@ -624,6 +643,7 @@ public class PipBoundsState {
        pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip);
        pw.println(innerPrefix + "mMinSize=" + mMinSize);
        pw.println(innerPrefix + "mMaxSize=" + mMaxSize);
        pw.println(innerPrefix + "mBoundsScale" + mBoundsScale);
        if (mPipReentryState == null) {
            pw.println(innerPrefix + "mPipReentryState=null");
        } else {
+16 −22
Original line number Diff line number Diff line
@@ -797,21 +797,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                    mPipBoundsAlgorithm.getMovementBounds(postChangeBounds),
                    mPipBoundsState.getStashedState());

            // Scale PiP on density dpi change, so it appears to be the same size physically.
            final boolean densityDpiChanged =
                    mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
                    && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
                            != layout.densityDpi());
            if (densityDpiChanged) {
                final float scale = (float) layout.densityDpi()
                        / mPipDisplayLayoutState.getDisplayLayout().densityDpi();
                postChangeBounds.set(0, 0,
                        (int) (postChangeBounds.width() * scale),
                        (int) (postChangeBounds.height() * scale));
            }

            updateDisplayLayout.run();

            // Resize the PiP bounds to be at the same scale relative to the new size spec. For
            // example, if PiP was resized to 90% of the maximum size on the previous layout,
            // make sure it is 90% of the new maximum size spec.
            postChangeBounds.set(0, 0,
                    (int) (mPipBoundsState.getMaxSize().x * mPipBoundsState.getBoundsScale()),
                    (int) (mPipBoundsState.getMaxSize().y * mPipBoundsState.getBoundsScale()));

            // Calculate the PiP bounds in the new orientation based on same fraction along the
            // rotated movement bounds.
            final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
@@ -822,6 +816,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                    mPipDisplayLayoutState.getDisplayBounds(),
                    mPipDisplayLayoutState.getDisplayLayout().stableInsets());

            // make sure we user resize to the updated bounds to avoid animating to any outdated
            // sizes from the previous layout upon double tap CUJ
            mPipBoundsState.setHasUserResizedPip(true);
            mTouchHandler.setUserResizeBounds(postChangeBounds);

            final boolean densityDpiChanged =
                    mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
                            && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
                            != layout.densityDpi());
            if (densityDpiChanged) {
                // Using PipMotionHelper#movePip directly here may cause race condition since
                // the app content in PiP mode may or may not be updated for the new density dpi.
@@ -833,15 +836,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                // Directly move PiP to its final destination bounds without animation.
                mPipTaskOrganizer.scheduleFinishResizePip(postChangeBounds);
            }

            // if the pip window size is beyond allowed bounds user resize to normal bounds
            if (mPipBoundsState.getBounds().width() < mPipBoundsState.getMinSize().x
                    || mPipBoundsState.getBounds().width() > mPipBoundsState.getMaxSize().x
                    || mPipBoundsState.getBounds().height() < mPipBoundsState.getMinSize().y
                    || mPipBoundsState.getBounds().height() > mPipBoundsState.getMaxSize().y) {
                mTouchHandler.userResizeTo(mPipBoundsState.getNormalBounds(), snapFraction);
            }

        } else {
            updateDisplayLayout.run();
        }
+3 −6
Original line number Diff line number Diff line
@@ -779,13 +779,10 @@ public class PipTouchHandler {
    }

    /**
     * Resizes the pip window and updates user resized bounds
     *
     * @param bounds target bounds to resize to
     * @param snapFraction snap fraction to apply after resizing
     * Sets the user resize bounds tracked by {@link PipResizeGestureHandler}
     */
    void userResizeTo(Rect bounds, float snapFraction) {
        mPipResizeGestureHandler.userResizeTo(bounds, snapFraction);
    void setUserResizeBounds(Rect bounds) {
        mPipResizeGestureHandler.setUserResizeBounds(bounds);
    }

    /**
+25 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -60,6 +61,9 @@ public class PipBoundsStateTest extends ShellTestCase {
    /** The minimum possible size of the override min size's width or height */
    private static final int OVERRIDABLE_MIN_SIZE = 40;

    /** The margin of error for floating point results. */
    private static final float MARGIN_OF_ERROR = 0.05f;

    private PipBoundsState mPipBoundsState;
    private SizeSpecSource mSizeSpecSource;
    private ComponentName mTestComponentName1;
@@ -87,6 +91,27 @@ public class PipBoundsStateTest extends ShellTestCase {
        assertEquals(bounds, mPipBoundsState.getBounds());
    }

    @Test
    public void testBoundsScale() {
        mPipBoundsState.setMaxSize(300, 300);
        mPipBoundsState.setBounds(new Rect(0, 0, 100, 100));

        final int currentWidth = mPipBoundsState.getBounds().width();
        final Point maxSize = mPipBoundsState.getMaxSize();
        final float expectedBoundsScale = Math.min((float) currentWidth / maxSize.x, 1.0f);

        // test for currentWidth < maxWidth
        assertEquals(expectedBoundsScale, mPipBoundsState.getBoundsScale(), MARGIN_OF_ERROR);

        // reset the bounds to be at the maximum size spec
        mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x, maxSize.y));
        assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f);

        // reset the bounds to be over the maximum size spec
        mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x * 2, maxSize.y * 2));
        assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f);
    }

    @Test
    public void testSetReentryState() {
        final Size size = new Size(100, 100);