Merge "Updating Pause detection to use motion events directly" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index b80830a..519939e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -172,7 +172,7 @@
         mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
                 || upDisplacement < mMotionPauseMinDisplacement
                 || upDisplacement > mMotionPauseMaxDisplacement);
-        mMotionPauseDetector.addPosition(displacement, event.getEventTime());
+        mMotionPauseDetector.addPosition(event);
         return super.onDrag(displacement, event);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 8628db0..799f1ad 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -317,7 +317,7 @@
         // home screen elements will appear in the shelf on motion pause.
         mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
                 || -displacement.y < mMotionPauseMinDisplacement);
-        mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime());
+        mMotionPauseDetector.addPosition(ev);
 
         if (mIsHomeScreenVisible) {
             // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index d3765c5..5ad48eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -31,8 +31,8 @@
 import com.android.launcher3.R;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.MotionPauseDetector;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
 /**
@@ -117,9 +117,7 @@
                     if (pointerIndex == -1) {
                         break;
                     }
-
-                    mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
-                            ev.getEventTime());
+                    mMotionPauseDetector.addPosition(ev, pointerIndex);
                 }
                 break;
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 3ee3c2d..8e7074d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -59,6 +59,7 @@
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -77,6 +78,7 @@
     public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
 
     private final RecentsAnimationDeviceState mDeviceState;
+    private final NavBarPosition mNavBarPosition;
     private final TaskAnimationManager mTaskAnimationManager;
     private final GestureState mGestureState;
     private RecentsAnimationCallbacks mActiveCallbacks;
@@ -126,13 +128,16 @@
             Factory handlerFactory) {
         super(base);
         mDeviceState = deviceState;
+        mNavBarPosition = mDeviceState.getNavBarPosition();
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
         mMainThreadHandler = new Handler(Looper.getMainLooper());
         mHandlerFactory = handlerFactory;
         mActivityInterface = mGestureState.getActivityInterface();
 
-        mMotionPauseDetector = new MotionPauseDetector(base);
+        mMotionPauseDetector = new MotionPauseDetector(base, false,
+                mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
+                        ? MotionEvent.AXIS_X : MotionEvent.AXIS_Y);
         mMotionPauseMinDisplacement = base.getResources().getDimension(
                 R.dimen.motion_pause_detector_min_displacement_from_app);
         mOnCompleteCallback = onCompleteCallback;
@@ -172,7 +177,7 @@
         if (mPassedWindowMoveSlop && mInteractionHandler != null
                 && !mRecentsViewDispatcher.hasConsumer()) {
             mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
-                    mDeviceState.getNavBarPosition().getRotationMode()));
+                    mNavBarPosition.getRotationMode()));
         }
         int edgeFlags = ev.getEdgeFlags();
         ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -285,7 +290,7 @@
                     if (mDeviceState.isFullyGesturalNavMode()) {
                         mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
                                 || isLikelyToStartNewTask);
-                        mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
+                        mMotionPauseDetector.addPosition(ev);
                         mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
                     }
                 }
@@ -354,9 +359,9 @@
                         ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
                 float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
                 float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
-                float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+                float velocity = mNavBarPosition.isRightEdge()
                         ? velocityX
-                        : mDeviceState.getNavBarPosition().isLeftEdge()
+                        : mNavBarPosition.isLeftEdge()
                                 ? -velocityX
                                 : velocityY;
 
@@ -410,9 +415,9 @@
     }
 
     private float getDisplacement(MotionEvent ev) {
-        if (mDeviceState.getNavBarPosition().isRightEdge()) {
+        if (mNavBarPosition.isRightEdge()) {
             return ev.getX() - mDownPos.x;
-        } else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
+        } else if (mNavBarPosition.isLeftEdge()) {
             return mDownPos.x - ev.getX();
         } else {
             return ev.getY() - mDownPos.y;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index d5ed321..b9827ff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -23,8 +23,8 @@
 import com.android.launcher3.R;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
-import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.MotionPauseDetector;
 
 /**
  * An input consumer that detects swipe up and hold to exit screen pinning mode.
@@ -72,7 +72,7 @@
             case MotionEvent.ACTION_MOVE:
                 float displacement = mTouchDownY - y;
                 mMotionPauseDetector.setDisallowPause(displacement < mMotionPauseMinDisplacement);
-                mMotionPauseDetector.addPosition(y, ev.getEventTime());
+                mMotionPauseDetector.addPosition(ev);
                 break;
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 801a560..d8b10b6 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -37,8 +37,7 @@
     private static final long FORCE_PAUSE_TIMEOUT = 300;
 
     /**
-     * After {@link #makePauseHarderToTrigger()}, must
-     * move slowly for this long to trigger a pause.
+     * After {@link #mMakePauseHarderToTrigger}, must move slowly for this long to trigger a pause.
      */
     private static final long HARDER_TRIGGER_TIMEOUT = 400;
 
@@ -49,13 +48,10 @@
     private final Alarm mForcePauseTimeout;
     private final boolean mMakePauseHarderToTrigger;
     private final Context mContext;
+    private final VelocityProvider mVelocityProvider;
 
-    private Long mPreviousTime = null;
-    private Float mPreviousPosition = null;
     private Float mPreviousVelocity = null;
 
-    private Float mFirstPosition = null;
-
     private OnMotionPauseListener mOnMotionPauseListener;
     private boolean mIsPaused;
     // Bias more for the first pause to make it feel extra responsive.
@@ -73,6 +69,13 @@
      * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
      */
     public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) {
+        this(context, makePauseHarderToTrigger, MotionEvent.AXIS_Y);
+    }
+
+    /**
+     * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
+     */
+    public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger, int axis) {
         mContext = context;
         Resources res = context.getResources();
         mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
@@ -82,6 +85,7 @@
         mForcePauseTimeout = new Alarm();
         mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
         mMakePauseHarderToTrigger = makePauseHarderToTrigger;
+        mVelocityProvider = new LinearVelocityProvider(axis);
     }
 
     /**
@@ -101,28 +105,28 @@
 
     /**
      * Computes velocity and acceleration to determine whether the motion is paused.
-     * @param position The x or y component of the motion being tracked.
+     * @param ev The motion being tracked.
      *
      * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
      */
