diff options
author | Adam Powell <adamp@google.com> | 2011-02-24 17:19:20 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-02-24 17:19:20 -0800 |
commit | 78b1dc594e53120ba7817d9ceb6a340278186891 (patch) | |
tree | 5d5520316bbd4ebb7271d3c9e04597cb8cc59e69 /core/java | |
parent | 30b1fe07a73d522e97b7140cc227232a0c8c1a1e (diff) | |
parent | e33cef8037cb87386e17bcf8701a47452d262fa6 (diff) | |
download | frameworks_base-78b1dc594e53120ba7817d9ceb6a340278186891.zip frameworks_base-78b1dc594e53120ba7817d9ceb6a340278186891.tar.gz frameworks_base-78b1dc594e53120ba7817d9ceb6a340278186891.tar.bz2 |
Merge "Ensure WebView passes a complete/in-order event stream to ScaleGestureDetector. Prerequisite for several ScaleGestureDetector bugfixes."
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/view/ScaleGestureDetector.java | 254 | ||||
-rw-r--r-- | core/java/android/webkit/WebView.java | 27 |
2 files changed, 209 insertions, 72 deletions
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index 17b5dd7..218ee4f 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -154,6 +154,11 @@ public class ScaleGestureDetector { private float mBottomSlopEdge; private boolean mSloppyGesture; + // Pointer IDs currently responsible for the two fingers controlling the gesture + private int mActiveId0; + private int mActiveId1; + private boolean mActive0MostRecent; + public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { ViewConfiguration config = ViewConfiguration.get(context); mContext = context; @@ -162,11 +167,21 @@ public class ScaleGestureDetector { } public boolean onTouchEvent(MotionEvent event) { - final int action = event.getAction(); + final int action = event.getActionMasked(); boolean handled = true; if (!mGestureInProgress) { - switch (action & MotionEvent.ACTION_MASK) { + switch (action) { + case MotionEvent.ACTION_DOWN: { + mActiveId0 = event.getPointerId(0); + mActive0MostRecent = true; + } + break; + + case MotionEvent.ACTION_UP: + reset(); + break; + case MotionEvent.ACTION_POINTER_DOWN: { // We have a new multi-finger gesture @@ -175,12 +190,15 @@ public class ScaleGestureDetector { mRightSlopEdge = metrics.widthPixels - mEdgeSlop; mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; - // Be paranoid in case we missed an event - reset(); - + if (mPrevEvent != null) mPrevEvent.recycle(); mPrevEvent = MotionEvent.obtain(event); mTimeDelta = 0; + int index1 = event.getActionIndex(); + int index0 = event.findPointerIndex(mActiveId0); + mActiveId1 = event.getPointerId(index1); + mActive0MostRecent = false; + setContext(event); // Check if we have a sloppy gesture. If so, delay @@ -190,10 +208,10 @@ public class ScaleGestureDetector { final float edgeSlop = mEdgeSlop; final float rightSlop = mRightSlopEdge; final float bottomSlop = mBottomSlopEdge; - final float x0 = event.getRawX(); - final float y0 = event.getRawY(); - final float x1 = getRawX(event, 1); - final float y1 = getRawY(event, 1); + float x0 = getRawX(event, index0); + float y0 = getRawY(event, index0); + float x1 = getRawX(event, index1); + float y1 = getRawY(event, index1); boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop || y0 > bottomSlop; @@ -205,14 +223,15 @@ public class ScaleGestureDetector { mFocusY = -1; mSloppyGesture = true; } else if (p0sloppy) { - mFocusX = event.getX(1); - mFocusY = event.getY(1); + mFocusX = event.getX(index1); + mFocusY = event.getY(index1); mSloppyGesture = true; } else if (p1sloppy) { - mFocusX = event.getX(0); - mFocusY = event.getY(0); + mFocusX = event.getX(index0); + mFocusY = event.getY(index0); mSloppyGesture = true; } else { + mSloppyGesture = false; mGestureInProgress = mListener.onScaleBegin(this); } } @@ -224,25 +243,51 @@ public class ScaleGestureDetector { final float edgeSlop = mEdgeSlop; final float rightSlop = mRightSlopEdge; final float bottomSlop = mBottomSlopEdge; - final float x0 = event.getRawX(); - final float y0 = event.getRawY(); - final float x1 = getRawX(event, 1); - final float y1 = getRawY(event, 1); + int index0 = event.findPointerIndex(mActiveId0); + int index1 = event.findPointerIndex(mActiveId1); + float x0 = getRawX(event, index0); + float y0 = getRawY(event, index0); + float x1 = getRawX(event, index1); + float y1 = getRawY(event, index1); boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop - || x0 > rightSlop || y0 > bottomSlop; + || x0 > rightSlop || y0 > bottomSlop; boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop - || x1 > rightSlop || y1 > bottomSlop; + || x1 > rightSlop || y1 > bottomSlop; + + if (p0sloppy) { + // Do we have a different pointer that isn't sloppy? + int index = findNewActiveIndex(event, mActiveId1, index0); + if (index >= 0) { + index0 = index; + mActiveId0 = event.getPointerId(index); + x0 = getRawX(event, index); + y0 = getRawY(event, index); + p0sloppy = false; + } + } + + if (p1sloppy) { + // Do we have a different pointer that isn't sloppy? + int index = findNewActiveIndex(event, mActiveId0, index1); + if (index >= 0) { + index1 = index; + mActiveId1 = event.getPointerId(index); + x1 = getRawX(event, index); + y1 = getRawY(event, index); + p1sloppy = false; + } + } if(p0sloppy && p1sloppy) { mFocusX = -1; mFocusY = -1; } else if (p0sloppy) { - mFocusX = event.getX(1); - mFocusY = event.getY(1); + mFocusX = event.getX(index1); + mFocusY = event.getY(index1); } else if (p1sloppy) { - mFocusX = event.getX(0); - mFocusY = event.getY(0); + mFocusX = event.getX(index0); + mFocusY = event.getY(index0); } else { mSloppyGesture = false; mGestureInProgress = mListener.onScaleBegin(this); @@ -252,43 +297,109 @@ public class ScaleGestureDetector { case MotionEvent.ACTION_POINTER_UP: if (mSloppyGesture) { - // Set focus point to the remaining finger - int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0; - mFocusX = event.getX(id); - mFocusY = event.getY(id); + final int pointerCount = event.getPointerCount(); + final int actionIndex = event.getActionIndex(); + final int actionId = event.getPointerId(actionIndex); + + if (pointerCount > 2) { + if (actionId == mActiveId0) { + final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex); + if (newIndex >= 0) mActiveId0 = event.getPointerId(newIndex); + } else if (actionId == mActiveId1) { + final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex); + if (newIndex >= 0) mActiveId1 = event.getPointerId(newIndex); + } + } else { + // Set focus point to the remaining finger + final int index = event.findPointerIndex(actionId == mActiveId0 ? + mActiveId1 : mActiveId0); + mActiveId0 = event.getPointerId(index); + mActive0MostRecent = true; + mActiveId1 = -1; + mFocusX = event.getX(index); + mFocusY = event.getY(index); + } } break; } } else { // Transform gesture in progress - attempt to handle it - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_POINTER_UP: - // Gesture ended + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: { + // End the old gesture and begin a new one with the most recent two fingers. + mListener.onScaleEnd(this); + final int oldActive0 = mActiveId0; + final int oldActive1 = mActiveId1; + reset(); + + mPrevEvent = MotionEvent.obtain(event); + mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1; + mActiveId1 = event.getPointerId(event.getActionIndex()); + mActive0MostRecent = false; + setContext(event); - // Set focus point to the remaining finger - int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0; - mFocusX = event.getX(id); - mFocusY = event.getY(id); + mGestureInProgress = mListener.onScaleBegin(this); + } + break; - if (!mSloppyGesture) { - mListener.onScaleEnd(this); + case MotionEvent.ACTION_POINTER_UP: { + final int pointerCount = event.getPointerCount(); + final int actionIndex = event.getActionIndex(); + final int actionId = event.getPointerId(actionIndex); + + boolean gestureEnded = false; + if (pointerCount > 2) { + if (actionId == mActiveId0) { + final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex); + if (newIndex >= 0) { + mActiveId0 = event.getPointerId(newIndex); + } else { + gestureEnded = true; + } + } else if (actionId == mActiveId1) { + final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex); + if (newIndex >= 0) { + mActiveId1 = event.getPointerId(newIndex); + } else { + gestureEnded = true; + } + } + mPrevEvent.recycle(); + mPrevEvent = MotionEvent.obtain(event); + setContext(event); + } else { + gestureEnded = true; } - reset(); - break; + if (gestureEnded) { + // Gesture ended + setContext(event); + + // Set focus point to the remaining finger + final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0; + final int index = event.findPointerIndex(activeId); + mFocusX = event.getX(index); + mFocusY = event.getY(index); - case MotionEvent.ACTION_CANCEL: - if (!mSloppyGesture) { mListener.onScaleEnd(this); + reset(); + mActiveId0 = activeId; + mActive0MostRecent = true; } + } + break; + case MotionEvent.ACTION_CANCEL: + mListener.onScaleEnd(this); reset(); break; - case MotionEvent.ACTION_MOVE: + case MotionEvent.ACTION_UP: + reset(); + break; + + case MotionEvent.ACTION_MOVE: { setContext(event); // Only accept the event if our relative pressure is within @@ -302,16 +413,43 @@ public class ScaleGestureDetector { mPrevEvent = MotionEvent.obtain(event); } } - break; + } + break; } } return handled; } + private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int oldIndex) { + final int pointerCount = ev.getPointerCount(); + + // It's ok if this isn't found and returns -1, it simply won't match. + final int otherActiveIndex = ev.findPointerIndex(otherActiveId); + int newActiveIndex = -1; + + // Pick a new id and update tracking state. Only pick pointers not on the slop edges. + for (int i = 0; i < pointerCount; i++) { + if (i != oldIndex && i != otherActiveIndex) { + final float edgeSlop = mEdgeSlop; + final float rightSlop = mRightSlopEdge; + final float bottomSlop = mBottomSlopEdge; + float x = getRawX(ev, i); + float y = getRawY(ev, i); + if (x >= edgeSlop && y >= edgeSlop && x <= rightSlop && y <= bottomSlop) { + newActiveIndex = i; + break; + } + } + } + + return newActiveIndex; + } + /** * MotionEvent has no getRawX(int) method; simulate it pending future API approval. */ private static float getRawX(MotionEvent event, int pointerIndex) { + if (pointerIndex == 0) return event.getRawX(); float offset = event.getRawX() - event.getX(); return event.getX(pointerIndex) + offset; } @@ -320,6 +458,7 @@ public class ScaleGestureDetector { * MotionEvent has no getRawY(int) method; simulate it pending future API approval. */ private static float getRawY(MotionEvent event, int pointerIndex) { + if (pointerIndex == 0) return event.getRawY(); float offset = event.getRawY() - event.getY(); return event.getY(pointerIndex) + offset; } @@ -336,14 +475,19 @@ public class ScaleGestureDetector { final MotionEvent prev = mPrevEvent; - final float px0 = prev.getX(0); - final float py0 = prev.getY(0); - final float px1 = prev.getX(1); - final float py1 = prev.getY(1); - final float cx0 = curr.getX(0); - final float cy0 = curr.getY(0); - final float cx1 = curr.getX(1); - final float cy1 = curr.getY(1); + final int prevIndex0 = prev.findPointerIndex(mActiveId0); + final int prevIndex1 = prev.findPointerIndex(mActiveId1); + final int currIndex0 = curr.findPointerIndex(mActiveId0); + final int currIndex1 = curr.findPointerIndex(mActiveId1); + + final float px0 = prev.getX(prevIndex0); + final float py0 = prev.getY(prevIndex0); + final float px1 = prev.getX(prevIndex1); + final float py1 = prev.getY(prevIndex1); + final float cx0 = curr.getX(currIndex0); + final float cy0 = curr.getY(currIndex0); + final float cx1 = curr.getX(currIndex1); + final float cy1 = curr.getY(currIndex1); final float pvx = px1 - px0; final float pvy = py1 - py0; @@ -357,8 +501,8 @@ public class ScaleGestureDetector { mFocusX = cx0 + cvx * 0.5f; mFocusY = cy0 + cvy * 0.5f; mTimeDelta = curr.getEventTime() - prev.getEventTime(); - mCurrPressure = curr.getPressure(0) + curr.getPressure(1); - mPrevPressure = prev.getPressure(0) + prev.getPressure(1); + mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1); + mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1); } private void reset() { @@ -372,6 +516,8 @@ public class ScaleGestureDetector { } mSloppyGesture = false; mGestureInProgress = false; + mActiveId0 = -1; + mActiveId1 = -1; } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index f8ece6b..06cb64e 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -5414,12 +5414,20 @@ public class WebView extends AbsoluteLayout // If WebKit already showed no interests in this sequence of events, // WebView handles them directly. - if (mPreventDefault == PREVENT_DEFAULT_NO && action == MotionEvent.ACTION_MOVE) { + if (mPreventDefault == PREVENT_DEFAULT_NO) { handleMultiTouchInWebView(ev); } else { passMultiTouchToWebKit(ev); } return true; + } else { + final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector(); + if (detector != null) { + // ScaleGestureDetector needs a consistent event stream to operate properly. + // It won't take any action with fewer than two pointers, but it needs to + // update internal bookkeeping state. + detector.onTouchEvent(ev); + } } // Skip ACTION_MOVE for single touch if it's still handling multi-touch. @@ -5959,23 +5967,6 @@ public class WebView extends AbsoluteLayout float x = ev.getX(); float y = ev.getY(); - if (!detector.isInProgress() && - ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) { - // Insert a fake pointer down event in order to start - // the zoom scale detector. - MotionEvent temp = MotionEvent.obtain(ev); - // Clear the original event and set it to - // ACTION_POINTER_DOWN. - try { - temp.setAction(temp.getAction() & - ~MotionEvent.ACTION_MASK | - MotionEvent.ACTION_POINTER_DOWN); - detector.onTouchEvent(temp); - } finally { - temp.recycle(); - } - } - detector.onTouchEvent(ev); if (detector.isInProgress()) { |