diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 0a148c413c0f7..c2ec1c55a59f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -232,7 +232,8 @@ public class PipResizeGestureHandler { } } - private void onInputEvent(InputEvent ev) { + @VisibleForTesting + void onInputEvent(InputEvent ev) { // Don't allow resize when PiP is stashed. if (mPipBoundsState.isStashed()) { return; @@ -366,7 +367,8 @@ public class PipResizeGestureHandler { return mIsSysUiStateValid; } - private void onPinchResize(MotionEvent ev) { + @VisibleForTesting + void onPinchResize(MotionEvent ev) { int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { @@ -411,7 +413,7 @@ public class PipResizeGestureHandler { if (!mThresholdCrossed && (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop || distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) { - mInputMonitor.pilferPointers(); + pilferPointers(); mThresholdCrossed = true; // Reset the down to begin resizing from this point mDownPoint.set(mLastPoint); @@ -548,6 +550,17 @@ public class PipResizeGestureHandler { return mUserResizeBounds; } + @VisibleForTesting + Rect getLastResizeBounds() { + return mLastResizeBounds; + } + + @VisibleForTesting + void pilferPointers() { + mInputMonitor.pilferPointers(); + } + + @VisibleForTesting public void updateMaxSize(int maxX, int maxY) { mMaxSize.set(maxX, maxY); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java new file mode 100644 index 0000000000000..dd10aa7752f59 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2021 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.phone; + +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipSnapAlgorithm; +import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip.PipUiEventLogger; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests against {@link PipResizeGestureHandler} + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipResizeGestureHandlerTest extends ShellTestCase { + private static final int STEP_SIZE = 40; + private final MotionEvent.PointerProperties[] mPp = new MotionEvent.PointerProperties[2]; + + @Mock + private PhonePipMenuController mPhonePipMenuController; + + @Mock + private PipTaskOrganizer mPipTaskOrganizer; + + @Mock + private PipDismissTargetHandler mPipDismissTargetHandler; + + @Mock + private PipTransitionController mMockPipTransitionController; + + @Mock + private FloatingContentCoordinator mFloatingContentCoordinator; + + @Mock + private PipUiEventLogger mPipUiEventLogger; + + @Mock + private ShellExecutor mMainExecutor; + + private PipResizeGestureHandler mPipResizeGestureHandler; + + private PipBoundsState mPipBoundsState; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mPipBoundsState = new PipBoundsState(mContext); + final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm(); + final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, + mPipBoundsState, pipSnapAlgorithm); + final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState, + mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm, + mMockPipTransitionController, mFloatingContentCoordinator); + mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm, + mPipBoundsState, motionHelper, mPipTaskOrganizer, mPipDismissTargetHandler, + (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController, + mMainExecutor) { + @Override + public void pilferPointers() { + // Overridden just to avoid calling into InputMonitor. + } + }; + + for (int i = 0; i < 2; i++) { + MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties(); + pointerProperty.id = i; + pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER; + mPp[i] = pointerProperty; + } + + mPipResizeGestureHandler.init(); + mPipResizeGestureHandler.onSystemUiStateChanged(true); + } + + @Test + public void twoInput_triggersPinchResize_getBigger() { + assertTrue(mPipResizeGestureHandler.isUsingPinchToZoom()); + + int topLeft = 200; + int bottomRight = 500; + mPipBoundsState.setBounds(new Rect(topLeft, topLeft, bottomRight, bottomRight)); + + // Start inside the PiP bounds first. + topLeft += STEP_SIZE; + bottomRight -= STEP_SIZE; + MotionEvent downEvent = + obtainMotionEvent(MotionEvent.ACTION_POINTER_DOWN, topLeft, bottomRight); + assertTrue(mPipResizeGestureHandler.willStartResizeGesture(downEvent)); + + // Slowly move outward. + topLeft -= STEP_SIZE; + bottomRight += STEP_SIZE; + MotionEvent moveEvent1 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight); + mPipResizeGestureHandler.onPinchResize(moveEvent1); + + // Move outward more. + topLeft -= STEP_SIZE; + bottomRight += STEP_SIZE; + MotionEvent moveEvent2 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight); + mPipResizeGestureHandler.onPinchResize(moveEvent2); + + verify(mPipTaskOrganizer, times(2)) + .scheduleUserResizePip(any(), any(), anyFloat(), any()); + + MotionEvent upEvent = obtainMotionEvent(MotionEvent.ACTION_UP, topLeft, bottomRight); + mPipResizeGestureHandler.onPinchResize(upEvent); + + verify(mPipTaskOrganizer, times(1)) + .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any()); + + assertTrue("The new size should be bigger than the original PiP size.", + mPipResizeGestureHandler.getLastResizeBounds().width() + > mPipBoundsState.getBounds().width()); + } + + @Test + public void twoInput_triggersPinchResize_getSmaller() { + assertTrue(mPipResizeGestureHandler.isUsingPinchToZoom()); + + int topLeft = 200; + int bottomRight = 500; + mPipBoundsState.setBounds(new Rect(topLeft, topLeft, bottomRight, bottomRight)); + + + topLeft += STEP_SIZE; + bottomRight -= STEP_SIZE; + MotionEvent downEvent = + obtainMotionEvent(MotionEvent.ACTION_POINTER_DOWN, topLeft, bottomRight); + assertTrue(mPipResizeGestureHandler.willStartResizeGesture(downEvent)); + + topLeft += STEP_SIZE; + bottomRight -= STEP_SIZE; + MotionEvent moveEvent1 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight); + mPipResizeGestureHandler.onPinchResize(moveEvent1); + + topLeft += STEP_SIZE; + bottomRight -= STEP_SIZE; + MotionEvent moveEvent2 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight); + mPipResizeGestureHandler.onPinchResize(moveEvent2); + + verify(mPipTaskOrganizer, times(2)) + .scheduleUserResizePip(any(), any(), anyFloat(), any()); + + MotionEvent upEvent = obtainMotionEvent(MotionEvent.ACTION_UP, topLeft, bottomRight); + mPipResizeGestureHandler.onPinchResize(upEvent); + + verify(mPipTaskOrganizer, times(1)) + .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any()); + + assertTrue("The new size should be smaller than the original PiP size.", + mPipResizeGestureHandler.getLastResizeBounds().width() + < mPipBoundsState.getBounds().width()); + } + + private MotionEvent obtainMotionEvent(int action, int topLeft, int bottomRight) { + final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[2]; + for (int i = 0; i < 2; i++) { + MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords(); + if (i == 0) { + pointerCoord.x = topLeft; + pointerCoord.y = topLeft; + } else { + pointerCoord.x = bottomRight; + pointerCoord.y = bottomRight; + } + pc[i] = pointerCoord; + } + return MotionEvent.obtain(0 /* downTime */, + System.currentTimeMillis(), + action, + 2 /* pointerCount */, + mPp, + pc, + 0 /* metaState */, + 0 /* buttonState */, + 0 /* xPrecision */, + 0 /* yPrecision */, + 0 /* deviceId */, + 0 /* edgeFlags */, + 0 /* source */, + 0 /* flags */); + } +}