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

Commit 19a953c5 authored by Ikram Gabiyev's avatar Ikram Gabiyev Committed by Android (Google) Code Review
Browse files

Merge "Enforce at least a minimal size change for resize" into main

parents 5752b267 70a58b70
Loading
Loading
Loading
Loading
+18 −32
Original line number Diff line number Diff line
@@ -52,24 +52,15 @@ public class PipDoubleTapHelper {
    public static final int SIZE_SPEC_MAX = 1;
    public static final int SIZE_SPEC_CUSTOM = 2;

    /**
     * Returns MAX or DEFAULT {@link PipSizeSpec} to toggle to/from.
     *
     * <p>Each double tap toggles back and forth between {@code PipSizeSpec.CUSTOM} and
     * either {@code PipSizeSpec.MAX} or {@code PipSizeSpec.DEFAULT}. The choice between
     * the latter two sizes is determined based on the current state of the pip screen.</p>
     *
     * @param mPipBoundsState current state of the pip screen
     */
    @PipSizeSpec
    private static int getMaxOrDefaultPipSizeSpec(@NonNull PipBoundsState mPipBoundsState) {
    private static int getMaxOrDefaultPipSizeSpec(@NonNull PipBoundsState pipBoundsState) {
        // determine the average pip screen width
        int averageWidth = (mPipBoundsState.getMaxSize().x
                + mPipBoundsState.getMinSize().x) / 2;
        int averageWidth = (pipBoundsState.getMaxSize().x
                + pipBoundsState.getMinSize().x) / 2;

        // If pip screen width is above average, DEFAULT is the size spec we need to
        // toggle to. Otherwise, we choose MAX.
        return (mPipBoundsState.getBounds().width() > averageWidth)
        return (pipBoundsState.getBounds().width() > averageWidth)
                ? SIZE_SPEC_DEFAULT
                : SIZE_SPEC_MAX;
    }
@@ -77,35 +68,33 @@ public class PipDoubleTapHelper {
    /**
     * Determines the {@link PipSizeSpec} to toggle to on double tap.
     *
     * @param mPipBoundsState current state of the pip screen
     * @param pipBoundsState current state of the pip bounds
     * @param userResizeBounds latest user resized bounds (by pinching in/out)
     * @return pip screen size to switch to
     */
    @PipSizeSpec
    public static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState,
    public static int nextSizeSpec(@NonNull PipBoundsState pipBoundsState,
            @NonNull Rect userResizeBounds) {
        // is pip screen at its maximum
        boolean isScreenMax = mPipBoundsState.getBounds().width()
                == mPipBoundsState.getMaxSize().x;

        // is pip screen at its normal default size
        boolean isScreenDefault = (mPipBoundsState.getBounds().width()
                == mPipBoundsState.getNormalBounds().width())
                && (mPipBoundsState.getBounds().height()
                == mPipBoundsState.getNormalBounds().height());
        boolean isScreenMax = pipBoundsState.getBounds().width() == pipBoundsState.getMaxSize().x
                && pipBoundsState.getBounds().height() == pipBoundsState.getMaxSize().y;
        boolean isScreenDefault = (pipBoundsState.getBounds().width()
                == pipBoundsState.getNormalBounds().width())
                && (pipBoundsState.getBounds().height()
                == pipBoundsState.getNormalBounds().height());

        // edge case 1
        // if user hasn't resized screen yet, i.e. CUSTOM size does not exist yet
        // or if user has resized exactly to DEFAULT, then we just want to maximize
        if (isScreenDefault
                && userResizeBounds.width() == mPipBoundsState.getNormalBounds().width()) {
                && userResizeBounds.width() == pipBoundsState.getNormalBounds().width()
                && userResizeBounds.height() == pipBoundsState.getNormalBounds().height()) {
            return SIZE_SPEC_MAX;
        }

        // edge case 2
        // if user has maximized, then we want to toggle to DEFAULT
        // if user has resized to max, then we want to toggle to DEFAULT
        if (isScreenMax
                && userResizeBounds.width() == mPipBoundsState.getMaxSize().x) {
                && userResizeBounds.width() == pipBoundsState.getMaxSize().x
                && userResizeBounds.height() == pipBoundsState.getMaxSize().y) {
            return SIZE_SPEC_DEFAULT;
        }

@@ -113,9 +102,6 @@ public class PipDoubleTapHelper {
        if (isScreenDefault || isScreenMax) {
            return SIZE_SPEC_CUSTOM;
        }

        // if we are currently in user resized CUSTOM size state
        // then we toggle either to MAX or DEFAULT depending on the current pip screen state
        return getMaxOrDefaultPipSizeSpec(mPipBoundsState);
        return getMaxOrDefaultPipSizeSpec(pipBoundsState);
    }
}
+8 −1
Original line number Diff line number Diff line
@@ -180,10 +180,17 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
            return;
        }
        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setBounds(pipTaskToken, toBounds);
        if (configAtEnd) {
            wct.deferConfigToTransitionEnd(pipTaskToken);

            if (mPipBoundsState.getBounds().width() == toBounds.width()
                    && mPipBoundsState.getBounds().height() == toBounds.height()) {
                // TODO (b/393159816): Config-at-End causes a flicker without size change.
                // If PiP size isn't changing enforce a minimal one-pixel change as a workaround.
                --toBounds.bottom;
            }
        }
        wct.setBounds(pipTaskToken, toBounds);
        mPipTransitionController.startResizeTransition(wct, duration);
    }

+1 −0
Original line number Diff line number Diff line
@@ -934,6 +934,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
                    }

                    // the size to toggle to after a double tap
                    mPipBoundsState.setNormalBounds(getAdjustedNormalBounds());
                    int nextSize = PipDoubleTapHelper
                            .nextSizeSpec(mPipBoundsState, getUserResizeBounds());

