From 278ce05b52a47a09d2177435b533f4ecb1b9c4da Mon Sep 17 00:00:00 2001 From: Cary Clark Date: Mon, 31 Aug 2009 16:08:42 -0400 Subject: [PATCH] accelerate consecutive flings Use the fact that the scroller keeps track of the current velocity to accelerate subsequent flings. The current velocity is added to the new velocity if the flings are in the same general direction and if the fling animation hasn't been aborted. This makes it easier to move to the top or bottom of very large pages (either in overview mode or reading mode) by taking consecutive flings as a cue from the user that they want to page faster. The user can stop the fling with any action as before. fixes http://b/issue?id=2066090 --- core/java/android/webkit/WebView.java | 43 +++++++++++++++++++++++--- core/java/android/widget/Scroller.java | 11 +++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index e39e3f1eddd77..792fdf29aa1b7 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -342,6 +342,9 @@ public class WebView extends AbsoluteLayout */ VelocityTracker mVelocityTracker; private int mMaximumFling; + private float mLastVelocity; + private float mLastVelX; + private float mLastVelY; // use this flag to control whether enabling the new double tap zoom static final boolean ENABLE_DOUBLETAP_ZOOM = true; @@ -1795,6 +1798,13 @@ public class WebView extends AbsoluteLayout , contentToView(x.right), contentToView(x.bottom)); } + // stop the scroll animation, and don't let a subsequent fling add + // to the existing velocity + private void abortAnimation() { + mScroller.abortAnimation(); + mLastVelocity = 0; + } + /* call from webcoreview.draw(), so we're still executing in the UI thread */ private void recordNewContentSize(int w, int h, boolean updateLayout) { @@ -1819,7 +1829,7 @@ public class WebView extends AbsoluteLayout mScrollY = pinLocY(mScrollY); // android.util.Log.d("skia", "recordNewContentSize - // abortAnimation"); - mScroller.abortAnimation(); // just in case + abortAnimation(); // just in case if (oldX != mScrollX || oldY != mScrollY) { sendOurVisibleRect(); } @@ -2339,7 +2349,7 @@ public class WebView extends AbsoluteLayout animationDuration > 0 ? animationDuration : computeDuration(dx, dy)); invalidate(); } else { - mScroller.abortAnimation(); // just in case + abortAnimation(); // just in case scrollTo(x, y); } return true; @@ -2963,7 +2973,7 @@ public class WebView extends AbsoluteLayout mLastTouchX = halfW; int halfH = height >> 1; mLastTouchY = halfH; - mScroller.abortAnimation(); + abortAnimation(); mZoomScrollStart = System.currentTimeMillis(); Rect zoomFrame = scrollZoomFrame(width, height , scrollZoomMagScale(mZoomScrollInvLimit)); @@ -3850,6 +3860,9 @@ public class WebView extends AbsoluteLayout mLastScrollY = mZoomScrollY; // If two taps are close, ignore the first tap } else if (!mScroller.isFinished()) { + // stop the current scroll animation, but if this is + // the start of a fling, allow it to add to the current + // fling's velocity mScroller.abortAnimation(); mTouchMode = TOUCH_DRAG_START_MODE; mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE); @@ -4110,6 +4123,7 @@ public class WebView extends AbsoluteLayout doFling(); break; } + mLastVelocity = 0; WebViewCore.resumeUpdate(mWebViewCore); break; case TOUCH_DRAG_START_MODE: @@ -4475,6 +4489,27 @@ public class WebView extends AbsoluteLayout vx = vx * 3 / 4; vy = vy * 3 / 4; } + float currentVelocity = mScroller.getCurrVelocity(); + if (mLastVelocity > 0 && currentVelocity > 0) { + float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX) + - Math.atan2(vy, vx))); + final float circle = (float) (Math.PI) * 2.0f; + if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) { + vx += currentVelocity * mLastVelX / mLastVelocity; + vy += currentVelocity * mLastVelY / mLastVelocity; + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy); + } + } else if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "doFling missed " + deltaR / circle); + } + } else if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "doFling start last=" + mLastVelocity + + " current=" + currentVelocity); + } + mLastVelX = vx; + mLastVelY = vy; + mLastVelocity = (float) Math.hypot(vx, vy); mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY); // TODO: duration is calculated based on velocity, if the range is @@ -4683,7 +4718,7 @@ public class WebView extends AbsoluteLayout mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY); mLastTouchTime = eventTime; if (!mScroller.isFinished()) { - mScroller.abortAnimation(); + abortAnimation(); mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE); } mSnapScrollMode = SNAP_NONE; diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java index 381641f0a52ce..11dab02f68e88 100644 --- a/core/java/android/widget/Scroller.java +++ b/core/java/android/widget/Scroller.java @@ -132,6 +132,17 @@ public class Scroller { return mCurrY; } + /** + * @hide + * Returns the current velocity. + * + * @return The original velocity less the deceleration. Result may be + * negative. + */ + public float getCurrVelocity() { + return mVelocity - mDeceleration * timePassed() / 2000.0f; + } + /** * Returns the start X offset in the scroll. *