Merge "Using LeastSquaresVelocityTrackerStrategy for calculating velocity when detecting motion pause events. This is the same logic used by the platform implementation VelocityTracker" into ub-launcher3-master
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index d8b10b6..7d52571 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_LSQ_VELOCITY_PROVIDER;
+
import android.content.Context;
import android.content.res.Resources;
import android.view.MotionEvent;
@@ -85,7 +87,8 @@
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
- mVelocityProvider = new LinearVelocityProvider(axis);
+ mVelocityProvider = ENABLE_LSQ_VELOCITY_PROVIDER.get()
+ ? new LSqVelocityProvider(axis) : new LinearVelocityProvider(axis);
}
/**
@@ -106,8 +109,6 @@
/**
* Computes velocity and acceleration to determine whether the motion is paused.
* @param ev The motion being tracked.
- *
- * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
*/
public void addPosition(MotionEvent ev) {
addPosition(ev, 0);
@@ -248,4 +249,137 @@
mPreviousPosition = null;
}
}
+
+ /**
+ * Java implementation of {@link android.view.VelocityTracker} using the Least Square (deg 2)
+ * algorithm.
+ */
+ private static class LSqVelocityProvider implements VelocityProvider {
+
+ // Maximum age of a motion event to be considered when calculating the velocity.
+ private static final long HORIZON_MS = 100;
+ // Number of samples to keep.
+ private static final int HISTORY_SIZE = 20;
+
+ // Position history are stored in a circular array
+ private final float[] mHistoricTimes = new float[HISTORY_SIZE];
+ private final float[] mHistoricPos = new float[HISTORY_SIZE];
+ private int mHistoryCount = 0;
+ private int mHistoryStart = 0;
+
+ private final int mAxis;
+
+ LSqVelocityProvider(int axis) {
+ mAxis = axis;
+ }
+
+ @Override
+ public void clear() {
+ mHistoryCount = mHistoryStart = 0;
+ }
+
+ private void addPositionAndTime(float eventTime, float eventPosition) {
+ mHistoricTimes[mHistoryStart] = eventTime;
+ mHistoricPos[mHistoryStart] = eventPosition;
+ mHistoryStart++;
+ if (mHistoryStart >= HISTORY_SIZE) {
+ mHistoryStart = 0;
+ }
+ mHistoryCount = Math.min(HISTORY_SIZE, mHistoryCount + 1);
+ }
+
+ @Override
+ public Float addMotionEvent(MotionEvent ev, int pointer) {
+ // Add all historic points
+ int historyCount = ev.getHistorySize();
+ for (int i = 0; i < historyCount; i++) {
+ addPositionAndTime(
+ ev.getHistoricalEventTime(i), ev.getHistoricalAxisValue(mAxis, pointer, i));
+ }
+
+ // Start index for the last position (about to be added)
+ int eventStartIndex = mHistoryStart;
+ addPositionAndTime(ev.getEventTime(), ev.getAxisValue(mAxis, pointer));
+ return solveUnweightedLeastSquaresDeg2(eventStartIndex);
+ }
+
+ /**
+ * Solves the instantaneous velocity.
+ * Based on solveUnweightedLeastSquaresDeg2 in VelocityTracker.cpp
+ */
+ private Float solveUnweightedLeastSquaresDeg2(final int pointPos) {
+ final float eventTime = mHistoricTimes[pointPos];
+
+ float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
+ int count = 0;
+ for (int i = 0; i < mHistoryCount; i++) {
+ int index = pointPos - i;
+ if (index < 0) {
+ index += HISTORY_SIZE;
+ }
+
+ float time = mHistoricTimes[index];
+ float age = eventTime - time;
+ if (age > HORIZON_MS) {
+ break;
+ }
+ count++;
+ float xi = -age;
+
+ float yi = mHistoricPos[index];
+ float xi2 = xi * xi;
+ float xi3 = xi2 * xi;
+ float xi4 = xi3 * xi;
+ float xiyi = xi * yi;
+ float xi2yi = xi2 * yi;
+
+ sxi += xi;
+ sxi2 += xi2;
+ sxiyi += xiyi;
+ sxi2yi += xi2yi;
+ syi += yi;
+ sxi3 += xi3;
+ sxi4 += xi4;
+ }
+
+ if (count < 3) {
+ // Too few samples
+ if (count == 2) {
+ int endPos = pointPos - 1;
+ if (endPos < 0) {
+ endPos += HISTORY_SIZE;
+ }
+ float denominator = eventTime - mHistoricTimes[endPos];
+ if (denominator != 0) {
+ return (eventTime - mHistoricPos[endPos]) / denominator;
+
+ }
+ }
+ return null;
+ }
+
+ float Sxx = sxi2 - sxi * sxi / count;
+ float Sxy = sxiyi - sxi * syi / count;
+ float Sxx2 = sxi3 - sxi * sxi2 / count;
+ float Sx2y = sxi2yi - sxi2 * syi / count;
+ float Sx2x2 = sxi4 - sxi2 * sxi2 / count;
+
+ float denominator = Sxx * Sx2x2 - Sxx2 * Sxx2;
+ if (denominator == 0) {
+ // division by 0 when computing velocity
+ return null;
+ }
+ // Compute a
+ // float numerator = Sx2y*Sxx - Sxy*Sxx2;
+
+ // Compute b
+ float numerator = Sxy * Sx2x2 - Sx2y * Sxx2;
+ float b = numerator / denominator;
+
+ // Compute c
+ // float c = syi/count - b * sxi/count - a * sxi2/count;
+
+ return b;
+ }
+ }
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 6413044..3d8a9d7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -135,6 +135,10 @@
"ENABLE_UNIVERSAL_SMARTSPACE", false,
"Replace Smartspace with a version rendered by System UI.");
+ public static final BooleanFlag ENABLE_LSQ_VELOCITY_PROVIDER = getDebugFlag(
+ "ENABLE_LSQ_VELOCITY_PROVIDER", false,
+ "Use Least Square algorithm for motion pause detection.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {