Use movement on the trackpad instead of on screen for trackpad gestures used for gesture nav
Bug: 254783214
Test: https://recall.googleplex.com/projects/3388b17c-d22f-46f8-b140-a102690377b4/sessions/f3311fbc-d8cf-4f19-b83c-8626aa285452
Change-Id: Iad2da5831af85dd3647e1e31b42fea0a6302b49c
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5cc5f10..9a04b40 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -293,7 +293,6 @@
private boolean mPassedOverviewThreshold;
private boolean mGestureStarted;
private boolean mLogDirectionUpOrLeft = true;
- private PointF mDownPos;
private boolean mIsLikelyToStartNewTask;
private final long mTouchTimeMs;
@@ -991,10 +990,9 @@
/**
* @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
* @param velocity The x and y components of the velocity when the gesture ends.
- * @param downPos The x and y value of where the gesture started.
*/
@UiThread
- public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
+ public void onGestureEnded(float endVelocity, PointF velocity) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_speed);
boolean isFling = mGestureStarted && !mIsMotionPaused
@@ -1006,7 +1004,6 @@
} else {
mLogDirectionUpOrLeft = velocity.x < 0;
}
- mDownPos = downPos;
Runnable handleNormalGestureEndCallback = () ->
handleNormalGestureEnd(endVelocity, isFling, velocity, /* isCancel= */ false);
if (mRecentsView != null) {
@@ -1281,7 +1278,7 @@
}
private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
- if (mDp == null || !mDp.isGestureMode || mDownPos == null) {
+ if (mDp == null || !mDp.isGestureMode) {
// We probably never received an animation controller, skip logging.
return;
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 875b72c..61891df 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -231,7 +231,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
activityInterface.runOnInitBackgroundStateUI(() ->
- interactionHandler.onGestureEnded(0, new PointF(), new PointF()));
+ interactionHandler.onGestureEnded(0, new PointF()));
cmd.removeListener(this);
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java
new file mode 100644
index 0000000..2373822
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/MotionEventsHandler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 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.quickstep.inputconsumers;
+
+import static android.view.MotionEvent.AXIS_GESTURE_X_OFFSET;
+import static android.view.MotionEvent.AXIS_GESTURE_Y_OFFSET;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.launcher3.Utilities.getTrackpadMotionEventScale;
+import static com.android.launcher3.Utilities.isTrackpadMotionEvent;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+
+import com.android.quickstep.util.NavBarPosition;
+
+/**
+ * A motion event handler that can tracks the states of a gesture, whether it's from on-screen
+ * touch or trackpad gesture.
+ */
+public class MotionEventsHandler {
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final int mScale;
+
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ private float mCurrentTrackpadOffsetX = 0;
+ private float mCurrentTrackpadOffsetY = 0;
+
+ public MotionEventsHandler(Context context) {
+ mScale = getTrackpadMotionEventScale(context);
+ }
+
+ public int getActivePointerId() {
+ return mActivePointerId;
+ }
+
+ public void onActionDown(MotionEvent ev) {
+ mActivePointerId = ev.getPointerId(0);
+ if (isTrackpadMotionEvent(ev)) {
+ mCurrentTrackpadOffsetX = ev.getAxisValue(AXIS_GESTURE_X_OFFSET);
+ mCurrentTrackpadOffsetY = ev.getAxisValue(AXIS_GESTURE_Y_OFFSET);
+ } else {
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ }
+ }
+
+ public void onActionMove(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (isTrackpadMotionEvent(ev)) {
+ mCurrentTrackpadOffsetX += ev.getAxisValue(AXIS_GESTURE_X_OFFSET, pointerIndex)
+ * mScale;
+ mCurrentTrackpadOffsetY += ev.getAxisValue(AXIS_GESTURE_Y_OFFSET, pointerIndex)
+ * mScale;
+ } else {
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ }
+ }
+
+ public void onActionPointerUp(MotionEvent ev) {
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId && !isTrackpadMotionEvent(ev)) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ }
+
+ public float getDisplacement(MotionEvent ev, NavBarPosition mNavBarPosition) {
+ if (mNavBarPosition.isRightEdge()) {
+ if (isTrackpadMotionEvent(ev)) {
+ return mCurrentTrackpadOffsetX;
+ }
+ return ev.getX() - mDownPos.x;
+ } else if (mNavBarPosition.isLeftEdge()) {
+ if (isTrackpadMotionEvent(ev)) {
+ return -mCurrentTrackpadOffsetX;
+ }
+ return mDownPos.x - ev.getX();
+ } else {
+ if (isTrackpadMotionEvent(ev)) {
+ return mCurrentTrackpadOffsetY;
+ }
+ return ev.getY() - mDownPos.y;
+ }
+ }
+
+ public float getDisplacementX(MotionEvent ev) {
+ return isTrackpadMotionEvent(ev) ? mCurrentTrackpadOffsetX : mLastPos.x - mDownPos.x;
+ }
+
+ public float getDisplacementY(MotionEvent ev) {
+ return isTrackpadMotionEvent(ev) ? mCurrentTrackpadOffsetY : mLastPos.y - mDownPos.y;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 9d269fb..3d972c6 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -26,6 +26,8 @@
import static com.android.launcher3.PagedView.ACTION_MOVE_ALLOW_EASY_FLING;
import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.getXVelocity;
+import static com.android.launcher3.Utilities.getYVelocity;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
@@ -115,9 +117,8 @@
private final FinishImmediatelyHandler mCleanupHandler = new FinishImmediatelyHandler();
private final boolean mIsDeferredDownTarget;
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private int mActivePointerId = INVALID_POINTER_ID;
+
+ private final MotionEventsHandler mMotionEventsHandler;
// Distance after which we start dragging the window.
private final float mTouchSlop;
@@ -166,6 +167,7 @@
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
mInputEventReceiver = inputEventReceiver;
+ mMotionEventsHandler = new MotionEventsHandler(base);
TaskbarUIController controller = mActivityInterface.getTaskbarController();
mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
@@ -236,9 +238,7 @@
Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
FLAG_CHECK_FOR_RACE_CONDITIONS);
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
+ mMotionEventsHandler.onActionDown(ev);
// Start the window animation on down to give more time for launcher to draw if the
// user didn't start the gesture over the back button
@@ -260,27 +260,20 @@
break;
}
case ACTION_POINTER_UP: {
- int ptrIdx = ev.getActionIndex();
- int ptrId = ev.getPointerId(ptrIdx);
- if (ptrId == mActivePointerId) {
- final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
- mDownPos.set(
- ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
- ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
- mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
- mActivePointerId = ev.getPointerId(newPointerIdx);
- }
+ mMotionEventsHandler.onActionPointerUp(ev);
break;
}
case ACTION_MOVE: {
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ int pointerIndex = ev.findPointerIndex(mMotionEventsHandler.getActivePointerId());
if (pointerIndex == INVALID_POINTER_ID) {
break;
}
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- float displacement = getDisplacement(ev);
- float displacementX = mLastPos.x - mDownPos.x;
- float displacementY = mLastPos.y - mDownPos.y;
+
+ mMotionEventsHandler.onActionMove(ev);
+ final float displacement = mMotionEventsHandler.getDisplacement(ev,
+ mNavBarPosition);
+ final float displacementX = mMotionEventsHandler.getDisplacementX(ev);
+ final float displacementY= mMotionEventsHandler.getDisplacementY(ev);
if (!mPassedWindowMoveSlop) {
if (!mIsDeferredDownTarget) {
@@ -359,8 +352,8 @@
case ACTION_CANCEL:
case ACTION_UP: {
if (DEBUG_FAILED_QUICKSWITCH && !mPassedWindowMoveSlop) {
- float displacementX = mLastPos.x - mDownPos.x;
- float displacementY = mLastPos.y - mDownPos.y;
+ final float displacementX = mMotionEventsHandler.getDisplacementX(ev);
+ final float displacementY = mMotionEventsHandler.getDisplacementY(ev);
Log.d("Quickswitch", "mPassedWindowMoveSlop=false"
+ " disp=" + squaredHypot(displacementX, displacementY)
+ " slop=" + mSquaredTouchSlop);
@@ -423,16 +416,18 @@
mInteractionHandler.onGestureCancelled();
} else {
mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
- float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
- float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
+ int activePointerId = mMotionEventsHandler.getActivePointerId();
+ float velocityX = getXVelocity(mVelocityTracker, ev, activePointerId);
+ float velocityY = getYVelocity(mVelocityTracker, ev, activePointerId);
float velocity = mNavBarPosition.isRightEdge()
? velocityX
: mNavBarPosition.isLeftEdge()
? -velocityX
: velocityY;
- mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
- mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
- mDownPos);
+ mInteractionHandler.updateDisplacement(
+ mMotionEventsHandler.getDisplacement(ev, mNavBarPosition)
+ - mStartDisplacement);
+ mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY));
}
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
@@ -495,16 +490,6 @@
}
}
- private float getDisplacement(MotionEvent ev) {
- if (mNavBarPosition.isRightEdge()) {
- return ev.getX() - mDownPos.x;
- } else if (mNavBarPosition.isLeftEdge()) {
- return mDownPos.x - ev.getX();
- } else {
- return ev.getY() - mDownPos.y;
- }
- }
-
@Override
public boolean allowInterceptByParent() {
return !mPassedPilferInputSlop;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c11de7f..3c05fe6 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -71,6 +71,7 @@
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
@@ -181,6 +182,8 @@
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
+ private static final int TRACKPAD_GESTURE_SCALE = 60;
+
public static void enableRunningInTestHarnessForTests() {
IS_RUNNING_IN_TEST_HARNESS = true;
}
@@ -927,6 +930,38 @@
&& (event.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
}
+ public static int getTrackpadMotionEventScale(Context context) {
+ return ViewConfiguration.get(context).getScaledTouchSlop() * TRACKPAD_GESTURE_SCALE;
+ }
+
+ public static float getXVelocity(VelocityTracker velocityTracker, MotionEvent event,
+ int pointerId) {
+ // Will be enabled after ag/20353570 is submitted
+// if (isTrackpadMotionEvent(event)) {
+// return velocityTracker.getAxisVelocity(AXIS_GESTURE_X_OFFSET, pointerId);
+// } else {
+ return velocityTracker.getXVelocity(pointerId);
+// }
+ }
+
+ public static float getXVelocity(VelocityTracker velocityTracker, MotionEvent event) {
+ return getXVelocity(velocityTracker, event, -1 /* ACTIVE_POINTER_ID */);
+ }
+
+ public static float getYVelocity(VelocityTracker velocityTracker, MotionEvent event,
+ int pointerId) {
+ // Will be enabled after ag/20353570 is submitted
+// if (isTrackpadMotionEvent(event)) {
+// return velocityTracker.getAxisVelocity(AXIS_GESTURE_Y_OFFSET, pointerId);
+// } else {
+ return velocityTracker.getYVelocity(pointerId);
+// }
+ }
+
+ public static float getYVelocity(VelocityTracker velocityTracker, MotionEvent event) {
+ return getYVelocity(velocityTracker, event, -1 /* ACTIVE_POINTER_ID */);
+ }
+
public static boolean bothNull(@Nullable Object a, @Nullable Object b) {
return a == null && b == null;
}