11ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
31ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *
41ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
51ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * you may not use this file except in compliance with the License.
61ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * You may obtain a copy of the License at
71ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *
81ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
91ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *
101ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * Unless required by applicable law or agreed to in writing, software
111ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
121ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * See the License for the specific language governing permissions and
141ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * limitations under the License.
151ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell */
161ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.view;
181ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
191ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.content.Context;
201ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.os.Build;
211ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.os.Handler;
221ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.os.Message;
231ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.GestureDetector;
241ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.GestureDetector.OnDoubleTapListener;
251ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.GestureDetector.OnGestureListener;
261ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.MotionEvent;
271ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.VelocityTracker;
281ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.View;
291ce805e30800bf2852fa5421b7277a18e089ee31Adam Powellimport android.view.ViewConfiguration;
301ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
311ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell/**
321ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * Detects various gestures and events using the supplied {@link MotionEvent}s.
331ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * The {@link OnGestureListener} callback will notify users when a particular
341ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * motion event has occurred. This class should only be used with {@link MotionEvent}s
351ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * reported via touch (don't use for trackball events).
361ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *
371ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * <p>This compatibility implementation of the framework's GestureDetector guarantees
381ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * the newer focal point scrolling behavior from Jellybean MR1 on all platform versions.</p>
391ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *
401ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * To use this class:
411ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * <ul>
421ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *  <li>Create an instance of the {@code GestureDetectorCompat} for your {@link View}
431ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
441ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your callback
451ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell *          will be executed when the events occur.
461ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell * </ul>
471ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell */
48c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class GestureDetectorCompat {
491ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    interface GestureDetectorCompatImpl {
501ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        boolean isLongpressEnabled();
511ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        boolean onTouchEvent(MotionEvent ev);
521ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        void setIsLongpressEnabled(boolean enabled);
531ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        void setOnDoubleTapListener(OnDoubleTapListener listener);
541ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
551ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
561ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    static class GestureDetectorCompatImplBase implements GestureDetectorCompatImpl {
571ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private int mTouchSlopSquare;
581ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private int mDoubleTapSlopSquare;
591ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private int mMinimumFlingVelocity;
601ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private int mMaximumFlingVelocity;
611ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
621ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
631ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
641ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
651ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
661ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        // constants for Message.what used by GestureHandler below
671ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private static final int SHOW_PRESS = 1;
681ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private static final int LONG_PRESS = 2;
691ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private static final int TAP = 3;
701ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
711ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private final Handler mHandler;
72552766fa685c63ad760c92239faaba12e6ad51f1Aurimas Liutikas        final OnGestureListener mListener;
73552766fa685c63ad760c92239faaba12e6ad51f1Aurimas Liutikas        OnDoubleTapListener mDoubleTapListener;
741ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
75552766fa685c63ad760c92239faaba12e6ad51f1Aurimas Liutikas        boolean mStillDown;
76552766fa685c63ad760c92239faaba12e6ad51f1Aurimas Liutikas        boolean mDeferConfirmSingleTap;
771ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private boolean mInLongPress;
781ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private boolean mAlwaysInTapRegion;
791ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private boolean mAlwaysInBiggerTapRegion;
801ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
81552766fa685c63ad760c92239faaba12e6ad51f1Aurimas Liutikas        MotionEvent mCurrentDownEvent;
821ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private MotionEvent mPreviousUpEvent;
831ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
841ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
851ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * True when the user is still touching for the second tap (down, move, and
861ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * up events). Can only be true if there is a double tap listener attached.
871ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
881ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private boolean mIsDoubleTapping;
891ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
901ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private float mLastFocusX;
911ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private float mLastFocusY;
921ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private float mDownFocusX;
931ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private float mDownFocusY;
941ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
951ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private boolean mIsLongpressEnabled;
961ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
971ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
981ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * Determines speed during touch scrolling
991ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
1001ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private VelocityTracker mVelocityTracker;
1011ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1021ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private class GestureHandler extends Handler {
1031ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            GestureHandler() {
1041ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                super();
1051ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1061ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1071ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            GestureHandler(Handler handler) {
1081ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                super(handler.getLooper());
1091ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1101ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1111ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            @Override
1121ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            public void handleMessage(Message msg) {
1131ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                switch (msg.what) {
1141ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                case SHOW_PRESS:
1151ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    mListener.onShowPress(mCurrentDownEvent);
1161ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    break;
1171ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1181ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                case LONG_PRESS:
1191ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    dispatchLongPress();
1201ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    break;
1211ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1221ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                case TAP:
1231ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    // If the user's finger is still down, do not count it as a tap
124f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                    if (mDoubleTapListener != null) {
125f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                        if (!mStillDown) {
126f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                            mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
127f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                        } else {
128f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                            mDeferConfirmSingleTap = true;
129f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                        }
1301ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    }
1311ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    break;
1321ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1331ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                default:
1341ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    throw new RuntimeException("Unknown message " + msg); //never
1351ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                }
1361ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1371ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
1381ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1391ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
1401ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * Creates a GestureDetector with the supplied listener.
1411ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * You may only use this constructor from a UI thread (this is the usual situation).
1421ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @see android.os.Handler#Handler()
1431ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *
1441ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @param context the application's context
1451ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @param listener the listener invoked for all the callbacks, this must
1461ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * not be null.
1471ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @param handler the handler to use
1481ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *
1491ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @throws NullPointerException if {@code listener} is null.
1501ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
1515211222f71426ccfc1f2cca9f5f6bb89c61c9ed5Jake Wharton        GestureDetectorCompatImplBase(Context context, OnGestureListener listener,
1521ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                Handler handler) {
1531ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (handler != null) {
1541ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                mHandler = new GestureHandler(handler);
1551ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            } else {
1561ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                mHandler = new GestureHandler();
1571ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1581ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mListener = listener;
1591ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (listener instanceof OnDoubleTapListener) {
1601ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                setOnDoubleTapListener((OnDoubleTapListener) listener);
1611ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1621ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            init(context);
1631ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
1641ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1651ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private void init(Context context) {
1661ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (context == null) {
1671ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                throw new IllegalArgumentException("Context must not be null");
1681ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1691ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (mListener == null) {
1701ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                throw new IllegalArgumentException("OnGestureListener must not be null");
1711ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
1721ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mIsLongpressEnabled = true;
1731ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1741ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final ViewConfiguration configuration = ViewConfiguration.get(context);
1751ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final int touchSlop = configuration.getScaledTouchSlop();
1761ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final int doubleTapSlop = configuration.getScaledDoubleTapSlop();
1771ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
1781ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
1791ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1801ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mTouchSlopSquare = touchSlop * touchSlop;
1811ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
1821ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
1831ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1841ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
1851ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * Sets the listener which will be called for double-tap and related
1861ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * gestures.
1871ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *
1881ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @param onDoubleTapListener the listener invoked for all the callbacks, or
1891ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *        null to stop listening for double-tap gestures.
1901ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
19115375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
1921ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
1931ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mDoubleTapListener = onDoubleTapListener;
1941ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
1951ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
1961ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
1971ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * Set whether longpress is enabled, if this is enabled when a user
1981ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * presses and holds down you get a longpress event and nothing further.
1991ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * If it's disabled the user can press and hold down and then later
2001ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * moved their finger and you will get scroll events. By default
2011ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * longpress is enabled.
2021ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *
2031ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @param isLongpressEnabled whether longpress should be enabled.
2041ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
20515375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
2061ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public void setIsLongpressEnabled(boolean isLongpressEnabled) {
2071ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mIsLongpressEnabled = isLongpressEnabled;
2081ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
2091ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2101ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
2111ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @return true if longpress is enabled, else false.
2121ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
21315375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
2141ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public boolean isLongpressEnabled() {
2151ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            return mIsLongpressEnabled;
2161ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
2171ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2181ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        /**
2191ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * Analyzes the given motion event and if applicable triggers the
2201ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * appropriate callbacks on the {@link OnGestureListener} supplied.
2211ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *
2221ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @param ev The current motion event.
2231ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         * @return true if the {@link OnGestureListener} consumed the event,
2241ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         *              else false.
2251ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell         */
22615375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
2271ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public boolean onTouchEvent(MotionEvent ev) {
2281ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final int action = ev.getAction();
2291ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2301ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (mVelocityTracker == null) {
2311ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                mVelocityTracker = VelocityTracker.obtain();
2321ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
2331ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mVelocityTracker.addMovement(ev);
2341ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2351ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final boolean pointerUp =
2366ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                    (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
2376ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
2381ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2391ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            // Determine focal point
2401ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            float sumX = 0, sumY = 0;
2411b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            final int count = ev.getPointerCount();
2421ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            for (int i = 0; i < count; i++) {
2431ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                if (skipIndex == i) continue;
2441b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                sumX += ev.getX(i);
2451b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                sumY += ev.getY(i);
2461ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
2471ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final int div = pointerUp ? count - 1 : count;
2481ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final float focusX = sumX / div;
2491ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            final float focusY = sumY / div;
2501ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2511ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            boolean handled = false;
2521ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
2536ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            switch (action & MotionEvent.ACTION_MASK) {
2546ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                case MotionEvent.ACTION_POINTER_DOWN:
255374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDownFocusX = mLastFocusX = focusX;
256374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDownFocusY = mLastFocusY = focusY;
257374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    // Cancel long press and taps
258374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    cancelTaps();
259374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    break;
260374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
2616ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                case MotionEvent.ACTION_POINTER_UP:
262374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDownFocusX = mLastFocusX = focusX;
263374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDownFocusY = mLastFocusY = focusY;
264374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
265374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    // Check the dot product of current velocities.
266374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    // If the pointer that left was opposing another velocity vector, clear.
267374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
2686ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                    final int upIndex = ev.getActionIndex();
269374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    final int id1 = ev.getPointerId(upIndex);
270374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    final float x1 = mVelocityTracker.getXVelocity(id1);
271374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    final float y1 = mVelocityTracker.getYVelocity(id1);
272374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    for (int i = 0; i < count; i++) {
273374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if (i == upIndex) continue;
274374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
275374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final int id2 = ev.getPointerId(i);
276374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final float x = x1 * mVelocityTracker.getXVelocity(id2);
277374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final float y = y1 * mVelocityTracker.getYVelocity(id2);
278374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
279374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final float dot = x + y;
280374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if (dot < 0) {
281374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mVelocityTracker.clear();
282374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            break;
283374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        }
2841ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    }
285374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    break;
286374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
287374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                case MotionEvent.ACTION_DOWN:
288374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mDoubleTapListener != null) {
289374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        boolean hadTapMessage = mHandler.hasMessages(TAP);
290374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if (hadTapMessage) mHandler.removeMessages(TAP);
291374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null)
292374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                                && hadTapMessage && isConsideredDoubleTap(
293374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                                        mCurrentDownEvent, mPreviousUpEvent, ev)) {
294374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            // This is a second tap
295374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mIsDoubleTapping = true;
296374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            // Give a callback with the first tap of the double-tap
297374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
298374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            // Give a callback with down event of the double-tap
299374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            handled |= mDoubleTapListener.onDoubleTapEvent(ev);
300374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        } else {
301374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            // This is a first tap
302374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
303374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        }
3041ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    }
3051ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
306374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDownFocusX = mLastFocusX = focusX;
307374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDownFocusY = mLastFocusY = focusY;
308374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mCurrentDownEvent != null) {
309374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mCurrentDownEvent.recycle();
310374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    }
311374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mCurrentDownEvent = MotionEvent.obtain(ev);
312374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mAlwaysInTapRegion = true;
313374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mAlwaysInBiggerTapRegion = true;
314374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mStillDown = true;
315374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mInLongPress = false;
316374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDeferConfirmSingleTap = false;
3171ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
318374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mIsLongpressEnabled) {
319374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mHandler.removeMessages(LONG_PRESS);
320374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
321374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                                + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
322374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    }
323374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
324374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
325374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    handled |= mListener.onDown(ev);
3261ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    break;
327374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
328374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                case MotionEvent.ACTION_MOVE:
329374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mInLongPress) {
330374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        break;
331374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    }
332374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    final float scrollX = mLastFocusX - focusX;
333374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    final float scrollY = mLastFocusY - focusY;
334374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mIsDoubleTapping) {
335374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        // Give the move events of the double-tap
336374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        handled |= mDoubleTapListener.onDoubleTapEvent(ev);
337374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    } else if (mAlwaysInTapRegion) {
338374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final int deltaX = (int) (focusX - mDownFocusX);
339374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final int deltaY = (int) (focusY - mDownFocusY);
340374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        int distance = (deltaX * deltaX) + (deltaY * deltaY);
341374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if (distance > mTouchSlopSquare) {
342374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
343374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mLastFocusX = focusX;
344374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mLastFocusY = focusY;
345374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mAlwaysInTapRegion = false;
346374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mHandler.removeMessages(TAP);
347374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mHandler.removeMessages(SHOW_PRESS);
348374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mHandler.removeMessages(LONG_PRESS);
349374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        }
350374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if (distance > mTouchSlopSquare) {
351374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mAlwaysInBiggerTapRegion = false;
352374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        }
353374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
3541ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                        handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
3551ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                        mLastFocusX = focusX;
3561ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                        mLastFocusY = focusY;
3571ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    }
358374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    break;
359374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
360374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                case MotionEvent.ACTION_UP:
361374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mStillDown = false;
362374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    MotionEvent currentUpEvent = MotionEvent.obtain(ev);
363374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mIsDoubleTapping) {
364374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        // Finally, give the up event of the double-tap
365374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        handled |= mDoubleTapListener.onDoubleTapEvent(ev);
366374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    } else if (mInLongPress) {
367374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mHandler.removeMessages(TAP);
368374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mInLongPress = false;
369374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    } else if (mAlwaysInTapRegion) {
370374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        handled = mListener.onSingleTapUp(ev);
371374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
372374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            mDoubleTapListener.onSingleTapConfirmed(ev);
373374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        }
374374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    } else {
375374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        // A fling must travel the minimum tap distance
376374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final VelocityTracker velocityTracker = mVelocityTracker;
377374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final int pointerId = ev.getPointerId(0);
378374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
379374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final float velocityY = velocityTracker.getYVelocity(pointerId);
380374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        final float velocityX = velocityTracker.getXVelocity(pointerId);
381374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
382374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        if ((Math.abs(velocityY) > mMinimumFlingVelocity)
383374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                                || (Math.abs(velocityX) > mMinimumFlingVelocity)) {
384374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                            handled = mListener.onFling(
385374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                                    mCurrentDownEvent, ev, velocityX, velocityY);
386374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        }
3871ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    }
388374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mPreviousUpEvent != null) {
389374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mPreviousUpEvent.recycle();
390f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell                    }
391374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    // Hold the event we obtained above - listeners may have changed the original.
392374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mPreviousUpEvent = currentUpEvent;
393374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    if (mVelocityTracker != null) {
394374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        // This may have been cleared when we called out to the
395374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        // application above.
396374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mVelocityTracker.recycle();
397374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                        mVelocityTracker = null;
3981ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                    }
399374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mIsDoubleTapping = false;
400374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mDeferConfirmSingleTap = false;
401374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mHandler.removeMessages(SHOW_PRESS);
402374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    mHandler.removeMessages(LONG_PRESS);
403374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    break;
404374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas
405374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                case MotionEvent.ACTION_CANCEL:
406374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    cancel();
407374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    break;
4081ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
4091ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4101ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            return handled;
4111ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4121ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4131ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private void cancel() {
4141ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(SHOW_PRESS);
4151ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(LONG_PRESS);
4161ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(TAP);
4171ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mVelocityTracker.recycle();
4181ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mVelocityTracker = null;
4191ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mIsDoubleTapping = false;
4201ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mStillDown = false;
4211ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mAlwaysInTapRegion = false;
4221ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mAlwaysInBiggerTapRegion = false;
423f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell            mDeferConfirmSingleTap = false;
4241ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (mInLongPress) {
4251ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                mInLongPress = false;
4261ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
4271ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4281ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4291ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private void cancelTaps() {
4301ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(SHOW_PRESS);
4311ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(LONG_PRESS);
4321ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(TAP);
4331ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mIsDoubleTapping = false;
4341ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mAlwaysInTapRegion = false;
4351ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mAlwaysInBiggerTapRegion = false;
436f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell            mDeferConfirmSingleTap = false;
4371ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (mInLongPress) {
4381ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                mInLongPress = false;
4391ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
4401ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4411ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4421ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
4431ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                MotionEvent secondDown) {
4441ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (!mAlwaysInBiggerTapRegion) {
4451ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                return false;
4461ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
4471ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4481ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
4491ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                return false;
4501ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            }
4511ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4521ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
4531ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
4541ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
4551ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4561ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
457552766fa685c63ad760c92239faaba12e6ad51f1Aurimas Liutikas        void dispatchLongPress() {
4581ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mHandler.removeMessages(TAP);
459f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell            mDeferConfirmSingleTap = false;
4601ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mInLongPress = true;
4611ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mListener.onLongPress(mCurrentDownEvent);
4621ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4631ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
4641ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
465f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell    static class GestureDetectorCompatImplJellybeanMr2 implements GestureDetectorCompatImpl {
4661ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        private final GestureDetector mDetector;
4671ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4685211222f71426ccfc1f2cca9f5f6bb89c61c9ed5Jake Wharton        GestureDetectorCompatImplJellybeanMr2(Context context, OnGestureListener listener,
4691ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell                Handler handler) {
4701ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mDetector = new GestureDetector(context, listener, handler);
4711ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4721ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4731ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        @Override
4741ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public boolean isLongpressEnabled() {
4751ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            return mDetector.isLongpressEnabled();
4761ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4771ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4781ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        @Override
4791ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public boolean onTouchEvent(MotionEvent ev) {
4801ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            return mDetector.onTouchEvent(ev);
4811ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4821ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4831ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        @Override
4841ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public void setIsLongpressEnabled(boolean enabled) {
4851ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mDetector.setIsLongpressEnabled(enabled);
4861ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4871ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4881ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        @Override
4891ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        public void setOnDoubleTapListener(OnDoubleTapListener listener) {
4901ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mDetector.setOnDoubleTapListener(listener);
4911ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
4921ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
4931ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4941ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    private final GestureDetectorCompatImpl mImpl;
4951ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
4961ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    /**
4971ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * Creates a GestureDetectorCompat with the supplied listener.
4981ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * As usual, you may only use this constructor from a UI thread.
4991ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @see android.os.Handler#Handler()
5001ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *
5011ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param context the application's context
5021ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param listener the listener invoked for all the callbacks, this must
5031ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * not be null.
5041ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     */
5051ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    public GestureDetectorCompat(Context context, OnGestureListener listener) {
5061ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        this(context, listener, null);
5071ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
5081ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
5091ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    /**
5101ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * Creates a GestureDetectorCompat with the supplied listener.
5111ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * As usual, you may only use this constructor from a UI thread.
5121ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @see android.os.Handler#Handler()
5131ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *
5141ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param context the application's context
5151ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param listener the listener invoked for all the callbacks, this must
5161ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * not be null.
5171ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param handler the handler that will be used for posting deferred messages
5181ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     */
5191ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    public GestureDetectorCompat(Context context, OnGestureListener listener, Handler handler) {
520f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell        if (Build.VERSION.SDK_INT > 17) {
521f48af3312a4f63d8ce232fe7476932c0201774d9Adam Powell            mImpl = new GestureDetectorCompatImplJellybeanMr2(context, listener, handler);
5221ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        } else {
5231ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell            mImpl = new GestureDetectorCompatImplBase(context, listener, handler);
5241ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        }
5251ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
5261ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
5271ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    /**
5281ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @return true if longpress is enabled, else false.
5291ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     */
5301ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    public boolean isLongpressEnabled() {
5311ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        return mImpl.isLongpressEnabled();
5321ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
5331ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
5341ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    /**
5351ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * Analyzes the given motion event and if applicable triggers the
5361ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * appropriate callbacks on the {@link OnGestureListener} supplied.
5371ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *
5381ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param event The current motion event.
5391ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @return true if the {@link OnGestureListener} consumed the event,
5401ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *              else false.
5411ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     */
5421ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    public boolean onTouchEvent(MotionEvent event) {
5431ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        return mImpl.onTouchEvent(event);
5441ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
5451ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
5461ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    /**
5471ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * Set whether longpress is enabled, if this is enabled when a user
5481ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * presses and holds down you get a longpress event and nothing further.
5491ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * If it's disabled the user can press and hold down and then later
5501ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * moved their finger and you will get scroll events. By default
5511ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * longpress is enabled.
5521ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *
5531ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param enabled whether longpress should be enabled.
5541ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     */
5551ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    public void setIsLongpressEnabled(boolean enabled) {
5561ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        mImpl.setIsLongpressEnabled(enabled);
5571ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
5581ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell
5591ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    /**
5601ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * Sets the listener which will be called for double-tap and related
5611ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * gestures.
5621ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *
5631ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     * @param listener the listener invoked for all the callbacks, or
5641ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     *        null to stop listening for double-tap gestures.
5651ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell     */
5661ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    public void setOnDoubleTapListener(OnDoubleTapListener listener) {
5671ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell        mImpl.setOnDoubleTapListener(listener);
5681ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell    }
5691ce805e30800bf2852fa5421b7277a18e089ee31Adam Powell}
570