From ef4a74170f2acc1150fc929775f084c4923c9f55 Mon Sep 17 00:00:00 2001 From: Mateusz Cicheński Date: Sat, 10 Jun 2023 00:35:28 +0000 Subject: Fix stashed PiP so it only moves up/down if outside of insets Add unit tests for adjust() method in PhonePipKeepClearAlgorithm. This covers test cases for applying gravity, moving PiP away from keep clear areas (which is equivalent to findUnoccludedPosition test cases), and for ignoring keep clear areas if PiP is stashed, testing only the up/down movement added by this change. Bug: 286478425 Test: atest PhonePipKeepClearAlgorithmTest Test: before http://recall/-/ekEuGtt9d9HWqkUtAzpHx8/dY9IMjhv5eBf0501pUCPKp Test: after http://recall/-/ekEuGtt9d9HWqkUtAzpHx8/eLrBdqzAKlGiNvFetdIsh9 Change-Id: I478d364a31652f6507d2a289295d336b62d0db81 --- .../pip/phone/PhonePipKeepClearAlgorithm.java | 9 + .../pip/phone/PhonePipKeepClearAlgorithmTest.java | 209 ++++++++++++++++++++- 2 files changed, 217 insertions(+), 1 deletion(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java index fc674a8aa59b..f9332e4bdb2e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java @@ -63,6 +63,15 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac if (pipBoundsState.isImeShowing()) { insets.bottom -= pipBoundsState.getImeHeight(); } + // if PiP is stashed we only adjust the vertical position if it's outside of insets and + // ignore all keep clear areas, since it's already on the side + if (pipBoundsState.isStashed()) { + if (startingBounds.bottom > insets.bottom || startingBounds.top < insets.top) { + // bring PiP back to be aligned by bottom inset + startingBounds.offset(0, insets.bottom - startingBounds.bottom); + } + return startingBounds; + } Rect pipBounds = new Rect(startingBounds); boolean shouldApplyGravity = false; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java index 4d7e9e450ceb..cc9e26b2c4f1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java @@ -18,6 +18,9 @@ package com.android.wm.shell.pip.phone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -26,10 +29,13 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipBoundsState; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import java.util.Set; @@ -42,6 +48,10 @@ import java.util.Set; public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm; + + @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; + @Mock private PipBoundsState mMockPipBoundsState; + private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); @Before @@ -73,7 +83,6 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { @Test public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() { - // TODO(b/183746978): update this test to accommodate for the updated algorithm final Rect inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(50, 50, 150, 150); @@ -93,4 +102,202 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { assertEquals(inBounds, outBounds); } + + @Test + public void adjust_withCollidingRestrictedKeepClearArea_moveBounds() { + final Rect pipBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(50, 50, 150, 150); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect)); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertFalse(outBounds.contains(keepClearRect)); + } + + @Test + public void adjust_withNonCollidingRestrictedKeepClearArea_boundsUnchanged() { + final Rect pipBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(100, 100, 150, 150); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect)); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertFalse(outBounds.contains(keepClearRect)); + } + + @Test + public void adjust_withCollidingRestrictedKeepClearArea_whileStashed_boundsUnchanged() { + final Rect pipBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(50, 50, 150, 150); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + when(mMockPipBoundsState.isStashed()).thenReturn(true); + when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect)); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(pipBounds, outBounds); + } + + @Test + public void adjust_withNonCollidingRestrictedKeepClearArea_whileStashed_boundsUnchanged() { + final Rect pipBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(100, 100, 150, 150); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + when(mMockPipBoundsState.isStashed()).thenReturn(true); + when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect)); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(pipBounds, outBounds); + } + + @Test + public void adjust_aboveDisplayBounds_onLeftEdge_appliesBottomLeftGravity() { + final Rect pipBounds = new Rect( + 0, DISPLAY_BOUNDS.top - 50, 100, DISPLAY_BOUNDS.top + 50); + final Rect expected = new Rect( + 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(0f); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(expected, outBounds); + } + + @Test + public void adjust_belowDisplayBounds_onLeftEdge_appliesBottomLeftGravity() { + final Rect pipBounds = new Rect( + 0, DISPLAY_BOUNDS.bottom - 50, 100, DISPLAY_BOUNDS.bottom + 50); + final Rect expected = new Rect( + 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(3f); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(expected, outBounds); + } + + @Test + public void adjust_aboveDisplayBounds_onRightEdge_appliesBottomRightGravity() { + final Rect pipBounds = new Rect( + DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.top - 50, + DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.top + 50); + final Rect expected = new Rect( + DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 100, + DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(1f); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(expected, outBounds); + } + + @Test + public void adjust_belowDisplayBounds_onRightEdge_appliesBottomRightGravity() { + final Rect pipBounds = new Rect( + DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 50, + DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom + 50); + final Rect expected = new Rect( + DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 100, + DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(2f); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(expected, outBounds); + } + + @Test + public void adjust_whileStashed_aboveDisplayBounds_alignsToBottomInset() { + final Rect pipBounds = new Rect( + 0, DISPLAY_BOUNDS.top - 50, 100, DISPLAY_BOUNDS.top + 50); + final Rect expected = new Rect( + 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + when(mMockPipBoundsState.isStashed()).thenReturn(true); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(expected, outBounds); + } + + @Test + public void adjust_whileStashed_belowDisplayBounds_alignsToBottomInset() { + final Rect pipBounds = new Rect( + 0, DISPLAY_BOUNDS.bottom - 50, 100, DISPLAY_BOUNDS.bottom + 50); + final Rect expected = new Rect( + 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom); + when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds); + when(mMockPipBoundsState.isStashed()).thenReturn(true); + doAnswer(invocation -> { + Rect arg0 = invocation.getArgument(0); + arg0.set(DISPLAY_BOUNDS); + return null; + }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class)); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust( + mMockPipBoundsState, mMockPipBoundsAlgorithm); + + assertEquals(expected, outBounds); + } } -- cgit v1.2.3-59-g8ed1b