diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index 9b739932f9de7..1bb5eda25058f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -279,12 +279,27 @@ public class PipBoundsAlgorithm { getInsetBounds(movementBounds); // Apply the movement bounds adjustments based on the current state. - mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds, + getMovementBounds(stackBounds, movementBounds, movementBounds, (adjustForIme && mPipBoundsState.isImeShowing()) ? mPipBoundsState.getImeHeight() : 0); + return movementBounds; } + /** + * Adjusts movementBoundsOut so that it is the movement bounds for the given stackBounds. + */ + public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut, + int bottomOffset) { + // Adjust the right/bottom to ensure the stack bounds never goes offscreen + movementBoundsOut.set(insetBounds); + movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right + - stackBounds.width()); + movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom + - stackBounds.height()); + movementBoundsOut.bottom -= bottomOffset; + } + /** * @return the default snap fraction to apply instead of the default gravity when calculating * the default stack bounds when first entering PiP. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java index d6dc536b9e365..0528e4d88374d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java @@ -22,6 +22,8 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; import android.graphics.Rect; +import com.android.internal.annotations.VisibleForTesting; + /** * Calculates the snap targets and the snap position for the PIP given a position and a velocity. * All bounds are relative to the display top/left. @@ -118,26 +120,12 @@ public class PipSnapAlgorithm { } } - /** - * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given - * {@param stackBounds}. - */ - public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut, - int bottomOffset) { - // Adjust the right/bottom to ensure the stack bounds never goes offscreen - movementBoundsOut.set(insetBounds); - movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right - - stackBounds.width()); - movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom - - stackBounds.height()); - movementBoundsOut.bottom -= bottomOffset; - } - /** * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes * the new bounds out to {@param boundsOut}. */ - public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, + @VisibleForTesting + void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, @PipBoundsState.StashType int stashType) { int leftEdge = stackBounds.left; if (stashType == STASH_TYPE_LEFT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 37d7e51519271..48fa2115305da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -307,8 +307,7 @@ public class PipTouchHandler { public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) { final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds, - toMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0); final int prevBottom = mPipBoundsState.getMovementBounds().bottom - mMovementBoundsExtraOffsets; if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) { @@ -339,13 +338,13 @@ public class PipTouchHandler { // Re-calculate the expanded bounds Rect normalMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(normalBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(normalBounds, insetBounds, normalMovementBounds, bottomOffset); if (mPipBoundsState.getMovementBounds().isEmpty()) { // mMovementBounds is not initialized yet and a clean movement bounds without // bottom offset shall be used later in this function. - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */); } @@ -358,7 +357,7 @@ public class PipTouchHandler { mPipBoundsState.setExpandedBounds( new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight())); Rect expandedMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds( + mPipBoundsAlgorithm.getMovementBounds( mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds, bottomOffset); @@ -381,7 +380,7 @@ public class PipTouchHandler { } else { final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, toMovementBounds, mIsImeShowing ? mImeHeight : 0); final int prevBottom = mPipBoundsState.getMovementBounds().bottom - mMovementBoundsExtraOffsets; @@ -659,7 +658,7 @@ public class PipTouchHandler { private void animateToUnexpandedState(Rect restoreBounds) { Rect restoredMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(restoreBounds, + mPipBoundsAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction, restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */); @@ -865,7 +864,7 @@ public class PipTouchHandler { * resized. */ private void updateMovementBounds() { - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mPipBoundsState.getBounds(), + mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(), mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0); mMotionHelper.onMovementBoundsChanged(); @@ -877,7 +876,7 @@ public class PipTouchHandler { private Rect getMovementBounds(Rect curBounds) { Rect movementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds, movementBounds, mIsImeShowing ? mImeHeight : 0); return movementBounds; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java new file mode 100644 index 0000000000000..dcee2e1847b2a --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link PipSnapAlgorithm}. **/ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipSnapAlgorithmTest extends ShellTestCase { + private static final int DEFAULT_STASH_OFFSET = 32; + private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 2000, 2000); + private static final Rect STACK_BOUNDS_CENTERED = new Rect(900, 900, 1100, 1100); + private static final Rect MOVEMENT_BOUNDS = new Rect(0, 0, + DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width(), + DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width()); + + private PipSnapAlgorithm mPipSnapAlgorithm; + + @Before + public void setUp() { + mPipSnapAlgorithm = new PipSnapAlgorithm(); + } + + @Test + public void testApplySnapFraction_topEdge() { + final float snapFraction = 0.25f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.width() / 4, bounds.left); + assertEquals(MOVEMENT_BOUNDS.top, bounds.top); + } + + @Test + public void testApplySnapFraction_rightEdge() { + final float snapFraction = 1.5f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + assertEquals(MOVEMENT_BOUNDS.height() / 2, bounds.top); + } + + @Test + public void testApplySnapFraction_bottomEdge() { + final float snapFraction = 2.25f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals((int) (MOVEMENT_BOUNDS.width() * 0.75f), bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_leftEdge() { + final float snapFraction = 3.75f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + assertEquals((int) (MOVEMENT_BOUNDS.height() * 0.25f), bounds.top); + } + + @Test + public void testApplySnapFraction_notStashed_isNotOffBounds() { + final float snapFraction = 2f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_NONE, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_stashedLeft() { + final float snapFraction = 3f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + final int offBoundsWidth = bounds.width() - DEFAULT_STASH_OFFSET; + assertEquals(MOVEMENT_BOUNDS.left - offBoundsWidth, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_stashedRight() { + final float snapFraction = 2f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_RIGHT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + assertEquals(DISPLAY_BOUNDS.right - DEFAULT_STASH_OFFSET, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_rightEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the right side. + bounds.offset(10, 0); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + } + + @Test + public void testSnapRectToClosestEdge_leftEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the left side. + bounds.offset(-10, 0); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + } + + @Test + public void testSnapRectToClosestEdge_topEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the top half. + bounds.offset(0, -10); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.top, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_bottomEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the bottom half. + bounds.offset(0, 10); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_stashed_unStahesBounds() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Stash it on the left side. + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, 3.5f, + PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_LEFT); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + } + + @Test + public void testGetSnapFraction_leftEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the left side. + bounds.offset(-10, 0); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(3.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_rightEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the right side. + bounds.offset(10, 0); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(1.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_topEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the top half. + bounds.offset(0, -10); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(0.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_bottomEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the bottom half. + bounds.offset(0, 10); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(2.5f, snapFraction, 0.1f); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index c00210e57398c..abbc681f53fe6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -115,7 +115,8 @@ public class PipTouchHandlerTest extends ShellTestCase { @Test public void updateMovementBounds_minBounds() { Rect expectedMinMovementBounds = new Rect(); - mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, + 0); mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds, mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation); @@ -134,7 +135,8 @@ public class PipTouchHandlerTest extends ShellTestCase { R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y); Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight()); Rect expectedMaxMovementBounds = new Rect(); - mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, + 0); mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds, mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);