Merge "Resize tasks from corners when in touch mode" into tm-qpr-dev am: 78b3719017

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20116051

Change-Id: I4535d45f3c5e86832149e265aaa118f46f11c0b8
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Ats Jenk
2022-10-10 19:47:23 +00:00
committed by Automerger Merge Worker
3 changed files with 164 additions and 23 deletions

View File

@@ -28,6 +28,7 @@ import android.os.Handler;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.View; import android.view.View;
import android.view.ViewConfiguration;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import com.android.wm.shell.R; import com.android.wm.shell.R;
@@ -55,6 +56,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
// Height of button (32dp) + 2 * margin (5dp each) // Height of button (32dp) + 2 * margin (5dp each)
private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42; private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42;
private static final int RESIZE_HANDLE_IN_DIP = 30; private static final int RESIZE_HANDLE_IN_DIP = 30;
private static final int RESIZE_CORNER_IN_DIP = 44;
private static final Rect EMPTY_OUTSET = new Rect(); private static final Rect EMPTY_OUTSET = new Rect();
private static final Rect RESIZE_HANDLE_OUTSET = new Rect( private static final Rect RESIZE_HANDLE_OUTSET = new Rect(
@@ -145,16 +147,19 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) { if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener(); closeDragResizeListener();
mDragResizeListener = new DragResizeInputListener( mDragResizeListener = new DragResizeInputListener(
mContext, mContext,
mHandler, mHandler,
mChoreographer, mChoreographer,
mDisplay.getDisplayId(), mDisplay.getDisplayId(),
mDecorationContainerSurface, mDecorationContainerSurface,
mDragResizeCallback); mDragResizeCallback);
} }
int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
mDragResizeListener.setGeometry( mDragResizeListener.setGeometry(
mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP)); mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP),
(int) (mResult.mDensity * RESIZE_CORNER_IN_DIP), touchSlop);
} }
/** /**

View File

@@ -16,11 +16,13 @@
package com.android.wm.shell.windowdecor; package com.android.wm.shell.windowdecor;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.content.Context; import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Region; import android.graphics.Region;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
@@ -42,8 +44,11 @@ import com.android.internal.view.BaseIWindow;
/** /**
* An input event listener registered to InputDispatcher to receive input events on task edges and * An input event listener registered to InputDispatcher to receive input events on task edges and
* convert them to drag resize requests. * and corners. Converts them to drag resize requests.
* Task edges are for resizing with a mouse.
* Task corners are for resizing with touch input.
*/ */
// TODO(b/251270585): investigate how to pass taps in corners to the tasks
class DragResizeInputListener implements AutoCloseable { class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener"; private static final String TAG = "DragResizeInputListener";
@@ -63,8 +68,15 @@ class DragResizeInputListener implements AutoCloseable {
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
private int mResizeHandleThickness; private int mResizeHandleThickness;
private int mCornerSize;
private Rect mLeftTopCornerBounds;
private Rect mRightTopCornerBounds;
private Rect mLeftBottomCornerBounds;
private Rect mRightBottomCornerBounds;
private int mDragPointerId = -1; private int mDragPointerId = -1;
private int mTouchSlop;
DragResizeInputListener( DragResizeInputListener(
Context context, Context context,
@@ -118,16 +130,23 @@ class DragResizeInputListener implements AutoCloseable {
* @param height The height of the drag resize handler in pixels, including resize handle * @param height The height of the drag resize handler in pixels, including resize handle
* thickness. That is task height + 2 * resize handle thickness. * thickness. That is task height + 2 * resize handle thickness.
* @param resizeHandleThickness The thickness of the resize handle in pixels. * @param resizeHandleThickness The thickness of the resize handle in pixels.
* @param cornerSize The size of the resize handle centered in each corner.
* @param touchSlop The distance in pixels user has to drag with touch for it to register as
* a resize action.
*/ */
void setGeometry(int width, int height, int resizeHandleThickness) { void setGeometry(int width, int height, int resizeHandleThickness, int cornerSize,
int touchSlop) {
if (mWidth == width && mHeight == height if (mWidth == width && mHeight == height
&& mResizeHandleThickness == resizeHandleThickness) { && mResizeHandleThickness == resizeHandleThickness
&& mCornerSize == cornerSize) {
return; return;
} }
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
mResizeHandleThickness = resizeHandleThickness; mResizeHandleThickness = resizeHandleThickness;
mCornerSize = cornerSize;
mTouchSlop = touchSlop;
Region touchRegion = new Region(); Region touchRegion = new Region();
final Rect topInputBounds = new Rect(0, 0, mWidth, mResizeHandleThickness); final Rect topInputBounds = new Rect(0, 0, mWidth, mResizeHandleThickness);
@@ -146,6 +165,40 @@ class DragResizeInputListener implements AutoCloseable {
mWidth, mHeight); mWidth, mHeight);
touchRegion.union(bottomInputBounds); touchRegion.union(bottomInputBounds);
// Set up touch areas in each corner.
int cornerRadius = mCornerSize / 2;
mLeftTopCornerBounds = new Rect(
mResizeHandleThickness - cornerRadius,
mResizeHandleThickness - cornerRadius,
mResizeHandleThickness + cornerRadius,
mResizeHandleThickness + cornerRadius
);
touchRegion.union(mLeftTopCornerBounds);
mRightTopCornerBounds = new Rect(
mWidth - mResizeHandleThickness - cornerRadius,
mResizeHandleThickness - cornerRadius,
mWidth - mResizeHandleThickness + cornerRadius,
mResizeHandleThickness + cornerRadius
);
touchRegion.union(mRightTopCornerBounds);
mLeftBottomCornerBounds = new Rect(
mResizeHandleThickness - cornerRadius,
mHeight - mResizeHandleThickness - cornerRadius,
mResizeHandleThickness + cornerRadius,
mHeight - mResizeHandleThickness + cornerRadius
);
touchRegion.union(mLeftBottomCornerBounds);
mRightBottomCornerBounds = new Rect(
mWidth - mResizeHandleThickness - cornerRadius,
mHeight - mResizeHandleThickness - cornerRadius,
mWidth - mResizeHandleThickness + cornerRadius,
mHeight - mResizeHandleThickness + cornerRadius
);
touchRegion.union(mRightBottomCornerBounds);
try { try {
mWindowSession.updateInputChannel( mWindowSession.updateInputChannel(
mInputChannel.getToken(), mInputChannel.getToken(),
@@ -173,6 +226,9 @@ class DragResizeInputListener implements AutoCloseable {
private final Choreographer mChoreographer; private final Choreographer mChoreographer;
private final Runnable mConsumeBatchEventRunnable; private final Runnable mConsumeBatchEventRunnable;
private boolean mConsumeBatchEventScheduled; private boolean mConsumeBatchEventScheduled;
private boolean mShouldHandleEvents;
private boolean mDragging;
private final PointF mActionDownPoint = new PointF();
private TaskResizeInputEventReceiver( private TaskResizeInputEventReceiver(
InputChannel inputChannel, Handler handler, Choreographer choreographer) { InputChannel inputChannel, Handler handler, Choreographer choreographer) {
@@ -216,41 +272,101 @@ class DragResizeInputListener implements AutoCloseable {
} }
MotionEvent e = (MotionEvent) inputEvent; MotionEvent e = (MotionEvent) inputEvent;
boolean result = false;
// Check if this is a touch event vs mouse event.
// Touch events are tracked in four corners. Other events are tracked in resize edges.
boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
switch (e.getActionMasked()) { switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0); float x = e.getX(0);
mCallback.onDragResizeStart( float y = e.getY(0);
calculateCtrlType(e.getX(0), e.getY(0)), e.getRawX(0), e.getRawY(0)); if (isTouch) {
mShouldHandleEvents = isInCornerBounds(x, y);
} else {
mShouldHandleEvents = isInResizeHandleBounds(x, y);
}
if (mShouldHandleEvents) {
mDragPointerId = e.getPointerId(0);
float rawX = e.getRawX(0);
float rawY = e.getRawY(0);
mActionDownPoint.set(rawX, rawY);
int ctrlType = calculateCtrlType(isTouch, x, y);
mCallback.onDragResizeStart(ctrlType, rawX, rawY);
result = true;
}
break; break;
} }
case MotionEvent.ACTION_MOVE: { case MotionEvent.ACTION_MOVE: {
if (!mShouldHandleEvents) {
break;
}
int dragPointerIndex = e.findPointerIndex(mDragPointerId); int dragPointerIndex = e.findPointerIndex(mDragPointerId);
mCallback.onDragResizeMove( float rawX = e.getRawX(dragPointerIndex);
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); float rawY = e.getRawY(dragPointerIndex);
if (isTouch) {
// Check for touch slop for touch events
float dx = rawX - mActionDownPoint.x;
float dy = rawY - mActionDownPoint.y;
if (!mDragging && Math.hypot(dx, dy) > mTouchSlop) {
mDragging = true;
}
} else {
// For all other types allow immediate dragging.
mDragging = true;
}
if (mDragging) {
mCallback.onDragResizeMove(rawX, rawY);
result = true;
}
break; break;
} }
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: { case MotionEvent.ACTION_CANCEL: {
int dragPointerIndex = e.findPointerIndex(mDragPointerId); if (mDragging) {
mCallback.onDragResizeEnd( int dragPointerIndex = e.findPointerIndex(mDragPointerId);
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); mCallback.onDragResizeEnd(
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
}
mDragging = false;
mShouldHandleEvents = false;
mActionDownPoint.set(0, 0);
mDragPointerId = -1; mDragPointerId = -1;
result = true;
break; break;
} }
case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE: { case MotionEvent.ACTION_HOVER_MOVE: {
updateCursorType(e.getXCursorPosition(), e.getYCursorPosition()); updateCursorType(e.getXCursorPosition(), e.getYCursorPosition());
result = true;
break; break;
} }
case MotionEvent.ACTION_HOVER_EXIT: case MotionEvent.ACTION_HOVER_EXIT:
mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT); mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT);
result = true;
break; break;
} }
return true; return result;
}
private boolean isInCornerBounds(float xf, float yf) {
return calculateCornersCtrlType(xf, yf) != 0;
}
private boolean isInResizeHandleBounds(float x, float y) {
return calculateResizeHandlesCtrlType(x, y) != 0;
} }
@TaskPositioner.CtrlType @TaskPositioner.CtrlType
private int calculateCtrlType(float x, float y) { private int calculateCtrlType(boolean isTouch, float x, float y) {
if (isTouch) {
return calculateCornersCtrlType(x, y);
}
return calculateResizeHandlesCtrlType(x, y);
}
@TaskPositioner.CtrlType
private int calculateResizeHandlesCtrlType(float x, float y) {
int ctrlType = 0; int ctrlType = 0;
if (x < mResizeHandleThickness) { if (x < mResizeHandleThickness) {
ctrlType |= TaskPositioner.CTRL_TYPE_LEFT; ctrlType |= TaskPositioner.CTRL_TYPE_LEFT;
@@ -267,8 +383,27 @@ class DragResizeInputListener implements AutoCloseable {
return ctrlType; return ctrlType;
} }
@TaskPositioner.CtrlType
private int calculateCornersCtrlType(float x, float y) {
int xi = (int) x;
int yi = (int) y;
if (mLeftTopCornerBounds.contains(xi, yi)) {
return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP;
}
if (mLeftBottomCornerBounds.contains(xi, yi)) {
return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM;
}
if (mRightTopCornerBounds.contains(xi, yi)) {
return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP;
}
if (mRightBottomCornerBounds.contains(xi, yi)) {
return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM;
}
return 0;
}
private void updateCursorType(float x, float y) { private void updateCursorType(float x, float y) {
@TaskPositioner.CtrlType int ctrlType = calculateCtrlType(x, y); @TaskPositioner.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y);
int cursorType = PointerIcon.TYPE_DEFAULT; int cursorType = PointerIcon.TYPE_DEFAULT;
switch (ctrlType) { switch (ctrlType) {

View File

@@ -25,9 +25,10 @@ import com.android.wm.shell.ShellTaskOrganizer;
class TaskPositioner implements DragResizeCallback { class TaskPositioner implements DragResizeCallback {
@IntDef({CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM}) @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
@interface CtrlType {} @interface CtrlType {}
static final int CTRL_TYPE_UNDEFINED = 0;
static final int CTRL_TYPE_LEFT = 1; static final int CTRL_TYPE_LEFT = 1;
static final int CTRL_TYPE_RIGHT = 2; static final int CTRL_TYPE_RIGHT = 2;
static final int CTRL_TYPE_TOP = 4; static final int CTRL_TYPE_TOP = 4;