blob: 1ccc66fdf63e792b0166c1c6dd298139a72adf1c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.view;
18
Adam Powell216bccf2010-02-01 15:03:17 -080019import android.content.Context;
20import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.os.Handler;
22import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
24/**
25 * Detects various gestures and events using the supplied {@link MotionEvent}s.
26 * The {@link OnGestureListener} callback will notify users when a particular
27 * motion event has occurred. This class should only be used with {@link MotionEvent}s
28 * reported via touch (don't use for trackball events).
29 *
30 * To use this class:
31 * <ul>
32 * <li>Create an instance of the {@code GestureDetector} for your {@link View}
33 * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
34 * {@link #onTouchEvent(MotionEvent)}. The methods defined in your callback
35 * will be executed when the events occur.
36 * </ul>
37 */
38public class GestureDetector {
39 /**
40 * The listener that is used to notify when gestures occur.
41 * If you want to listen for all the different gestures then implement
42 * this interface. If you only want to listen for a subset it might
43 * be easier to extend {@link SimpleOnGestureListener}.
44 */
45 public interface OnGestureListener {
46
47 /**
48 * Notified when a tap occurs with the down {@link MotionEvent}
49 * that triggered it. This will be triggered immediately for
50 * every down event. All other events should be preceded by this.
51 *
52 * @param e The down motion event.
53 */
54 boolean onDown(MotionEvent e);
55
56 /**
57 * The user has performed a down {@link MotionEvent} and not performed
58 * a move or up yet. This event is commonly used to provide visual
59 * feedback to the user to let them know that their action has been
60 * recognized i.e. highlight an element.
61 *
62 * @param e The down motion event
63 */
64 void onShowPress(MotionEvent e);
65
66 /**
67 * Notified when a tap occurs with the up {@link MotionEvent}
68 * that triggered it.
69 *
70 * @param e The up motion event that completed the first tap
71 * @return true if the event is consumed, else false
72 */
73 boolean onSingleTapUp(MotionEvent e);
74
75 /**
76 * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
77 * current move {@link MotionEvent}. The distance in x and y is also supplied for
78 * convenience.
79 *
80 * @param e1 The first down motion event that started the scrolling.
81 * @param e2 The move motion event that triggered the current onScroll.
82 * @param distanceX The distance along the X axis that has been scrolled since the last
83 * call to onScroll. This is NOT the distance between {@code e1}
84 * and {@code e2}.
85 * @param distanceY The distance along the Y axis that has been scrolled since the last
86 * call to onScroll. This is NOT the distance between {@code e1}
87 * and {@code e2}.
88 * @return true if the event is consumed, else false
89 */
90 boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
91
92 /**
93 * Notified when a long press occurs with the initial on down {@link MotionEvent}
94 * that trigged it.
95 *
96 * @param e The initial on down motion event that started the longpress.
97 */
98 void onLongPress(MotionEvent e);
99
100 /**
101 * Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
102 * and the matching up {@link MotionEvent}. The calculated velocity is supplied along
103 * the x and y axis in pixels per second.
104 *
105 * @param e1 The first down motion event that started the fling.
106 * @param e2 The move motion event that triggered the current onFling.
107 * @param velocityX The velocity of this fling measured in pixels per second
108 * along the x axis.
109 * @param velocityY The velocity of this fling measured in pixels per second
110 * along the y axis.
111 * @return true if the event is consumed, else false
112 */
113 boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
114 }
115
116 /**
117 * The listener that is used to notify when a double-tap or a confirmed
118 * single-tap occur.
119 */
120 public interface OnDoubleTapListener {
121 /**
122 * Notified when a single-tap occurs.
123 * <p>
124 * Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this
125 * will only be called after the detector is confident that the user's
126 * first tap is not followed by a second tap leading to a double-tap
127 * gesture.
128 *
129 * @param e The down motion event of the single-tap.
130 * @return true if the event is consumed, else false
131 */
132 boolean onSingleTapConfirmed(MotionEvent e);
133
134 /**
135 * Notified when a double-tap occurs.
136 *
137 * @param e The down motion event of the first tap of the double-tap.
138 * @return true if the event is consumed, else false
139 */
140 boolean onDoubleTap(MotionEvent e);
141
142 /**
143 * Notified when an event within a double-tap gesture occurs, including
144 * the down, move, and up events.
145 *
146 * @param e The motion event that occurred during the double-tap gesture.
147 * @return true if the event is consumed, else false
148 */
149 boolean onDoubleTapEvent(MotionEvent e);
150 }
151
152 /**
153 * A convenience class to extend when you only want to listen for a subset
154 * of all the gestures. This implements all methods in the
155 * {@link OnGestureListener} and {@link OnDoubleTapListener} but does
156 * nothing and return {@code false} for all applicable methods.
157 */
158 public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {
159 public boolean onSingleTapUp(MotionEvent e) {
160 return false;
161 }
162
163 public void onLongPress(MotionEvent e) {
164 }
165
166 public boolean onScroll(MotionEvent e1, MotionEvent e2,
167 float distanceX, float distanceY) {
168 return false;
169 }
170
171 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
172 float velocityY) {
173 return false;
174 }
175
176 public void onShowPress(MotionEvent e) {
177 }
178
179 public boolean onDown(MotionEvent e) {
180 return false;
181 }
182
183 public boolean onDoubleTap(MotionEvent e) {
184 return false;
185 }
186
187 public boolean onDoubleTapEvent(MotionEvent e) {
188 return false;
189 }
190
191 public boolean onSingleTapConfirmed(MotionEvent e) {
192 return false;
193 }
194 }
195
196 // TODO: ViewConfiguration
197 private int mBiggerTouchSlopSquare = 20 * 20;
198
199 private int mTouchSlopSquare;
200 private int mDoubleTapSlopSquare;
201 private int mMinimumFlingVelocity;
Romain Guy4296fc42009-07-06 11:48:52 -0700202 private int mMaximumFlingVelocity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203
204 private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
205 private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
207
208 // constants for Message.what used by GestureHandler below
209 private static final int SHOW_PRESS = 1;
210 private static final int LONG_PRESS = 2;
211 private static final int TAP = 3;
212
213 private final Handler mHandler;
214 private final OnGestureListener mListener;
215 private OnDoubleTapListener mDoubleTapListener;
216
217 private boolean mStillDown;
218 private boolean mInLongPress;
219 private boolean mAlwaysInTapRegion;
220 private boolean mAlwaysInBiggerTapRegion;
221
222 private MotionEvent mCurrentDownEvent;
223 private MotionEvent mPreviousUpEvent;
224
225 /**
226 * True when the user is still touching for the second tap (down, move, and
227 * up events). Can only be true if there is a double tap listener attached.
228 */
229 private boolean mIsDoubleTapping;
230
231 private float mLastMotionY;
232 private float mLastMotionX;
233
234 private boolean mIsLongpressEnabled;
Adam Powell216bccf2010-02-01 15:03:17 -0800235
236 /**
237 * True if we are at a target API level of >= Froyo or the developer can
238 * explicitly set it. If true, input events with > 1 pointer will be ignored
239 * so we can work side by side with multitouch gesture detectors.
240 */
241 private boolean mIgnoreMultitouch;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242
243 /**
244 * Determines speed during touch scrolling
245 */
246 private VelocityTracker mVelocityTracker;
247
Jeff Brown21bc5c92011-02-28 18:27:14 -0800248 /**
249 * Consistency verifier for debugging purposes.
250 */
251 private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
252 InputEventConsistencyVerifier.isInstrumentationEnabled() ?
253 new InputEventConsistencyVerifier(this, 0) : null;
254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 private class GestureHandler extends Handler {
256 GestureHandler() {
257 super();
258 }
259
260 GestureHandler(Handler handler) {
261 super(handler.getLooper());
262 }
263
264 @Override
265 public void handleMessage(Message msg) {
266 switch (msg.what) {
267 case SHOW_PRESS:
268 mListener.onShowPress(mCurrentDownEvent);
269 break;
270
271 case LONG_PRESS:
272 dispatchLongPress();
273 break;
274
275 case TAP:
276 // If the user's finger is still down, do not count it as a tap
277 if (mDoubleTapListener != null && !mStillDown) {
278 mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
279 }
280 break;
281
282 default:
283 throw new RuntimeException("Unknown message " + msg); //never
284 }
285 }
286 }
287
288 /**
289 * Creates a GestureDetector with the supplied listener.
290 * This variant of the constructor should be used from a non-UI thread
291 * (as it allows specifying the Handler).
292 *
293 * @param listener the listener invoked for all the callbacks, this must
294 * not be null.
295 * @param handler the handler to use
296 *
297 * @throws NullPointerException if either {@code listener} or
298 * {@code handler} is null.
299 *
300 * @deprecated Use {@link #GestureDetector(android.content.Context,
301 * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
302 */
303 @Deprecated
304 public GestureDetector(OnGestureListener listener, Handler handler) {
305 this(null, listener, handler);
306 }
307
308 /**
309 * Creates a GestureDetector with the supplied listener.
310 * You may only use this constructor from a UI thread (this is the usual situation).
311 * @see android.os.Handler#Handler()
312 *
313 * @param listener the listener invoked for all the callbacks, this must
314 * not be null.
315 *
316 * @throws NullPointerException if {@code listener} is null.
317 *
318 * @deprecated Use {@link #GestureDetector(android.content.Context,
319 * android.view.GestureDetector.OnGestureListener)} instead.
320 */
321 @Deprecated
322 public GestureDetector(OnGestureListener listener) {
323 this(null, listener, null);
324 }
325
326 /**
327 * Creates a GestureDetector with the supplied listener.
328 * You may only use this constructor from a UI thread (this is the usual situation).
329 * @see android.os.Handler#Handler()
330 *
331 * @param context the application's context
332 * @param listener the listener invoked for all the callbacks, this must
333 * not be null.
334 *
335 * @throws NullPointerException if {@code listener} is null.
336 */
337 public GestureDetector(Context context, OnGestureListener listener) {
338 this(context, listener, null);
339 }
340
341 /**
342 * Creates a GestureDetector with the supplied listener.
343 * You may only use this constructor from a UI thread (this is the usual situation).
344 * @see android.os.Handler#Handler()
345 *
346 * @param context the application's context
347 * @param listener the listener invoked for all the callbacks, this must
348 * not be null.
349 * @param handler the handler to use
350 *
351 * @throws NullPointerException if {@code listener} is null.
352 */
353 public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
Adam Powell216bccf2010-02-01 15:03:17 -0800354 this(context, listener, handler, context != null &&
355 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
356 }
357
358 /**
359 * Creates a GestureDetector with the supplied listener.
360 * You may only use this constructor from a UI thread (this is the usual situation).
361 * @see android.os.Handler#Handler()
362 *
363 * @param context the application's context
364 * @param listener the listener invoked for all the callbacks, this must
365 * not be null.
366 * @param handler the handler to use
367 * @param ignoreMultitouch whether events involving more than one pointer should
368 * be ignored.
369 *
370 * @throws NullPointerException if {@code listener} is null.
371 */
372 public GestureDetector(Context context, OnGestureListener listener, Handler handler,
373 boolean ignoreMultitouch) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 if (handler != null) {
375 mHandler = new GestureHandler(handler);
376 } else {
377 mHandler = new GestureHandler();
378 }
379 mListener = listener;
380 if (listener instanceof OnDoubleTapListener) {
381 setOnDoubleTapListener((OnDoubleTapListener) listener);
382 }
Adam Powell216bccf2010-02-01 15:03:17 -0800383 init(context, ignoreMultitouch);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 }
385
Adam Powell216bccf2010-02-01 15:03:17 -0800386 private void init(Context context, boolean ignoreMultitouch) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 if (mListener == null) {
388 throw new NullPointerException("OnGestureListener must not be null");
389 }
390 mIsLongpressEnabled = true;
Adam Powell216bccf2010-02-01 15:03:17 -0800391 mIgnoreMultitouch = ignoreMultitouch;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392
393 // Fallback to support pre-donuts releases
394 int touchSlop, doubleTapSlop;
395 if (context == null) {
396 //noinspection deprecation
397 touchSlop = ViewConfiguration.getTouchSlop();
398 doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
399 //noinspection deprecation
400 mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
Romain Guy4296fc42009-07-06 11:48:52 -0700401 mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 } else {
403 final ViewConfiguration configuration = ViewConfiguration.get(context);
404 touchSlop = configuration.getScaledTouchSlop();
405 doubleTapSlop = configuration.getScaledDoubleTapSlop();
406 mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
Romain Guy4296fc42009-07-06 11:48:52 -0700407 mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 }
409 mTouchSlopSquare = touchSlop * touchSlop;
410 mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
411 }
412
413 /**
414 * Sets the listener which will be called for double-tap and related
415 * gestures.
416 *
417 * @param onDoubleTapListener the listener invoked for all the callbacks, or
418 * null to stop listening for double-tap gestures.
419 */
420 public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
421 mDoubleTapListener = onDoubleTapListener;
422 }
423
424 /**
425 * Set whether longpress is enabled, if this is enabled when a user
426 * presses and holds down you get a longpress event and nothing further.
427 * If it's disabled the user can press and hold down and then later
428 * moved their finger and you will get scroll events. By default
429 * longpress is enabled.
430 *
431 * @param isLongpressEnabled whether longpress should be enabled.
432 */
433 public void setIsLongpressEnabled(boolean isLongpressEnabled) {
434 mIsLongpressEnabled = isLongpressEnabled;
435 }
436
437 /**
438 * @return true if longpress is enabled, else false.
439 */
440 public boolean isLongpressEnabled() {
441 return mIsLongpressEnabled;
442 }
443
444 /**
445 * Analyzes the given motion event and if applicable triggers the
446 * appropriate callbacks on the {@link OnGestureListener} supplied.
447 *
448 * @param ev The current motion event.
449 * @return true if the {@link OnGestureListener} consumed the event,
450 * else false.
451 */
452 public boolean onTouchEvent(MotionEvent ev) {
Jeff Brown21bc5c92011-02-28 18:27:14 -0800453 if (mInputEventConsistencyVerifier != null) {
454 mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
455 }
456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 final int action = ev.getAction();
458 final float y = ev.getY();
459 final float x = ev.getX();
460
461 if (mVelocityTracker == null) {
462 mVelocityTracker = VelocityTracker.obtain();
463 }
464 mVelocityTracker.addMovement(ev);
465
466 boolean handled = false;
467
Adam Powell216bccf2010-02-01 15:03:17 -0800468 switch (action & MotionEvent.ACTION_MASK) {
469 case MotionEvent.ACTION_POINTER_DOWN:
470 if (mIgnoreMultitouch) {
471 // Multitouch event - abort.
472 cancel();
473 }
474 break;
475
476 case MotionEvent.ACTION_POINTER_UP:
477 // Ending a multitouch gesture and going back to 1 finger
478 if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
Dianne Hackbornb125dc52010-02-12 15:52:09 -0800479 int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
480 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
481 mLastMotionX = ev.getX(index);
482 mLastMotionY = ev.getY(index);
Adam Powell216bccf2010-02-01 15:03:17 -0800483 mVelocityTracker.recycle();
484 mVelocityTracker = VelocityTracker.obtain();
485 }
486 break;
487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 case MotionEvent.ACTION_DOWN:
489 if (mDoubleTapListener != null) {
490 boolean hadTapMessage = mHandler.hasMessages(TAP);
491 if (hadTapMessage) mHandler.removeMessages(TAP);
492 if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
493 isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
494 // This is a second tap
495 mIsDoubleTapping = true;
496 // Give a callback with the first tap of the double-tap
497 handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
498 // Give a callback with down event of the double-tap
499 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
500 } else {
501 // This is a first tap
502 mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
503 }
504 }
505
506 mLastMotionX = x;
507 mLastMotionY = y;
Adam Powell45f2ca72010-02-18 11:35:13 -0800508 if (mCurrentDownEvent != null) {
509 mCurrentDownEvent.recycle();
510 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 mCurrentDownEvent = MotionEvent.obtain(ev);
512 mAlwaysInTapRegion = true;
513 mAlwaysInBiggerTapRegion = true;
514 mStillDown = true;
515 mInLongPress = false;
516
517 if (mIsLongpressEnabled) {
518 mHandler.removeMessages(LONG_PRESS);
519 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
520 + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
521 }
522 mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
523 handled |= mListener.onDown(ev);
524 break;
525
526 case MotionEvent.ACTION_MOVE:
Adam Powell216bccf2010-02-01 15:03:17 -0800527 if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 break;
529 }
530 final float scrollX = mLastMotionX - x;
531 final float scrollY = mLastMotionY - y;
532 if (mIsDoubleTapping) {
533 // Give the move events of the double-tap
534 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
535 } else if (mAlwaysInTapRegion) {
536 final int deltaX = (int) (x - mCurrentDownEvent.getX());
537 final int deltaY = (int) (y - mCurrentDownEvent.getY());
538 int distance = (deltaX * deltaX) + (deltaY * deltaY);
539 if (distance > mTouchSlopSquare) {
540 handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
541 mLastMotionX = x;
542 mLastMotionY = y;
543 mAlwaysInTapRegion = false;
544 mHandler.removeMessages(TAP);
545 mHandler.removeMessages(SHOW_PRESS);
546 mHandler.removeMessages(LONG_PRESS);
547 }
548 if (distance > mBiggerTouchSlopSquare) {
549 mAlwaysInBiggerTapRegion = false;
550 }
551 } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
552 handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
553 mLastMotionX = x;
554 mLastMotionY = y;
555 }
556 break;
557
558 case MotionEvent.ACTION_UP:
559 mStillDown = false;
560 MotionEvent currentUpEvent = MotionEvent.obtain(ev);
561 if (mIsDoubleTapping) {
562 // Finally, give the up event of the double-tap
563 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 } else if (mInLongPress) {
565 mHandler.removeMessages(TAP);
566 mInLongPress = false;
The Android Open Source Projectf5b4b982009-03-05 20:00:43 -0800567 } else if (mAlwaysInTapRegion) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 handled = mListener.onSingleTapUp(ev);
569 } else {
570
571 // A fling must travel the minimum tap distance
572 final VelocityTracker velocityTracker = mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700573 velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 final float velocityY = velocityTracker.getYVelocity();
575 final float velocityX = velocityTracker.getXVelocity();
576
577 if ((Math.abs(velocityY) > mMinimumFlingVelocity)
578 || (Math.abs(velocityX) > mMinimumFlingVelocity)){
Adam Powell45f2ca72010-02-18 11:35:13 -0800579 handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 }
581 }
Adam Powell45f2ca72010-02-18 11:35:13 -0800582 if (mPreviousUpEvent != null) {
583 mPreviousUpEvent.recycle();
584 }
585 // Hold the event we obtained above - listeners may have changed the original.
586 mPreviousUpEvent = currentUpEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 mVelocityTracker.recycle();
588 mVelocityTracker = null;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700589 mIsDoubleTapping = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 mHandler.removeMessages(SHOW_PRESS);
591 mHandler.removeMessages(LONG_PRESS);
592 break;
Jeff Brownbbdc50b2011-04-19 23:46:52 -0700593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 case MotionEvent.ACTION_CANCEL:
Adam Powell216bccf2010-02-01 15:03:17 -0800595 cancel();
Jeff Brownbbdc50b2011-04-19 23:46:52 -0700596 break;
597 }
598
599 if (!handled && mInputEventConsistencyVerifier != null) {
600 mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602 return handled;
603 }
604
Adam Powell216bccf2010-02-01 15:03:17 -0800605 private void cancel() {
606 mHandler.removeMessages(SHOW_PRESS);
607 mHandler.removeMessages(LONG_PRESS);
608 mHandler.removeMessages(TAP);
609 mVelocityTracker.recycle();
610 mVelocityTracker = null;
611 mIsDoubleTapping = false;
612 mStillDown = false;
613 if (mInLongPress) {
614 mInLongPress = false;
615 }
616 }
617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
619 MotionEvent secondDown) {
620 if (!mAlwaysInBiggerTapRegion) {
621 return false;
622 }
623
624 if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
625 return false;
626 }
627
628 int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
629 int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
630 return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
631 }
632
633 private void dispatchLongPress() {
634 mHandler.removeMessages(TAP);
635 mInLongPress = true;
636 mListener.onLongPress(mCurrentDownEvent);
637 }
638}