-    public void addPosition(float position, long time) {
-        if (mFirstPosition == null) {
-            mFirstPosition = position;
-        }
+    public void addPosition(MotionEvent ev) {
+        addPosition(ev, 0);
+    }
+
+    /**
+     * Computes velocity and acceleration to determine whether the motion is paused.
+     * @param ev The motion being tracked.
+     * @param pointerIndex Index for the pointer being tracked in the motion event
+     */
+    public void addPosition(MotionEvent ev, int pointerIndex) {
         mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
                 ? HARDER_TRIGGER_TIMEOUT
                 : FORCE_PAUSE_TIMEOUT);
-        if (mPreviousTime != null && mPreviousPosition != null) {
-            long changeInTime = Math.max(1, time - mPreviousTime);
-            float changeInPosition = position - mPreviousPosition;
-            float velocity = changeInPosition / changeInTime;
-            if (mPreviousVelocity != null) {
-                checkMotionPaused(velocity, mPreviousVelocity, time);
-            }
-            mPreviousVelocity = velocity;
+        Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
+        if (newVelocity != null && mPreviousVelocity != null) {
+            checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
         }
-        mPreviousTime = time;
-        mPreviousPosition = position;
+        mPreviousVelocity = newVelocity;
     }
 
     private void checkMotionPaused(float velocity, float prevVelocity, long time) {
@@ -178,10 +182,8 @@
     }
 
     public void clear() {
-        mPreviousTime = null;
-        mPreviousPosition = null;
+        mVelocityProvider.clear();
         mPreviousVelocity = null;
-        mFirstPosition = null;
         setOnMotionPauseListener(null);
         mIsPaused = mHasEverBeenPaused = false;
         mSlowStartTime = 0;
@@ -195,4 +197,55 @@
     public interface OnMotionPauseListener {
         void onMotionPauseChanged(boolean isPaused);
     }
+
+    /**
+     * Interface to abstract out velocity calculations
+     */
+    protected interface VelocityProvider {
+
+        /**
+         * Adds a new motion events, and returns the velocity at this point, or null if
+         * the velocity is not available
+         */
+        Float addMotionEvent(MotionEvent ev, int pointer);
+
+        /**
+         * Clears all stored motion event records
+         */
+        void clear();
+    }
+
+    private static class LinearVelocityProvider implements VelocityProvider {
+
+        private Long mPreviousTime = null;
+        private Float mPreviousPosition = null;
+
+        private final int mAxis;
+
+        LinearVelocityProvider(int axis) {
+            mAxis = axis;
+        }
+
+        @Override
+        public Float addMotionEvent(MotionEvent ev, int pointer) {
+            long time = ev.getEventTime();
+            float position = ev.getAxisValue(mAxis, pointer);
+            Float velocity = null;
+
+            if (mPreviousTime != null && mPreviousPosition != null) {
+                long changeInTime = Math.max(1, time - mPreviousTime);
+                float changeInPosition = position - mPreviousPosition;
+                velocity = changeInPosition / changeInTime;
+            }
+            mPreviousTime = time;
+            mPreviousPosition = position;
+            return velocity;
+        }
+
+        @Override
+        public void clear() {
+            mPreviousTime = null;
+            mPreviousPosition = null;
+        }
+    }
 }