diff options
| author | 2025-02-12 11:06:51 -0800 | |
|---|---|---|
| committer | 2025-02-12 11:06:51 -0800 | |
| commit | ee4daa24e6baecd66df9350fc5fc1100453d7af6 (patch) | |
| tree | 602750202d63f6c3ed40249bb158ecdbb7e734c0 | |
| parent | 02dec96abf10bb3c5df7f689ce386b60985c3752 (diff) | |
| parent | 96b677267a871ca7999d0a82e042918909e0512b (diff) | |
Merge "[PiP2 on Desktop] Drag-corner-to-resize PiP." into main
6 files changed, 451 insertions, 108 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java index c10c2c905c97..c6afc313b239 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java @@ -81,8 +81,8 @@ public class PipDesktopState { return false; } - /** Returns whether PiP is exiting while we're in a Desktop Mode session. */ - private boolean isPipExitingToDesktopMode() { + /** Returns whether PiP is active in a display that is in active Desktop Mode session. */ + public boolean isPipInDesktopMode() { // Early return if PiP in Desktop Windowing is not supported. if (!isDesktopWindowingPipEnabled()) { return false; @@ -137,7 +137,7 @@ public class PipDesktopState { // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will // resolve the windowing mode to the display's windowing mode. // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM. - if (isPipExitingToDesktopMode()) { + if (isPipInDesktopMode()) { if (isDisplayInFreeform()) { return WINDOWING_MODE_UNDEFINED; } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index e7c76bbd91b2..7d80ee5f3bb6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -174,6 +174,7 @@ public abstract class Pip2Module { @NonNull PipScheduler pipScheduler, @NonNull SizeSpecSource sizeSpecSource, @NonNull PipDisplayLayoutState pipDisplayLayoutState, + PipDesktopState pipDesktopState, DisplayController displayController, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, @@ -182,8 +183,8 @@ public abstract class Pip2Module { Optional<PipPerfHintController> pipPerfHintControllerOptional) { return new PipTouchHandler(context, shellInit, shellCommandHandler, menuPhoneController, pipBoundsAlgorithm, pipBoundsState, pipTransitionState, pipScheduler, - sizeSpecSource, pipDisplayLayoutState, displayController, pipMotionHelper, - floatingContentCoordinator, pipUiEventLogger, mainExecutor, + sizeSpecSource, pipDisplayLayoutState, pipDesktopState, displayController, + pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDragToResizeHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDragToResizeHandler.java new file mode 100644 index 000000000000..bd0b810b2a44 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDragToResizeHandler.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2025 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.pip2.phone; + +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP; +import static com.android.wm.shell.pip2.phone.PipMenuView.ANIM_TYPE_NONE; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.Region; +import android.view.MotionEvent; + +import com.android.internal.policy.TaskResizingAlgorithm; +import com.android.wm.shell.R; +import com.android.wm.shell.common.pip.PipBoundsAlgorithm; +import com.android.wm.shell.common.pip.PipBoundsState; + +import java.util.function.Function; + +/** Helper for handling drag-corner-to-resize gestures. */ +public class PipDragToResizeHandler { + private final Context mContext; + private final PipResizeGestureHandler mPipResizeGestureHandler; + private final PipBoundsState mPipBoundsState; + private final PhonePipMenuController mPhonePipMenuController; + private final PipBoundsAlgorithm mPipBoundsAlgorithm; + private final PipScheduler mPipScheduler; + + private final Region mTmpRegion = new Region(); + private final Rect mDragCornerSize = new Rect(); + private final Rect mTmpTopLeftCorner = new Rect(); + private final Rect mTmpTopRightCorner = new Rect(); + private final Rect mTmpBottomLeftCorner = new Rect(); + private final Rect mTmpBottomRightCorner = new Rect(); + private final Rect mDisplayBounds = new Rect(); + private final Function<Rect, Rect> mMovementBoundsSupplier; + private int mDelta; + + public PipDragToResizeHandler(Context context, PipResizeGestureHandler pipResizeGestureHandler, + PipBoundsState pipBoundsState, + PhonePipMenuController phonePipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, + PipScheduler pipScheduler, Function<Rect, Rect> movementBoundsSupplier) { + mContext = context; + mPipResizeGestureHandler = pipResizeGestureHandler; + mPipBoundsState = pipBoundsState; + mPhonePipMenuController = phonePipMenuController; + mPipBoundsAlgorithm = pipBoundsAlgorithm; + mPipScheduler = pipScheduler; + mMovementBoundsSupplier = movementBoundsSupplier; + } + + /** Invoked by {@link PipResizeGestureHandler#reloadResources}. */ + void reloadResources() { + final Resources res = mContext.getResources(); + mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size); + } + + /** Invoked by {@link PipResizeGestureHandler#onInputEvent} if drag-corner-to-resize is + * enabled. */ + void onDragCornerResize(MotionEvent ev, Rect lastResizeBounds, PointF downPoint, + Rect downBounds, Point minSize, Point maxSize, float touchSlop) { + int action = ev.getActionMasked(); + float x = ev.getX(); + float y = ev.getY(); + if (action == MotionEvent.ACTION_DOWN) { + lastResizeBounds.setEmpty(); + final boolean allowGesture = isWithinDragResizeRegion((int) x, (int) y); + mPipResizeGestureHandler.setAllowGesture(allowGesture); + if (allowGesture) { + setCtrlType((int) x, (int) y); + downPoint.set(x, y); + downBounds.set(mPipBoundsState.getBounds()); + } + } else if (mPipResizeGestureHandler.getAllowGesture()) { + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: + // We do not support multi touch for resizing via drag + mPipResizeGestureHandler.setAllowGesture(false); + break; + case MotionEvent.ACTION_MOVE: + final boolean thresholdCrossed = mPipResizeGestureHandler.getThresholdCrossed(); + // Capture inputs + if (!mPipResizeGestureHandler.getThresholdCrossed() + && Math.hypot(x - downPoint.x, y - downPoint.y) > touchSlop) { + mPipResizeGestureHandler.setThresholdCrossed(true); + // Reset the down to begin resizing from this point + downPoint.set(x, y); + mPipResizeGestureHandler.pilferPointers(); + } + if (mPipResizeGestureHandler.getThresholdCrossed()) { + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE, + false /* resize */); + } + final Rect currentPipBounds = mPipBoundsState.getBounds(); + lastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, + downPoint.x, downPoint.y, currentPipBounds, + mPipResizeGestureHandler.getCtrlType(), minSize.x, + minSize.y, maxSize, true, + downBounds.width() > downBounds.height())); + mPipBoundsAlgorithm.transformBoundsToAspectRatio(lastResizeBounds, + mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */, + true /* useCurrentSize */); + mPipScheduler.scheduleUserResizePip(lastResizeBounds); + mPipBoundsState.setHasUserResizedPip(true); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mPipResizeGestureHandler.finishResize(); + break; + } + } + } + + /** + * Check whether the current x,y coordinate is within the region in which drag-resize should + * start. + * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which + * overlaps with the PIP window while the rest goes outside of the PIP window. + * _ _ _ _ + * |_|_|_________|_|_| + * |_|_| |_|_| + * | PIP | + * | WINDOW | + * _|_ _|_ + * |_|_|_________|_|_| + * |_|_| |_|_| + */ + boolean isWithinDragResizeRegion(int x, int y) { + final Rect currentPipBounds = mPipBoundsState.getBounds(); + if (currentPipBounds == null) { + return false; + } + resetDragCorners(); + mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2, + currentPipBounds.top - mDelta / 2); + mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2, + currentPipBounds.top - mDelta / 2); + mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2, + currentPipBounds.bottom - mDelta / 2); + mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2, + currentPipBounds.bottom - mDelta / 2); + + mTmpRegion.setEmpty(); + mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION); + mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION); + mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION); + mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION); + + return mTmpRegion.contains(x, y); + } + + private void resetDragCorners() { + mDragCornerSize.set(0, 0, mDelta, mDelta); + mTmpTopLeftCorner.set(mDragCornerSize); + mTmpTopRightCorner.set(mDragCornerSize); + mTmpBottomLeftCorner.set(mDragCornerSize); + mTmpBottomRightCorner.set(mDragCornerSize); + } + + private void setCtrlType(int x, int y) { + final Rect currentPipBounds = mPipBoundsState.getBounds(); + int ctrlType = mPipResizeGestureHandler.getCtrlType(); + + Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds); + + mDisplayBounds.set(movementBounds.left, + movementBounds.top, + movementBounds.right + currentPipBounds.width(), + movementBounds.bottom + currentPipBounds.height()); + + if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top + && currentPipBounds.left != mDisplayBounds.left) { + ctrlType |= CTRL_LEFT; + ctrlType |= CTRL_TOP; + } + if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top + && currentPipBounds.right != mDisplayBounds.right) { + ctrlType |= CTRL_RIGHT; + ctrlType |= CTRL_TOP; + } + if (mTmpBottomRightCorner.contains(x, y) + && currentPipBounds.bottom != mDisplayBounds.bottom + && currentPipBounds.right != mDisplayBounds.right) { + ctrlType |= CTRL_RIGHT; + ctrlType |= CTRL_BOTTOM; + } + if (mTmpBottomLeftCorner.contains(x, y) + && currentPipBounds.bottom != mDisplayBounds.bottom + && currentPipBounds.left != mDisplayBounds.left) { + ctrlType |= CTRL_LEFT; + ctrlType |= CTRL_BOTTOM; + } + + mPipResizeGestureHandler.setCtrlType(ctrlType); + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipPinchToResizeHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipPinchToResizeHandler.java new file mode 100644 index 000000000000..1e41af379864 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipPinchToResizeHandler.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2025 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.pip2.phone; + +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.view.MotionEvent; + +import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm; + +/** Helper for handling pinch-to-resize gestures. */ +public class PipPinchToResizeHandler { + private final PipResizeGestureHandler mPipResizeGestureHandler; + private final PipBoundsState mPipBoundsState; + private final PhonePipMenuController mPhonePipMenuController; + private final PipScheduler mPipScheduler; + private final PipPinchResizingAlgorithm mPinchResizingAlgorithm; + + private int mFirstIndex = -1; + private int mSecondIndex = -1; + + public PipPinchToResizeHandler(PipResizeGestureHandler pipResizeGestureHandler, + PipBoundsState pipBoundsState, PhonePipMenuController phonePipMenuController, + PipScheduler pipScheduler) { + mPipResizeGestureHandler = pipResizeGestureHandler; + mPipBoundsState = pipBoundsState; + mPhonePipMenuController = phonePipMenuController; + mPipScheduler = pipScheduler; + + mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); + } + + /** Invoked by {@link PipResizeGestureHandler#onInputEvent} if pinch-to-resize is enabled. */ + void onPinchResize(MotionEvent ev, PointF downPoint, PointF downSecondPoint, Rect downBounds, + PointF lastPoint, PointF lastSecondPoint, Rect lastResizeBounds, float touchSlop, + Point minSize, Point maxSize) { + int action = ev.getActionMasked(); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mFirstIndex = -1; + mSecondIndex = -1; + mPipResizeGestureHandler.setAllowGesture(false); + mPipResizeGestureHandler.finishResize(); + } + + if (ev.getPointerCount() != 2) { + return; + } + + final Rect pipBounds = mPipBoundsState.getBounds(); + if (action == MotionEvent.ACTION_POINTER_DOWN) { + if (mFirstIndex == -1 && mSecondIndex == -1 + && pipBounds.contains((int) ev.getRawX(0), (int) ev.getRawY(0)) + && pipBounds.contains((int) ev.getRawX(1), (int) ev.getRawY(1))) { + mPipResizeGestureHandler.setAllowGesture(true); + mFirstIndex = 0; + mSecondIndex = 1; + downPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex)); + downSecondPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex)); + downBounds.set(pipBounds); + + lastPoint.set(downPoint); + lastSecondPoint.set(lastSecondPoint); + lastResizeBounds.set(downBounds); + + // start the high perf session as the second pointer gets detected + mPipResizeGestureHandler.startHighPerfSession(); + } + } + + if (action == MotionEvent.ACTION_MOVE) { + if (mFirstIndex == -1 || mSecondIndex == -1) { + return; + } + + float x0 = ev.getRawX(mFirstIndex); + float y0 = ev.getRawY(mFirstIndex); + float x1 = ev.getRawX(mSecondIndex); + float y1 = ev.getRawY(mSecondIndex); + lastPoint.set(x0, y0); + lastSecondPoint.set(x1, y1); + + // Capture inputs + if (!mPipResizeGestureHandler.getThresholdCrossed() + && (distanceBetween(downSecondPoint, lastSecondPoint) > touchSlop + || distanceBetween(downPoint, lastPoint) > touchSlop)) { + mPipResizeGestureHandler.pilferPointers(); + mPipResizeGestureHandler.setThresholdCrossed(true); + // Reset the down to begin resizing from this point + downPoint.set(lastPoint); + downSecondPoint.set(lastSecondPoint); + + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(); + } + } + + if (mPipResizeGestureHandler.getThresholdCrossed()) { + final float angle = mPinchResizingAlgorithm.calculateBoundsAndAngle(downPoint, + downSecondPoint, lastPoint, lastSecondPoint, minSize, maxSize, + downBounds, lastResizeBounds); + + mPipResizeGestureHandler.setAngle(angle); + mPipScheduler.scheduleUserResizePip(lastResizeBounds, angle); + mPipBoundsState.setHasUserResizedPip(true); + } + } + } + + private float distanceBetween(PointF p1, PointF p2) { + return (float) Math.hypot(p2.x - p1.x, p2.y - p1.y); + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java index e4be3f60f86e..b869bf153c34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java @@ -44,13 +44,14 @@ import com.android.wm.shell.R; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipDesktopState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.common.pip.PipPerfHintController; -import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.pip2.animation.PipResizeAnimator; import java.io.PrintWriter; +import java.util.function.Function; /** * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to @@ -72,8 +73,8 @@ public class PipResizeGestureHandler implements private final PipTransitionState mPipTransitionState; private final PhonePipMenuController mPhonePipMenuController; private final PipDisplayLayoutState mPipDisplayLayoutState; + private final PipDesktopState mPipDesktopState; private final PipUiEventLogger mPipUiEventLogger; - private final PipPinchResizingAlgorithm mPinchResizingAlgorithm; private final ShellExecutor mMainExecutor; private final PointF mDownPoint = new PointF(); @@ -93,16 +94,18 @@ public class PipResizeGestureHandler implements private boolean mIsAttached; private boolean mIsEnabled; private boolean mEnablePinchResize; + private boolean mEnableDragCornerResize; private boolean mIsSysUiStateValid; private boolean mThresholdCrossed; private boolean mOngoingPinchToResize = false; private boolean mWaitingForBoundsChangeTransition = false; private float mAngle = 0; - int mFirstIndex = -1; - int mSecondIndex = -1; + private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; + private PipDragToResizeHandler mPipDragToResizeHandler; + private PipPinchToResizeHandler mPipPinchToResizeHandler; @Nullable private final PipPerfHintController mPipPerfHintController; @@ -121,7 +124,9 @@ public class PipResizeGestureHandler implements PipTransitionState pipTransitionState, PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController, + Function<Rect, Rect> movementBoundsSupplier, PipDisplayLayoutState pipDisplayLayoutState, + PipDesktopState pipDesktopState, ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) { mContext = context; @@ -137,8 +142,13 @@ public class PipResizeGestureHandler implements mPhonePipMenuController = menuActivityController; mPipDisplayLayoutState = pipDisplayLayoutState; + mPipDesktopState = pipDesktopState; mPipUiEventLogger = pipUiEventLogger; - mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); + + mPipDragToResizeHandler = new PipDragToResizeHandler(context, this, pipBoundsState, + menuActivityController, pipBoundsAlgorithm, pipScheduler, movementBoundsSupplier); + mPipPinchToResizeHandler = new PipPinchToResizeHandler(this, pipBoundsState, + menuActivityController, pipScheduler); } void init() { @@ -163,6 +173,7 @@ public class PipResizeGestureHandler implements } private void reloadResources() { + mPipDragToResizeHandler.reloadResources(); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); } @@ -180,6 +191,8 @@ public class PipResizeGestureHandler implements void onActivityPinned() { mIsAttached = true; updateIsEnabled(); + // Only enable drag-corner-to-resize if PiP was entered when Desktop Mode session is active. + mEnableDragCornerResize = mPipDesktopState.isPipInDesktopMode(); } void onActivityUnpinned() { @@ -211,9 +224,44 @@ public class PipResizeGestureHandler implements } } + boolean getAllowGesture() { + return mAllowGesture; + } + + void setAllowGesture(boolean allowGesture) { + mAllowGesture = allowGesture; + } + + boolean getThresholdCrossed() { + return mThresholdCrossed; + } + + void setThresholdCrossed(boolean thresholdCrossed) { + mThresholdCrossed = thresholdCrossed; + } + + int getCtrlType() { + return mCtrlType; + } + + void setCtrlType(int ctrlType) { + mCtrlType = ctrlType; + } + + void setAngle(float angle) { + mAngle = angle; + } + + void startHighPerfSession() { + if (mPipPerfHintController != null) { + mPipHighPerfSession = mPipPerfHintController.startSession( + this::onHighPerfSessionTimeout, "onPinchResize"); + } + } + @VisibleForTesting void onInputEvent(InputEvent ev) { - if (!mEnablePinchResize) { + if (!mEnableDragCornerResize && !mEnablePinchResize) { // No need to handle anything if resizing isn't enabled. return; } @@ -240,7 +288,12 @@ public class PipResizeGestureHandler implements } if (mOngoingPinchToResize) { - onPinchResize(mv); + mPipPinchToResizeHandler.onPinchResize(mv, mDownPoint, mDownSecondPoint, + mDownBounds, mLastPoint, mLastSecondPoint, mLastResizeBounds, mTouchSlop, + mMinSize, mMaxSize); + } else if (mEnableDragCornerResize) { + mPipDragToResizeHandler.onDragCornerResize(mv, mLastResizeBounds, mDownPoint, + mDownBounds, mMinSize, mMaxSize, mTouchSlop); } } } @@ -261,20 +314,31 @@ public class PipResizeGestureHandler implements } boolean willStartResizeGesture(MotionEvent ev) { - if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - if (mEnablePinchResize && ev.getPointerCount() == 2) { - onPinchResize(ev); - mOngoingPinchToResize = mAllowGesture; - return mAllowGesture; - } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if (mEnableDragCornerResize && mPipDragToResizeHandler.isWithinDragResizeRegion( + (int) ev.getRawX(), + (int) ev.getRawY())) { + return true; + } + break; + + case MotionEvent.ACTION_POINTER_DOWN: + if (mEnablePinchResize && ev.getPointerCount() == 2) { + mPipPinchToResizeHandler.onPinchResize(ev, mDownPoint, mDownSecondPoint, + mDownBounds, mLastPoint, mLastSecondPoint, mLastResizeBounds, + mTouchSlop, mMinSize, mMaxSize); + mOngoingPinchToResize = mAllowGesture; + return mAllowGesture; + } + break; + + default: + break; } return false; } - private boolean isInValidSysUiState() { - return mIsSysUiStateValid; - } - private void onHighPerfSessionTimeout(PipPerfHintController.PipHighPerfSession session) {} private void cleanUpHighPerfSessionMaybe() { @@ -285,83 +349,6 @@ public class PipResizeGestureHandler implements } } - @VisibleForTesting - void onPinchResize(MotionEvent ev) { - int action = ev.getActionMasked(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - mFirstIndex = -1; - mSecondIndex = -1; - mAllowGesture = false; - finishResize(); - } - - if (ev.getPointerCount() != 2) { - return; - } - - final Rect pipBounds = mPipBoundsState.getBounds(); - if (action == MotionEvent.ACTION_POINTER_DOWN) { - if (mFirstIndex == -1 && mSecondIndex == -1 - && pipBounds.contains((int) ev.getRawX(0), (int) ev.getRawY(0)) - && pipBounds.contains((int) ev.getRawX(1), (int) ev.getRawY(1))) { - mAllowGesture = true; - mFirstIndex = 0; - mSecondIndex = 1; - mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex)); - mDownSecondPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex)); - mDownBounds.set(pipBounds); - - mLastPoint.set(mDownPoint); - mLastSecondPoint.set(mLastSecondPoint); - mLastResizeBounds.set(mDownBounds); - - // start the high perf session as the second pointer gets detected - if (mPipPerfHintController != null) { - mPipHighPerfSession = mPipPerfHintController.startSession( - this::onHighPerfSessionTimeout, "onPinchResize"); - } - } - } - - if (action == MotionEvent.ACTION_MOVE) { - if (mFirstIndex == -1 || mSecondIndex == -1) { - return; - } - - float x0 = ev.getRawX(mFirstIndex); - float y0 = ev.getRawY(mFirstIndex); - float x1 = ev.getRawX(mSecondIndex); - float y1 = ev.getRawY(mSecondIndex); - mLastPoint.set(x0, y0); - mLastSecondPoint.set(x1, y1); - - // Capture inputs - if (!mThresholdCrossed - && (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop - || distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) { - pilferPointers(); - mThresholdCrossed = true; - // Reset the down to begin resizing from this point - mDownPoint.set(mLastPoint); - mDownSecondPoint.set(mLastSecondPoint); - - if (mPhonePipMenuController.isMenuVisible()) { - mPhonePipMenuController.hideMenu(); - } - } - - if (mThresholdCrossed) { - mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint, - mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize, - mDownBounds, mLastResizeBounds); - - mPipScheduler.scheduleUserResizePip(mLastResizeBounds, mAngle); - mPipBoundsState.setHasUserResizedPip(true); - } - } - } - private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) { final int leftEdge = bounds.left; @@ -404,17 +391,21 @@ public class PipResizeGestureHandler implements // mPipTaskOrganizer.scheduleFinishResizePip(finalBounds, mUpdateResizeBoundsCallback); } - private void finishResize() { + /** Handles additional resizing and state changes after gesture resizing is done. */ + void finishResize() { if (mLastResizeBounds.isEmpty()) { resetState(); } - if (!mOngoingPinchToResize) { - return; - } // Cache initial bounds after release for animation before mLastResizeBounds are modified. mStartBoundsAfterRelease.set(mLastResizeBounds); + // Drag-corner-to-resize - we don't need to adjust the bounds at this point + if (!mOngoingPinchToResize) { + scheduleBoundsChange(); + return; + } + // If user resize is pretty close to max size, just auto resize to max. if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) { @@ -438,6 +429,10 @@ public class PipResizeGestureHandler implements mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); + scheduleBoundsChange(); + } + + private void scheduleBoundsChange() { // Update the transition state to schedule a resize transition. Bundle extra = new Bundle(); extra.putBoolean(RESIZE_BOUNDS_CHANGE, true); @@ -489,10 +484,6 @@ public class PipResizeGestureHandler implements mOhmOffset = offset; } - private float distanceBetween(PointF p1, PointF p2) { - return (float) Math.hypot(p2.x - p1.x, p2.y - p1.y); - } - private void resizeRectAboutCenter(Rect rect, int w, int h) { int cx = rect.centerX(); int cy = rect.centerY(); @@ -573,6 +564,7 @@ public class PipResizeGestureHandler implements pw.println(innerPrefix + "mIsAttached=" + mIsAttached); pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled); pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize); + pw.println(innerPrefix + "mEnableDragCornerResize=" + mEnableDragCornerResize); pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed); pw.println(innerPrefix + "mOhmOffset=" + mOhmOffset); pw.println(innerPrefix + "mMinSize=" + mMinSize); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index e405f3339054..72346b335a8e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -59,6 +59,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipDesktopState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.common.pip.PipDoubleTapHelper; import com.android.wm.shell.common.pip.PipPerfHintController; @@ -187,6 +188,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha @NonNull PipScheduler pipScheduler, @NonNull SizeSpecSource sizeSpecSource, @NonNull PipDisplayLayoutState pipDisplayLayoutState, + PipDesktopState pipDesktopState, DisplayController displayController, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, @@ -226,7 +228,8 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha mainExecutor); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger, - menuController, mPipDisplayLayoutState, mainExecutor, mPipPerfHintController); + menuController, this::getMovementBounds, mPipDisplayLayoutState, pipDesktopState, + mainExecutor, mPipPerfHintController); mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> { updateMinMaxSize(aspectRatio); onAspectRatioChanged(); |