Index: content/public/android/java/src/org/chromium/content/browser/third_party/GestureDetector.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/third_party/GestureDetector.java b/content/public/android/java/src/org/chromium/content/browser/third_party/GestureDetector.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6c98320fa3ff96b6f8bf36fdcdd906423896d6bb |
--- /dev/null |
+++ b/content/public/android/java/src/org/chromium/content/browser/third_party/GestureDetector.java |
@@ -0,0 +1,701 @@ |
+/* |
+ * Copyright (C) 2008 The Android Open Source Project |
+ * |
+ * Licensed under the Apache License, Version 2.0 (the "License"); |
+ * you may not use this file except in compliance with the License. |
+ * You may obtain a copy of the License at |
+ * |
+ * http://www.apache.org/licenses/LICENSE-2.0 |
+ * |
+ * Unless required by applicable law or agreed to in writing, software |
+ * distributed under the License is distributed on an "AS IS" BASIS, |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+ * See the License for the specific language governing permissions and |
+ * limitations under the License. |
+ */ |
+ |
+// This is a copy of Android GestureDetector.java from AOSP 4.2. We include it in Chromium in order |
+// to have JB-style behavior on ICS devices. Some edits for NDK v16 compliance were needed; they're |
+// noted explicitly below. |
+ |
+// New imports in Chromium for NDK compliance. |
+package org.chromium.content.browser.third_party; |
+import android.view.MotionEvent; |
+import android.view.VelocityTracker; |
+import android.view.ViewConfiguration; |
+ |
+/* Commented out in Chromium for NDK compliance |
+package android.view; |
+*/ |
+ |
+import android.content.Context; |
+import android.os.Build; |
+import android.os.Handler; |
+import android.os.Message; |
+ |
+/** |
+ * Detects various gestures and events using the supplied {@link MotionEvent}s. |
+ * The {@link OnGestureListener} callback will notify users when a particular |
+ * motion event has occurred. This class should only be used with {@link MotionEvent}s |
+ * reported via touch (don't use for trackball events). |
+ * |
+ * To use this class: |
+ * <ul> |
+ * <li>Create an instance of the {@code GestureDetector} for your {@link View} |
+ * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call |
+ * {@link #onTouchEvent(MotionEvent)}. The methods defined in your callback |
+ * will be executed when the events occur. |
+ * </ul> |
+ */ |
+public class GestureDetector { |
+ /** |
+ * The listener that is used to notify when gestures occur. |
+ * If you want to listen for all the different gestures then implement |
+ * this interface. If you only want to listen for a subset it might |
+ * be easier to extend {@link SimpleOnGestureListener}. |
+ */ |
+ public interface OnGestureListener { |
+ |
+ /** |
+ * Notified when a tap occurs with the down {@link MotionEvent} |
+ * that triggered it. This will be triggered immediately for |
+ * every down event. All other events should be preceded by this. |
+ * |
+ * @param e The down motion event. |
+ */ |
+ boolean onDown(MotionEvent e); |
+ |
+ /** |
+ * The user has performed a down {@link MotionEvent} and not performed |
+ * a move or up yet. This event is commonly used to provide visual |
+ * feedback to the user to let them know that their action has been |
+ * recognized i.e. highlight an element. |
+ * |
+ * @param e The down motion event |
+ */ |
+ void onShowPress(MotionEvent e); |
+ |
+ /** |
+ * Notified when a tap occurs with the up {@link MotionEvent} |
+ * that triggered it. |
+ * |
+ * @param e The up motion event that completed the first tap |
+ * @return true if the event is consumed, else false |
+ */ |
+ boolean onSingleTapUp(MotionEvent e); |
+ |
+ /** |
+ * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the |
+ * current move {@link MotionEvent}. The distance in x and y is also supplied for |
+ * convenience. |
+ * |
+ * @param e1 The first down motion event that started the scrolling. |
+ * @param e2 The move motion event that triggered the current onScroll. |
+ * @param distanceX The distance along the X axis that has been scrolled since the last |
+ * call to onScroll. This is NOT the distance between {@code e1} |
+ * and {@code e2}. |
+ * @param distanceY The distance along the Y axis that has been scrolled since the last |
+ * call to onScroll. This is NOT the distance between {@code e1} |
+ * and {@code e2}. |
+ * @return true if the event is consumed, else false |
+ */ |
+ boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY); |
+ |
+ /** |
+ * Notified when a long press occurs with the initial on down {@link MotionEvent} |
+ * that trigged it. |
+ * |
+ * @param e The initial on down motion event that started the longpress. |
+ */ |
+ void onLongPress(MotionEvent e); |
+ |
+ /** |
+ * Notified of a fling event when it occurs with the initial on down {@link MotionEvent} |
+ * and the matching up {@link MotionEvent}. The calculated velocity is supplied along |
+ * the x and y axis in pixels per second. |
+ * |
+ * @param e1 The first down motion event that started the fling. |
+ * @param e2 The move motion event that triggered the current onFling. |
+ * @param velocityX The velocity of this fling measured in pixels per second |
+ * along the x axis. |
+ * @param velocityY The velocity of this fling measured in pixels per second |
+ * along the y axis. |
+ * @return true if the event is consumed, else false |
+ */ |
+ boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); |
+ } |
+ |
+ /** |
+ * The listener that is used to notify when a double-tap or a confirmed |
+ * single-tap occur. |
+ */ |
+ public interface OnDoubleTapListener { |
+ /** |
+ * Notified when a single-tap occurs. |
+ * <p> |
+ * Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this |
+ * will only be called after the detector is confident that the user's |
+ * first tap is not followed by a second tap leading to a double-tap |
+ * gesture. |
+ * |
+ * @param e The down motion event of the single-tap. |
+ * @return true if the event is consumed, else false |
+ */ |
+ boolean onSingleTapConfirmed(MotionEvent e); |
+ |
+ /** |
+ * Notified when a double-tap occurs. |
+ * |
+ * @param e The down motion event of the first tap of the double-tap. |
+ * @return true if the event is consumed, else false |
+ */ |
+ boolean onDoubleTap(MotionEvent e); |
+ |
+ /** |
+ * Notified when an event within a double-tap gesture occurs, including |
+ * the down, move, and up events. |
+ * |
+ * @param e The motion event that occurred during the double-tap gesture. |
+ * @return true if the event is consumed, else false |
+ */ |
+ boolean onDoubleTapEvent(MotionEvent e); |
+ } |
+ |
+ /** |
+ * A convenience class to extend when you only want to listen for a subset |
+ * of all the gestures. This implements all methods in the |
+ * {@link OnGestureListener} and {@link OnDoubleTapListener} but does |
+ * nothing and return {@code false} for all applicable methods. |
+ */ |
+ public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener { |
+ public boolean onSingleTapUp(MotionEvent e) { |
+ return false; |
+ } |
+ |
+ public void onLongPress(MotionEvent e) { |
+ } |
+ |
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, |
+ float distanceX, float distanceY) { |
+ return false; |
+ } |
+ |
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, |
+ float velocityY) { |
+ return false; |
+ } |
+ |
+ public void onShowPress(MotionEvent e) { |
+ } |
+ |
+ public boolean onDown(MotionEvent e) { |
+ return false; |
+ } |
+ |
+ public boolean onDoubleTap(MotionEvent e) { |
+ return false; |
+ } |
+ |
+ public boolean onDoubleTapEvent(MotionEvent e) { |
+ return false; |
+ } |
+ |
+ public boolean onSingleTapConfirmed(MotionEvent e) { |
+ return false; |
+ } |
+ } |
+ |
+ private int mTouchSlopSquare; |
+ private int mDoubleTapTouchSlopSquare; |
+ private int mDoubleTapSlopSquare; |
+ private int mMinimumFlingVelocity; |
+ private int mMaximumFlingVelocity; |
+ |
+ private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); |
+ private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); |
+ private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout(); |
+ |
+ // constants for Message.what used by GestureHandler below |
+ private static final int SHOW_PRESS = 1; |
+ private static final int LONG_PRESS = 2; |
+ private static final int TAP = 3; |
+ |
+ private final Handler mHandler; |
+ private final OnGestureListener mListener; |
+ private OnDoubleTapListener mDoubleTapListener; |
+ |
+ private boolean mStillDown; |
+ private boolean mInLongPress; |
+ private boolean mAlwaysInTapRegion; |
+ private boolean mAlwaysInBiggerTapRegion; |
+ |
+ private MotionEvent mCurrentDownEvent; |
+ private MotionEvent mPreviousUpEvent; |
+ |
+ /** |
+ * True when the user is still touching for the second tap (down, move, and |
+ * up events). Can only be true if there is a double tap listener attached. |
+ */ |
+ private boolean mIsDoubleTapping; |
+ |
+ private float mLastFocusX; |
+ private float mLastFocusY; |
+ private float mDownFocusX; |
+ private float mDownFocusY; |
+ |
+ private boolean mIsLongpressEnabled; |
+ |
+ /** |
+ * Determines speed during touch scrolling |
+ */ |
+ private VelocityTracker mVelocityTracker; |
+ |
+ /** |
+ * Consistency verifier for debugging purposes. |
+ */ |
+/* Commented out in Chromium for NDK compliance |
+ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier = |
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ? |
+ new InputEventConsistencyVerifier(this, 0) : null; |
+*/ |
+ |
+ private class GestureHandler extends Handler { |
+ GestureHandler() { |
+ super(); |
+ } |
+ |
+ GestureHandler(Handler handler) { |
+ super(handler.getLooper()); |
+ } |
+ |
+ @Override |
+ public void handleMessage(Message msg) { |
+ switch (msg.what) { |
+ case SHOW_PRESS: |
+ mListener.onShowPress(mCurrentDownEvent); |
+ break; |
+ |
+ case LONG_PRESS: |
+ dispatchLongPress(); |
+ break; |
+ |
+ case TAP: |
+ // If the user's finger is still down, do not count it as a tap |
+ if (mDoubleTapListener != null && !mStillDown) { |
+ mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); |
+ } |
+ break; |
+ |
+ default: |
+ throw new RuntimeException("Unknown message " + msg); //never |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Creates a GestureDetector with the supplied listener. |
+ * This variant of the constructor should be used from a non-UI thread |
+ * (as it allows specifying the Handler). |
+ * |
+ * @param listener the listener invoked for all the callbacks, this must |
+ * not be null. |
+ * @param handler the handler to use |
+ * |
+ * @throws NullPointerException if either {@code listener} or |
+ * {@code handler} is null. |
+ * |
+ * @deprecated Use {@link #GestureDetector(android.content.Context, |
+ * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead. |
+ */ |
+ @Deprecated |
+ public GestureDetector(OnGestureListener listener, Handler handler) { |
+ this(null, listener, handler); |
+ } |
+ |
+ /** |
+ * Creates a GestureDetector with the supplied listener. |
+ * You may only use this constructor from a UI thread (this is the usual situation). |
+ * @see android.os.Handler#Handler() |
+ * |
+ * @param listener the listener invoked for all the callbacks, this must |
+ * not be null. |
+ * |
+ * @throws NullPointerException if {@code listener} is null. |
+ * |
+ * @deprecated Use {@link #GestureDetector(android.content.Context, |
+ * android.view.GestureDetector.OnGestureListener)} instead. |
+ */ |
+ @Deprecated |
+ public GestureDetector(OnGestureListener listener) { |
+ this(null, listener, null); |
+ } |
+ |
+ /** |
+ * Creates a GestureDetector with the supplied listener. |
+ * You may only use this constructor from a UI thread (this is the usual situation). |
+ * @see android.os.Handler#Handler() |
+ * |
+ * @param context the application's context |
+ * @param listener the listener invoked for all the callbacks, this must |
+ * not be null. |
+ * |
+ * @throws NullPointerException if {@code listener} is null. |
+ */ |
+ public GestureDetector(Context context, OnGestureListener listener) { |
+ this(context, listener, null); |
+ } |
+ |
+ /** |
+ * Creates a GestureDetector with the supplied listener. |
+ * You may only use this constructor from a UI thread (this is the usual situation). |
+ * @see android.os.Handler#Handler() |
+ * |
+ * @param context the application's context |
+ * @param listener the listener invoked for all the callbacks, this must |
+ * not be null. |
+ * @param handler the handler to use |
+ * |
+ * @throws NullPointerException if {@code listener} is null. |
+ */ |
+ public GestureDetector(Context context, OnGestureListener listener, Handler handler) { |
+ if (handler != null) { |
+ mHandler = new GestureHandler(handler); |
+ } else { |
+ mHandler = new GestureHandler(); |
+ } |
+ mListener = listener; |
+ if (listener instanceof OnDoubleTapListener) { |
+ setOnDoubleTapListener((OnDoubleTapListener) listener); |
+ } |
+ init(context); |
+ } |
+ |
+ /** |
+ * Creates a GestureDetector with the supplied listener. |
+ * You may only use this constructor from a UI thread (this is the usual situation). |
+ * @see android.os.Handler#Handler() |
+ * |
+ * @param context the application's context |
+ * @param listener the listener invoked for all the callbacks, this must |
+ * not be null. |
+ * @param handler the handler to use |
+ * |
+ * @throws NullPointerException if {@code listener} is null. |
+ */ |
+ public GestureDetector(Context context, OnGestureListener listener, Handler handler, |
+ boolean unused) { |
+ this(context, listener, handler); |
+ } |
+ |
+ private void init(Context context) { |
+ if (mListener == null) { |
+ throw new NullPointerException("OnGestureListener must not be null"); |
+ } |
+ mIsLongpressEnabled = true; |
+ |
+ // Fallback to support pre-donuts releases |
+ int touchSlop, doubleTapSlop, doubleTapTouchSlop; |
+/* Commented out in Chromium for NDK compliance |
+ if (context == null) { |
+ //noinspection deprecation |
+ touchSlop = ViewConfiguration.getTouchSlop(); |
+ doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this |
+ doubleTapSlop = ViewConfiguration.getDoubleTapSlop(); |
+ //noinspection deprecation |
+ mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); |
+ mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); |
+ } else */ { |
+ final ViewConfiguration configuration = ViewConfiguration.get(context); |
+ touchSlop = configuration.getScaledTouchSlop(); |
+/* Commented out in Chromium for NDK compliance and replaced with the following line. Note that |
+ * ViewConfiguration.TOUCH_SLOP has the same value as DOUBLE_TAP_TOUCH_SLOP in current Android, so |
+ * this doesn't introduce a behavior difference in Android versions <= 4.2. |
+ doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop(); |
+*/ |
+ doubleTapTouchSlop = touchSlop; |
+ doubleTapSlop = configuration.getScaledDoubleTapSlop(); |
+ mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); |
+ mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity(); |
+ } |
+ mTouchSlopSquare = touchSlop * touchSlop; |
+ mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop; |
+ mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; |
+ } |
+ |
+ /** |
+ * Sets the listener which will be called for double-tap and related |
+ * gestures. |
+ * |
+ * @param onDoubleTapListener the listener invoked for all the callbacks, or |
+ * null to stop listening for double-tap gestures. |
+ */ |
+ public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) { |
+ mDoubleTapListener = onDoubleTapListener; |
+ } |
+ |
+ /** |
+ * Set whether longpress is enabled, if this is enabled when a user |
+ * presses and holds down you get a longpress event and nothing further. |
+ * If it's disabled the user can press and hold down and then later |
+ * moved their finger and you will get scroll events. By default |
+ * longpress is enabled. |
+ * |
+ * @param isLongpressEnabled whether longpress should be enabled. |
+ */ |
+ public void setIsLongpressEnabled(boolean isLongpressEnabled) { |
+ mIsLongpressEnabled = isLongpressEnabled; |
+ } |
+ |
+ /** |
+ * @return true if longpress is enabled, else false. |
+ */ |
+ public boolean isLongpressEnabled() { |
+ return mIsLongpressEnabled; |
+ } |
+ |
+ /** |
+ * Analyzes the given motion event and if applicable triggers the |
+ * appropriate callbacks on the {@link OnGestureListener} supplied. |
+ * |
+ * @param ev The current motion event. |
+ * @return true if the {@link OnGestureListener} consumed the event, |
+ * else false. |
+ */ |
+ public boolean onTouchEvent(MotionEvent ev) { |
+/* Commented out in Chromium for NDK compliance |
+ if (mInputEventConsistencyVerifier != null) { |
+ mInputEventConsistencyVerifier.onTouchEvent(ev, 0); |
+ } |
+*/ |
+ |
+ final int action = ev.getAction(); |
+ |
+ if (mVelocityTracker == null) { |
+ mVelocityTracker = VelocityTracker.obtain(); |
+ } |
+ mVelocityTracker.addMovement(ev); |
+ |
+ final boolean pointerUp = |
+ (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP; |
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1; |
+ |
+ // Determine focal point |
+ float sumX = 0, sumY = 0; |
+ final int count = ev.getPointerCount(); |
+ for (int i = 0; i < count; i++) { |
+ if (skipIndex == i) continue; |
+ sumX += ev.getX(i); |
+ sumY += ev.getY(i); |
+ } |
+ final int div = pointerUp ? count - 1 : count; |
+ final float focusX = sumX / div; |
+ final float focusY = sumY / div; |
+ |
+ boolean handled = false; |
+ |
+ switch (action & MotionEvent.ACTION_MASK) { |
+ case MotionEvent.ACTION_POINTER_DOWN: |
+ mDownFocusX = mLastFocusX = focusX; |
+ mDownFocusY = mLastFocusY = focusY; |
+ // Cancel long press and taps |
+ cancelTaps(); |
+ break; |
+ |
+ case MotionEvent.ACTION_POINTER_UP: |
+ mDownFocusX = mLastFocusX = focusX; |
+ mDownFocusY = mLastFocusY = focusY; |
+ |
+ // Check the dot product of current velocities. |
+ // If the pointer that left was opposing another velocity vector, clear. |
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); |
+ final int upIndex = ev.getActionIndex(); |
+ final int id1 = ev.getPointerId(upIndex); |
+ final float x1 = mVelocityTracker.getXVelocity(id1); |
+ final float y1 = mVelocityTracker.getYVelocity(id1); |
+ for (int i = 0; i < count; i++) { |
+ if (i == upIndex) continue; |
+ |
+ final int id2 = ev.getPointerId(i); |
+ final float x = x1 * mVelocityTracker.getXVelocity(id2); |
+ final float y = y1 * mVelocityTracker.getYVelocity(id2); |
+ |
+ final float dot = x + y; |
+ if (dot < 0) { |
+ mVelocityTracker.clear(); |
+ break; |
+ } |
+ } |
+ break; |
+ |
+ case MotionEvent.ACTION_DOWN: |
+ if (mDoubleTapListener != null) { |
+ boolean hadTapMessage = mHandler.hasMessages(TAP); |
+ if (hadTapMessage) mHandler.removeMessages(TAP); |
+ if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage && |
+ isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { |
+ // This is a second tap |
+ mIsDoubleTapping = true; |
+ // Give a callback with the first tap of the double-tap |
+ handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); |
+ // Give a callback with down event of the double-tap |
+ handled |= mDoubleTapListener.onDoubleTapEvent(ev); |
+ } else { |
+ // This is a first tap |
+ mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); |
+ } |
+ } |
+ |
+ mDownFocusX = mLastFocusX = focusX; |
+ mDownFocusY = mLastFocusY = focusY; |
+ if (mCurrentDownEvent != null) { |
+ mCurrentDownEvent.recycle(); |
+ } |
+ mCurrentDownEvent = MotionEvent.obtain(ev); |
+ mAlwaysInTapRegion = true; |
+ mAlwaysInBiggerTapRegion = true; |
+ mStillDown = true; |
+ mInLongPress = false; |
+ |
+ if (mIsLongpressEnabled) { |
+ mHandler.removeMessages(LONG_PRESS); |
+ mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() |
+ + TAP_TIMEOUT + LONGPRESS_TIMEOUT); |
+ } |
+ mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); |
+ handled |= mListener.onDown(ev); |
+ break; |
+ |
+ case MotionEvent.ACTION_MOVE: |
+ if (mInLongPress) { |
+ break; |
+ } |
+ final float scrollX = mLastFocusX - focusX; |
+ final float scrollY = mLastFocusY - focusY; |
+ if (mIsDoubleTapping) { |
+ // Give the move events of the double-tap |
+ handled |= mDoubleTapListener.onDoubleTapEvent(ev); |
+ } else if (mAlwaysInTapRegion) { |
+ final int deltaX = (int) (focusX - mDownFocusX); |
+ final int deltaY = (int) (focusY - mDownFocusY); |
+ int distance = (deltaX * deltaX) + (deltaY * deltaY); |
+ if (distance > mTouchSlopSquare) { |
+ handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); |
+ mLastFocusX = focusX; |
+ mLastFocusY = focusY; |
+ mAlwaysInTapRegion = false; |
+ mHandler.removeMessages(TAP); |
+ mHandler.removeMessages(SHOW_PRESS); |
+ mHandler.removeMessages(LONG_PRESS); |
+ } |
+ if (distance > mDoubleTapTouchSlopSquare) { |
+ mAlwaysInBiggerTapRegion = false; |
+ } |
+ } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { |
+ handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); |
+ mLastFocusX = focusX; |
+ mLastFocusY = focusY; |
+ } |
+ break; |
+ |
+ case MotionEvent.ACTION_UP: |
+ mStillDown = false; |
+ MotionEvent currentUpEvent = MotionEvent.obtain(ev); |
+ if (mIsDoubleTapping) { |
+ // Finally, give the up event of the double-tap |
+ handled |= mDoubleTapListener.onDoubleTapEvent(ev); |
+ } else if (mInLongPress) { |
+ mHandler.removeMessages(TAP); |
+ mInLongPress = false; |
+ } else if (mAlwaysInTapRegion) { |
+ handled = mListener.onSingleTapUp(ev); |
+ } else { |
+ |
+ // A fling must travel the minimum tap distance |
+ final VelocityTracker velocityTracker = mVelocityTracker; |
+ final int pointerId = ev.getPointerId(0); |
+ velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); |
+ final float velocityY = velocityTracker.getYVelocity(pointerId); |
+ final float velocityX = velocityTracker.getXVelocity(pointerId); |
+ |
+ if ((Math.abs(velocityY) > mMinimumFlingVelocity) |
+ || (Math.abs(velocityX) > mMinimumFlingVelocity)){ |
+ handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY); |
+ } |
+ } |
+ if (mPreviousUpEvent != null) { |
+ mPreviousUpEvent.recycle(); |
+ } |
+ // Hold the event we obtained above - listeners may have changed the original. |
+ mPreviousUpEvent = currentUpEvent; |
+ if (mVelocityTracker != null) { |
+ // This may have been cleared when we called out to the |
+ // application above. |
+ mVelocityTracker.recycle(); |
+ mVelocityTracker = null; |
+ } |
+ mIsDoubleTapping = false; |
+ mHandler.removeMessages(SHOW_PRESS); |
+ mHandler.removeMessages(LONG_PRESS); |
+ break; |
+ |
+ case MotionEvent.ACTION_CANCEL: |
+ cancel(); |
+ break; |
+ } |
+ |
+/* Commented out in Chromium for NDK compliance |
+ if (!handled && mInputEventConsistencyVerifier != null) { |
+ mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0); |
+ } |
+*/ |
+ return handled; |
+ } |
+ |
+ private void cancel() { |
+ mHandler.removeMessages(SHOW_PRESS); |
+ mHandler.removeMessages(LONG_PRESS); |
+ mHandler.removeMessages(TAP); |
+ mVelocityTracker.recycle(); |
+ mVelocityTracker = null; |
+ mIsDoubleTapping = false; |
+ mStillDown = false; |
+ mAlwaysInTapRegion = false; |
+ mAlwaysInBiggerTapRegion = false; |
+ if (mInLongPress) { |
+ mInLongPress = false; |
+ } |
+ } |
+ |
+ private void cancelTaps() { |
+ mHandler.removeMessages(SHOW_PRESS); |
+ mHandler.removeMessages(LONG_PRESS); |
+ mHandler.removeMessages(TAP); |
+ mIsDoubleTapping = false; |
+ mAlwaysInTapRegion = false; |
+ mAlwaysInBiggerTapRegion = false; |
+ if (mInLongPress) { |
+ mInLongPress = false; |
+ } |
+ } |
+ |
+ private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp, |
+ MotionEvent secondDown) { |
+ if (!mAlwaysInBiggerTapRegion) { |
+ return false; |
+ } |
+ |
+ if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) { |
+ return false; |
+ } |
+ |
+ int deltaX = (int) firstDown.getX() - (int) secondDown.getX(); |
+ int deltaY = (int) firstDown.getY() - (int) secondDown.getY(); |
+ return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare); |
+ } |
+ |
+ private void dispatchLongPress() { |
+ mHandler.removeMessages(TAP); |
+ mInLongPress = true; |
+ mListener.onLongPress(mCurrentDownEvent); |
+ } |
+} |