+53 −110
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_DEFAU
import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_MAX;
import static com.android.wm.shell.common.pip.PipDoubleTapHelper.nextSizeSpec;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.graphics.Point;
@@ -41,131 +40,75 @@ import org.mockito.Mock;
 */
@RunWith(AndroidTestingRunner.class)
public class PipDoubleTapHelperTest extends ShellTestCase {
    // represents the current pip window state and has information on current
    // max, min, and normal sizes
    @Mock private PipBoundsState mBoundStateMock;
    // tied to boundsStateMock.getBounds() in setUp()
    @Mock private Rect mBoundsMock;
    @Mock private PipBoundsState mMockPipBoundsState;

    // represents the most recent manually resized bounds
    // i.e. dimensions from the most recent pinch in/out
    @Mock private Rect mUserResizeBoundsMock;
    // Actual dimension guidelines of the PiP bounds.
    private static final int MAX_EDGE_SIZE = 100;
    private static final int DEFAULT_EDGE_SIZE = 60;
    private static final int MIN_EDGE_SIZE = 50;
    private static final int AVERAGE_EDGE_SIZE = (MAX_EDGE_SIZE + MIN_EDGE_SIZE) / 2;

    // actual dimensions of the pip screen bounds
    private static final int MAX_WIDTH = 100;
    private static final int DEFAULT_WIDTH = 40;
    private static final int MIN_WIDTH = 10;

    private static final int AVERAGE_WIDTH = (MAX_WIDTH + MIN_WIDTH) / 2;

    /**
     * Initializes mocks and assigns values for different pip screen bounds.
     */
    @Before
    public void setUp() {
        // define pip bounds
        when(mBoundStateMock.getMaxSize()).thenReturn(new Point(MAX_WIDTH, 20));
        when(mBoundStateMock.getMinSize()).thenReturn(new Point(MIN_WIDTH, 2));
        when(mMockPipBoundsState.getMaxSize()).thenReturn(new Point(MAX_EDGE_SIZE, MAX_EDGE_SIZE));
        when(mMockPipBoundsState.getMinSize()).thenReturn(new Point(MIN_EDGE_SIZE, MIN_EDGE_SIZE));

        Rect rectMock = mock(Rect.class);
        when(rectMock.width()).thenReturn(DEFAULT_WIDTH);
        when(mBoundStateMock.getNormalBounds()).thenReturn(rectMock);
        final Rect normalBounds = new Rect(0, 0, DEFAULT_EDGE_SIZE, DEFAULT_EDGE_SIZE);
        when(mMockPipBoundsState.getNormalBounds()).thenReturn(normalBounds);
    }

        when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
        when(mBoundStateMock.getBounds()).thenReturn(mBoundsMock);
    @Test
    public void nextSizeSpec_resizedWiderThanAverage_returnDefaultThenCustom() {
        final int resizeEdgeSize = (MAX_EDGE_SIZE + AVERAGE_EDGE_SIZE) / 2;
        final Rect userResizeBounds = new Rect(0, 0, resizeEdgeSize, resizeEdgeSize);
        when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_DEFAULT);

