Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / third_party / GestureDetector.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // This is a copy of Android GestureDetector.java from AOSP 4.2.  We include it in Chromium in order
18 // to have JB-style behavior on ICS devices.  Some edits for NDK v16 compliance were needed; they're
19 // noted explicitly below.
20
21 // New imports in Chromium for NDK compliance.
22 package org.chromium.content.browser.third_party;
23 import android.view.MotionEvent;
24 import android.view.VelocityTracker;
25 import android.view.ViewConfiguration;
26
27 /*  Commented out in Chromium for NDK compliance
28 package android.view;
29 */
30
31 import android.content.Context;
32 import android.os.Build;
33 import android.os.Handler;
34 import android.os.Message;
35
36 /**
37  * Detects various gestures and events using the supplied {@link MotionEvent}s.
38  * The {@link OnGestureListener} callback will notify users when a particular
39  * motion event has occurred. This class should only be used with {@link MotionEvent}s
40  * reported via touch (don't use for trackball events).
41  *
42  * To use this class:
43  * <ul>
44  *  <li>Create an instance of the {@code GestureDetector} for your {@link View}
45  *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
46  *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your callback
47  *          will be executed when the events occur.
48  * </ul>
49  */
50 public class GestureDetector {
51     /**
52      * The listener that is used to notify when gestures occur.
53      * If you want to listen for all the different gestures then implement
54      * this interface. If you only want to listen for a subset it might
55      * be easier to extend {@link SimpleOnGestureListener}.
56      */
57     public interface OnGestureListener {
58
59         /**
60          * Notified when a tap occurs with the down {@link MotionEvent}
61          * that triggered it. This will be triggered immediately for
62          * every down event. All other events should be preceded by this.
63          *
64          * @param e The down motion event.
65          */
66         boolean onDown(MotionEvent e);
67
68         /**
69          * The user has performed a down {@link MotionEvent} and not performed
70          * a move or up yet. This event is commonly used to provide visual
71          * feedback to the user to let them know that their action has been
72          * recognized i.e. highlight an element.
73          *
74          * @param e The down motion event
75          */
76         void onShowPress(MotionEvent e);
77
78         /**
79          * Notified when a tap occurs with the up {@link MotionEvent}
80          * that triggered it.
81          *
82          * @param e The up motion event that completed the first tap
83          * @return true if the event is consumed, else false
84          */
85         boolean onSingleTapUp(MotionEvent e);
86
87         /**
88          * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
89          * current move {@link MotionEvent}. The distance in x and y is also supplied for
90          * convenience.
91          *
92          * @param e1 The first down motion event that started the scrolling.
93          * @param e2 The move motion event that triggered the current onScroll.
94          * @param distanceX The distance along the X axis that has been scrolled since the last
95          *              call to onScroll. This is NOT the distance between {@code e1}
96          *              and {@code e2}.
97          * @param distanceY The distance along the Y axis that has been scrolled since the last
98          *              call to onScroll. This is NOT the distance between {@code e1}
99          *              and {@code e2}.
100          * @return true if the event is consumed, else false
101          */
102         boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
103
104         /**
105          * Notified when a long press occurs with the initial on down {@link MotionEvent}
106          * that trigged it.
107          *
108          * @param e The initial on down motion event that started the longpress.
109          * @return true if the event is consumed, else false
110          */
111         boolean onLongPress(MotionEvent e);
112
113         /**
114          * Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
115          * and the matching up {@link MotionEvent}. The calculated velocity is supplied along
116          * the x and y axis in pixels per second.
117          *
118          * @param e1 The first down motion event that started the fling.
119          * @param e2 The move motion event that triggered the current onFling.
120          * @param velocityX The velocity of this fling measured in pixels per second
121          *              along the x axis.
122          * @param velocityY The velocity of this fling measured in pixels per second
123          *              along the y axis.
124          * @return true if the event is consumed, else false
125          */
126         boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
127     }
128
129     /**
130      * The listener that is used to notify when a double-tap or a confirmed
131      * single-tap occur.
132      */
133     public interface OnDoubleTapListener {
134         /**
135          * Notified when a single-tap occurs.
136          * <p>
137          * Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this
138          * will only be called after the detector is confident that the user's
139          * first tap is not followed by a second tap leading to a double-tap
140          * gesture.
141          *
142          * @param e The down motion event of the single-tap.
143          * @return true if the event is consumed, else false
144          */
145         boolean onSingleTapConfirmed(MotionEvent e);
146  
147         /**
148          * Notified when a double-tap occurs.
149          *
150          * @param e The down motion event of the first tap of the double-tap.
151          * @return true if the event is consumed, else false
152          */
153         boolean onDoubleTap(MotionEvent e);
154
155         /**
156          * Notified when an event within a double-tap gesture occurs, including
157          * the down, move, and up events.
158          *
159          * @param e The motion event that occurred during the double-tap gesture.
160          * @return true if the event is consumed, else false
161          */
162         boolean onDoubleTapEvent(MotionEvent e);
163     }
164
165     /**
166      * A convenience class to extend when you only want to listen for a subset
167      * of all the gestures. This implements all methods in the
168      * {@link OnGestureListener} and {@link OnDoubleTapListener} but does
169      * nothing and return {@code false} for all applicable methods.
170      */
171     public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {
172         public boolean onSingleTapUp(MotionEvent e) {
173             return false;
174         }
175
176         public boolean onLongPress(MotionEvent e) {
177             return false;
178         }
179
180         public boolean onScroll(MotionEvent e1, MotionEvent e2,
181                 float distanceX, float distanceY) {
182             return false;
183         }
184
185         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
186                 float velocityY) {
187             return false;
188         }
189
190         public void onShowPress(MotionEvent e) {
191         }
192
193         public boolean onDown(MotionEvent e) {
194             return false;
195         }
196
197         public boolean onDoubleTap(MotionEvent e) {
198             return false;
199         }
200
201         public boolean onDoubleTapEvent(MotionEvent e) {
202             return false;
203         }
204
205         public boolean onSingleTapConfirmed(MotionEvent e) {
206             return false;
207         }
208     }
209
210     private int mTouchSlopSquare;
211     private int mDoubleTapTouchSlopSquare;
212     private int mDoubleTapSlopSquare;
213     private int mMinimumFlingVelocity;
214     private int mMaximumFlingVelocity;
215
216     private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
217     private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
218     private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
219
220     // constants for Message.what used by GestureHandler below
221     private static final int SHOW_PRESS = 1;
222     private static final int LONG_PRESS = 2;
223     private static final int TAP = 3;
224
225     private final Handler mHandler;
226     private final OnGestureListener mListener;
227     private OnDoubleTapListener mDoubleTapListener;
228
229     private boolean mStillDown;
230     private boolean mDeferConfirmSingleTap;
231     private boolean mInLongPress;
232     private boolean mAlwaysInTapRegion;
233     private boolean mAlwaysInBiggerTapRegion;
234
235     private MotionEvent mCurrentDownEvent;
236     private MotionEvent mPreviousUpEvent;
237
238     /**
239      * True when the user is still touching for the second tap (down, move, and
240      * up events). Can only be true if there is a double tap listener attached.
241      */
242     private boolean mIsDoubleTapping;
243
244     private float mLastFocusX;
245     private float mLastFocusY;
246     private float mDownFocusX;
247     private float mDownFocusY;
248
249     private boolean mIsLongpressEnabled;
250
251     /**
252      * Determines speed during touch scrolling
253      */
254     private VelocityTracker mVelocityTracker;
255
256     /**
257      * Consistency verifier for debugging purposes.
258      */
259 /*  Commented out in Chromium for NDK compliance
260     private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
261             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
262                     new InputEventConsistencyVerifier(this, 0) : null;
263 */
264
265     private class GestureHandler extends Handler {
266         GestureHandler() {
267             super();
268         }
269
270         GestureHandler(Handler handler) {
271             super(handler.getLooper());
272         }
273
274         @Override
275         public void handleMessage(Message msg) {
276             switch (msg.what) {
277             case SHOW_PRESS:
278                 mListener.onShowPress(mCurrentDownEvent);
279                 break;
280                 
281             case LONG_PRESS:
282                 dispatchLongPress();
283                 break;
284                 
285             case TAP:
286                 // If the user's finger is still down, do not count it as a tap
287                 if (mDoubleTapListener != null) {
288                     if (!mStillDown) {
289                         mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
290                     } else {
291                         mDeferConfirmSingleTap = true;
292                     }
293                 }
294                 break;
295
296             default:
297                 throw new RuntimeException("Unknown message " + msg); //never
298             }
299         }
300     }
301
302     /**
303      * Creates a GestureDetector with the supplied listener.
304      * This variant of the constructor should be used from a non-UI thread 
305      * (as it allows specifying the Handler).
306      * 
307      * @param listener the listener invoked for all the callbacks, this must
308      * not be null.
309      * @param handler the handler to use
310      *
311      * @throws NullPointerException if either {@code listener} or
312      * {@code handler} is null.
313      *
314      * @deprecated Use {@link #GestureDetector(android.content.Context,
315      *      android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
316      */
317     @Deprecated
318     public GestureDetector(OnGestureListener listener, Handler handler) {
319         this(null, listener, handler);
320     }
321
322     /**
323      * Creates a GestureDetector with the supplied listener.
324      * You may only use this constructor from a UI thread (this is the usual situation).
325      * @see android.os.Handler#Handler()
326      * 
327      * @param listener the listener invoked for all the callbacks, this must
328      * not be null.
329      * 
330      * @throws NullPointerException if {@code listener} is null.
331      *
332      * @deprecated Use {@link #GestureDetector(android.content.Context,
333      *      android.view.GestureDetector.OnGestureListener)} instead.
334      */
335     @Deprecated
336     public GestureDetector(OnGestureListener listener) {
337         this(null, listener, null);
338     }
339
340     /**
341      * Creates a GestureDetector with the supplied listener.
342      * You may only use this constructor from a UI thread (this is the usual situation).
343      * @see android.os.Handler#Handler()
344      *
345      * @param context the application's context
346      * @param listener the listener invoked for all the callbacks, this must
347      * not be null.
348      *
349      * @throws NullPointerException if {@code listener} is null.
350      */
351     public GestureDetector(Context context, OnGestureListener listener) {
352         this(context, listener, null);
353     }
354
355     /**
356      * Creates a GestureDetector with the supplied listener.
357      * You may only use this constructor from a UI thread (this is the usual situation).
358      * @see android.os.Handler#Handler()
359      *
360      * @param context the application's context
361      * @param listener the listener invoked for all the callbacks, this must
362      * not be null.
363      * @param handler the handler to use     
364      *
365      * @throws NullPointerException if {@code listener} is null.
366      */
367     public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
368         if (handler != null) {
369             mHandler = new GestureHandler(handler);
370         } else {
371             mHandler = new GestureHandler();
372         }
373         mListener = listener;
374         if (listener instanceof OnDoubleTapListener) {
375             setOnDoubleTapListener((OnDoubleTapListener) listener);
376         }
377         init(context);
378     }
379     
380     /**
381      * Creates a GestureDetector with the supplied listener.
382      * You may only use this constructor from a UI thread (this is the usual situation).
383      * @see android.os.Handler#Handler()
384      *
385      * @param context the application's context
386      * @param listener the listener invoked for all the callbacks, this must
387      * not be null.
388      * @param handler the handler to use
389      *
390      * @throws NullPointerException if {@code listener} is null.
391      */
392     public GestureDetector(Context context, OnGestureListener listener, Handler handler,
393             boolean unused) {
394         this(context, listener, handler);
395     }
396
397     private void init(Context context) {
398         if (mListener == null) {
399             throw new NullPointerException("OnGestureListener must not be null");
400         }
401         mIsLongpressEnabled = true;
402
403         // Fallback to support pre-donuts releases
404         int touchSlop, doubleTapSlop, doubleTapTouchSlop;
405 /*  Commented out in Chromium for NDK compliance
406         if (context == null) {
407             //noinspection deprecation
408             touchSlop = ViewConfiguration.getTouchSlop();
409             doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
410             doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
411             //noinspection deprecation
412             mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
413             mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
414         } else */ {
415             final ViewConfiguration configuration = ViewConfiguration.get(context);
416             touchSlop = configuration.getScaledTouchSlop();
417 /*  Commented out in Chromium for NDK compliance and replaced with the following line.  Note that
418  *  ViewConfiguration.TOUCH_SLOP has the same value as DOUBLE_TAP_TOUCH_SLOP in current Android, so
419  *  this doesn't introduce a behavior difference in Android versions <= 4.2.
420             doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
421 */
422             doubleTapTouchSlop = touchSlop;
423             doubleTapSlop = configuration.getScaledDoubleTapSlop();
424             mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
425             mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
426         }
427         mTouchSlopSquare = touchSlop * touchSlop;
428         mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
429         mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
430     }
431
432     /**
433      * Sets the listener which will be called for double-tap and related
434      * gestures.
435      * 
436      * @param onDoubleTapListener the listener invoked for all the callbacks, or
437      *        null to stop listening for double-tap gestures.
438      */
439     public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
440         mDoubleTapListener = onDoubleTapListener;
441     }
442
443     /**
444      * Set whether longpress is enabled, if this is enabled when a user
445      * presses and holds down you get a longpress event and nothing further.
446      * If it's disabled the user can press and hold down and then later
447      * moved their finger and you will get scroll events. By default
448      * longpress is enabled.
449      *
450      * @param isLongpressEnabled whether longpress should be enabled.
451      */
452     public void setIsLongpressEnabled(boolean isLongpressEnabled) {
453         mIsLongpressEnabled = isLongpressEnabled;
454     }
455
456     /**
457      * @return true if longpress is enabled, else false.
458      */
459     public boolean isLongpressEnabled() {
460         return mIsLongpressEnabled;
461     }
462
463     /**
464      * Analyzes the given motion event and if applicable triggers the
465      * appropriate callbacks on the {@link OnGestureListener} supplied.
466      *
467      * @param ev The current motion event.
468      * @return true if the {@link OnGestureListener} consumed the event,
469      *              else false.
470      */
471     public boolean onTouchEvent(MotionEvent ev) {
472 /*  Commented out in Chromium for NDK compliance
473         if (mInputEventConsistencyVerifier != null) {
474             mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
475         }
476 */
477
478         final int action = ev.getAction();
479
480         if (mVelocityTracker == null) {
481             mVelocityTracker = VelocityTracker.obtain();
482         }
483         mVelocityTracker.addMovement(ev);
484
485         final boolean pointerUp =
486                 (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
487         final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
488
489         // Determine focal point
490         float sumX = 0, sumY = 0;
491         final int count = ev.getPointerCount();
492         for (int i = 0; i < count; i++) {
493             if (skipIndex == i) continue;
494             sumX += ev.getX(i);
495             sumY += ev.getY(i);
496         }
497         final int div = pointerUp ? count - 1 : count;
498         final float focusX = sumX / div;
499         final float focusY = sumY / div;
500
501         boolean handled = false;
502
503         switch (action & MotionEvent.ACTION_MASK) {
504         case MotionEvent.ACTION_POINTER_DOWN:
505             mDownFocusX = mLastFocusX = focusX;
506             mDownFocusY = mLastFocusY = focusY;
507             // Cancel long press and taps
508             cancelTaps();
509             break;
510
511         case MotionEvent.ACTION_POINTER_UP:
512             mDownFocusX = mLastFocusX = focusX;
513             mDownFocusY = mLastFocusY = focusY;
514
515             // Check the dot product of current velocities.
516             // If the pointer that left was opposing another velocity vector, clear.
517             mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
518             final int upIndex = ev.getActionIndex();
519             final int id1 = ev.getPointerId(upIndex);
520             final float x1 = mVelocityTracker.getXVelocity(id1);
521             final float y1 = mVelocityTracker.getYVelocity(id1);
522             for (int i = 0; i < count; i++) {
523                 if (i == upIndex) continue;
524
525                 final int id2 = ev.getPointerId(i);
526                 final float x = x1 * mVelocityTracker.getXVelocity(id2);
527                 final float y = y1 * mVelocityTracker.getYVelocity(id2);
528
529                 final float dot = x + y;
530                 if (dot < 0) {
531                     mVelocityTracker.clear();
532                     break;
533                 }
534             }
535             break;
536
537         case MotionEvent.ACTION_DOWN:
538             if (mDoubleTapListener != null) {
539                 boolean hadTapMessage = mHandler.hasMessages(TAP);
540                 if (hadTapMessage) mHandler.removeMessages(TAP);
541                 if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
542                         isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
543                     // This is a second tap
544                     mIsDoubleTapping = true;
545                     // Give a callback with the first tap of the double-tap
546                     handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
547                     // Give a callback with down event of the double-tap
548                     handled |= mDoubleTapListener.onDoubleTapEvent(ev);
549                 } else {
550                     // This is a first tap
551                     mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
552                 }
553             }
554
555             mDownFocusX = mLastFocusX = focusX;
556             mDownFocusY = mLastFocusY = focusY;
557             if (mCurrentDownEvent != null) {
558                 mCurrentDownEvent.recycle();
559             }
560             mCurrentDownEvent = MotionEvent.obtain(ev);
561             mAlwaysInTapRegion = true;
562             mAlwaysInBiggerTapRegion = true;
563             mStillDown = true;
564             mInLongPress = false;
565             mDeferConfirmSingleTap = false;
566
567             if (mIsLongpressEnabled) {
568                 mHandler.removeMessages(LONG_PRESS);
569                 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
570                         + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
571             }
572             mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
573             handled |= mListener.onDown(ev);
574             break;
575
576         case MotionEvent.ACTION_MOVE:
577             if (mInLongPress) {
578                 break;
579             }
580             final float scrollX = mLastFocusX - focusX;
581             final float scrollY = mLastFocusY - focusY;
582             if (mIsDoubleTapping) {
583                 // Give the move events of the double-tap
584                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
585             } else if (mAlwaysInTapRegion) {
586                 final int deltaX = (int) (focusX - mDownFocusX);
587                 final int deltaY = (int) (focusY - mDownFocusY);
588                 int distance = (deltaX * deltaX) + (deltaY * deltaY);
589                 if (distance > mTouchSlopSquare) {
590                     handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
591                     mLastFocusX = focusX;
592                     mLastFocusY = focusY;
593                     mAlwaysInTapRegion = false;
594                     mHandler.removeMessages(TAP);
595                     mHandler.removeMessages(SHOW_PRESS);
596                     mHandler.removeMessages(LONG_PRESS);
597                 }
598                 if (distance > mDoubleTapTouchSlopSquare) {
599                     mAlwaysInBiggerTapRegion = false;
600                 }
601             } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
602                 handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
603                 mLastFocusX = focusX;
604                 mLastFocusY = focusY;
605             }
606             break;
607
608         case MotionEvent.ACTION_UP:
609             mStillDown = false;
610             MotionEvent currentUpEvent = MotionEvent.obtain(ev);
611             if (mIsDoubleTapping) {
612                 // Finally, give the up event of the double-tap
613                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
614             } else if (mInLongPress) {
615                 mHandler.removeMessages(TAP);
616                 mInLongPress = false;
617             } else if (mAlwaysInTapRegion) {
618                 handled = mListener.onSingleTapUp(ev);
619                 if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
620                     mDoubleTapListener.onSingleTapConfirmed(ev);
621                 }
622             } else {
623
624                 // A fling must travel the minimum tap distance
625                 final VelocityTracker velocityTracker = mVelocityTracker;
626                 final int pointerId = ev.getPointerId(0);
627                 velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
628                 final float velocityY = velocityTracker.getYVelocity(pointerId);
629                 final float velocityX = velocityTracker.getXVelocity(pointerId);
630
631                 if ((Math.abs(velocityY) > mMinimumFlingVelocity)
632                         || (Math.abs(velocityX) > mMinimumFlingVelocity)){
633                     handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
634                 }
635             }
636             if (mPreviousUpEvent != null) {
637                 mPreviousUpEvent.recycle();
638             }
639             // Hold the event we obtained above - listeners may have changed the original.
640             mPreviousUpEvent = currentUpEvent;
641             if (mVelocityTracker != null) {
642                 // This may have been cleared when we called out to the
643                 // application above.
644                 mVelocityTracker.recycle();
645                 mVelocityTracker = null;
646             }
647             mIsDoubleTapping = false;
648             mDeferConfirmSingleTap = false;
649             mHandler.removeMessages(SHOW_PRESS);
650             mHandler.removeMessages(LONG_PRESS);
651             break;
652
653         case MotionEvent.ACTION_CANCEL:
654             cancel();
655             break;
656         }
657
658 /*  Commented out in Chromium for NDK compliance
659         if (!handled && mInputEventConsistencyVerifier != null) {
660             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
661         }
662 */
663         return handled;
664     }
665
666     private void cancel() {
667         mHandler.removeMessages(SHOW_PRESS);
668         mHandler.removeMessages(LONG_PRESS);
669         mHandler.removeMessages(TAP);
670         mVelocityTracker.recycle();
671         mVelocityTracker = null;
672         mIsDoubleTapping = false;
673         mStillDown = false;
674         mAlwaysInTapRegion = false;
675         mAlwaysInBiggerTapRegion = false;
676         mDeferConfirmSingleTap = false;
677         if (mInLongPress) {
678             mInLongPress = false;
679         }
680     }
681
682     private void cancelTaps() {
683         mHandler.removeMessages(SHOW_PRESS);
684         mHandler.removeMessages(LONG_PRESS);
685         mHandler.removeMessages(TAP);
686         mIsDoubleTapping = false;
687         mAlwaysInTapRegion = false;
688         mAlwaysInBiggerTapRegion = false;
689         mDeferConfirmSingleTap = false;
690         if (mInLongPress) {
691             mInLongPress = false;
692         }
693     }
694
695     private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
696             MotionEvent secondDown) {
697         if (!mAlwaysInBiggerTapRegion) {
698             return false;
699         }
700
701         if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
702             return false;
703         }
704
705         int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
706         int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
707         return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
708     }
709
710     private void dispatchLongPress() {
711         mHandler.removeMessages(TAP);
712         mDeferConfirmSingleTap = false;
713 /*  Changed in Chromium to allow scrolling after a longpress.
714         mInLongPress = true;
715         mListener.onLongPress(mCurrentDownEvent);
716 */
717         mInLongPress = mListener.onLongPress(mCurrentDownEvent);
718     }
719 }