blob: 598c58e50e792e289ff159253f43f6e4877f559e [file] [log] [blame]
John Spurlock57306e62013-04-22 09:48:49 -04001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jorim Jaggib10e33f2015-02-04 21:57:40 +010017package com.android.server.policy;
John Spurlock57306e62013-04-22 09:48:49 -040018
19import android.content.Context;
Michael Wrighta4d22d72015-09-16 23:19:26 +010020import android.os.Handler;
21import android.os.Looper;
22import android.os.SystemClock;
John Spurlock57306e62013-04-22 09:48:49 -040023import android.util.Slog;
Michael Wrighta4d22d72015-09-16 23:19:26 +010024import android.view.GestureDetector;
Jun Mukai99465292015-10-01 18:42:33 +090025import android.view.InputDevice;
John Spurlock57306e62013-04-22 09:48:49 -040026import android.view.MotionEvent;
Craig Mautner037aa8d2013-06-07 10:35:44 -070027import android.view.WindowManagerPolicy.PointerEventListener;
Michael Wrighta4d22d72015-09-16 23:19:26 +010028import android.widget.OverScroller;
John Spurlock57306e62013-04-22 09:48:49 -040029
30/*
31 * Listens for system-wide input gestures, firing callbacks when detected.
32 * @hide
33 */
Craig Mautner037aa8d2013-06-07 10:35:44 -070034public class SystemGesturesPointerEventListener implements PointerEventListener {
John Spurlock57306e62013-04-22 09:48:49 -040035 private static final String TAG = "SystemGestures";
36 private static final boolean DEBUG = false;
37 private static final long SWIPE_TIMEOUT_MS = 500;
38 private static final int MAX_TRACKED_POINTERS = 32; // max per input system
39 private static final int UNTRACKED_POINTER = -1;
Michael Wrighta4d22d72015-09-16 23:19:26 +010040 private static final int MAX_FLING_TIME_MILLIS = 5000;
John Spurlock57306e62013-04-22 09:48:49 -040041
John Spurlockad3e6cb2013-04-30 08:47:43 -040042 private static final int SWIPE_NONE = 0;
43 private static final int SWIPE_FROM_TOP = 1;
44 private static final int SWIPE_FROM_BOTTOM = 2;
45 private static final int SWIPE_FROM_RIGHT = 3;
Adrian Roos85d202b2016-06-02 16:27:47 -070046 private static final int SWIPE_FROM_LEFT = 4;
John Spurlockad3e6cb2013-04-30 08:47:43 -040047
Michael Wrighta4d22d72015-09-16 23:19:26 +010048 private final Context mContext;
John Spurlock57306e62013-04-22 09:48:49 -040049 private final int mSwipeStartThreshold;
John Spurlock9ba21fd2013-07-29 08:57:32 -040050 private final int mSwipeDistanceThreshold;
John Spurlock57306e62013-04-22 09:48:49 -040051 private final Callbacks mCallbacks;
52 private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
John Spurlockad3e6cb2013-04-30 08:47:43 -040053 private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
John Spurlock57306e62013-04-22 09:48:49 -040054 private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
55 private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
56
Michael Wrighta4d22d72015-09-16 23:19:26 +010057 private GestureDetector mGestureDetector;
58 private OverScroller mOverscroller;
59
John Spurlockad3e6cb2013-04-30 08:47:43 -040060 int screenHeight;
61 int screenWidth;
John Spurlock57306e62013-04-22 09:48:49 -040062 private int mDownPointers;
John Spurlockad3e6cb2013-04-30 08:47:43 -040063 private boolean mSwipeFireable;
64 private boolean mDebugFireable;
Jun Mukaid2e7e352015-07-22 17:14:02 -070065 private boolean mMouseHoveringAtEdge;
Michael Wrighta4d22d72015-09-16 23:19:26 +010066 private long mLastFlingTime;
John Spurlock57306e62013-04-22 09:48:49 -040067
Craig Mautner037aa8d2013-06-07 10:35:44 -070068 public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
Michael Wrighta4d22d72015-09-16 23:19:26 +010069 mContext = context;
John Spurlock57306e62013-04-22 09:48:49 -040070 mCallbacks = checkNull("callbacks", callbacks);
71 mSwipeStartThreshold = checkNull("context", context).getResources()
72 .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
John Spurlock9ba21fd2013-07-29 08:57:32 -040073 mSwipeDistanceThreshold = mSwipeStartThreshold;
74 if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold
75 + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
John Spurlock57306e62013-04-22 09:48:49 -040076 }
77
78 private static <T> T checkNull(String name, T arg) {
79 if (arg == null) {
80 throw new IllegalArgumentException(name + " must not be null");
81 }
82 return arg;
83 }
84
Michael Wrighta4d22d72015-09-16 23:19:26 +010085 public void systemReady() {
86 Handler h = new Handler(Looper.myLooper());
87 mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
88 mOverscroller = new OverScroller(mContext);
89 }
90
John Spurlock57306e62013-04-22 09:48:49 -040091 @Override
Craig Mautner037aa8d2013-06-07 10:35:44 -070092 public void onPointerEvent(MotionEvent event) {
Dennis Kempinfb5168a2016-01-04 17:06:53 -080093 if (mGestureDetector != null && event.isTouchEvent()) {
Michael Wrighta4d22d72015-09-16 23:19:26 +010094 mGestureDetector.onTouchEvent(event);
95 }
John Spurlock57306e62013-04-22 09:48:49 -040096 switch (event.getActionMasked()) {
97 case MotionEvent.ACTION_DOWN:
John Spurlockad3e6cb2013-04-30 08:47:43 -040098 mSwipeFireable = true;
99 mDebugFireable = true;
John Spurlock57306e62013-04-22 09:48:49 -0400100 mDownPointers = 0;
101 captureDown(event, 0);
Jun Mukaid2e7e352015-07-22 17:14:02 -0700102 if (mMouseHoveringAtEdge) {
103 mMouseHoveringAtEdge = false;
104 mCallbacks.onMouseLeaveFromEdge();
105 }
Adrian Roos3595be42015-03-05 16:31:15 +0100106 mCallbacks.onDown();
John Spurlock57306e62013-04-22 09:48:49 -0400107 break;
108 case MotionEvent.ACTION_POINTER_DOWN:
109 captureDown(event, event.getActionIndex());
John Spurlockad3e6cb2013-04-30 08:47:43 -0400110 if (mDebugFireable) {
111 mDebugFireable = event.getPointerCount() < 5;
112 if (!mDebugFireable) {
113 if (DEBUG) Slog.d(TAG, "Firing debug");
114 mCallbacks.onDebug();
115 }
116 }
John Spurlock57306e62013-04-22 09:48:49 -0400117 break;
118 case MotionEvent.ACTION_MOVE:
John Spurlockad3e6cb2013-04-30 08:47:43 -0400119 if (mSwipeFireable) {
120 final int swipe = detectSwipe(event);
121 mSwipeFireable = swipe == SWIPE_NONE;
122 if (swipe == SWIPE_FROM_TOP) {
123 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
124 mCallbacks.onSwipeFromTop();
125 } else if (swipe == SWIPE_FROM_BOTTOM) {
126 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
127 mCallbacks.onSwipeFromBottom();
128 } else if (swipe == SWIPE_FROM_RIGHT) {
129 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
130 mCallbacks.onSwipeFromRight();
Adrian Roos85d202b2016-06-02 16:27:47 -0700131 } else if (swipe == SWIPE_FROM_LEFT) {
132 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
133 mCallbacks.onSwipeFromLeft();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400134 }
John Spurlock57306e62013-04-22 09:48:49 -0400135 }
136 break;
Jun Mukaid2e7e352015-07-22 17:14:02 -0700137 case MotionEvent.ACTION_HOVER_MOVE:
Jun Mukaic5bd9bc2015-10-15 02:56:15 -0700138 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
Jun Mukaid2e7e352015-07-22 17:14:02 -0700139 if (!mMouseHoveringAtEdge && event.getY() == 0) {
140 mCallbacks.onMouseHoverAtTop();
141 mMouseHoveringAtEdge = true;
142 } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
143 mCallbacks.onMouseHoverAtBottom();
144 mMouseHoveringAtEdge = true;
145 } else if (mMouseHoveringAtEdge
146 && (event.getY() > 0 && event.getY() < screenHeight - 1)) {
147 mCallbacks.onMouseLeaveFromEdge();
148 mMouseHoveringAtEdge = false;
149 }
150 }
151 break;
John Spurlock57306e62013-04-22 09:48:49 -0400152 case MotionEvent.ACTION_UP:
153 case MotionEvent.ACTION_CANCEL:
John Spurlockad3e6cb2013-04-30 08:47:43 -0400154 mSwipeFireable = false;
155 mDebugFireable = false;
Adrian Roos3595be42015-03-05 16:31:15 +0100156 mCallbacks.onUpOrCancel();
John Spurlock57306e62013-04-22 09:48:49 -0400157 break;
158 default:
159 if (DEBUG) Slog.d(TAG, "Ignoring " + event);
160 }
161 }
162
163 private void captureDown(MotionEvent event, int pointerIndex) {
164 final int pointerId = event.getPointerId(pointerIndex);
165 final int i = findIndex(pointerId);
166 if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
167 " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
168 if (i != UNTRACKED_POINTER) {
John Spurlockad3e6cb2013-04-30 08:47:43 -0400169 mDownX[i] = event.getX(pointerIndex);
John Spurlock57306e62013-04-22 09:48:49 -0400170 mDownY[i] = event.getY(pointerIndex);
171 mDownTime[i] = event.getEventTime();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400172 if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
173 " down x=" + mDownX[i] + " y=" + mDownY[i]);
John Spurlock57306e62013-04-22 09:48:49 -0400174 }
175 }
176
177 private int findIndex(int pointerId) {
178 for (int i = 0; i < mDownPointers; i++) {
179 if (mDownPointerId[i] == pointerId) {
180 return i;
181 }
182 }
183 if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
184 return UNTRACKED_POINTER;
185 }
186 mDownPointerId[mDownPointers++] = pointerId;
187 return mDownPointers - 1;
188 }
189
John Spurlockad3e6cb2013-04-30 08:47:43 -0400190 private int detectSwipe(MotionEvent move) {
John Spurlock57306e62013-04-22 09:48:49 -0400191 final int historySize = move.getHistorySize();
192 final int pointerCount = move.getPointerCount();
193 for (int p = 0; p < pointerCount; p++) {
194 final int pointerId = move.getPointerId(p);
195 final int i = findIndex(pointerId);
196 if (i != UNTRACKED_POINTER) {
197 for (int h = 0; h < historySize; h++) {
198 final long time = move.getHistoricalEventTime(h);
John Spurlockad3e6cb2013-04-30 08:47:43 -0400199 final float x = move.getHistoricalX(p, h);
John Spurlock57306e62013-04-22 09:48:49 -0400200 final float y = move.getHistoricalY(p, h);
John Spurlockad3e6cb2013-04-30 08:47:43 -0400201 final int swipe = detectSwipe(i, time, x, y);
202 if (swipe != SWIPE_NONE) {
203 return swipe;
John Spurlock57306e62013-04-22 09:48:49 -0400204 }
205 }
John Spurlockad3e6cb2013-04-30 08:47:43 -0400206 final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
207 if (swipe != SWIPE_NONE) {
208 return swipe;
John Spurlock57306e62013-04-22 09:48:49 -0400209 }
210 }
211 }
John Spurlockad3e6cb2013-04-30 08:47:43 -0400212 return SWIPE_NONE;
John Spurlock57306e62013-04-22 09:48:49 -0400213 }
214
John Spurlockad3e6cb2013-04-30 08:47:43 -0400215 private int detectSwipe(int i, long time, float x, float y) {
216 final float fromX = mDownX[i];
John Spurlock57306e62013-04-22 09:48:49 -0400217 final float fromY = mDownY[i];
218 final long elapsed = time - mDownTime[i];
219 if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
John Spurlockad3e6cb2013-04-30 08:47:43 -0400220 + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
221 if (fromY <= mSwipeStartThreshold
John Spurlock9ba21fd2013-07-29 08:57:32 -0400222 && y > fromY + mSwipeDistanceThreshold
John Spurlockad3e6cb2013-04-30 08:47:43 -0400223 && elapsed < SWIPE_TIMEOUT_MS) {
224 return SWIPE_FROM_TOP;
225 }
226 if (fromY >= screenHeight - mSwipeStartThreshold
John Spurlock9ba21fd2013-07-29 08:57:32 -0400227 && y < fromY - mSwipeDistanceThreshold
John Spurlockad3e6cb2013-04-30 08:47:43 -0400228 && elapsed < SWIPE_TIMEOUT_MS) {
229 return SWIPE_FROM_BOTTOM;
230 }
231 if (fromX >= screenWidth - mSwipeStartThreshold
John Spurlock9ba21fd2013-07-29 08:57:32 -0400232 && x < fromX - mSwipeDistanceThreshold
John Spurlockad3e6cb2013-04-30 08:47:43 -0400233 && elapsed < SWIPE_TIMEOUT_MS) {
234 return SWIPE_FROM_RIGHT;
235 }
Adrian Roos85d202b2016-06-02 16:27:47 -0700236 if (fromX <= mSwipeStartThreshold
237 && x > fromX + mSwipeDistanceThreshold
238 && elapsed < SWIPE_TIMEOUT_MS) {
239 return SWIPE_FROM_LEFT;
240 }
John Spurlockad3e6cb2013-04-30 08:47:43 -0400241 return SWIPE_NONE;
John Spurlock57306e62013-04-22 09:48:49 -0400242 }
243
Michael Wrighta4d22d72015-09-16 23:19:26 +0100244 private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
245 @Override
246 public boolean onSingleTapUp(MotionEvent e) {
247 if (!mOverscroller.isFinished()) {
248 mOverscroller.forceFinished(true);
249 }
250 return true;
251 }
252 @Override
253 public boolean onFling(MotionEvent down, MotionEvent up,
254 float velocityX, float velocityY) {
255 mOverscroller.computeScrollOffset();
256 long now = SystemClock.uptimeMillis();
257
258 if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
259 mOverscroller.forceFinished(true);
260 }
261 mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
262 Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
263 int duration = mOverscroller.getDuration();
264 if (duration > MAX_FLING_TIME_MILLIS) {
265 duration = MAX_FLING_TIME_MILLIS;
266 }
267 mLastFlingTime = now;
268 mCallbacks.onFling(duration);
269 return true;
270 }
271 }
272
John Spurlock57306e62013-04-22 09:48:49 -0400273 interface Callbacks {
274 void onSwipeFromTop();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400275 void onSwipeFromBottom();
276 void onSwipeFromRight();
Adrian Roos85d202b2016-06-02 16:27:47 -0700277 void onSwipeFromLeft();
Michael Wrighta4d22d72015-09-16 23:19:26 +0100278 void onFling(int durationMs);
Adrian Roos3595be42015-03-05 16:31:15 +0100279 void onDown();
280 void onUpOrCancel();
Jun Mukaid2e7e352015-07-22 17:14:02 -0700281 void onMouseHoverAtTop();
282 void onMouseHoverAtBottom();
283 void onMouseLeaveFromEdge();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400284 void onDebug();
John Spurlock57306e62013-04-22 09:48:49 -0400285 }
286}