Using velocity tracker for computing the velocity of motion events
Change-Id: I14f2f970825a2936f4bb285834405d67daf8667c
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 88f2315..54269f09 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -220,7 +220,7 @@
}
@Override
- public boolean onDrag(float displacement, float velocity) {
+ public boolean onDrag(float displacement) {
float totalDisplacement = displacement + mDisplacementShift;
boolean isGoingUp =
totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 5c0e259..78627ec 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -179,7 +179,7 @@
@Override
- public boolean onDrag(float displacement, float velocity) {
+ public boolean onDrag(float displacement) {
setContentTranslation(canChildBeDismissed()
? displacement : OverScroll.dampedScroll(displacement, getWidth()));
mContentTranslateAnimator.cancel();
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index fd9157e..85be513 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -256,7 +256,7 @@
}
@Override
- public boolean onDrag(float displacement, float velocity) {
+ public boolean onDrag(float displacement) {
float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
float progress = deltaProgress + mStartProgress;
updateProgress(progress);
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 6ffc0ef..a0a410e 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -21,6 +21,7 @@
import android.graphics.PointF;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
@@ -52,12 +53,6 @@
*/
public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
- /**
- * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
- * Cutoff frequency is set at 10 Hz.
- */
- public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
-
/* Scroll state, this is set to true during dragging and animation. */
private ScrollState mState = ScrollState.IDLE;
@@ -75,6 +70,8 @@
* Distance in pixels a touch can wander before we think the user is scrolling.
*/
abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+
+ abstract float getVelocity(VelocityTracker tracker);
}
public static final Direction VERTICAL = new Direction() {
@@ -88,6 +85,11 @@
float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
return Math.abs(ev.getX(pointerIndex) - downPos.x);
}
+
+ @Override
+ float getVelocity(VelocityTracker tracker) {
+ return tracker.getYVelocity();
+ }
};
public static final Direction HORIZONTAL = new Direction() {
@@ -101,6 +103,11 @@
float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
return Math.abs(ev.getY(pointerIndex) - downPos.y);
}
+
+ @Override
+ float getVelocity(VelocityTracker tracker) {
+ return tracker.getXVelocity();
+ }
};
//------------------- ScrollState transition diagram -----------------------------------
@@ -151,16 +158,16 @@
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
- private Direction mDir;
+ private final Direction mDir;
private final float mTouchSlop;
+ private final float mMaxVelocity;
/* Client of this gesture detector can register a callback. */
private final Listener mListener;
- private long mCurrentMillis;
+ private VelocityTracker mVelocityTracker;
- private float mVelocity;
private float mLastDisplacement;
private float mDisplacement;
@@ -170,24 +177,22 @@
public interface Listener {
void onDragStart(boolean start);
- boolean onDrag(float displacement, float velocity);
+ boolean onDrag(float displacement);
void onDragEnd(float velocity, boolean fling);
}
public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
- this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+ this(ViewConfiguration.get(context), l, dir);
}
@VisibleForTesting
- protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
- mTouchSlop = touchSlope;
+ protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
+ @NonNull Direction dir) {
mListener = l;
mDir = dir;
- }
-
- public void updateDirection(Direction dir) {
- mDir = dir;
+ mTouchSlop = config.getScaledTouchSlop();
+ mMaxVelocity = config.getScaledMaximumFlingVelocity();
}
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -215,14 +220,22 @@
}
public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getActionMasked()) {
+ int actionMasked = ev.getActionMasked();
+ if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mLastDisplacement = 0;
mDisplacement = 0;
- mVelocity = 0;
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
setState(ScrollState.DRAGGING);
@@ -247,8 +260,6 @@
break;
}
mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
- computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
- ev.getEventTime());
// handle state and listener calls.
if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
@@ -265,6 +276,8 @@
if (mState == ScrollState.DRAGGING) {
setState(ScrollState.SETTLING);
}
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
break;
default:
break;
@@ -308,55 +321,24 @@
private boolean reportDragging() {
if (mDisplacement != mLastDisplacement) {
if (DBG) {
- Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
- mDisplacement, mVelocity));
+ Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
}
mLastDisplacement = mDisplacement;
- return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
+ return mListener.onDrag(mDisplacement - mSubtractDisplacement);
}
return true;
}
private void reportDragEnd() {
+ mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+ float velocity = mDir.getVelocity(mVelocityTracker) / 1000;
if (DBG) {
Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
- mDisplacement, mVelocity));
+ mDisplacement, velocity));
}
- mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
- }
-
- /**
- * Computes the damped velocity.
- */
- public float computeVelocity(float delta, long currentMillis) {
- long previousMillis = mCurrentMillis;
- mCurrentMillis = currentMillis;
-
- float deltaTimeMillis = mCurrentMillis - previousMillis;
- float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
- if (Math.abs(mVelocity) < 0.001f) {
- mVelocity = velocity;
- } else {
- float alpha = computeDampeningFactor(deltaTimeMillis);
- mVelocity = interpolate(mVelocity, velocity, alpha);
- }
- return mVelocity;
- }
-
- /**
- * Returns a time-dependent dampening factor using delta time.
- */
- private static float computeDampeningFactor(float deltaTime) {
- return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
- }
-
- /**
- * Returns the linear interpolation between two values
- */
- public static float interpolate(float from, float to, float alpha) {
- return (1.0f - alpha) * from + alpha * to;
+ mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
}
public static long calculateDuration(float velocity, float progressNeeded) {
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 26c8f24..f948beb 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -128,7 +128,7 @@
public void onDragStart(boolean start) { }
@Override
- public boolean onDrag(float displacement, float velocity) {
+ public boolean onDrag(float displacement) {
float range = mContent.getHeight();
displacement = Utilities.boundToRange(displacement, 0, range);
setTranslationShift(displacement / range);
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
index 73b6daf..b600473 100644
--- a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -19,7 +19,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import android.util.Log;
-import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.launcher3.testcomponent.TouchEventGenerator;
@@ -32,6 +31,7 @@
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -51,25 +51,28 @@
@Mock
private SwipeDetector.Listener mMockListener;
+ @Mock
+ private ViewConfiguration mMockConfig;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mGenerator = new TouchEventGenerator(new TouchEventGenerator.Listener() {
- @Override
- public void onTouchEvent(MotionEvent event) {
- mDetector.onTouchEvent(event);
- }
- });
+ mGenerator = new TouchEventGenerator((ev) -> mDetector.onTouchEvent(ev));
+ ViewConfiguration orgConfig = ViewConfiguration
+ .get(InstrumentationRegistry.getTargetContext());
+ doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
+ .getScaledMaximumFlingVelocity();
- mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.VERTICAL);
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL);
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
- mTouchSlop = ViewConfiguration.get(InstrumentationRegistry.getTargetContext())
- .getScaledTouchSlop();
+ mTouchSlop = orgConfig.getScaledTouchSlop();
+ doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
+
L("mTouchSlop=", mTouchSlop);
}
@Test
- public void testDragStart_vertical() throws Exception {
+ public void testDragStart_vertical() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -77,7 +80,7 @@
}
@Test
- public void testDragStart_failed() throws Exception {
+ public void testDragStart_failed() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -85,8 +88,8 @@
}
@Test
- public void testDragStart_horizontal() throws Exception {
- mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.HORIZONTAL);
+ public void testDragStart_horizontal() {
+ mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL);
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
mGenerator.put(0, 100, 100);
@@ -96,15 +99,15 @@
}
@Test
- public void testDrag() throws Exception {
+ public void testDrag() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
- verify(mMockListener).onDrag(anyFloat(), anyFloat());
+ verify(mMockListener).onDrag(anyFloat());
}
@Test
- public void testDragEnd() throws Exception {
+ public void testDragEnd() {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
mGenerator.move(0, 100, 100 + mTouchSlop * 2);