diff options
| author | 2011-03-17 00:09:03 -0700 | |
|---|---|---|
| committer | 2011-03-17 01:32:17 -0700 | |
| commit | d0197f3669efda060c7ee2069ff41bd970fd6d9c (patch) | |
| tree | 17b0e3a80bb3517eeca68be8c566180c2c9b1bf7 | |
| parent | f40e638ec62cd9e1a1851809b7c8bf5e4187fad2 (diff) | |
Fix bug 4111271 and bug 4077526 - WebView touch event handling when
WebCore is too slow
Make sure that we can recover properly from a bad gesture with missing
events that never come back from webcore. Lower timeout to 1 second.
Confirm movement on touch event enqueue so that we don't get phantom
taps or long presses when webcore is slow to respond.
Add sanity check in ScaleGestureDetector to end a gesture early on a
bad MotionEvent stream rather than throwing up.
Change-Id: I69690409d7edd6b4320dbcf3b052aba4023360fe
| -rw-r--r-- | core/java/android/view/ScaleGestureDetector.java | 13 | ||||
| -rw-r--r-- | core/java/android/webkit/WebView.java | 56 |
2 files changed, 53 insertions, 16 deletions
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index 5521e9292342..d638e7080b32 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -156,6 +156,7 @@ public class ScaleGestureDetector { private float mRightSlopEdge; private float mBottomSlopEdge; private boolean mSloppyGesture; + private boolean mInvalidGesture; // Pointer IDs currently responsible for the two fingers controlling the gesture private int mActiveId0; @@ -177,6 +178,8 @@ public class ScaleGestureDetector { reset(); // Start fresh } + if (mInvalidGesture) return false; + if (!mGestureInProgress) { switch (action) { case MotionEvent.ACTION_DOWN: { @@ -518,6 +521,15 @@ public class ScaleGestureDetector { final int currIndex0 = curr.findPointerIndex(mActiveId0); final int currIndex1 = curr.findPointerIndex(mActiveId1); + if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) { + mInvalidGesture = true; + Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable()); + if (mGestureInProgress) { + mListener.onScaleEnd(this); + } + return; + } + final float px0 = prev.getX(prevIndex0); final float py0 = prev.getY(prevIndex0); final float px1 = prev.getX(prevIndex1); @@ -556,6 +568,7 @@ public class ScaleGestureDetector { mGestureInProgress = false; mActiveId0 = -1; mActiveId1 = -1; + mInvalidGesture = false; } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index e20dc81f5997..01c73a8f6f11 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -7185,14 +7185,15 @@ public class WebView extends AbsoluteLayout private class TouchEventQueue { private long mNextTouchSequence = Long.MIN_VALUE + 1; private long mLastHandledTouchSequence = Long.MIN_VALUE; - private long mIgnoreUntilSequence = Long.MIN_VALUE; + private long mIgnoreUntilSequence = Long.MIN_VALUE + 1; private QueuedTouch mTouchEventQueue; private QueuedTouch mQueuedTouchRecycleBin; private int mQueuedTouchRecycleCount; + private long mLastEventTime = Long.MAX_VALUE; private static final int MAX_RECYCLED_QUEUED_TOUCH = 15; // milliseconds until we abandon hope of getting all of a previous gesture - private static final int QUEUED_GESTURE_TIMEOUT = 2000; + private static final int QUEUED_GESTURE_TIMEOUT = 1000; private QueuedTouch obtainQueuedTouch() { if (mQueuedTouchRecycleBin != null) { @@ -7226,7 +7227,7 @@ public class WebView extends AbsoluteLayout public void reset() { mNextTouchSequence = Long.MIN_VALUE + 1; mLastHandledTouchSequence = Long.MIN_VALUE; - mIgnoreUntilSequence = Long.MIN_VALUE; + mIgnoreUntilSequence = Long.MIN_VALUE + 1; while (mTouchEventQueue != null) { QueuedTouch recycleMe = mTouchEventQueue; mTouchEventQueue = mTouchEventQueue.mNext; @@ -7260,7 +7261,9 @@ public class WebView extends AbsoluteLayout return; } - dropStaleGestures(ted.mMotionEvent, ted.mSequence); + if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) { + return; + } if (mLastHandledTouchSequence + 1 == ted.mSequence) { handleQueuedTouchEventData(ted); @@ -7295,7 +7298,9 @@ public class WebView extends AbsoluteLayout public void enqueueTouchEvent(MotionEvent ev) { final long sequence = nextTouchSequence(); - dropStaleGestures(ev, sequence); + if (dropStaleGestures(ev, sequence)) { + return; + } if (mLastHandledTouchSequence + 1 == sequence) { handleQueuedMotionEvent(ev); @@ -7318,16 +7323,30 @@ public class WebView extends AbsoluteLayout } } - private void dropStaleGestures(MotionEvent ev, long sequence) { - if (mTouchEventQueue == null) return; + private boolean dropStaleGestures(MotionEvent ev, long sequence) { + if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && !mConfirmMove) { + // This is to make sure that we don't attempt to process a tap + // or long press when webkit takes too long to get back to us. + // The movement will be properly confirmed when we process the + // enqueued event later. + final int dx = Math.round(ev.getX()) - mLastTouchX; + final int dy = Math.round(ev.getY()) - mLastTouchY; + if (dx * dx + dy * dy > mTouchSlopSquare) { + mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + } + } - MotionEvent nextQueueEvent = mTouchEventQueue.mTed != null ? - mTouchEventQueue.mTed.mMotionEvent : mTouchEventQueue.mEvent; + if (mTouchEventQueue == null) { + return sequence <= mLastHandledTouchSequence; + } - if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN && nextQueueEvent != null) { + // If we have a new down event and it's been a while since the last event + // we saw, just reset and keep going. + if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN) { long eventTime = ev.getEventTime(); - long nextQueueTime = nextQueueEvent.getEventTime(); - if (eventTime > nextQueueTime + QUEUED_GESTURE_TIMEOUT) { + long lastHandledEventTime = mLastEventTime; + if (eventTime > lastHandledEventTime + QUEUED_GESTURE_TIMEOUT) { Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " + "Ignoring previous queued events."); QueuedTouch qd = mTouchEventQueue; @@ -7341,17 +7360,18 @@ public class WebView extends AbsoluteLayout } } - if (mIgnoreUntilSequence > mLastHandledTouchSequence) { + if (mIgnoreUntilSequence - 1 > mLastHandledTouchSequence) { QueuedTouch qd = mTouchEventQueue; - while (qd != null && qd.mSequence < mIgnoreUntilSequence && - qd.mSequence < sequence) { - mLastHandledTouchSequence = qd.mSequence; + while (qd != null && qd.mSequence < mIgnoreUntilSequence) { QueuedTouch recycleMe = qd; qd = qd.mNext; recycleQueuedTouch(recycleMe); } mTouchEventQueue = qd; + mLastHandledTouchSequence = mIgnoreUntilSequence - 1; } + + return sequence <= mLastHandledTouchSequence; } private void handleQueuedTouch(QueuedTouch qt) { @@ -7364,6 +7384,7 @@ public class WebView extends AbsoluteLayout } private void handleQueuedMotionEvent(MotionEvent ev) { + mLastEventTime = ev.getEventTime(); int action = ev.getActionMasked(); if (ev.getPointerCount() > 1) { // Multi-touch handleMultiTouchInWebView(ev); @@ -7381,6 +7402,9 @@ public class WebView extends AbsoluteLayout } private void handleQueuedTouchEventData(TouchEventData ted) { + if (ted.mMotionEvent != null) { + mLastEventTime = ted.mMotionEvent.getEventTime(); + } if (!ted.mReprocess) { if (ted.mAction == MotionEvent.ACTION_DOWN && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) { |