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;
     }