19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.view;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powellimport android.content.Context;
20216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powellimport android.os.Build;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Detects various gestures and events using the supplied {@link MotionEvent}s.
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The {@link OnGestureListener} callback will notify users when a particular
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * motion event has occurred. This class should only be used with {@link MotionEvent}s
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * reported via touch (don't use for trackball events).
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * To use this class:
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <ul>
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *  <li>Create an instance of the {@code GestureDetector} for your {@link View}
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your callback
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *          will be executed when the events occur.
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </ul>
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class GestureDetector {
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The listener that is used to notify when gestures occur.
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If you want to listen for all the different gestures then implement
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * this interface. If you only want to listen for a subset it might
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be easier to extend {@link SimpleOnGestureListener}.
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface OnGestureListener {
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when a tap occurs with the down {@link MotionEvent}
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * that triggered it. This will be triggered immediately for
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * every down event. All other events should be preceded by this.
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The down motion event.
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onDown(MotionEvent e);
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The user has performed a down {@link MotionEvent} and not performed
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * a move or up yet. This event is commonly used to provide visual
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * feedback to the user to let them know that their action has been
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * recognized i.e. highlight an element.
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The down motion event
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onShowPress(MotionEvent e);
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when a tap occurs with the up {@link MotionEvent}
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * that triggered it.
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The up motion event that completed the first tap
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the event is consumed, else false
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onSingleTapUp(MotionEvent e);
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * current move {@link MotionEvent}. The distance in x and y is also supplied for
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * convenience.
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e1 The first down motion event that started the scrolling.
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e2 The move motion event that triggered the current onScroll.
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param distanceX The distance along the X axis that has been scrolled since the last
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              call to onScroll. This is NOT the distance between {@code e1}
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              and {@code e2}.
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param distanceY The distance along the Y axis that has been scrolled since the last
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              call to onScroll. This is NOT the distance between {@code e1}
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              and {@code e2}.
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the event is consumed, else false
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when a long press occurs with the initial on down {@link MotionEvent}
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * that trigged it.
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The initial on down motion event that started the longpress.
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onLongPress(MotionEvent e);
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * and the matching up {@link MotionEvent}. The calculated velocity is supplied along
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * the x and y axis in pixels per second.
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e1 The first down motion event that started the fling.
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e2 The move motion event that triggered the current onFling.
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param velocityX The velocity of this fling measured in pixels per second
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              along the x axis.
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param velocityY The velocity of this fling measured in pixels per second
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              along the y axis.
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the event is consumed, else false
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The listener that is used to notify when a double-tap or a confirmed
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * single-tap occur.
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface OnDoubleTapListener {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when a single-tap occurs.
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <p>
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * will only be called after the detector is confident that the user's
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * first tap is not followed by a second tap leading to a double-tap
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * gesture.
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The down motion event of the single-tap.
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the event is consumed, else false
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onSingleTapConfirmed(MotionEvent e);
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when a double-tap occurs.
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The down motion event of the first tap of the double-tap.
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the event is consumed, else false
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onDoubleTap(MotionEvent e);
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Notified when an event within a double-tap gesture occurs, including
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * the down, move, and up events.
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param e The motion event that occurred during the double-tap gesture.
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the event is consumed, else false
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean onDoubleTapEvent(MotionEvent e);
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A convenience class to extend when you only want to listen for a subset
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * of all the gestures. This implements all methods in the
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link OnGestureListener} and {@link OnDoubleTapListener} but does
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * nothing and return {@code false} for all applicable methods.
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onSingleTapUp(MotionEvent e) {
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onLongPress(MotionEvent e) {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onScroll(MotionEvent e1, MotionEvent e2,
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float distanceX, float distanceY) {
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float velocityY) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onShowPress(MotionEvent e) {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onDown(MotionEvent e) {
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onDoubleTap(MotionEvent e) {
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onDoubleTapEvent(MotionEvent e) {
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean onSingleTapConfirmed(MotionEvent e) {
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTouchSlopSquare;
197006fa48bce7759013d9025376cd5167236c434a7Gilles Debunne    private int mDoubleTapTouchSlopSquare;
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDoubleTapSlopSquare;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mMinimumFlingVelocity;
2004296fc4d326447875c26a925f12b3935632f13bbRomain Guy    private int mMaximumFlingVelocity;
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // constants for Message.what used by GestureHandler below
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int SHOW_PRESS = 1;
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int LONG_PRESS = 2;
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAP = 3;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Handler mHandler;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final OnGestureListener mListener;
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnDoubleTapListener mDoubleTapListener;
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mStillDown;
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mInLongPress;
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mAlwaysInTapRegion;
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mAlwaysInBiggerTapRegion;
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MotionEvent mCurrentDownEvent;
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MotionEvent mPreviousUpEvent;
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * True when the user is still touching for the second tap (down, move, and
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * up events). Can only be true if there is a double tap listener attached.
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mIsDoubleTapping;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22905a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    private float mLastFocusX;
23005a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    private float mLastFocusY;
23105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    private float mDownFocusX;
23205a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    private float mDownFocusY;
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mIsLongpressEnabled;
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Determines speed during touch scrolling
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private VelocityTracker mVelocityTracker;
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
24121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown    /**
24221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown     * Consistency verifier for debugging purposes.
24321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown     */
24421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown    private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
24521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
24621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown                    new InputEventConsistencyVerifier(this, 0) : null;
24721bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class GestureHandler extends Handler {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        GestureHandler() {
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super();
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        GestureHandler(Handler handler) {
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(handler.getLooper());
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleMessage(Message msg) {
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (msg.what) {
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case SHOW_PRESS:
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mListener.onShowPress(mCurrentDownEvent);
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case LONG_PRESS:
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dispatchLongPress();
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case TAP:
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If the user's finger is still down, do not count it as a tap
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mDoubleTapListener != null && !mStillDown) {
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            default:
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new RuntimeException("Unknown message " + msg); //never
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a GestureDetector with the supplied listener.
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This variant of the constructor should be used from a non-UI thread
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (as it allows specifying the Handler).
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param listener the listener invoked for all the callbacks, this must
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not be null.
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param handler the handler to use
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if either {@code listener} or
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@code handler} is null.
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @deprecated Use {@link #GestureDetector(android.content.Context,
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *      android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Deprecated
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public GestureDetector(OnGestureListener listener, Handler handler) {
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(null, listener, handler);
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a GestureDetector with the supplied listener.
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * You may only use this constructor from a UI thread (this is the usual situation).
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.os.Handler#Handler()
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param listener the listener invoked for all the callbacks, this must
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not be null.
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if {@code listener} is null.
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @deprecated Use {@link #GestureDetector(android.content.Context,
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *      android.view.GestureDetector.OnGestureListener)} instead.
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Deprecated
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public GestureDetector(OnGestureListener listener) {
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(null, listener, null);
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a GestureDetector with the supplied listener.
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * You may only use this constructor from a UI thread (this is the usual situation).
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.os.Handler#Handler()
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the application's context
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param listener the listener invoked for all the callbacks, this must
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not be null.
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if {@code listener} is null.
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public GestureDetector(Context context, OnGestureListener listener) {
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, listener, null);
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a GestureDetector with the supplied listener.
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * You may only use this constructor from a UI thread (this is the usual situation).
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.os.Handler#Handler()
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the application's context
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param listener the listener invoked for all the callbacks, this must
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not be null.
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param handler the handler to use
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if {@code listener} is null.
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
34705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        if (handler != null) {
34805a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mHandler = new GestureHandler(handler);
34905a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        } else {
35005a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mHandler = new GestureHandler();
35105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        }
35205a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mListener = listener;
35305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        if (listener instanceof OnDoubleTapListener) {
35405a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            setOnDoubleTapListener((OnDoubleTapListener) listener);
35505a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        }
35605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        init(context);
357216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell    }
358216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell
359216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell    /**
360216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * Creates a GestureDetector with the supplied listener.
361216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * You may only use this constructor from a UI thread (this is the usual situation).
362216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * @see android.os.Handler#Handler()
363216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     *
364216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * @param context the application's context
365216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * @param listener the listener invoked for all the callbacks, this must
366216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * not be null.
367216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * @param handler the handler to use
368216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     *
369216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     * @throws NullPointerException if {@code listener} is null.
370216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell     */
371216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell    public GestureDetector(Context context, OnGestureListener listener, Handler handler,
37205a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            boolean unused) {
37305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        this(context, listener, handler);
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
37605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    private void init(Context context) {
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mListener == null) {
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new NullPointerException("OnGestureListener must not be null");
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIsLongpressEnabled = true;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Fallback to support pre-donuts releases
383006fa48bce7759013d9025376cd5167236c434a7Gilles Debunne        int touchSlop, doubleTapSlop, doubleTapTouchSlop;
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (context == null) {
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection deprecation
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            touchSlop = ViewConfiguration.getTouchSlop();
387006fa48bce7759013d9025376cd5167236c434a7Gilles Debunne            doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection deprecation
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
3914296fc4d326447875c26a925f12b3935632f13bbRomain Guy            mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final ViewConfiguration configuration = ViewConfiguration.get(context);
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            touchSlop = configuration.getScaledTouchSlop();
395006fa48bce7759013d9025376cd5167236c434a7Gilles Debunne            doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            doubleTapSlop = configuration.getScaledDoubleTapSlop();
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
3984296fc4d326447875c26a925f12b3935632f13bbRomain Guy            mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTouchSlopSquare = touchSlop * touchSlop;
401006fa48bce7759013d9025376cd5167236c434a7Gilles Debunne        mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the listener which will be called for double-tap and related
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * gestures.
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param onDoubleTapListener the listener invoked for all the callbacks, or
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        null to stop listening for double-tap gestures.
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDoubleTapListener = onDoubleTapListener;
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set whether longpress is enabled, if this is enabled when a user
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * presses and holds down you get a longpress event and nothing further.
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If it's disabled the user can press and hold down and then later
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * moved their finger and you will get scroll events. By default
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * longpress is enabled.
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param isLongpressEnabled whether longpress should be enabled.
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setIsLongpressEnabled(boolean isLongpressEnabled) {
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIsLongpressEnabled = isLongpressEnabled;
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if longpress is enabled, else false.
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isLongpressEnabled() {
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mIsLongpressEnabled;
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Analyzes the given motion event and if applicable triggers the
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * appropriate callbacks on the {@link OnGestureListener} supplied.
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param ev The current motion event.
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if the {@link OnGestureListener} consumed the event,
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *              else false.
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onTouchEvent(MotionEvent ev) {
44521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown        if (mInputEventConsistencyVerifier != null) {
44621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown            mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
44721bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown        }
44821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int action = ev.getAction();
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mVelocityTracker == null) {
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mVelocityTracker = VelocityTracker.obtain();
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVelocityTracker.addMovement(ev);
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
456f90165aeda1a8353c1b5e837b1ef4a818ecbefc5Adam Powell        final boolean pointerUp =
457f90165aeda1a8353c1b5e837b1ef4a818ecbefc5Adam Powell                (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
45805a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
45905a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell
46005a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        // Determine focal point
46105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        float sumX = 0, sumY = 0;
46205a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        final int count = ev.getPointerCount();
46305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        for (int i = 0; i < count; i++) {
46405a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            if (skipIndex == i) continue;
46505a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            sumX += ev.getX(i);
46605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            sumY += ev.getY(i);
46705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        }
46805a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        final int div = pointerUp ? count - 1 : count;
46905a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        final float focusX = sumX / div;
47005a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        final float focusY = sumY / div;
47105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean handled = false;
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
474216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        switch (action & MotionEvent.ACTION_MASK) {
475216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        case MotionEvent.ACTION_POINTER_DOWN:
47605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mDownFocusX = mLastFocusX = focusX;
47705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mDownFocusY = mLastFocusY = focusY;
47805a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            // Cancel long press and taps
47905a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            cancelTaps();
480216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell            break;
481216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell
482216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        case MotionEvent.ACTION_POINTER_UP:
48305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mDownFocusX = mLastFocusX = focusX;
48405a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mDownFocusY = mLastFocusY = focusY;
485cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell
486cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            // Check the dot product of current velocities.
487cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            // If the pointer that left was opposing another velocity vector, clear.
488cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
489cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            final int upIndex = ev.getActionIndex();
490cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            final int id1 = ev.getPointerId(upIndex);
491cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            final float x1 = mVelocityTracker.getXVelocity(id1);
492cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            final float y1 = mVelocityTracker.getYVelocity(id1);
493cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            for (int i = 0; i < count; i++) {
494cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                if (i == upIndex) continue;
495cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell
496cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                final int id2 = ev.getPointerId(i);
497cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                final float x = x1 * mVelocityTracker.getXVelocity(id2);
498cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                final float y = y1 * mVelocityTracker.getYVelocity(id2);
499cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell
500cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                final float dot = x + y;
501cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                if (dot < 0) {
502cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                    mVelocityTracker.clear();
503cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                    break;
504cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell                }
505cd66359ab9d072b18631276ee4431b2e689a870bAdam Powell            }
506216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell            break;
507216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case MotionEvent.ACTION_DOWN:
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mDoubleTapListener != null) {
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean hadTapMessage = mHandler.hasMessages(TAP);
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (hadTapMessage) mHandler.removeMessages(TAP);
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // This is a second tap
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mIsDoubleTapping = true;
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Give a callback with the first tap of the double-tap
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Give a callback with down event of the double-tap
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // This is a first tap
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
52605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mDownFocusX = mLastFocusX = focusX;
52705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mDownFocusY = mLastFocusY = focusY;
52845f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell            if (mCurrentDownEvent != null) {
52945f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell                mCurrentDownEvent.recycle();
53045f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell            }
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCurrentDownEvent = MotionEvent.obtain(ev);
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlwaysInTapRegion = true;
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlwaysInBiggerTapRegion = true;
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStillDown = true;
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInLongPress = false;
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mIsLongpressEnabled) {
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mHandler.removeMessages(LONG_PRESS);
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handled |= mListener.onDown(ev);
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case MotionEvent.ACTION_MOVE:
54705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            if (mInLongPress) {
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
55005a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            final float scrollX = mLastFocusX - focusX;
55105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            final float scrollY = mLastFocusY - focusY;
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mIsDoubleTapping) {
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Give the move events of the double-tap
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mAlwaysInTapRegion) {
55605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                final int deltaX = (int) (focusX - mDownFocusX);
55705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                final int deltaY = (int) (focusY - mDownFocusY);
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int distance = (deltaX * deltaX) + (deltaY * deltaY);
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (distance > mTouchSlopSquare) {
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
56105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                    mLastFocusX = focusX;
56205a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                    mLastFocusY = focusY;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mAlwaysInTapRegion = false;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.removeMessages(TAP);
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.removeMessages(SHOW_PRESS);
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mHandler.removeMessages(LONG_PRESS);
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
568006fa48bce7759013d9025376cd5167236c434a7Gilles Debunne                if (distance > mDoubleTapTouchSlopSquare) {
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mAlwaysInBiggerTapRegion = false;
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
57305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                mLastFocusX = focusX;
57405a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                mLastFocusY = focusY;
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case MotionEvent.ACTION_UP:
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStillDown = false;
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            MotionEvent currentUpEvent = MotionEvent.obtain(ev);
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mIsDoubleTapping) {
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Finally, give the up event of the double-tap
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mInLongPress) {
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mHandler.removeMessages(TAP);
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mInLongPress = false;
587f5b4b98fada53d91c4c2ebeb5a1d33ccc95c94d2The Android Open Source Project            } else if (mAlwaysInTapRegion) {
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                handled = mListener.onSingleTapUp(ev);
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // A fling must travel the minimum tap distance
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final VelocityTracker velocityTracker = mVelocityTracker;
59305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                final int pointerId = ev.getPointerId(0);
5944296fc4d326447875c26a925f12b3935632f13bbRomain Guy                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
59505a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                final float velocityY = velocityTracker.getYVelocity(pointerId);
59605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell                final float velocityX = velocityTracker.getXVelocity(pointerId);
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if ((Math.abs(velocityY) > mMinimumFlingVelocity)
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        || (Math.abs(velocityX) > mMinimumFlingVelocity)){
60045f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell                    handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
60345f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell            if (mPreviousUpEvent != null) {
60445f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell                mPreviousUpEvent.recycle();
60545f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell            }
60645f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell            // Hold the event we obtained above - listeners may have changed the original.
60745f2ca7f7d9fac55aa228e022f46ecbac8712a15Adam Powell            mPreviousUpEvent = currentUpEvent;
6087a83b93e12e4a215b90bfa32a753a5a53525d011Dianne Hackborn            if (mVelocityTracker != null) {
6097a83b93e12e4a215b90bfa32a753a5a53525d011Dianne Hackborn                // This may have been cleared when we called out to the
6107a83b93e12e4a215b90bfa32a753a5a53525d011Dianne Hackborn                // application above.
6117a83b93e12e4a215b90bfa32a753a5a53525d011Dianne Hackborn                mVelocityTracker.recycle();
6127a83b93e12e4a215b90bfa32a753a5a53525d011Dianne Hackborn                mVelocityTracker = null;
6137a83b93e12e4a215b90bfa32a753a5a53525d011Dianne Hackborn            }
614b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project            mIsDoubleTapping = false;
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeMessages(SHOW_PRESS);
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHandler.removeMessages(LONG_PRESS);
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
618bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case MotionEvent.ACTION_CANCEL:
620216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell            cancel();
621bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown            break;
622bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown        }
623bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown
624bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown        if (!handled && mInputEventConsistencyVerifier != null) {
625bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return handled;
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
630216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell    private void cancel() {
631216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mHandler.removeMessages(SHOW_PRESS);
632216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mHandler.removeMessages(LONG_PRESS);
633216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mHandler.removeMessages(TAP);
634216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mVelocityTracker.recycle();
635216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mVelocityTracker = null;
636216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mIsDoubleTapping = false;
637216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        mStillDown = false;
63817921eefe3d2219ee2348face5d650d33db64b06Adam Powell        mAlwaysInTapRegion = false;
63917921eefe3d2219ee2348face5d650d33db64b06Adam Powell        mAlwaysInBiggerTapRegion = false;
640216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        if (mInLongPress) {
641216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell            mInLongPress = false;
642216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell        }
643216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell    }
644216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell
64505a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    private void cancelTaps() {
64605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mHandler.removeMessages(SHOW_PRESS);
64705a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mHandler.removeMessages(LONG_PRESS);
64805a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mHandler.removeMessages(TAP);
64905a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mIsDoubleTapping = false;
65005a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mAlwaysInTapRegion = false;
65105a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        mAlwaysInBiggerTapRegion = false;
65205a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        if (mInLongPress) {
65305a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell            mInLongPress = false;
65405a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell        }
65505a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell    }
65605a1e1f64e06b113a64591be2c28c9f6814490a5Adam Powell
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            MotionEvent secondDown) {
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mAlwaysInBiggerTapRegion) {
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void dispatchLongPress() {
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHandler.removeMessages(TAP);
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInLongPress = true;
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mListener.onLongPress(mCurrentDownEvent);
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
678