Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +9 −0 Original line number Diff line number Diff line Loading @@ -745,6 +745,15 @@ 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(); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +61 −19 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ public class PipResizeGestureHandler { private final Rect mDisplayBounds = new Rect(); private final Function<Rect, Rect> mMovementBoundsSupplier; private final Runnable mUpdateMovementBoundsRunnable; private final Consumer<Rect> mUpdateResizeBoundsCallback; private int mDelta; private float mTouchSlop; Loading Loading @@ -137,6 +138,13 @@ public class PipResizeGestureHandler { mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); mUpdateResizeBoundsCallback = (rect) -> { mUserResizeBounds.set(rect); mMotionHelper.synchronizePinnedStackBounds(); mUpdateMovementBoundsRunnable.run(); resetState(); }; } public void init() { Loading Loading @@ -508,15 +516,50 @@ public class PipResizeGestureHandler { } } private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) { final int leftEdge = bounds.left; final int fromLeft = Math.abs(leftEdge - movementBounds.left); final int fromRight = Math.abs(movementBounds.right - leftEdge); // The PIP will be snapped to either the right or left edge, so calculate which one // is closest to the current position. final int newLeft = fromLeft < fromRight ? movementBounds.left : movementBounds.right; bounds.offsetTo(newLeft, mLastResizeBounds.top); } /** * Resizes the pip window and updates user-resized bounds. * * @param bounds target bounds to resize to * @param snapFraction snap fraction to apply after resizing */ void userResizeTo(Rect bounds, float snapFraction) { Rect finalBounds = new Rect(bounds); // get the current movement bounds final Rect movementBounds = mPipBoundsAlgorithm.getMovementBounds(finalBounds); // snap the target bounds to the either left or right edge, by choosing the closer one snapToMovementBoundsEdge(finalBounds, movementBounds); // apply the requested snap fraction onto the target bounds mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction); // resize from current bounds to target bounds without animation mPipTaskOrganizer.scheduleUserResizePip(mPipBoundsState.getBounds(), finalBounds, null); // set the flag that pip has been resized mPipBoundsState.setHasUserResizedPip(true); // finish the resize operation and update the state of the bounds mPipTaskOrganizer.scheduleFinishResizePip(finalBounds, mUpdateResizeBoundsCallback); } private void finishResize() { if (!mLastResizeBounds.isEmpty()) { final Consumer<Rect> callback = (rect) -> { mUserResizeBounds.set(mLastResizeBounds); mMotionHelper.synchronizePinnedStackBounds(); mUpdateMovementBoundsRunnable.run(); resetState(); }; // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped // position correctly. Drag-resize does not need to move, so just finalize resize. if (mOngoingPinchToResize) { Loading @@ -526,24 +569,23 @@ public class PipResizeGestureHandler { || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) { resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y); } final int leftEdge = mLastResizeBounds.left; final Rect movementBounds = mPipBoundsAlgorithm.getMovementBounds(mLastResizeBounds); final int fromLeft = Math.abs(leftEdge - movementBounds.left); final int fromRight = Math.abs(movementBounds.right - leftEdge); // The PIP will be snapped to either the right or left edge, so calculate which one // is closest to the current position. final int newLeft = fromLeft < fromRight ? movementBounds.left : movementBounds.right; mLastResizeBounds.offsetTo(newLeft, mLastResizeBounds.top); // get the current movement bounds final Rect movementBounds = mPipBoundsAlgorithm .getMovementBounds(mLastResizeBounds); // snap mLastResizeBounds to the correct edge based on movement bounds snapToMovementBoundsEdge(mLastResizeBounds, movementBounds); final float snapFraction = mPipBoundsAlgorithm.getSnapFraction( mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, PINCH_RESIZE_SNAP_DURATION, mAngle, callback); PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback); } else { mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback); PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, mUpdateResizeBoundsCallback); } final float magnetRadiusPercent = (float) mLastResizeBounds.width() / mMinSize.x / 2.f; mPipDismissTargetHandler Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +10 −0 Original line number Diff line number Diff line Loading @@ -824,6 +824,16 @@ public class PipTouchHandler { return mPipResizeGestureHandler.getUserResizeBounds(); } /** * Resizes the pip window and updates user resized bounds * * @param bounds target bounds to resize to * @param snapFraction snap fraction to apply after resizing */ void userResizeTo(Rect bounds, float snapFraction) { mPipResizeGestureHandler.userResizeTo(bounds, snapFraction); } /** * Gesture controlling normal movement of the PIP. */ Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.pip.phone; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; Loading Loading @@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) public class PipResizeGestureHandlerTest extends ShellTestCase { private static final float DEFAULT_SNAP_FRACTION = 2.0f; private static final int STEP_SIZE = 40; private final MotionEvent.PointerProperties[] mPp = new MotionEvent.PointerProperties[2]; Loading Loading @@ -196,6 +198,51 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { < mPipBoundsState.getBounds().width()); } @Test public void testUserResizeTo() { // resizing the bounds to normal bounds at first mPipResizeGestureHandler.userResizeTo(mPipBoundsState.getNormalBounds(), DEFAULT_SNAP_FRACTION); assertPipBoundsUserResizedTo(mPipBoundsState.getNormalBounds()); verify(mPipTaskOrganizer, times(1)) .scheduleUserResizePip(any(), any(), any()); verify(mPipTaskOrganizer, times(1)) .scheduleFinishResizePip(any(), any()); // bounds with max size final Rect maxBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x, mPipBoundsState.getMaxSize().y); // resizing the bounds to maximum bounds the second time mPipResizeGestureHandler.userResizeTo(maxBounds, DEFAULT_SNAP_FRACTION); assertPipBoundsUserResizedTo(maxBounds); // another call to scheduleUserResizePip() and scheduleFinishResizePip() makes // the total number of invocations 2 for each method verify(mPipTaskOrganizer, times(2)) .scheduleUserResizePip(any(), any(), any()); verify(mPipTaskOrganizer, times(2)) .scheduleFinishResizePip(any(), any()); } private void assertPipBoundsUserResizedTo(Rect bounds) { // check user-resized bounds assertEquals(mPipResizeGestureHandler.getUserResizeBounds().width(), bounds.width()); assertEquals(mPipResizeGestureHandler.getUserResizeBounds().height(), bounds.height()); // check if the bounds are the same assertEquals(mPipBoundsState.getBounds().width(), bounds.width()); assertEquals(mPipBoundsState.getBounds().height(), bounds.height()); // a flag should be set to indicate pip has been resized by the user assertTrue(mPipBoundsState.hasUserResizedPip()); } private MotionEvent obtainMotionEvent(int action, int topLeft, int bottomRight) { final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[2]; for (int i = 0; i < 2; i++) { Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +9 −0 Original line number Diff line number Diff line Loading @@ -745,6 +745,15 @@ 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(); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +61 −19 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ public class PipResizeGestureHandler { private final Rect mDisplayBounds = new Rect(); private final Function<Rect, Rect> mMovementBoundsSupplier; private final Runnable mUpdateMovementBoundsRunnable; private final Consumer<Rect> mUpdateResizeBoundsCallback; private int mDelta; private float mTouchSlop; Loading Loading @@ -137,6 +138,13 @@ public class PipResizeGestureHandler { mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); mUpdateResizeBoundsCallback = (rect) -> { mUserResizeBounds.set(rect); mMotionHelper.synchronizePinnedStackBounds(); mUpdateMovementBoundsRunnable.run(); resetState(); }; } public void init() { Loading Loading @@ -508,15 +516,50 @@ public class PipResizeGestureHandler { } } private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) { final int leftEdge = bounds.left; final int fromLeft = Math.abs(leftEdge - movementBounds.left); final int fromRight = Math.abs(movementBounds.right - leftEdge); // The PIP will be snapped to either the right or left edge, so calculate which one // is closest to the current position. final int newLeft = fromLeft < fromRight ? movementBounds.left : movementBounds.right; bounds.offsetTo(newLeft, mLastResizeBounds.top); } /** * Resizes the pip window and updates user-resized bounds. * * @param bounds target bounds to resize to * @param snapFraction snap fraction to apply after resizing */ void userResizeTo(Rect bounds, float snapFraction) { Rect finalBounds = new Rect(bounds); // get the current movement bounds final Rect movementBounds = mPipBoundsAlgorithm.getMovementBounds(finalBounds); // snap the target bounds to the either left or right edge, by choosing the closer one snapToMovementBoundsEdge(finalBounds, movementBounds); // apply the requested snap fraction onto the target bounds mPipBoundsAlgorithm.applySnapFraction(finalBounds, snapFraction); // resize from current bounds to target bounds without animation mPipTaskOrganizer.scheduleUserResizePip(mPipBoundsState.getBounds(), finalBounds, null); // set the flag that pip has been resized mPipBoundsState.setHasUserResizedPip(true); // finish the resize operation and update the state of the bounds mPipTaskOrganizer.scheduleFinishResizePip(finalBounds, mUpdateResizeBoundsCallback); } private void finishResize() { if (!mLastResizeBounds.isEmpty()) { final Consumer<Rect> callback = (rect) -> { mUserResizeBounds.set(mLastResizeBounds); mMotionHelper.synchronizePinnedStackBounds(); mUpdateMovementBoundsRunnable.run(); resetState(); }; // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped // position correctly. Drag-resize does not need to move, so just finalize resize. if (mOngoingPinchToResize) { Loading @@ -526,24 +569,23 @@ public class PipResizeGestureHandler { || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) { resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y); } final int leftEdge = mLastResizeBounds.left; final Rect movementBounds = mPipBoundsAlgorithm.getMovementBounds(mLastResizeBounds); final int fromLeft = Math.abs(leftEdge - movementBounds.left); final int fromRight = Math.abs(movementBounds.right - leftEdge); // The PIP will be snapped to either the right or left edge, so calculate which one // is closest to the current position. final int newLeft = fromLeft < fromRight ? movementBounds.left : movementBounds.right; mLastResizeBounds.offsetTo(newLeft, mLastResizeBounds.top); // get the current movement bounds final Rect movementBounds = mPipBoundsAlgorithm .getMovementBounds(mLastResizeBounds); // snap mLastResizeBounds to the correct edge based on movement bounds snapToMovementBoundsEdge(mLastResizeBounds, movementBounds); final float snapFraction = mPipBoundsAlgorithm.getSnapFraction( mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, PINCH_RESIZE_SNAP_DURATION, mAngle, callback); PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback); } else { mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback); PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, mUpdateResizeBoundsCallback); } final float magnetRadiusPercent = (float) mLastResizeBounds.width() / mMinSize.x / 2.f; mPipDismissTargetHandler Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +10 −0 Original line number Diff line number Diff line Loading @@ -824,6 +824,16 @@ public class PipTouchHandler { return mPipResizeGestureHandler.getUserResizeBounds(); } /** * Resizes the pip window and updates user resized bounds * * @param bounds target bounds to resize to * @param snapFraction snap fraction to apply after resizing */ void userResizeTo(Rect bounds, float snapFraction) { mPipResizeGestureHandler.userResizeTo(bounds, snapFraction); } /** * Gesture controlling normal movement of the PIP. */ Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.pip.phone; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; Loading Loading @@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) public class PipResizeGestureHandlerTest extends ShellTestCase { private static final float DEFAULT_SNAP_FRACTION = 2.0f; private static final int STEP_SIZE = 40; private final MotionEvent.PointerProperties[] mPp = new MotionEvent.PointerProperties[2]; Loading Loading @@ -196,6 +198,51 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { < mPipBoundsState.getBounds().width()); } @Test public void testUserResizeTo() { // resizing the bounds to normal bounds at first mPipResizeGestureHandler.userResizeTo(mPipBoundsState.getNormalBounds(), DEFAULT_SNAP_FRACTION); assertPipBoundsUserResizedTo(mPipBoundsState.getNormalBounds()); verify(mPipTaskOrganizer, times(1)) .scheduleUserResizePip(any(), any(), any()); verify(mPipTaskOrganizer, times(1)) .scheduleFinishResizePip(any(), any()); // bounds with max size final Rect maxBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x, mPipBoundsState.getMaxSize().y); // resizing the bounds to maximum bounds the second time mPipResizeGestureHandler.userResizeTo(maxBounds, DEFAULT_SNAP_FRACTION); assertPipBoundsUserResizedTo(maxBounds); // another call to scheduleUserResizePip() and scheduleFinishResizePip() makes // the total number of invocations 2 for each method verify(mPipTaskOrganizer, times(2)) .scheduleUserResizePip(any(), any(), any()); verify(mPipTaskOrganizer, times(2)) .scheduleFinishResizePip(any(), any()); } private void assertPipBoundsUserResizedTo(Rect bounds) { // check user-resized bounds assertEquals(mPipResizeGestureHandler.getUserResizeBounds().width(), bounds.width()); assertEquals(mPipResizeGestureHandler.getUserResizeBounds().height(), bounds.height()); // check if the bounds are the same assertEquals(mPipBoundsState.getBounds().width(), bounds.width()); assertEquals(mPipBoundsState.getBounds().height(), bounds.height()); // a flag should be set to indicate pip has been resized by the user assertTrue(mPipBoundsState.hasUserResizedPip()); } private MotionEvent obtainMotionEvent(int action, int topLeft, int bottomRight) { final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[2]; for (int i = 0; i < 2; i++) { Loading