Bug 805479 - When calculating the initial fling velocity use an average of recent velocities to work around bad input events. r=Cwiiis a=lsblakk
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 12 Mar 2013 11:16:28 +0000
changeset 132377 b37f5b5a758962f7708c360e6fc160d834623f9a
parent 132376 e3987f396053e3b33bc1235f7445fd2db9e5d56f
child 132378 333a93611b4aabe1ef52ffb5abc72d68d42c0a9c
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCwiiis, lsblakk
bugs805479
milestone21.0a2
Bug 805479 - When calculating the initial fling velocity use an average of recent velocities to work around bad input events. r=Cwiiis a=lsblakk
mobile/android/base/gfx/Axis.java
--- a/mobile/android/base/gfx/Axis.java
+++ b/mobile/android/base/gfx/Axis.java
@@ -81,16 +81,17 @@ abstract class Axis {
             @Override public void finish() {
                 setPrefs(mPrefs);
             }
         });
     }
 
     static final float MS_PER_FRAME = 1000.0f / 60.0f;
     private static final float FRAMERATE_MULTIPLIER = (1000f/60f) / MS_PER_FRAME;
+    private static final int FLING_VELOCITY_POINTS = 8;
 
     //  The values we use for friction are based on a 16.6ms frame, adjust them to MS_PER_FRAME:
     //  FRICTION^1 = FRICTION_ADJUSTED^(16/MS_PER_FRAME)
     //  FRICTION_ADJUSTED = e ^ ((ln(FRICTION))/FRAMERATE_MULTIPLIER)
     static float getFrameAdjustedFriction(float baseFriction) {
         return (float)Math.pow(Math.E, (Math.log(baseFriction) / FRAMERATE_MULTIPLIER));
     }
 
@@ -126,30 +127,33 @@ abstract class Axis {
 
     private final SubdocumentScrollHelper mSubscroller;
 
     private int mOverscrollMode; /* Default to only overscrolling if we're allowed to scroll in a direction */
     private float mFirstTouchPos;           /* Position of the first touch event on the current drag. */
     private float mTouchPos;                /* Position of the most recent touch event on the current drag. */
     private float mLastTouchPos;            /* Position of the touch event before touchPos. */
     private float mVelocity;                /* Velocity in this direction; pixels per animation frame. */
+    private float[] mRecentVelocities;      /* Circular buffer of recent velocities since last touch start. */
+    private int mRecentVelocityCount;       /* Number of values put into mRecentVelocities (unbounded). */
     private boolean mScrollingDisabled;     /* Whether movement on this axis is locked. */
     private boolean mDisableSnap;           /* Whether overscroll snapping is disabled. */
     private float mDisplacement;
 
     private FlingStates mFlingState;        /* The fling state we're in on this axis. */
 
     protected abstract float getOrigin();
     protected abstract float getViewportLength();
     protected abstract float getPageStart();
     protected abstract float getPageLength();
 
     Axis(SubdocumentScrollHelper subscroller) {
         mSubscroller = subscroller;
         mOverscrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS;
+        mRecentVelocities = new float[FLING_VELOCITY_POINTS];
     }
 
     public void setOverScrollMode(int overscrollMode) {
         mOverscrollMode = overscrollMode;
     }
 
     public int getOverScrollMode() {
         return mOverscrollMode;
@@ -162,16 +166,17 @@ abstract class Axis {
     private float getPageEnd() {
         return getPageStart() + getPageLength();
     }
 
     void startTouch(float pos) {
         mVelocity = 0.0f;
         mScrollingDisabled = false;
         mFirstTouchPos = mTouchPos = mLastTouchPos = pos;
+        mRecentVelocityCount = 0;
     }
 
     float panDistance(float currentPos) {
         return currentPos - mFirstTouchPos;
     }
 
     void setScrollingDisabled(boolean disabled) {
         mScrollingDisabled = disabled;
@@ -179,16 +184,19 @@ abstract class Axis {
 
     void saveTouchPos() {
         mLastTouchPos = mTouchPos;
     }
 
     void updateWithTouchAt(float pos, float timeDelta) {
         float newVelocity = (mTouchPos - pos) / timeDelta * MS_PER_FRAME;
 
+        mRecentVelocities[mRecentVelocityCount % FLING_VELOCITY_POINTS] = newVelocity;
+        mRecentVelocityCount++;
+
         // If there's a direction change, or current velocity is very low,
         // allow setting of the velocity outright. Otherwise, use the current
         // velocity and a maximum change factor to set the new velocity.
         boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f / FRAMERATE_MULTIPLIER;
         boolean directionChange = (mVelocity > 0) != (newVelocity > 0);
         if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
             mVelocity = newVelocity;
         } else {
@@ -268,22 +276,35 @@ abstract class Axis {
     float getRealVelocity() {
         return scrollable() ? mVelocity : 0f;
     }
 
     void startPan() {
         mFlingState = FlingStates.PANNING;
     }
 
+    private float calculateFlingVelocity() {
+        int usablePoints = Math.min(mRecentVelocityCount, FLING_VELOCITY_POINTS);
+        if (usablePoints <= 1) {
+            return mVelocity;
+        }
+        float average = 0;
+        for (int i = 0; i < usablePoints; i++) {
+            average += mRecentVelocities[i];
+        }
+        return average / usablePoints;
+    }
+
     void startFling(boolean stopped) {
         mDisableSnap = mSubscroller.scrolling();
 
         if (stopped) {
             mFlingState = FlingStates.STOPPED;
         } else {
+            mVelocity = calculateFlingVelocity();
             mFlingState = FlingStates.FLINGING;
         }
     }
 
     /* Advances a fling animation by one step. */
     boolean advanceFling() {
         if (mFlingState != FlingStates.FLINGING) {
             return false;