SystemGesturesPointerEventListener.java revision a4d22d718affbc7145d1012157feb819557b5c06
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;
2557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.view.MotionEvent;
26037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.view.WindowManagerPolicy.PointerEventListener;
27a4d22d718affbc7145d1012157feb819557b5c06Michael Wrightimport android.widget.OverScroller;
2857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
2957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock/*
3057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * Listens for system-wide input gestures, firing callbacks when detected.
3157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * @hide
3257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock */
33037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerpublic class SystemGesturesPointerEventListener implements PointerEventListener {
3457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final String TAG = "SystemGestures";
3557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final boolean DEBUG = false;
3657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final long SWIPE_TIMEOUT_MS = 500;
3757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final int MAX_TRACKED_POINTERS = 32;  // max per input system
3857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static final int UNTRACKED_POINTER = -1;
39a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private static final int MAX_FLING_TIME_MILLIS = 5000;
4057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
41ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_NONE = 0;
42ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_FROM_TOP = 1;
43ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_FROM_BOTTOM = 2;
44ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private static final int SWIPE_FROM_RIGHT = 3;
45ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock
46a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private final Context mContext;
4757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final int mSwipeStartThreshold;
489ba21fdc9ddf1d132215d29054b55af416561367John Spurlock    private final int mSwipeDistanceThreshold;
4957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final Callbacks mCallbacks;
5057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
51ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
5257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
5357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
5457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
55a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private GestureDetector mGestureDetector;
56a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private OverScroller mOverscroller;
57a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
58ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    int screenHeight;
59ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    int screenWidth;
6057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private int mDownPointers;
61ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private boolean mSwipeFireable;
62ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private boolean mDebugFireable;
63a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private long mLastFlingTime;
6457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
65037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
66a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        mContext = context;
6757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        mCallbacks = checkNull("callbacks", callbacks);
6857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        mSwipeStartThreshold = checkNull("context", context).getResources()
6957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
709ba21fdc9ddf1d132215d29054b55af416561367John Spurlock        mSwipeDistanceThreshold = mSwipeStartThreshold;
719ba21fdc9ddf1d132215d29054b55af416561367John Spurlock        if (DEBUG) Slog.d(TAG,  "mSwipeStartThreshold=" + mSwipeStartThreshold
729ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
7357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
7457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
7557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private static <T> T checkNull(String name, T arg) {
7657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (arg == null) {
7757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            throw new IllegalArgumentException(name + " must not be null");
7857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
7957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        return arg;
8057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
8157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
82a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    public void systemReady() {
83a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        Handler h = new Handler(Looper.myLooper());
84a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
85a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        mOverscroller = new OverScroller(mContext);
86a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    }
87a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
8857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    @Override
89037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    public void onPointerEvent(MotionEvent event) {
90a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        if (mGestureDetector != null) {
91a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mGestureDetector.onTouchEvent(event);
92a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        }
9357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        switch (event.getActionMasked()) {
9457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_DOWN:
95ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mSwipeFireable = true;
96ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mDebugFireable = true;
9757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                mDownPointers = 0;
9857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                captureDown(event, 0);
993595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos                mCallbacks.onDown();
10057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
10157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_POINTER_DOWN:
10257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                captureDown(event, event.getActionIndex());
103ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                if (mDebugFireable) {
104ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    mDebugFireable = event.getPointerCount() < 5;
105ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    if (!mDebugFireable) {
106ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing debug");
107ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onDebug();
108ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    }
109ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                }
11057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
11157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_MOVE:
112ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                if (mSwipeFireable) {
113ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    final int swipe = detectSwipe(event);
114ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    mSwipeFireable = swipe == SWIPE_NONE;
115ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    if (swipe == SWIPE_FROM_TOP) {
116ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
117ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onSwipeFromTop();
118ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    } else if (swipe == SWIPE_FROM_BOTTOM) {
119ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
120ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onSwipeFromBottom();
121ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    } else if (swipe == SWIPE_FROM_RIGHT) {
122ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
123ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        mCallbacks.onSwipeFromRight();
124ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    }
12557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                }
12657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
12757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_UP:
12857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            case MotionEvent.ACTION_CANCEL:
129ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mSwipeFireable = false;
130ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                mDebugFireable = false;
1313595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos                mCallbacks.onUpOrCancel();
13257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                break;
13357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            default:
13457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                if (DEBUG) Slog.d(TAG, "Ignoring " + event);
13557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
13657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
13757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
13857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private void captureDown(MotionEvent event, int pointerIndex) {
13957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int pointerId = event.getPointerId(pointerIndex);
14057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int i = findIndex(pointerId);
14157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
14257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
14357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (i != UNTRACKED_POINTER) {
144ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            mDownX[i] = event.getX(pointerIndex);
14557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            mDownY[i] = event.getY(pointerIndex);
14657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            mDownTime[i] = event.getEventTime();
147ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
148ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    " down x=" + mDownX[i] + " y=" + mDownY[i]);
14957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
15057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
15157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
15257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    private int findIndex(int pointerId) {
15357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        for (int i = 0; i < mDownPointers; i++) {
15457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            if (mDownPointerId[i] == pointerId) {
15557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                return i;
15657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            }
15757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
15857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
15957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            return UNTRACKED_POINTER;
16057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
16157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        mDownPointerId[mDownPointers++] = pointerId;
16257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        return mDownPointers - 1;
16357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
16457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
165ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private int detectSwipe(MotionEvent move) {
16657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int historySize = move.getHistorySize();
16757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final int pointerCount = move.getPointerCount();
16857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        for (int p = 0; p < pointerCount; p++) {
16957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            final int pointerId = move.getPointerId(p);
17057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            final int i = findIndex(pointerId);
17157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            if (i != UNTRACKED_POINTER) {
17257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                for (int h = 0; h < historySize; h++) {
17357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                    final long time = move.getHistoricalEventTime(h);
174ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    final float x = move.getHistoricalX(p, h);
17557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                    final float y = move.getHistoricalY(p,  h);
176ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    final int swipe = detectSwipe(i, time, x, y);
177ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    if (swipe != SWIPE_NONE) {
178ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                        return swipe;
17957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                    }
18057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                }
181ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
182ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                if (swipe != SWIPE_NONE) {
183ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                    return swipe;
18457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock                }
18557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock            }
18657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        }
187ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        return SWIPE_NONE;
18857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
18957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
190ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock    private int detectSwipe(int i, long time, float x, float y) {
191ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        final float fromX = mDownX[i];
19257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final float fromY = mDownY[i];
19357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        final long elapsed = time - mDownTime[i];
19457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
195ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
196ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        if (fromY <= mSwipeStartThreshold
1979ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                && y > fromY + mSwipeDistanceThreshold
198ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                && elapsed < SWIPE_TIMEOUT_MS) {
199ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            return SWIPE_FROM_TOP;
200ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        }
201ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        if (fromY >= screenHeight - mSwipeStartThreshold
2029ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                && y < fromY - mSwipeDistanceThreshold
203ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                && elapsed < SWIPE_TIMEOUT_MS) {
204ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            return SWIPE_FROM_BOTTOM;
205ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        }
206ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        if (fromX >= screenWidth - mSwipeStartThreshold
2079ba21fdc9ddf1d132215d29054b55af416561367John Spurlock                && x < fromX - mSwipeDistanceThreshold
208ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock                && elapsed < SWIPE_TIMEOUT_MS) {
209ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock            return SWIPE_FROM_RIGHT;
210ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        }
211ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        return SWIPE_NONE;
21257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
21357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock
214a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
215a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        @Override
216a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        public boolean onSingleTapUp(MotionEvent e) {
217a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            if (!mOverscroller.isFinished()) {
218a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                mOverscroller.forceFinished(true);
219a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            }
220a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            return true;
221a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        }
222a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        @Override
223a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        public boolean onFling(MotionEvent down, MotionEvent up,
224a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                float velocityX, float velocityY) {
225a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mOverscroller.computeScrollOffset();
226a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            long now = SystemClock.uptimeMillis();
227a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
228a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
229a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                mOverscroller.forceFinished(true);
230a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            }
231a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
232a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
233a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            int duration = mOverscroller.getDuration();
234a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            if (duration > MAX_FLING_TIME_MILLIS) {
235a4d22d718affbc7145d1012157feb819557b5c06Michael Wright                duration = MAX_FLING_TIME_MILLIS;
236a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            }
237a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mLastFlingTime = now;
238a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            mCallbacks.onFling(duration);
239a4d22d718affbc7145d1012157feb819557b5c06Michael Wright            return true;
240a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        }
241a4d22d718affbc7145d1012157feb819557b5c06Michael Wright    }
242a4d22d718affbc7145d1012157feb819557b5c06Michael Wright
24357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    interface Callbacks {
24457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock        void onSwipeFromTop();
245ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        void onSwipeFromBottom();
246ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        void onSwipeFromRight();
247a4d22d718affbc7145d1012157feb819557b5c06Michael Wright        void onFling(int durationMs);
2483595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos        void onDown();
2493595be4d19caaa7ddfbff0b979d135aaf5ac20b1Adrian Roos        void onUpOrCancel();
250ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock        void onDebug();
25157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock    }
25257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock}
253