Merge changes Ib0fae780,I6d14f106 into ub-launcher3-master
* changes:
Standardize quickstep velocities
Don't detach recents from app window after motion pause
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 313db8c..c85fe6c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -35,16 +35,13 @@
loading full resolution screenshots. -->
<dimen name="recents_fast_fling_velocity">600dp</dimen>
- <!-- These velocities are in dp / s -->
- <dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
- <dimen name="quickstep_fling_min_velocity">250dp</dimen>
-
<!-- These speeds are in dp / ms -->
<dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
<dimen name="motion_pause_detector_speed_slow">0.15dp</dimen>
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
<dimen name="motion_pause_detector_speed_fast">1.4dp</dimen>
<dimen name="motion_pause_detector_min_displacement_from_app">36dp</dimen>
+ <dimen name="quickstep_fling_threshold_speed">0.5dp</dimen>
<!-- Launcher app transition -->
<dimen name="content_trans_y">50dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 591d3ca..c78d474 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -75,6 +75,7 @@
private final float mMotionPauseMinDisplacement;
private boolean mDidTouchStartInNavBar;
+ private boolean mStartedOverview;
private boolean mReachedOverview;
// The last recorded displacement before we reached overview.
private PointF mStartDisplacement = new PointF();
@@ -128,7 +129,7 @@
mMotionPauseDetector.clear();
if (handlingOverviewAnim()) {
- mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
+ mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
}
if (mFromState == NORMAL && mToState == HINT_STATE) {
@@ -138,6 +139,7 @@
mFromState.getOverviewScrimAlpha(mLauncher),
mToState.getOverviewScrimAlpha(mLauncher));
}
+ mStartedOverview = false;
mReachedOverview = false;
mOverviewResistYAnim = null;
}
@@ -152,7 +154,7 @@
@Override
public void onDragEnd(float velocity) {
- if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
+ if (mStartedOverview) {
goToOverviewOrHomeOnDragEnd(velocity);
} else {
super.onDragEnd(velocity);
@@ -185,7 +187,7 @@
}
}
- private void onMotionPauseChanged(boolean isPaused) {
+ private void onMotionPauseDetected() {
if (mCurrentAnimation == null) {
return;
}
@@ -199,6 +201,7 @@
maybeSwipeInteractionToOverviewComplete();
});
});
+ mStartedOverview = true;
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
}
@@ -219,7 +222,7 @@
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController");
}
- if (mMotionPauseDetector.isPaused()) {
+ if (mStartedOverview) {
if (!mReachedOverview) {
mStartDisplacement.set(xDisplacement, yDisplacement);
mStartY = event.getY();
@@ -234,8 +237,6 @@
* OVERVIEW_MOVEMENT_FACTOR);
}
}
- // Stay in Overview.
- return true;
}
float upDisplacement = -yDisplacement;
@@ -243,13 +244,12 @@
|| upDisplacement < mMotionPauseMinDisplacement);
mMotionPauseDetector.addPosition(event);
- return super.onDrag(yDisplacement, xDisplacement, event);
+ // Stay in Overview.
+ return mStartedOverview || super.onDrag(yDisplacement, xDisplacement, event);
}
private void goToOverviewOrHomeOnDragEnd(float velocity) {
- float velocityDp = dpiFromPx(velocity);
- boolean isFling = Math.abs(velocityDp) > 1;
- boolean goToHomeInsteadOfOverview = isFling;
+ boolean goToHomeInsteadOfOverview = !mMotionPauseDetector.isPaused();
if (goToHomeInsteadOfOverview) {
new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING))
.animateWithVelocity(velocity);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 4b0642f..87c246f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -80,7 +80,7 @@
* the user as possible, also handles swipe up and hold to go to overview and swiping back home.
*/
public class NoButtonQuickSwitchTouchController implements TouchController,
- BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener {
+ BothAxesSwipeDetector.Listener {
/** The minimum progress of the scale/translationY animation until drag end. */
private static final float Y_ANIM_MIN_PROGRESS = 0.25f;
@@ -167,7 +167,7 @@
if (start) {
mStartState = mLauncher.getStateManager().getState();
- mMotionPauseDetector.setOnMotionPauseListener(this);
+ mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
// We have detected horizontal drag start, now allow swipe up as well.
mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP,
@@ -177,8 +177,7 @@
}
}
- @Override
- public void onMotionPauseChanged(boolean isPaused) {
+ private void onMotionPauseDetected() {
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index aaa2720..4bdc59e 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -89,6 +89,7 @@
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
+import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TransformParams;
@@ -201,6 +202,7 @@
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
private boolean mIsMotionPaused;
+ private boolean mHasMotionEverBeenPaused;
private boolean mContinuingLastGesture;
@@ -482,13 +484,20 @@
.getHighResLoadingState().setVisible(true);
}
- /**
- * Called when motion pause is detected
- */
- public void onMotionPauseChanged(boolean isPaused) {
- mIsMotionPaused = isPaused;
- maybeUpdateRecentsAttachedState();
- performHapticFeedback();
+ public MotionPauseDetector.OnMotionPauseListener getMotionPauseListener() {
+ return new MotionPauseDetector.OnMotionPauseListener() {
+ @Override
+ public void onMotionPauseDetected() {
+ mHasMotionEverBeenPaused = true;
+ maybeUpdateRecentsAttachedState();
+ performHapticFeedback();
+ }
+
+ @Override
+ public void onMotionPauseChanged(boolean isPaused) {
+ mIsMotionPaused = isPaused;
+ }
+ };
}
public void maybeUpdateRecentsAttachedState() {
@@ -519,7 +528,7 @@
// The window is going away so make sure recents is always visible in this case.
recentsAttachedToAppWindow = true;
} else {
- recentsAttachedToAppWindow = mIsMotionPaused || mIsLikelyToStartNewTask;
+ recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
}
mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
@@ -742,8 +751,9 @@
@UiThread
public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
float flingThreshold = mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_threshold_velocity);
- boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
+ .getDimension(R.dimen.quickstep_fling_threshold_speed);
+ boolean isFling = mGestureStarted && !mIsMotionPaused
+ && Math.abs(endVelocity) > flingThreshold;
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
@@ -858,7 +868,7 @@
if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
endTarget = HOME;
- } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsMotionPaused) {
+ } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
// If swiping at a diagonal, base end target on the faster velocity.
endTarget = NEW_TASK;
} else if (isSwipeUp) {
@@ -878,7 +888,6 @@
@UiThread
private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
boolean isCancel) {
- PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000);
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
@@ -893,14 +902,12 @@
startShift = currentShift;
interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
} else {
- startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
+ startShift = Utilities.boundToRange(currentShift - velocity.y
* getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
- float minFlingVelocity = mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_min_velocity);
- if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
+ if (mTransitionDragLength > 0) {
if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
- startShift, endShift, endShift, endVelocity / 1000,
+ startShift, endShift, endShift, endVelocity,
mTransitionDragLength, mContext);
endShift = overshoot.end;
interpolator = overshoot.interpolator;
@@ -912,7 +919,7 @@
// we want the page's snap velocity to approximately match the velocity at
// which the user flings, so we scale the duration by a value near to the
// derivative of the scroll interpolator at zero, ie. 2.
- long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
+ long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y));
duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
if (endTarget == RECENTS) {
@@ -952,7 +959,7 @@
mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
}
- animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
+ animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity);
}
private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 584ff28..5aaea00 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -35,7 +36,6 @@
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
@@ -180,12 +180,11 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+ mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
float velocityY = mVelocityTracker.getYVelocity();
float flingThreshold = mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+ .getDimension(R.dimen.quickstep_fling_threshold_speed);
boolean dismissTask;
if (Math.abs(velocityY) > flingThreshold) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index f02e5e6..b1a1133 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
+import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -52,9 +53,9 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.AbsSwipeUpHandler;
import com.android.quickstep.AbsSwipeUpHandler.Factory;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
@@ -365,7 +366,7 @@
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
mTaskAnimationManager.isRecentsAnimationRunning());
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
- mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
+ mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
mInteractionHandler.initWhenReady(intent);
@@ -393,8 +394,7 @@
if (ev.getActionMasked() == ACTION_CANCEL) {
mInteractionHandler.onGestureCancelled();
} else {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+ mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
float velocity = mNavBarPosition.isRightEdge()
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index b9827ff..a8bf333 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -42,18 +42,16 @@
mMotionPauseMinDisplacement = context.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
- mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
- if (isPaused) {
- SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
- BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
- .getCreatedActivity();
- if (launcherActivity != null) {
- launcherActivity.getRootView().performHapticFeedback(
- HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- }
- mMotionPauseDetector.clear();
+ mMotionPauseDetector.setOnMotionPauseListener(() -> {
+ SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
+ BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
+ .getCreatedActivity();
+ if (launcherActivity != null) {
+ launcherActivity.getRootView().performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+ mMotionPauseDetector.clear();
});
}
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 81c4d0c..f897ecc 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -144,7 +144,6 @@
if (mGestureCallback == null || mAssistantGestureActive) {
return;
}
- finalVelocity.set(finalVelocity.x / 1000, finalVelocity.y / 1000);
if (mTouchCameFromNavBar) {
mGestureCallback.onNavBarGestureAttempted(wasFling
? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED, finalVelocity);
@@ -182,7 +181,7 @@
mLaunchedAssistant = false;
mSwipeUpTouchTracker.init();
mMotionPauseDetector.clear();
- mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
+ mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
break;
case MotionEvent.ACTION_MOVE:
mLastPos.set(event.getX(), event.getY());
@@ -220,7 +219,6 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mMotionPauseDetector.clear();
- mMotionPauseDetector.setOnMotionPauseListener(null);
if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
mGestureCallback.onNavBarGestureAttempted(
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
@@ -252,10 +250,8 @@
return intercepted;
}
- protected void onMotionPauseChanged(boolean isPaused) {
- if (isPaused) {
- VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
- }
+ protected void onMotionPauseDetected() {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index b862936..d0f6879 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -184,11 +184,15 @@
}
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
+ boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
if (mIsPaused) {
AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
mHasEverBeenPaused = true;
}
if (mOnMotionPauseListener != null) {
+ if (isFirstDetectedPause) {
+ mOnMotionPauseListener.onMotionPauseDetected();
+ }
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
}
}
@@ -211,7 +215,10 @@
}
public interface OnMotionPauseListener {
- void onMotionPauseChanged(boolean isPaused);
+ /** Called only the first time motion pause is detected. */
+ void onMotionPauseDetected();
+ /** Called every time motion changes from paused to not paused and vice versa. */
+ default void onMotionPauseChanged(boolean isPaused) { }
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
index 29b9558..7bbde30 100644
--- a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
+++ b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
@@ -21,13 +21,14 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
/**
@@ -50,7 +51,8 @@
NavBarPosition navBarPosition, Runnable onInterceptTouch,
OnSwipeUpListener onSwipeUp) {
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
- mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+ mMinFlingVelocity = context.getResources().getDimension(
+ R.dimen.quickstep_fling_threshold_speed);
mNavBarPosition = navBarPosition;
mDisableHorizontalSwipe = disableHorizontalSwipe;
mOnInterceptTouch = onInterceptTouch;
@@ -130,7 +132,7 @@
}
private void onGestureEnd(MotionEvent ev) {
- mVelocityTracker.computeCurrentVelocity(1000);
+ mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
float velocityX = mVelocityTracker.getXVelocity();
float velocityY = mVelocityTracker.getYVelocity();
float velocity = mNavBarPosition.isRightEdge()
diff --git a/src/com/android/launcher3/util/VelocityUtils.java b/src/com/android/launcher3/util/VelocityUtils.java
new file mode 100644
index 0000000..d5962ed
--- /dev/null
+++ b/src/com/android/launcher3/util/VelocityUtils.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.launcher3.util;
+
+/**
+ * Contains some constants and functions to standardize velocity usage.
+ */
+public class VelocityUtils {
+
+ /**
+ * Unit to pass to {@link android.view.VelocityTracker#computeCurrentVelocity(int)}.
+ */
+ public static final int PX_PER_MS = 1;
+
+}