157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock/*
257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * Copyright (C) 2013 The Android Open Source Project
357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock *
457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * Licensed under the Apache License, Version 2.0 (the "License");
557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * you may not use this file except in compliance with the License.
657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * You may obtain a copy of the License at
757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock *
857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock *      http://www.apache.org/licenses/LICENSE-2.0
957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock *
1057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * Unless required by applicable law or agreed to in writing, software
1157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * distributed under the License is distributed on an "AS IS" BASIS,
1257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * See the License for the specific language governing permissions and
1457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * limitations under the License.
1557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock */
1657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
17b10e33ff804a831c71be9303146cea892b9aeb5dJorim Jaggipackage com.android.server.policy;
1857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
1957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.content.Context;
20a4d22d718affbc7145d1012157feb819557b5c06Michael Wrightimport android.os.Handler;
21a4d22d718affbc7145d1012157feb819557b5c06Michael Wrightimport android.os.Looper;
22a4d22d718affbc7145d1012157feb819557b5c06Michael Wrightimport android.os.SystemClock;
2357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.util.Slog;
24a4d22d718affbc7145d1012157feb819557b5c06Michael Wrightimport android.view.GestureDetector;
25994652991b5e3b5f85540af6aeb04609401509edJun Mukaiimport android.view.InputDevice;
2657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.view.MotionEvent;
27037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.view.WindowManagerPolicy.PointerEventListener;
28a4d22d718affbc7145d1012157feb819557b5c06Michael Wrightimport android.widget.OverScroller;
2957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
3057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock/*
3157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * Listens for system-wide input gestures, firing callbacks when detected.
3257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * @hide
3357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock */
34037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerpublic class SystemGesturesPointerEventListener implements PointerEventListener {
3557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final String TAG = "SystemGestures";
3657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final boolean DEBUG = false;
3757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final long SWIPE_TIMEOUT_MS = 500;
3857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final int MAX_TRACKED_POINTERS = 32;  // max per input system
3957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final int UNTRACKED_POINTER = -1;
40a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private static final int MAX_FLING_TIME_MILLIS = 5000;
4157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
42ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_NONE = 0;
43ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_FROM_TOP = 1;
44ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_FROM_BOTTOM = 2;
45ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_FROM_RIGHT = 3;
4685d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos    private static final int SWIPE_FROM_LEFT = 4;
47ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock
48a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private final Context mContext;
4957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final int mSwipeStartThreshold;
509ba21fdc9ddf1d132215d29054b55af416561367John Spurlock    private final int mSwipeDistanceThreshold;
5157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final Callbacks mCallbacks;
5257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
53ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
5457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
5557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
5657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
57a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private GestureDetector mGestureDetector;
58a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private OverScroller mOverscroller;
59a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
60ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    int screenHeight;
61ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    int screenWidth;
6257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private int mDownPointers;
63ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private boolean mSwipeFireable;
64ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private boolean mDebugFireable;
65d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai    private boolean mMouseHoveringAtEdge;
66a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private long mLastFlingTime;
6757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
68037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
69a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        mContext = context;
7057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        mCallbacks = checkNull("callbacks", callbacks);
7157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        mSwipeStartThreshold = checkNull("context", context).getResources()
7257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
739ba21fdc9ddf1d132215d29054b55af416561367John Spurlock        mSwipeDistanceThreshold = mSwipeStartThreshold;
749ba21fdc9ddf1d132215d29054b55af416561367John Spurlock        if (DEBUG) Slog.d(TAG,  "mSwipeStartThreshold=" + mSwipeStartThreshold
759ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
7657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
7757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
7857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static <T> T checkNull(String name, T arg) {
7957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (arg == null) {
8057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            throw new IllegalArgumentException(name + " must not be null");
8157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
8257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        return arg;
8357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
8457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
85a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    public void systemReady() {
86a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        Handler h = new Handler(Looper.myLooper());
87a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
88a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        mOverscroller = new OverScroller(mContext);
89a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    }
90a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
9157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    @Override
92037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    public void onPointerEvent(MotionEvent event) {
93fb5168a3e3749dbb8517ad39f34d450e2aea49daDennis Kempin        if (mGestureDetector != null && event.isTouchEvent()) {
94a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mGestureDetector.onTouchEvent(event);
95a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        }
9657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        switch (event.getActionMasked()) {
9757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_DOWN:
98ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mSwipeFireable = true;
99ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mDebugFireable = true;
10057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                mDownPointers = 0;
10157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                captureDown(event, 0);
102d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                if (mMouseHoveringAtEdge) {
103d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                    mMouseHoveringAtEdge = false;
104d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                    mCallbacks.onMouseLeaveFromEdge();
105d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                }
1063595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos                mCallbacks.onDown();
10757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
10857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_POINTER_DOWN:
10957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                captureDown(event, event.getActionIndex());
110ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                if (mDebugFireable) {
111ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    mDebugFireable = event.getPointerCount() < 5;
112ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    if (!mDebugFireable) {
113ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing debug");
114ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onDebug();
115ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    }
116ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                }
11757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
11857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_MOVE:
119ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                if (mSwipeFireable) {
120ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    final int swipe = detectSwipe(event);
121ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    mSwipeFireable = swipe == SWIPE_NONE;
122ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    if (swipe == SWIPE_FROM_TOP) {
123ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
124ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onSwipeFromTop();
125ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    } else if (swipe == SWIPE_FROM_BOTTOM) {
126ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
127ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onSwipeFromBottom();
128ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    } else if (swipe == SWIPE_FROM_RIGHT) {
129ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
130ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onSwipeFromRight();
13185d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos                    } else if (swipe == SWIPE_FROM_LEFT) {
13285d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
13385d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos                        mCallbacks.onSwipeFromLeft();
134ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    }
13557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                }
13657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
137d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai            case MotionEvent.ACTION_HOVER_MOVE:
138c5bd9bc8dce74966667012939a2f53b639ec10fdJun Mukai                if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
139d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                    if (!mMouseHoveringAtEdge && event.getY() == 0) {
140d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                        mCallbacks.onMouseHoverAtTop();
141d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                        mMouseHoveringAtEdge = true;
142d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                    } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
143d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                        mCallbacks.onMouseHoverAtBottom();
144d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                        mMouseHoveringAtEdge = true;
145d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                    } else if (mMouseHoveringAtEdge
146d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                            && (event.getY() > 0 && event.getY() < screenHeight - 1)) {
147d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                        mCallbacks.onMouseLeaveFromEdge();
148d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                        mMouseHoveringAtEdge = false;
149d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                    }
150d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                }
151d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai                break;
15257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_UP:
15357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_CANCEL:
154ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mSwipeFireable = false;
155ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mDebugFireable = false;
1563595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos                mCallbacks.onUpOrCancel();
15757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
15857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            default:
15957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                if (DEBUG) Slog.d(TAG, "Ignoring " + event);
16057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
16157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
16257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
16357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private void captureDown(MotionEvent event, int pointerIndex) {
16457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int pointerId = event.getPointerId(pointerIndex);
16557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int i = findIndex(pointerId);
16657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
16757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
16857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (i != UNTRACKED_POINTER) {
169ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            mDownX[i] = event.getX(pointerIndex);
17057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            mDownY[i] = event.getY(pointerIndex);
17157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            mDownTime[i] = event.getEventTime();
172ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
173ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    " down x=" + mDownX[i] + " y=" + mDownY[i]);
17457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
17557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
17657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
17757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private int findIndex(int pointerId) {
17857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        for (int i = 0; i < mDownPointers; i++) {
17957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            if (mDownPointerId[i] == pointerId) {
18057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                return i;
18157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            }
18257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
18357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
18457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            return UNTRACKED_POINTER;
18557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
18657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        mDownPointerId[mDownPointers++] = pointerId;
18757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        return mDownPointers - 1;
18857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
18957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
190ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private int detectSwipe(MotionEvent move) {
19157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int historySize = move.getHistorySize();
19257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int pointerCount = move.getPointerCount();
19357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        for (int p = 0; p < pointerCount; p++) {
19457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            final int pointerId = move.getPointerId(p);
19557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            final int i = findIndex(pointerId);
19657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            if (i != UNTRACKED_POINTER) {
19757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                for (int h = 0; h < historySize; h++) {
19857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                    final long time = move.getHistoricalEventTime(h);
199ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    final float x = move.getHistoricalX(p, h);
20057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                    final float y = move.getHistoricalY(p,  h);
201ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    final int swipe = detectSwipe(i, time, x, y);
202ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    if (swipe != SWIPE_NONE) {
203ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        return swipe;
20457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                    }
20557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                }
206ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
207ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                if (swipe != SWIPE_NONE) {
208ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    return swipe;
20957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                }
21057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            }
21157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
212ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        return SWIPE_NONE;
21357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
21457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
215ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private int detectSwipe(int i, long time, float x, float y) {
216ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        final float fromX = mDownX[i];
21757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final float fromY = mDownY[i];
21857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final long elapsed = time - mDownTime[i];
21957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
220ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
221ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        if (fromY <= mSwipeStartThreshold
2229ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                && y > fromY + mSwipeDistanceThreshold
223ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                && elapsed < SWIPE_TIMEOUT_MS) {
224ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            return SWIPE_FROM_TOP;
225ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        }
226ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        if (fromY >= screenHeight - mSwipeStartThreshold
2279ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                && y < fromY - mSwipeDistanceThreshold
228ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                && elapsed < SWIPE_TIMEOUT_MS) {
229ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            return SWIPE_FROM_BOTTOM;
230ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        }
231ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        if (fromX >= screenWidth - mSwipeStartThreshold
2329ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                && x < fromX - mSwipeDistanceThreshold
233ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                && elapsed < SWIPE_TIMEOUT_MS) {
234ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            return SWIPE_FROM_RIGHT;
235ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        }
23685d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos        if (fromX <= mSwipeStartThreshold
23785d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos                && x > fromX + mSwipeDistanceThreshold
23885d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos                && elapsed < SWIPE_TIMEOUT_MS) {
23985d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos            return SWIPE_FROM_LEFT;
24085d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos        }
241ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        return SWIPE_NONE;
24257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
24357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
244a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
245a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        @Override
246a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        public boolean onSingleTapUp(MotionEvent e) {
247a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            if (!mOverscroller.isFinished()) {
248a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                mOverscroller.forceFinished(true);
249a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            }
250a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            return true;
251a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        }
252a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        @Override
253a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        public boolean onFling(MotionEvent down, MotionEvent up,
254a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                float velocityX, float velocityY) {
255a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mOverscroller.computeScrollOffset();
256a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            long now = SystemClock.uptimeMillis();
257a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
258a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
259a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                mOverscroller.forceFinished(true);
260a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            }
261a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
262a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
263a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            int duration = mOverscroller.getDuration();
264a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            if (duration > MAX_FLING_TIME_MILLIS) {
265a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                duration = MAX_FLING_TIME_MILLIS;
266a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            }
267a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mLastFlingTime = now;
268a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mCallbacks.onFling(duration);
269a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            return true;
270a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        }
271a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    }
272a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
27357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    interface Callbacks {
27457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        void onSwipeFromTop();
275ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        void onSwipeFromBottom();
276ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        void onSwipeFromRight();
27785d202b18ada8dc95f151087ab9778d1a31f7a30Adrian Roos        void onSwipeFromLeft();
278a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        void onFling(int durationMs);
2793595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos        void onDown();
2803595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos        void onUpOrCancel();
281d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai        void onMouseHoverAtTop();
282d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai        void onMouseHoverAtBottom();
283d2e7e355d836f18adcdb44db9c1931d1c3663ef1Jun Mukai        void onMouseLeaveFromEdge();
284ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        void onDebug();
28557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
28657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock}
287