Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -63,6 +63,15 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac if (pipBoundsState.isImeShowing()) { if (pipBoundsState.isImeShowing()) { insets.bottom -= pipBoundsState.getImeHeight(); 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); Rect pipBounds = new Rect(startingBounds); boolean shouldApplyGravity = false; boolean shouldApplyGravity = false; Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java +208 −1 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.wm.shell.pip.phone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; 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.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner; Loading @@ -26,10 +29,13 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; 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.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.Mock; import java.util.Set; import java.util.Set; Loading @@ -42,6 +48,10 @@ import java.util.Set; public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm; private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; @Mock private PipBoundsState mMockPipBoundsState; private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); @Before @Before Loading Loading @@ -73,7 +83,6 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { @Test @Test public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() { 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 inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(50, 50, 150, 150); final Rect keepClearRect = new Rect(50, 50, 150, 150); Loading @@ -93,4 +102,202 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { assertEquals(inBounds, outBounds); 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); } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java +9 −0 Original line number Original line Diff line number Diff line Loading @@ -63,6 +63,15 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac if (pipBoundsState.isImeShowing()) { if (pipBoundsState.isImeShowing()) { insets.bottom -= pipBoundsState.getImeHeight(); 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); Rect pipBounds = new Rect(startingBounds); boolean shouldApplyGravity = false; boolean shouldApplyGravity = false; Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java +208 −1 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.wm.shell.pip.phone; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; 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.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner; Loading @@ -26,10 +29,13 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; 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.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.Mock; import java.util.Set; import java.util.Set; Loading @@ -42,6 +48,10 @@ import java.util.Set; public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm; private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; @Mock private PipBoundsState mMockPipBoundsState; private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); @Before @Before Loading Loading @@ -73,7 +83,6 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { @Test @Test public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() { 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 inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(50, 50, 150, 150); final Rect keepClearRect = new Rect(50, 50, 150, 150); Loading @@ -93,4 +102,202 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { assertEquals(inBounds, outBounds); 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); } } }