diff options
author | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-02 19:11:46 +0000 |
---|---|---|
committer | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-02 19:11:46 +0000 |
commit | 75147f5e8bba70b224050852d23a22d26edcb57e (patch) | |
tree | b3a083f71c2d10c6d5de3b5d8e54845f5f4fa75f /android_webview | |
parent | 29afb163da5208e3c13ce2a9e1d739afd1e8da64 (diff) | |
download | chromium_src-75147f5e8bba70b224050852d23a22d26edcb57e.zip chromium_src-75147f5e8bba70b224050852d23a22d26edcb57e.tar.gz chromium_src-75147f5e8bba70b224050852d23a22d26edcb57e.tar.bz2 |
Handle root layer fling Java-side in android_webview.
The android_webview implementation needs to handle root layer fling
Java-side. This is required for proper integration with the android
view system which requires the fling scroll offset update to be
performed as a result of the computeScroll method.
BUG=None
TEST=AndroidWebViewTest
Review URL: https://chromiumcodereview.appspot.com/20990009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215339 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview')
5 files changed, 184 insertions, 18 deletions
diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/in_process_view_renderer.cc index d761124..73069cd 100644 --- a/android_webview/browser/in_process_view_renderer.cc +++ b/android_webview/browser/in_process_view_renderer.cc @@ -585,7 +585,11 @@ void InProcessViewRenderer::ScrollTo(gfx::Vector2d new_value) { gfx::Vector2dF new_value_css = gfx::ToRoundedVector2d( gfx::ScaleVector2d(new_value, 1.0f / (dip_scale_ * page_scale_factor_))); - DCHECK(scroll_offset_css_ != new_value_css); + // It's possible that more than one set of unique physical coordinates maps + // to the same set of CSS coordinates which means we can't reliably early-out + // earlier in the call stack. + if (scroll_offset_css_ == new_value_css) + return; scroll_offset_css_ = new_value_css; diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 22be0d4..2d498da 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -31,6 +31,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.webkit.GeolocationPermissions; import android.webkit.ValueCallback; +import android.widget.OverScroller; import com.google.common.annotations.VisibleForTesting; @@ -372,10 +373,15 @@ public class AwContents { public int getContainerViewScrollY() { return mContainerView.getScrollY(); } + + @Override + public void invalidate() { + mContainerView.invalidate(); + } } //-------------------------------------------------------------------------------------------- - private class AwPinchGestureStateListener implements ContentViewCore.PinchGestureStateListener { + private class AwGestureStateListener implements ContentViewCore.GestureStateListener { @Override public void onPinchGestureStart() { // While it's possible to re-layout the view during a pinch gesture, the effect is very @@ -387,9 +393,26 @@ public class AwContents { mLayoutSizer.freezeLayoutRequests(); } + @Override public void onPinchGestureEnd() { mLayoutSizer.unfreezeLayoutRequests(); } + + @Override + public void onFlingStartGesture(int velocityX, int velocityY) { + mScrollOffsetManager.onFlingStartGesture(velocityX, velocityY); + } + + + @Override + public void onFlingCancelGesture() { + mScrollOffsetManager.onFlingCancelGesture(); + } + + @Override + public void onUnhandledFlingStartEvent() { + mScrollOffsetManager.onUnhandledFlingStartEvent(); + } } //-------------------------------------------------------------------------------------------- @@ -424,7 +447,7 @@ public class AwContents { private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView, InternalAccessDelegate internalDispatcher, int nativeWebContents, - ContentViewCore.PinchGestureStateListener pinchGestureStateListener, + ContentViewCore.GestureStateListener pinchGestureStateListener, ContentViewClient contentViewClient, ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) { ContentViewCore contentViewCore = new ContentViewCore(containerView.getContext()); @@ -432,7 +455,7 @@ public class AwContents { // compositor, not because input events are delivered immediately. contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, null, ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY); - contentViewCore.setPinchGestureStateListener(pinchGestureStateListener); + contentViewCore.setGestureStateListener(pinchGestureStateListener); contentViewCore.setContentViewClient(contentViewClient); contentViewCore.setZoomControlsDelegate(zoomControlsDelegate); return contentViewCore; @@ -479,7 +502,8 @@ public class AwContents { mSettings.setDefaultVideoPosterURL( mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); mContentsClient.setDIPScale(mDIPScale); - mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate()); + mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate(), + new OverScroller(mContainerView.getContext())); setOverScrollMode(mContainerView.getOverScrollMode()); @@ -513,7 +537,7 @@ public class AwContents { int nativeWebContents = nativeGetWebContents(mNativeAwContents); mContentViewCore = createAndInitializeContentViewCore( mContainerView, mInternalAccessAdapter, nativeWebContents, - new AwPinchGestureStateListener(), mContentsClient.getContentViewClient(), + new AwGestureStateListener(), mContentsClient.getContentViewClient(), mZoomControls); nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge, mIoThreadClient, mInterceptNavigationDelegate); @@ -870,6 +894,13 @@ public class AwContents { } /** + * @see View.computeScroll() + */ + public void computeScroll() { + mScrollOffsetManager.computeScrollAndAbsorbGlow(mOverScrollGlow); + } + + /** * @see View#computeHorizontalScrollRange() */ public int computeHorizontalScrollRange() { @@ -1174,8 +1205,8 @@ public class AwContents { /** * @see android.webkit.WebView#flingScroll(int, int) */ - public void flingScroll(int vx, int vy) { - mContentViewCore.flingScroll(vx, vy); + public void flingScroll(int velocityX, int velocityY) { + mContentViewCore.flingScroll(velocityX, velocityY); } /** @@ -1656,7 +1687,7 @@ public class AwContents { mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); } - mScrollOffsetManager.overscrollBy(deltaX, deltaY); + mScrollOffsetManager.overScrollBy(deltaX, deltaY); if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) { mContainerView.invalidate(); diff --git a/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java b/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java index 4564b42..86f693b 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java +++ b/android_webview/java/src/org/chromium/android_webview/AwScrollOffsetManager.java @@ -4,6 +4,10 @@ package org.chromium.android_webview; +import android.widget.OverScroller; + +import com.google.common.annotations.VisibleForTesting; + import org.chromium.base.CalledByNative; /** @@ -12,6 +16,7 @@ import org.chromium.base.CalledByNative; * * Unless otherwise values (sizes, scroll offsets) are in physical pixels. */ +@VisibleForTesting public class AwScrollOffsetManager { // The unit of all the values in this delegate are physical pixels. public interface Delegate { @@ -24,8 +29,11 @@ public class AwScrollOffsetManager { // operation, the native side shouldn't synchronously alter the scroll offset from within // this call. void scrollNativeTo(int x, int y); + int getContainerViewScrollX(); int getContainerViewScrollY(); + + void invalidate(); } private final Delegate mDelegate; @@ -42,14 +50,24 @@ public class AwScrollOffsetManager { private int mContainerViewWidth; private int mContainerViewHeight; + // Whether we're in the middle of processing a touch event. private boolean mProcessingTouchEvent; + // Whether (and to what value) to update the native side scroll offset after we've finished + // provessing a touch event. private boolean mApplyDeferredNativeScroll; private int mDeferredNativeScrollX; private int mDeferredNativeScrollY; - public AwScrollOffsetManager(Delegate delegate) { + // The velocity of the last recorded fling, + private int mLastFlingVelocityX; + private int mLastFlingVelocityY; + + private OverScroller mScroller; + + public AwScrollOffsetManager(Delegate delegate, OverScroller overScroller) { mDelegate = delegate; + mScroller = overScroller; } //----- Scroll range and extent calculation methods ------------------------------------------- @@ -133,12 +151,25 @@ public class AwScrollOffsetManager { } // Called by the native side to over-scroll the container view. - public void overscrollBy(int deltaX, int deltaY) { + public void overScrollBy(int deltaX, int deltaY) { + // TODO(mkosiba): Once http://crbug.com/260663 and http://crbug.com/261239 are fixed it + // should be possible to uncomment the following asserts: + // if (deltaX < 0) assert mDelegate.getContainerViewScrollX() == 0; + // if (deltaX > 0) assert mDelegate.getContainerViewScrollX() == + // computeMaximumHorizontalScrollOffset(); + scrollBy(deltaX, deltaY); + } + + private void scrollBy(int deltaX, int deltaY) { + if (deltaX == 0 && deltaY == 0) return; + final int scrollX = mDelegate.getContainerViewScrollX(); final int scrollY = mDelegate.getContainerViewScrollY(); final int scrollRangeX = computeMaximumHorizontalScrollOffset(); final int scrollRangeY = computeMaximumVerticalScrollOffset(); + // The android.view.View.overScrollBy method is used for both scrolling and over-scrolling + // which is why we use it here. mDelegate.overScrollContainerViewBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, mProcessingTouchEvent); } @@ -201,4 +232,62 @@ public class AwScrollOffsetManager { mDelegate.scrollNativeTo(x, y); } + + // Called at the beginning of every fling gesture. + public void onFlingStartGesture(int velocityX, int velocityY) { + mLastFlingVelocityX = velocityX; + mLastFlingVelocityY = velocityY; + } + + // Called whenever some other touch interaction requires the fling gesture to be canceled. + public void onFlingCancelGesture() { + // TODO(mkosiba): Support speeding up a fling by flinging again. + // http://crbug.com/265841 + mScroller.forceFinished(true); + } + + // Called when a fling gesture is not handled by the renderer. + // We explicitly ask the renderer not to handle fling gestures targeted at the root + // scroll layer. + public void onUnhandledFlingStartEvent() { + flingScroll(-mLastFlingVelocityX, -mLastFlingVelocityY); + } + + // Starts the fling animation. Called both as a response to a fling gesture and as via the + // public WebView#flingScroll(int, int) API. + public void flingScroll(int velocityX, int velocityY) { + final int scrollX = mDelegate.getContainerViewScrollX(); + final int scrollY = mDelegate.getContainerViewScrollY(); + final int rangeX = computeMaximumHorizontalScrollOffset(); + final int rangeY = computeMaximumVerticalScrollOffset(); + + mScroller.fling(scrollX, scrollY, velocityX, velocityY, + 0, rangeX, 0, rangeY); + mDelegate.invalidate(); + } + + // Called immediately before the draw to update the scroll offset. + public void computeScrollAndAbsorbGlow(OverScrollGlow overScrollGlow) { + final boolean stillAnimating = mScroller.computeScrollOffset(); + if (!stillAnimating) return; + + final int oldX = mDelegate.getContainerViewScrollX(); + final int oldY = mDelegate.getContainerViewScrollY(); + int x = mScroller.getCurrX(); + int y = mScroller.getCurrY(); + + int rangeX = computeMaximumHorizontalScrollOffset(); + int rangeY = computeMaximumVerticalScrollOffset(); + + if (overScrollGlow != null) { + overScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY, + mScroller.getCurrVelocity()); + } + + // The mScroller is configured not to go outside of the scrollable range, so this call + // should never result in attempting to scroll outside of the scrollable region. + scrollBy(x - oldX, y - oldY); + + mDelegate.invalidate(); + } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwScrollOffsetManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwScrollOffsetManagerTest.java index 74891a4..c394774 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwScrollOffsetManagerTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwScrollOffsetManagerTest.java @@ -4,8 +4,10 @@ package org.chromium.android_webview.test; +import android.content.Context; import android.view.View; import android.view.View.MeasureSpec; +import android.widget.OverScroller; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -21,6 +23,7 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { private int mScrollY; private int mNativeScrollX; private int mNativeScrollY; + private int mInvalidateCount; public int getOverScrollDeltaX() { return mOverScrollDeltaX; @@ -50,6 +53,10 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { return mNativeScrollY; } + public int getInvalidateCount() { + return mInvalidateCount; + } + @Override public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, boolean isTouchEvent) { @@ -79,6 +86,11 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { public int getContainerViewScrollY() { return mScrollY; } + + @Override + public void invalidate() { + mInvalidateCount += 1; + } } private void simulateScrolling(AwScrollOffsetManager offsetManager, @@ -98,7 +110,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { @Feature({"AndroidWebView"}) public void testWhenContentSizeMatchesView() { TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); final int width = 132; final int height = 212; @@ -150,7 +163,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { @Feature({"AndroidWebView"}) public void testScrollRangeAndMaxOffset() { TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); offsetManager.setContentSize(CONTENT_WIDTH, CONTENT_HEIGHT); offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); @@ -207,7 +221,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { return overrideScrollY; } }; - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); offsetManager.setContentSize(CONTENT_WIDTH, CONTENT_HEIGHT); offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); @@ -233,7 +248,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { return overrideScrollY; } }; - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); offsetManager.setContentSize(CONTENT_WIDTH, CONTENT_HEIGHT); offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); @@ -247,7 +263,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { @Feature({"AndroidWebView"}) public void testScrollContainerViewTo() { TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); final int scrollX = 31; final int scrollY = 41; @@ -269,7 +286,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { @Feature({"AndroidWebView"}) public void testOnContainerViewOverScrolled() { TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); final int scrollX = 31; final int scrollY = 41; @@ -293,7 +311,8 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { @Feature({"AndroidWebView"}) public void testDefersScrollUntilTouchEnd() { TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); - AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); final int scrollX = 31; final int scrollY = 41; @@ -314,4 +333,22 @@ public class AwScrollOffsetManagerTest extends InstrumentationTestCase { assertEquals(scrollX, delegate.getNativeScrollX()); assertEquals(scrollY, delegate.getNativeScrollY()); } + + @SmallTest + @Feature({"AndroidWebView"}) + public void testFlingScroll() { + TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); + OverScroller scroller = new OverScroller(getInstrumentation().getContext()); + AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); + + offsetManager.flingScroll(0, 101); + assertTrue(!scroller.isFinished()); + assertTrue(delegate.getInvalidateCount() == 1); + assertEquals(101, (int) scroller.getCurrVelocity()); + + offsetManager.flingScroll(111, 0); + assertTrue(!scroller.isFinished()); + assertTrue(delegate.getInvalidateCount() == 2); + assertEquals(111, (int) scroller.getCurrVelocity()); + } } diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java index 3860484..b83690e 100644 --- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java +++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java @@ -119,6 +119,11 @@ public class AwTestContainerView extends FrameLayout { } @Override + public void computeScroll() { + mAwContents.computeScroll(); + } + + @Override public void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); mAwContents.onVisibilityChanged(changedView, visibility); |