Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java +3 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +3 −2 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDragToResizeHandler.java 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * 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); } } libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipPinchToResizeHandler.java 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * 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); } } libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java +94 −102 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java +3 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +3 −2 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDragToResizeHandler.java 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * 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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipPinchToResizeHandler.java 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * 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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java +94 −102 File changed.Preview size limit exceeded, changes collapsed. Show changes