        // once we toggle to DEFAULT only PiP bounds state gets updated - not the user resize bounds
        when(mMockPipBoundsState.getBounds()).thenReturn(
                new Rect(0, 0, DEFAULT_EDGE_SIZE, DEFAULT_EDGE_SIZE));
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_CUSTOM);
    }

    /**
     * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
     *
     * <p>when the user resizes the screen to a larger than the average but not the maximum width,
     * then we toggle between {@code PipSizeSpec.CUSTOM} and {@code PipSizeSpec.DEFAULT}
     */
    @Test
    public void testNextScreenSize_resizedWiderThanAverage_returnDefaultThenCustom() {
        // make the user resize width in between MAX and average
        when(mUserResizeBoundsMock.width()).thenReturn((MAX_WIDTH + AVERAGE_WIDTH) / 2);
        // make current bounds same as resized bound since no double tap yet
        when(mBoundsMock.width()).thenReturn((MAX_WIDTH + AVERAGE_WIDTH) / 2);

        // then nextScreenSize() i.e. double tapping should
        // toggle to DEFAULT state
        Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
                SIZE_SPEC_DEFAULT);

        // once we toggle to DEFAULT our screen size gets updated
        // but not the user resize bounds
        when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);

        // then nextScreenSize() i.e. double tapping should
        // toggle to CUSTOM state
        Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
                SIZE_SPEC_CUSTOM);
    public void nextSizeSpec_resizedSmallerThanAverage_returnMaxThenCustom() {
        final int resizeEdgeSize = (AVERAGE_EDGE_SIZE + MIN_EDGE_SIZE) / 2;
        final Rect userResizeBounds = new Rect(0, 0, resizeEdgeSize, resizeEdgeSize);
        when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_MAX);

        // Once we toggle to MAX our screen size gets updated but not the user resize bounds
        when(mMockPipBoundsState.getBounds()).thenReturn(
                new Rect(0, 0, MAX_EDGE_SIZE, MAX_EDGE_SIZE));
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_CUSTOM);
    }

    /**
     * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
     *
     * <p>when the user resizes the screen to a smaller than the average but not the default width,
     * then we toggle between {@code PipSizeSpec.CUSTOM} and {@code PipSizeSpec.MAX}
     */
    @Test
    public void testNextScreenSize_resizedNarrowerThanAverage_returnMaxThenCustom() {
        // make the user resize width in between MIN and average
        when(mUserResizeBoundsMock.width()).thenReturn((MIN_WIDTH + AVERAGE_WIDTH) / 2);
        // make current bounds same as resized bound since no double tap yet
        when(mBoundsMock.width()).thenReturn((MIN_WIDTH + AVERAGE_WIDTH) / 2);

        // then nextScreenSize() i.e. double tapping should
        // toggle to MAX state
        Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
                SIZE_SPEC_MAX);

        // once we toggle to MAX our screen size gets updated
        // but not the user resize bounds
        when(mBoundsMock.width()).thenReturn(MAX_WIDTH);

        // then nextScreenSize() i.e. double tapping should
        // toggle to CUSTOM state
        Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
                SIZE_SPEC_CUSTOM);
    public void nextSizeSpec_resizedToMax_returnDefault() {
        final Rect userResizeBounds = new Rect(0, 0, MAX_EDGE_SIZE, MAX_EDGE_SIZE);
        when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_DEFAULT);
    }

    /**
     * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
     *
     * <p>when the user resizes the screen to exactly the maximum width
     * then we toggle to {@code PipSizeSpec.DEFAULT}
     */
    @Test
    public void testNextScreenSize_resizedToMax_returnDefault() {
        // the resized width is the same as MAX_WIDTH
        when(mUserResizeBoundsMock.width()).thenReturn(MAX_WIDTH);
        // the current bounds are also at MAX_WIDTH
        when(mBoundsMock.width()).thenReturn(MAX_WIDTH);

        // then nextScreenSize() i.e. double tapping should
        // toggle to DEFAULT state
        Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
                SIZE_SPEC_DEFAULT);
    public void nextSizeSpec_resizedToDefault_returnMax() {
        final Rect userResizeBounds = new Rect(0, 0, DEFAULT_EDGE_SIZE, DEFAULT_EDGE_SIZE);
        when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_MAX);
    }

    @Test
    public void nextSizeSpec_resizedToAlmostMax_returnDefault() {
        final Rect userResizeBounds = new Rect(0, 0, MAX_EDGE_SIZE, MAX_EDGE_SIZE - 1);
        when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_DEFAULT);
    }

    /**
     * Tests {@link PipDoubleTapHelper#nextSizeSpec(PipBoundsState, Rect)}.
     *
     * <p>when the user resizes the screen to exactly the default width
     * then we toggle to {@code PipSizeSpec.MAX}
     */
    @Test
    public void testNextScreenSize_resizedToDefault_returnMax() {
        // the resized width is the same as DEFAULT_WIDTH
        when(mUserResizeBoundsMock.width()).thenReturn(DEFAULT_WIDTH);
        // the current bounds are also at DEFAULT_WIDTH
        when(mBoundsMock.width()).thenReturn(DEFAULT_WIDTH);

        // then nextScreenSize() i.e. double tapping should
        // toggle to MAX state
        Assert.assertSame(nextSizeSpec(mBoundStateMock, mUserResizeBoundsMock),
                SIZE_SPEC_MAX);
    public void nextSizeSpec_resizedToAlmostMin_returnMax() {
        final Rect userResizeBounds = new Rect(0, 0, MIN_EDGE_SIZE, MIN_EDGE_SIZE + 1);
        when(mMockPipBoundsState.getBounds()).thenReturn(userResizeBounds);
        Assert.assertEquals(nextSizeSpec(mMockPipBoundsState, userResizeBounds), SIZE_SPEC_MAX);
    }
}