Merge "Refactor DragDetector and fix a few resizing bugs" into tm-qpr-dev
This commit is contained in:
@@ -143,8 +143,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
|
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
|
||||||
return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
|
return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
|
||||||
|| (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
|
|| (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
|
||||||
&& taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
|
&& taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
|
||||||
== WINDOWING_MODE_FREEFORM);
|
== WINDOWING_MODE_FREEFORM);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createWindowDecoration(
|
private void createWindowDecoration(
|
||||||
@@ -175,16 +175,18 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
new CaptionTouchEventListener(taskInfo, taskPositioner);
|
new CaptionTouchEventListener(taskInfo, taskPositioner);
|
||||||
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
|
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
|
||||||
windowDecoration.setDragResizeCallback(taskPositioner);
|
windowDecoration.setDragResizeCallback(taskPositioner);
|
||||||
|
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
|
||||||
windowDecoration.relayout(taskInfo, startT, finishT);
|
windowDecoration.relayout(taskInfo, startT, finishT);
|
||||||
setupCaptionColor(taskInfo, windowDecoration);
|
setupCaptionColor(taskInfo, windowDecoration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CaptionTouchEventListener implements
|
private class CaptionTouchEventListener implements
|
||||||
View.OnClickListener, View.OnTouchListener {
|
View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
|
||||||
|
|
||||||
private final int mTaskId;
|
private final int mTaskId;
|
||||||
private final WindowContainerToken mTaskToken;
|
private final WindowContainerToken mTaskToken;
|
||||||
private final DragResizeCallback mDragResizeCallback;
|
private final DragResizeCallback mDragResizeCallback;
|
||||||
|
private final DragDetector mDragDetector;
|
||||||
|
|
||||||
private int mDragPointerId = -1;
|
private int mDragPointerId = -1;
|
||||||
|
|
||||||
@@ -194,6 +196,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
mTaskId = taskInfo.taskId;
|
mTaskId = taskInfo.taskId;
|
||||||
mTaskToken = taskInfo.token;
|
mTaskToken = taskInfo.token;
|
||||||
mDragResizeCallback = dragResizeCallback;
|
mDragResizeCallback = dragResizeCallback;
|
||||||
|
mDragDetector = new DragDetector(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -216,7 +219,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
if (v.getId() != R.id.caption) {
|
if (v.getId() != R.id.caption) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
handleEventForMove(e);
|
mDragDetector.onMotionEvent(e);
|
||||||
|
|
||||||
if (e.getAction() != MotionEvent.ACTION_DOWN) {
|
if (e.getAction() != MotionEvent.ACTION_DOWN) {
|
||||||
return false;
|
return false;
|
||||||
@@ -235,10 +238,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
* @param e {@link MotionEvent} to process
|
* @param e {@link MotionEvent} to process
|
||||||
* @return {@code true} if a drag is happening; or {@code false} if it is not
|
* @return {@code true} if a drag is happening; or {@code false} if it is not
|
||||||
*/
|
*/
|
||||||
private void handleEventForMove(MotionEvent e) {
|
@Override
|
||||||
|
public boolean handleMotionEvent(MotionEvent e) {
|
||||||
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
||||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
switch (e.getActionMasked()) {
|
switch (e.getActionMasked()) {
|
||||||
case MotionEvent.ACTION_DOWN: {
|
case MotionEvent.ACTION_DOWN: {
|
||||||
@@ -261,6 +265,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
|
|||||||
private View.OnTouchListener mOnCaptionTouchListener;
|
private View.OnTouchListener mOnCaptionTouchListener;
|
||||||
private DragResizeCallback mDragResizeCallback;
|
private DragResizeCallback mDragResizeCallback;
|
||||||
private DragResizeInputListener mDragResizeListener;
|
private DragResizeInputListener mDragResizeListener;
|
||||||
private final DragDetector mDragDetector;
|
private DragDetector mDragDetector;
|
||||||
|
|
||||||
private RelayoutParams mRelayoutParams = new RelayoutParams();
|
private RelayoutParams mRelayoutParams = new RelayoutParams();
|
||||||
private final RelayoutResult<WindowDecorLinearLayout> mResult =
|
private final RelayoutResult<WindowDecorLinearLayout> mResult =
|
||||||
@@ -69,7 +69,6 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
|
|||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
mChoreographer = choreographer;
|
mChoreographer = choreographer;
|
||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCaptionListeners(
|
void setCaptionListeners(
|
||||||
@@ -83,6 +82,11 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
|
|||||||
mDragResizeCallback = dragResizeCallback;
|
mDragResizeCallback = dragResizeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDragDetector(DragDetector dragDetector) {
|
||||||
|
mDragDetector = dragDetector;
|
||||||
|
mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void relayout(RunningTaskInfo taskInfo) {
|
void relayout(RunningTaskInfo taskInfo) {
|
||||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class DesktopModeTouchEventListener implements
|
private class DesktopModeTouchEventListener implements
|
||||||
View.OnClickListener, View.OnTouchListener {
|
View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
|
||||||
|
|
||||||
private final int mTaskId;
|
private final int mTaskId;
|
||||||
private final WindowContainerToken mTaskToken;
|
private final WindowContainerToken mTaskToken;
|
||||||
@@ -216,12 +216,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
|
|
||||||
private DesktopModeTouchEventListener(
|
private DesktopModeTouchEventListener(
|
||||||
RunningTaskInfo taskInfo,
|
RunningTaskInfo taskInfo,
|
||||||
DragResizeCallback dragResizeCallback,
|
DragResizeCallback dragResizeCallback) {
|
||||||
DragDetector dragDetector) {
|
|
||||||
mTaskId = taskInfo.taskId;
|
mTaskId = taskInfo.taskId;
|
||||||
mTaskToken = taskInfo.token;
|
mTaskToken = taskInfo.token;
|
||||||
mDragResizeCallback = dragResizeCallback;
|
mDragResizeCallback = dragResizeCallback;
|
||||||
mDragDetector = dragDetector;
|
mDragDetector = new DragDetector(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -254,8 +253,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (id == R.id.caption_handle) {
|
if (id == R.id.caption_handle) {
|
||||||
isDrag = mDragDetector.detectDragEvent(e);
|
isDrag = mDragDetector.onMotionEvent(e);
|
||||||
handleEventForMove(e);
|
|
||||||
}
|
}
|
||||||
if (e.getAction() != MotionEvent.ACTION_DOWN) {
|
if (e.getAction() != MotionEvent.ACTION_DOWN) {
|
||||||
return isDrag;
|
return isDrag;
|
||||||
@@ -272,19 +270,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param e {@link MotionEvent} to process
|
* @param e {@link MotionEvent} to process
|
||||||
* @return {@code true} if a drag is happening; or {@code false} if it is not
|
* @return {@code true} if the motion event is handled.
|
||||||
*/
|
*/
|
||||||
private void handleEventForMove(MotionEvent e) {
|
@Override
|
||||||
|
public boolean handleMotionEvent(MotionEvent e) {
|
||||||
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
||||||
if (DesktopModeStatus.isProto2Enabled()
|
if (DesktopModeStatus.isProto2Enabled()
|
||||||
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
|
if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
|
||||||
&& mDesktopModeController.get().getDisplayAreaWindowingMode(
|
&& mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
|
||||||
taskInfo.displayId)
|
|
||||||
== WINDOWING_MODE_FULLSCREEN) {
|
== WINDOWING_MODE_FULLSCREEN) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
switch (e.getActionMasked()) {
|
switch (e.getActionMasked()) {
|
||||||
case MotionEvent.ACTION_DOWN: {
|
case MotionEvent.ACTION_DOWN: {
|
||||||
@@ -324,6 +322,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,10 +559,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
|
|||||||
final TaskPositioner taskPositioner =
|
final TaskPositioner taskPositioner =
|
||||||
new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
|
new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
|
||||||
final DesktopModeTouchEventListener touchEventListener =
|
final DesktopModeTouchEventListener touchEventListener =
|
||||||
new DesktopModeTouchEventListener(
|
new DesktopModeTouchEventListener(taskInfo, taskPositioner);
|
||||||
taskInfo, taskPositioner, windowDecoration.getDragDetector());
|
|
||||||
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
|
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
|
||||||
windowDecoration.setDragResizeCallback(taskPositioner);
|
windowDecoration.setDragResizeCallback(taskPositioner);
|
||||||
|
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
|
||||||
windowDecoration.relayout(taskInfo, startT, finishT);
|
windowDecoration.relayout(taskInfo, startT, finishT);
|
||||||
incrementEventReceiverTasks(taskInfo.displayId);
|
incrementEventReceiverTasks(taskInfo.displayId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
|
|||||||
private View.OnTouchListener mOnCaptionTouchListener;
|
private View.OnTouchListener mOnCaptionTouchListener;
|
||||||
private DragResizeCallback mDragResizeCallback;
|
private DragResizeCallback mDragResizeCallback;
|
||||||
private DragResizeInputListener mDragResizeListener;
|
private DragResizeInputListener mDragResizeListener;
|
||||||
private final DragDetector mDragDetector;
|
private DragDetector mDragDetector;
|
||||||
|
|
||||||
private RelayoutParams mRelayoutParams = new RelayoutParams();
|
private RelayoutParams mRelayoutParams = new RelayoutParams();
|
||||||
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
|
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
|
||||||
@@ -81,7 +81,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
|
|||||||
mChoreographer = choreographer;
|
mChoreographer = choreographer;
|
||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
mDesktopActive = DesktopModeStatus.isActive(mContext);
|
mDesktopActive = DesktopModeStatus.isActive(mContext);
|
||||||
mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCaptionListeners(
|
void setCaptionListeners(
|
||||||
@@ -95,8 +94,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
|
|||||||
mDragResizeCallback = dragResizeCallback;
|
mDragResizeCallback = dragResizeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
DragDetector getDragDetector() {
|
void setDragDetector(DragDetector dragDetector) {
|
||||||
return mDragDetector;
|
mDragDetector = dragDetector;
|
||||||
|
mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.windowdecor;
|
package com.android.wm.shell.windowdecor;
|
||||||
|
|
||||||
|
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
|
||||||
import static android.view.MotionEvent.ACTION_CANCEL;
|
import static android.view.MotionEvent.ACTION_CANCEL;
|
||||||
import static android.view.MotionEvent.ACTION_DOWN;
|
import static android.view.MotionEvent.ACTION_DOWN;
|
||||||
import static android.view.MotionEvent.ACTION_MOVE;
|
import static android.view.MotionEvent.ACTION_MOVE;
|
||||||
@@ -25,63 +26,82 @@ import android.graphics.PointF;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A detector for touch inputs that differentiates between drag and click inputs.
|
* A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
|
||||||
|
* of {@link MotionEvent} and generates a new flow of motion events with slop in consideration to
|
||||||
|
* the event handler. In particular, it always passes down, up and cancel events. It'll pass move
|
||||||
|
* events only when there is at least one move event that's beyond the slop threshold. For the
|
||||||
|
* purpose of convenience it also passes all events of other actions.
|
||||||
|
*
|
||||||
* All touch events must be passed through this class to track a drag event.
|
* All touch events must be passed through this class to track a drag event.
|
||||||
*/
|
*/
|
||||||
public class DragDetector {
|
class DragDetector {
|
||||||
|
private final MotionEventHandler mEventHandler;
|
||||||
|
|
||||||
|
private final PointF mInputDownPoint = new PointF();
|
||||||
private int mTouchSlop;
|
private int mTouchSlop;
|
||||||
private PointF mInputDownPoint;
|
|
||||||
private boolean mIsDragEvent;
|
private boolean mIsDragEvent;
|
||||||
private int mDragPointerId;
|
private int mDragPointerId;
|
||||||
public DragDetector(int touchSlop) {
|
|
||||||
mTouchSlop = touchSlop;
|
private boolean mResultOfDownAction;
|
||||||
mInputDownPoint = new PointF();
|
|
||||||
mIsDragEvent = false;
|
DragDetector(MotionEventHandler eventHandler) {
|
||||||
mDragPointerId = -1;
|
resetState();
|
||||||
|
mEventHandler = eventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if {@link MotionEvent} is part of a drag event.
|
* The receiver of the {@link MotionEvent} flow.
|
||||||
* @return {@code true} if this is a drag event, {@code false} if not
|
*
|
||||||
*/
|
* @return the result returned by {@link #mEventHandler}, or the result when
|
||||||
public boolean detectDragEvent(MotionEvent ev) {
|
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
|
||||||
switch (ev.getAction()) {
|
*/
|
||||||
|
boolean onMotionEvent(MotionEvent ev) {
|
||||||
|
switch (ev.getActionMasked()) {
|
||||||
case ACTION_DOWN: {
|
case ACTION_DOWN: {
|
||||||
|
// Only touch screens generate noisy moves.
|
||||||
|
mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
|
||||||
mDragPointerId = ev.getPointerId(0);
|
mDragPointerId = ev.getPointerId(0);
|
||||||
float rawX = ev.getRawX(0);
|
float rawX = ev.getRawX(0);
|
||||||
float rawY = ev.getRawY(0);
|
float rawY = ev.getRawY(0);
|
||||||
mInputDownPoint.set(rawX, rawY);
|
mInputDownPoint.set(rawX, rawY);
|
||||||
return false;
|
mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
|
||||||
|
return mResultOfDownAction;
|
||||||
}
|
}
|
||||||
case ACTION_MOVE: {
|
case ACTION_MOVE: {
|
||||||
if (!mIsDragEvent) {
|
if (!mIsDragEvent) {
|
||||||
int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
|
int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
|
||||||
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
|
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
|
||||||
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
|
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
|
||||||
if (Math.hypot(dx, dy) > mTouchSlop) {
|
mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
|
||||||
mIsDragEvent = true;
|
}
|
||||||
}
|
if (mIsDragEvent) {
|
||||||
|
return mEventHandler.handleMotionEvent(ev);
|
||||||
|
} else {
|
||||||
|
return mResultOfDownAction;
|
||||||
}
|
}
|
||||||
return mIsDragEvent;
|
|
||||||
}
|
|
||||||
case ACTION_UP: {
|
|
||||||
boolean result = mIsDragEvent;
|
|
||||||
mIsDragEvent = false;
|
|
||||||
mInputDownPoint.set(0, 0);
|
|
||||||
mDragPointerId = -1;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
case ACTION_UP:
|
||||||
case ACTION_CANCEL: {
|
case ACTION_CANCEL: {
|
||||||
mIsDragEvent = false;
|
resetState();
|
||||||
mInputDownPoint.set(0, 0);
|
return mEventHandler.handleMotionEvent(ev);
|
||||||
mDragPointerId = -1;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return mEventHandler.handleMotionEvent(ev);
|
||||||
}
|
}
|
||||||
return mIsDragEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTouchSlop(int touchSlop) {
|
void setTouchSlop(int touchSlop) {
|
||||||
mTouchSlop = touchSlop;
|
mTouchSlop = touchSlop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetState() {
|
||||||
|
mIsDragEvent = false;
|
||||||
|
mInputDownPoint.set(0, 0);
|
||||||
|
mDragPointerId = -1;
|
||||||
|
mResultOfDownAction = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MotionEventHandler {
|
||||||
|
boolean handleMotionEvent(MotionEvent ev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ import com.android.internal.view.BaseIWindow;
|
|||||||
* Task edges are for resizing with a mouse.
|
* Task edges are for resizing with a mouse.
|
||||||
* Task corners are for resizing with touch input.
|
* 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";
|
||||||
|
|
||||||
@@ -115,7 +114,8 @@ class DragResizeInputListener implements AutoCloseable {
|
|||||||
mInputEventReceiver = new TaskResizeInputEventReceiver(
|
mInputEventReceiver = new TaskResizeInputEventReceiver(
|
||||||
mInputChannel, mHandler, mChoreographer);
|
mInputChannel, mHandler, mChoreographer);
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
mDragDetector = new DragDetector(ViewConfiguration.get(context).getScaledTouchSlop());
|
mDragDetector = new DragDetector(mInputEventReceiver);
|
||||||
|
mDragDetector.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -223,12 +223,12 @@ class DragResizeInputListener implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TaskResizeInputEventReceiver extends InputEventReceiver {
|
private class TaskResizeInputEventReceiver extends InputEventReceiver
|
||||||
|
implements DragDetector.MotionEventHandler {
|
||||||
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 mShouldHandleEvents;
|
||||||
private boolean mDragging;
|
|
||||||
|
|
||||||
private TaskResizeInputEventReceiver(
|
private TaskResizeInputEventReceiver(
|
||||||
InputChannel inputChannel, Handler handler, Choreographer choreographer) {
|
InputChannel inputChannel, Handler handler, Choreographer choreographer) {
|
||||||
@@ -270,15 +270,15 @@ class DragResizeInputListener implements AutoCloseable {
|
|||||||
if (!(inputEvent instanceof MotionEvent)) {
|
if (!(inputEvent instanceof MotionEvent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return mDragDetector.onMotionEvent((MotionEvent) inputEvent);
|
||||||
|
}
|
||||||
|
|
||||||
MotionEvent e = (MotionEvent) inputEvent;
|
@Override
|
||||||
|
public boolean handleMotionEvent(MotionEvent e) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
// Check if this is a touch event vs mouse event.
|
// Check if this is a touch event vs mouse event.
|
||||||
// Touch events are tracked in four corners. Other events are tracked in resize edges.
|
// Touch events are tracked in four corners. Other events are tracked in resize edges.
|
||||||
boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
|
boolean isTouch = (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
|
||||||
if (isTouch) {
|
|
||||||
mDragging = mDragDetector.detectDragEvent(e);
|
|
||||||
}
|
|
||||||
switch (e.getActionMasked()) {
|
switch (e.getActionMasked()) {
|
||||||
case MotionEvent.ACTION_DOWN: {
|
case MotionEvent.ACTION_DOWN: {
|
||||||
float x = e.getX(0);
|
float x = e.getX(0);
|
||||||
@@ -305,24 +305,17 @@ class DragResizeInputListener implements AutoCloseable {
|
|||||||
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
|
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
|
||||||
float rawX = e.getRawX(dragPointerIndex);
|
float rawX = e.getRawX(dragPointerIndex);
|
||||||
float rawY = e.getRawY(dragPointerIndex);
|
float rawY = e.getRawY(dragPointerIndex);
|
||||||
if (!isTouch) {
|
mCallback.onDragResizeMove(rawX, rawY);
|
||||||
// For all other types allow immediate dragging.
|
result = true;
|
||||||
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: {
|
||||||
if (mShouldHandleEvents && mDragging) {
|
if (mShouldHandleEvents) {
|
||||||
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
|
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
|
||||||
mCallback.onDragResizeEnd(
|
mCallback.onDragResizeEnd(
|
||||||
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
|
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
|
||||||
}
|
}
|
||||||
mDragging = false;
|
|
||||||
mShouldHandleEvents = false;
|
mShouldHandleEvents = false;
|
||||||
mDragPointerId = -1;
|
mDragPointerId = -1;
|
||||||
result = true;
|
result = true;
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ class TaskPositioner implements DragResizeCallback {
|
|||||||
private final Rect mTaskBoundsAtDragStart = new Rect();
|
private final Rect mTaskBoundsAtDragStart = new Rect();
|
||||||
private final PointF mResizeStartPoint = new PointF();
|
private final PointF mResizeStartPoint = new PointF();
|
||||||
private final Rect mResizeTaskBounds = new Rect();
|
private final Rect mResizeTaskBounds = new Rect();
|
||||||
// Whether the |dragResizing| hint should be sent with the next bounds change WCT.
|
private boolean mHasMoved = false;
|
||||||
// Used to optimized fluid resizing of freeform tasks.
|
|
||||||
private boolean mPendingDragResizeHint = false;
|
|
||||||
|
|
||||||
private int mCtrlType;
|
private int mCtrlType;
|
||||||
private DragStartListener mDragStartListener;
|
private DragStartListener mDragStartListener;
|
||||||
@@ -60,11 +58,7 @@ class TaskPositioner implements DragResizeCallback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDragResizeStart(int ctrlType, float x, float y) {
|
public void onDragResizeStart(int ctrlType, float x, float y) {
|
||||||
if (ctrlType != CTRL_TYPE_UNDEFINED) {
|
mHasMoved = false;
|
||||||
// The task is being resized, send the |dragResizing| hint to core with the first
|
|
||||||
// bounds-change wct.
|
|
||||||
mPendingDragResizeHint = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
|
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
|
||||||
mCtrlType = ctrlType;
|
mCtrlType = ctrlType;
|
||||||
@@ -78,30 +72,44 @@ class TaskPositioner implements DragResizeCallback {
|
|||||||
public void onDragResizeMove(float x, float y) {
|
public void onDragResizeMove(float x, float y) {
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
if (changeBounds(wct, x, y)) {
|
if (changeBounds(wct, x, y)) {
|
||||||
if (mPendingDragResizeHint) {
|
// The task is being resized, send the |dragResizing| hint to core with the first
|
||||||
|
// bounds-change wct.
|
||||||
|
if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
|
||||||
// This is the first bounds change since drag resize operation started.
|
// This is the first bounds change since drag resize operation started.
|
||||||
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
|
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
|
||||||
mPendingDragResizeHint = false;
|
|
||||||
}
|
}
|
||||||
mTaskOrganizer.applyTransaction(wct);
|
mTaskOrganizer.applyTransaction(wct);
|
||||||
|
mHasMoved = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDragResizeEnd(float x, float y) {
|
public void onDragResizeEnd(float x, float y) {
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
// |mHasMoved| being false means there is no real change to the task bounds in WM core, so
|
||||||
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
|
// we don't need a WCT to finish it.
|
||||||
changeBounds(wct, x, y);
|
if (mHasMoved) {
|
||||||
mTaskOrganizer.applyTransaction(wct);
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
|
||||||
|
changeBounds(wct, x, y);
|
||||||
|
mTaskOrganizer.applyTransaction(wct);
|
||||||
|
}
|
||||||
|
|
||||||
mCtrlType = 0;
|
mCtrlType = CTRL_TYPE_UNDEFINED;
|
||||||
mTaskBoundsAtDragStart.setEmpty();
|
mTaskBoundsAtDragStart.setEmpty();
|
||||||
mResizeStartPoint.set(0, 0);
|
mResizeStartPoint.set(0, 0);
|
||||||
mPendingDragResizeHint = false;
|
mHasMoved = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
|
private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
|
||||||
float deltaX = x - mResizeStartPoint.x;
|
// |mResizeTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not true,
|
||||||
|
// we can compare it against |mTaskBoundsAtDragStart|.
|
||||||
|
final int oldLeft = mHasMoved ? mResizeTaskBounds.left : mTaskBoundsAtDragStart.left;
|
||||||
|
final int oldTop = mHasMoved ? mResizeTaskBounds.top : mTaskBoundsAtDragStart.top;
|
||||||
|
final int oldRight = mHasMoved ? mResizeTaskBounds.right : mTaskBoundsAtDragStart.right;
|
||||||
|
final int oldBottom = mHasMoved ? mResizeTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
|
||||||
|
|
||||||
|
final float deltaX = x - mResizeStartPoint.x;
|
||||||
|
final float deltaY = y - mResizeStartPoint.y;
|
||||||
mResizeTaskBounds.set(mTaskBoundsAtDragStart);
|
mResizeTaskBounds.set(mTaskBoundsAtDragStart);
|
||||||
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
|
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
|
||||||
mResizeTaskBounds.left += deltaX;
|
mResizeTaskBounds.left += deltaX;
|
||||||
@@ -109,22 +117,22 @@ class TaskPositioner implements DragResizeCallback {
|
|||||||
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
|
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
|
||||||
mResizeTaskBounds.right += deltaX;
|
mResizeTaskBounds.right += deltaX;
|
||||||
}
|
}
|
||||||
float deltaY = y - mResizeStartPoint.y;
|
|
||||||
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
|
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
|
||||||
mResizeTaskBounds.top += deltaY;
|
mResizeTaskBounds.top += deltaY;
|
||||||
}
|
}
|
||||||
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
|
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
|
||||||
mResizeTaskBounds.bottom += deltaY;
|
mResizeTaskBounds.bottom += deltaY;
|
||||||
}
|
}
|
||||||
if (mCtrlType == 0) {
|
if (mCtrlType == CTRL_TYPE_UNDEFINED) {
|
||||||
mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
|
mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mResizeTaskBounds.isEmpty()) {
|
if (oldLeft == mResizeTaskBounds.left && oldTop == mResizeTaskBounds.top
|
||||||
wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
|
&& oldRight == mResizeTaskBounds.right && oldBottom == mResizeTaskBounds.bottom) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DragStartListener {
|
interface DragStartListener {
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.windowdecor
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.InputDevice
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
import org.mockito.Mockito.any
|
||||||
|
import org.mockito.Mockito.argThat
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for [DragDetector].
|
||||||
|
*
|
||||||
|
* Build/Install/Run:
|
||||||
|
* atest WMShellUnitTests:DragDetectorTest
|
||||||
|
*/
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class DragDetectorTest {
|
||||||
|
private val motionEvents = mutableListOf<MotionEvent>()
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var eventHandler: DragDetector.MotionEventHandler
|
||||||
|
|
||||||
|
private lateinit var dragDetector: DragDetector
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
`when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
|
||||||
|
|
||||||
|
dragDetector = DragDetector(eventHandler)
|
||||||
|
dragDetector.setTouchSlop(SLOP)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
motionEvents.forEach {
|
||||||
|
it.recycle()
|
||||||
|
}
|
||||||
|
motionEvents.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNoMove_passesDownAndUp() {
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMoveInSlop_touch_passesDownAndUp() {
|
||||||
|
`when`(eventHandler.handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_DOWN
|
||||||
|
})).thenReturn(false)
|
||||||
|
|
||||||
|
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
|
||||||
|
val newX = X + SLOP - 1
|
||||||
|
assertFalse(
|
||||||
|
dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
|
||||||
|
verify(eventHandler, never()).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_MOVE
|
||||||
|
})
|
||||||
|
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMoveInSlop_mouse_passesDownMoveAndUp() {
|
||||||
|
`when`(eventHandler.handleMotionEvent(argThat {
|
||||||
|
it.action == MotionEvent.ACTION_DOWN
|
||||||
|
})).thenReturn(false)
|
||||||
|
|
||||||
|
assertFalse(dragDetector.onMotionEvent(
|
||||||
|
createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_MOUSE
|
||||||
|
})
|
||||||
|
|
||||||
|
val newX = X + SLOP - 1
|
||||||
|
assertTrue(dragDetector.onMotionEvent(
|
||||||
|
createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_MOUSE
|
||||||
|
})
|
||||||
|
|
||||||
|
assertTrue(dragDetector.onMotionEvent(
|
||||||
|
createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_MOUSE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMoveBeyondSlop_passesDownMoveAndUp() {
|
||||||
|
`when`(eventHandler.handleMotionEvent(argThat {
|
||||||
|
it.action == MotionEvent.ACTION_DOWN
|
||||||
|
})).thenReturn(false)
|
||||||
|
|
||||||
|
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
|
||||||
|
val newX = X + SLOP + 1
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
|
||||||
|
it.source == InputDevice.SOURCE_TOUCHSCREEN
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPassesHoverEnter() {
|
||||||
|
`when`(eventHandler.handleMotionEvent(argThat {
|
||||||
|
it.action == MotionEvent.ACTION_HOVER_ENTER
|
||||||
|
})).thenReturn(false)
|
||||||
|
|
||||||
|
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPassesHoverMove() {
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPassesHoverExit() {
|
||||||
|
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
|
||||||
|
verify(eventHandler).handleMotionEvent(argThat {
|
||||||
|
return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMotionEvent(action: Int, x: Float = X, y: Float = Y, isTouch: Boolean = true):
|
||||||
|
MotionEvent {
|
||||||
|
val time = SystemClock.uptimeMillis()
|
||||||
|
val ev = MotionEvent.obtain(time, time, action, x, y, 0)
|
||||||
|
ev.source = if (isTouch) InputDevice.SOURCE_TOUCHSCREEN else InputDevice.SOURCE_MOUSE
|
||||||
|
motionEvents.add(ev)
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SLOP = 10
|
||||||
|
private const val X = 123f
|
||||||
|
private const val Y = 234f
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.android.wm.shell.windowdecor
|
package com.android.wm.shell.windowdecor
|
||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
|
import android.app.WindowConfiguration
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
@@ -10,6 +11,7 @@ import androidx.test.filters.SmallTest
|
|||||||
import com.android.wm.shell.ShellTaskOrganizer
|
import com.android.wm.shell.ShellTaskOrganizer
|
||||||
import com.android.wm.shell.ShellTestCase
|
import com.android.wm.shell.ShellTestCase
|
||||||
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
|
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
|
||||||
|
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
|
||||||
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
|
import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -62,6 +64,90 @@ class TaskPositionerTest : ShellTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDragResize_notMove_skipsTransactionOnEnd() {
|
||||||
|
taskPositioner.onDragResizeStart(
|
||||||
|
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
|
||||||
|
STARTING_BOUNDS.left.toFloat(),
|
||||||
|
STARTING_BOUNDS.top.toFloat()
|
||||||
|
)
|
||||||
|
|
||||||
|
taskPositioner.onDragResizeEnd(
|
||||||
|
STARTING_BOUNDS.left.toFloat() + 10,
|
||||||
|
STARTING_BOUNDS.top.toFloat() + 10
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
|
||||||
|
return@argThat wct.changes.any { (token, change) ->
|
||||||
|
token == taskBinder &&
|
||||||
|
((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDragResize_noEffectiveMove_skipsTransactionOnMoveAndEnd() {
|
||||||
|
taskPositioner.onDragResizeStart(
|
||||||
|
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
|
||||||
|
STARTING_BOUNDS.left.toFloat(),
|
||||||
|
STARTING_BOUNDS.top.toFloat()
|
||||||
|
)
|
||||||
|
|
||||||
|
taskPositioner.onDragResizeMove(
|
||||||
|
STARTING_BOUNDS.left.toFloat(),
|
||||||
|
STARTING_BOUNDS.top.toFloat()
|
||||||
|
)
|
||||||
|
|
||||||
|
taskPositioner.onDragResizeEnd(
|
||||||
|
STARTING_BOUNDS.left.toFloat() + 10,
|
||||||
|
STARTING_BOUNDS.top.toFloat() + 10
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
|
||||||
|
return@argThat wct.changes.any { (token, change) ->
|
||||||
|
token == taskBinder &&
|
||||||
|
((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDragResize_hasEffectiveMove_issuesTransactionOnMoveAndEnd() {
|
||||||
|
taskPositioner.onDragResizeStart(
|
||||||
|
CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
|
||||||
|
STARTING_BOUNDS.left.toFloat(),
|
||||||
|
STARTING_BOUNDS.top.toFloat()
|
||||||
|
)
|
||||||
|
|
||||||
|
taskPositioner.onDragResizeMove(
|
||||||
|
STARTING_BOUNDS.left.toFloat() + 10,
|
||||||
|
STARTING_BOUNDS.top.toFloat()
|
||||||
|
)
|
||||||
|
val rectAfterMove = Rect(STARTING_BOUNDS)
|
||||||
|
rectAfterMove.right += 10
|
||||||
|
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
|
||||||
|
return@argThat wct.changes.any { (token, change) ->
|
||||||
|
token == taskBinder &&
|
||||||
|
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
|
||||||
|
change.configuration.windowConfiguration.bounds == rectAfterMove
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
taskPositioner.onDragResizeEnd(
|
||||||
|
STARTING_BOUNDS.left.toFloat() + 10,
|
||||||
|
STARTING_BOUNDS.top.toFloat() + 10
|
||||||
|
)
|
||||||
|
val rectAfterEnd = Rect(rectAfterMove)
|
||||||
|
rectAfterEnd.top += 10
|
||||||
|
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
|
||||||
|
return@argThat wct.changes.any { (token, change) ->
|
||||||
|
token == taskBinder &&
|
||||||
|
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
|
||||||
|
change.configuration.windowConfiguration.bounds == rectAfterEnd
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDragResize_move_skipsDragResizingFlag() {
|
fun testDragResize_move_skipsDragResizingFlag() {
|
||||||
taskPositioner.onDragResizeStart(
|
taskPositioner.onDragResizeStart(
|
||||||
|
|||||||
Reference in New Issue
Block a user