blob: bd4e542033c5a0b39847f385185347542ff81d6d [file] [log] [blame]
John Spurlock57306e62013-04-22 09:48:49 -04001/*
Tiger Huang7c610aa2018-10-27 00:01:01 +08002 * Copyright (C) 2018 The Android Open Source Project
John Spurlock57306e62013-04-22 09:48:49 -04003 *
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
Tiger Huang7c610aa2018-10-27 00:01:01 +080017package com.android.server.wm;
John Spurlock57306e62013-04-22 09:48:49 -040018
19import android.content.Context;
Michael Wrighta4d22d72015-09-16 23:19:26 +010020import android.os.Handler;
Michael Wrighta4d22d72015-09-16 23:19:26 +010021import android.os.SystemClock;
John Spurlock57306e62013-04-22 09:48:49 -040022import android.util.Slog;
Michael Wrighta4d22d72015-09-16 23:19:26 +010023import android.view.GestureDetector;
Jun Mukai99465292015-10-01 18:42:33 +090024import android.view.InputDevice;
John Spurlock57306e62013-04-22 09:48:49 -040025import android.view.MotionEvent;
Adrian Roose99bc052017-11-20 17:55:31 +010026import android.view.WindowManagerPolicyConstants.PointerEventListener;
Michael Wrighta4d22d72015-09-16 23:19:26 +010027import android.widget.OverScroller;
John Spurlock57306e62013-04-22 09:48:49 -040028
Tiger Huang7c610aa2018-10-27 00:01:01 +080029/**
John Spurlock57306e62013-04-22 09:48:49 -040030 * Listens for system-wide input gestures, firing callbacks when detected.
31 * @hide
32 */
Tiger Huang7c610aa2018-10-27 00:01:01 +080033class SystemGesturesPointerEventListener implements PointerEventListener {
John Spurlock57306e62013-04-22 09:48:49 -040034 private static final String TAG = "SystemGestures";
35 private static final boolean DEBUG = false;
36 private static final long SWIPE_TIMEOUT_MS = 500;
37 private static final int MAX_TRACKED_POINTERS = 32; // max per input system
38 private static final int UNTRACKED_POINTER = -1;
Michael Wrighta4d22d72015-09-16 23:19:26 +010039 private static final int MAX_FLING_TIME_MILLIS = 5000;
John Spurlock57306e62013-04-22 09:48:49 -040040
John Spurlockad3e6cb2013-04-30 08:47:43 -040041 private static final int SWIPE_NONE = 0;
42 private static final int SWIPE_FROM_TOP = 1;
43 private static final int SWIPE_FROM_BOTTOM = 2;
44 private static final int SWIPE_FROM_RIGHT = 3;
Adrian Roos85d202b2016-06-02 16:27:47 -070045 private static final int SWIPE_FROM_LEFT = 4;
John Spurlockad3e6cb2013-04-30 08:47:43 -040046
Michael Wrighta4d22d72015-09-16 23:19:26 +010047 private final Context mContext;
Tiger Huang7c610aa2018-10-27 00:01:01 +080048 private final Handler mHandler;
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;
Michael Wrighta4d22d72015-09-16 23:19:26 +010058
John Spurlockad3e6cb2013-04-30 08:47:43 -040059 int screenHeight;
60 int screenWidth;
John Spurlock57306e62013-04-22 09:48:49 -040061 private int mDownPointers;
John Spurlockad3e6cb2013-04-30 08:47:43 -040062 private boolean mSwipeFireable;
63 private boolean mDebugFireable;
Jun Mukaid2e7e352015-07-22 17:14:02 -070064 private boolean mMouseHoveringAtEdge;
Michael Wrighta4d22d72015-09-16 23:19:26 +010065 private long mLastFlingTime;
John Spurlock57306e62013-04-22 09:48:49 -040066
Tiger Huang7c610aa2018-10-27 00:01:01 +080067 SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
Michael Wrighta4d22d72015-09-16 23:19:26 +010068 mContext = context;
Tiger Huang7c610aa2018-10-27 00:01:01 +080069 mHandler = handler;
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() {
Philip Quinnd90413a2019-03-14 10:38:31 -070086 // GestureDetector records statistics about gesture classification events to inform gesture
87 // usage trends. SystemGesturesPointerEventListener creates a lot of noise in these
88 // statistics because it passes every touch event though a GestureDetector. By creating an
89 // anonymous subclass of GestureDetector, these statistics will be recorded with a unique
90 // source name that can be filtered.
91 mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {};
Michael Wrighta4d22d72015-09-16 23:19:26 +010092 }
93
John Spurlock57306e62013-04-22 09:48:49 -040094 @Override
Craig Mautner037aa8d2013-06-07 10:35:44 -070095 public void onPointerEvent(MotionEvent event) {
Dennis Kempinfb5168a2016-01-04 17:06:53 -080096 if (mGestureDetector != null && event.isTouchEvent()) {
Michael Wrighta4d22d72015-09-16 23:19:26 +010097 mGestureDetector.onTouchEvent(event);
98 }
John Spurlock57306e62013-04-22 09:48:49 -040099 switch (event.getActionMasked()) {
100 case MotionEvent.ACTION_DOWN:
John Spurlockad3e6cb2013-04-30 08:47:43 -0400101 mSwipeFireable = true;
102 mDebugFireable = true;
John Spurlock57306e62013-04-22 09:48:49 -0400103 mDownPointers = 0;
104 captureDown(event, 0);
Jun Mukaid2e7e352015-07-22 17:14:02 -0700105 if (mMouseHoveringAtEdge) {
106 mMouseHoveringAtEdge = false;
107 mCallbacks.onMouseLeaveFromEdge();
108 }
Adrian Roos3595be42015-03-05 16:31:15 +0100109 mCallbacks.onDown();
John Spurlock57306e62013-04-22 09:48:49 -0400110 break;
111 case MotionEvent.ACTION_POINTER_DOWN:
112 captureDown(event, event.getActionIndex());
John Spurlockad3e6cb2013-04-30 08:47:43 -0400113 if (mDebugFireable) {
114 mDebugFireable = event.getPointerCount() < 5;
115 if (!mDebugFireable) {
116 if (DEBUG) Slog.d(TAG, "Firing debug");
117 mCallbacks.onDebug();
118 }
119 }
John Spurlock57306e62013-04-22 09:48:49 -0400120 break;
121 case MotionEvent.ACTION_MOVE:
John Spurlockad3e6cb2013-04-30 08:47:43 -0400122 if (mSwipeFireable) {
123 final int swipe = detectSwipe(event);
124 mSwipeFireable = swipe == SWIPE_NONE;
125 if (swipe == SWIPE_FROM_TOP) {
126 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
127 mCallbacks.onSwipeFromTop();
128 } else if (swipe == SWIPE_FROM_BOTTOM) {
129 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
130 mCallbacks.onSwipeFromBottom();
131 } else if (swipe == SWIPE_FROM_RIGHT) {
132 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
133 mCallbacks.onSwipeFromRight();
Adrian Roos85d202b2016-06-02 16:27:47 -0700134 } else if (swipe == SWIPE_FROM_LEFT) {
135 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
136 mCallbacks.onSwipeFromLeft();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400137 }
John Spurlock57306e62013-04-22 09:48:49 -0400138 }
139 break;
Jun Mukaid2e7e352015-07-22 17:14:02 -0700140 case MotionEvent.ACTION_HOVER_MOVE:
Jun Mukaic5bd9bc2015-10-15 02:56:15 -0700141 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
Jun Mukaid2e7e352015-07-22 17:14:02 -0700142 if (!mMouseHoveringAtEdge && event.getY() == 0) {
143 mCallbacks.onMouseHoverAtTop();
144 mMouseHoveringAtEdge = true;
145 } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
146 mCallbacks.onMouseHoverAtBottom();
147 mMouseHoveringAtEdge = true;
148 } else if (mMouseHoveringAtEdge
149 && (event.getY() > 0 && event.getY() < screenHeight - 1)) {
150 mCallbacks.onMouseLeaveFromEdge();
151 mMouseHoveringAtEdge = false;
152 }
153 }
154 break;
John Spurlock57306e62013-04-22 09:48:49 -0400155 case MotionEvent.ACTION_UP:
156 case MotionEvent.ACTION_CANCEL:
John Spurlockad3e6cb2013-04-30 08:47:43 -0400157 mSwipeFireable = false;
158 mDebugFireable = false;
Adrian Roos3595be42015-03-05 16:31:15 +0100159 mCallbacks.onUpOrCancel();
John Spurlock57306e62013-04-22 09:48:49 -0400160 break;
161 default:
162 if (DEBUG) Slog.d(TAG, "Ignoring " + event);
163 }
164 }
165
166 private void captureDown(MotionEvent event, int pointerIndex) {
167 final int pointerId = event.getPointerId(pointerIndex);
168 final int i = findIndex(pointerId);
Tiger Huang7c610aa2018-10-27 00:01:01 +0800169 if (DEBUG) Slog.d(TAG, "pointer " + pointerId
170 + " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
John Spurlock57306e62013-04-22 09:48:49 -0400171 if (i != UNTRACKED_POINTER) {
John Spurlockad3e6cb2013-04-30 08:47:43 -0400172 mDownX[i] = event.getX(pointerIndex);
John Spurlock57306e62013-04-22 09:48:49 -0400173 mDownY[i] = event.getY(pointerIndex);
174 mDownTime[i] = event.getEventTime();
Tiger Huang7c610aa2018-10-27 00:01:01 +0800175 if (DEBUG) Slog.d(TAG, "pointer " + pointerId
176 + " down x=" + mDownX[i] + " y=" + mDownY[i]);
John Spurlock57306e62013-04-22 09:48:49 -0400177 }
178 }
179
180 private int findIndex(int pointerId) {
181 for (int i = 0; i < mDownPointers; i++) {
182 if (mDownPointerId[i] == pointerId) {
183 return i;
184 }
185 }
186 if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
187 return UNTRACKED_POINTER;
188 }
189 mDownPointerId[mDownPointers++] = pointerId;
190 return mDownPointers - 1;
191 }
192
John Spurlockad3e6cb2013-04-30 08:47:43 -0400193 private int detectSwipe(MotionEvent move) {
John Spurlock57306e62013-04-22 09:48:49 -0400194 final int historySize = move.getHistorySize();
195 final int pointerCount = move.getPointerCount();
196 for (int p = 0; p < pointerCount; p++) {
197 final int pointerId = move.getPointerId(p);
198 final int i = findIndex(pointerId);
199 if (i != UNTRACKED_POINTER) {
200 for (int h = 0; h < historySize; h++) {
201 final long time = move.getHistoricalEventTime(h);
John Spurlockad3e6cb2013-04-30 08:47:43 -0400202 final float x = move.getHistoricalX(p, h);
John Spurlock57306e62013-04-22 09:48:49 -0400203 final float y = move.getHistoricalY(p, h);
John Spurlockad3e6cb2013-04-30 08:47:43 -0400204 final int swipe = detectSwipe(i, time, x, y);
205 if (swipe != SWIPE_NONE) {
206 return swipe;
John Spurlock57306e62013-04-22 09:48:49 -0400207 }
208 }
John Spurlockad3e6cb2013-04-30 08:47:43 -0400209 final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
210 if (swipe != SWIPE_NONE) {
211 return swipe;
John Spurlock57306e62013-04-22 09:48:49 -0400212 }
213 }
214 }
John Spurlockad3e6cb2013-04-30 08:47:43 -0400215 return SWIPE_NONE;
John Spurlock57306e62013-04-22 09:48:49 -0400216 }
217
John Spurlockad3e6cb2013-04-30 08:47:43 -0400218 private int detectSwipe(int i, long time, float x, float y) {
219 final float fromX = mDownX[i];
John Spurlock57306e62013-04-22 09:48:49 -0400220 final float fromY = mDownY[i];
221 final long elapsed = time - mDownTime[i];
222 if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
John Spurlockad3e6cb2013-04-30 08:47:43 -0400223 + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
224 if (fromY <= mSwipeStartThreshold
John Spurlock9ba21fd2013-07-29 08:57:32 -0400225 && y > fromY + mSwipeDistanceThreshold
John Spurlockad3e6cb2013-04-30 08:47:43 -0400226 && elapsed < SWIPE_TIMEOUT_MS) {
227 return SWIPE_FROM_TOP;
228 }
229 if (fromY >= screenHeight - mSwipeStartThreshold
John Spurlock9ba21fd2013-07-29 08:57:32 -0400230 && y < fromY - mSwipeDistanceThreshold
John Spurlockad3e6cb2013-04-30 08:47:43 -0400231 && elapsed < SWIPE_TIMEOUT_MS) {
232 return SWIPE_FROM_BOTTOM;
233 }
234 if (fromX >= screenWidth - mSwipeStartThreshold
John Spurlock9ba21fd2013-07-29 08:57:32 -0400235 && x < fromX - mSwipeDistanceThreshold
John Spurlockad3e6cb2013-04-30 08:47:43 -0400236 && elapsed < SWIPE_TIMEOUT_MS) {
237 return SWIPE_FROM_RIGHT;
238 }
Adrian Roos85d202b2016-06-02 16:27:47 -0700239 if (fromX <= mSwipeStartThreshold
240 && x > fromX + mSwipeDistanceThreshold
241 && elapsed < SWIPE_TIMEOUT_MS) {
242 return SWIPE_FROM_LEFT;
243 }
John Spurlockad3e6cb2013-04-30 08:47:43 -0400244 return SWIPE_NONE;
John Spurlock57306e62013-04-22 09:48:49 -0400245 }
246
Michael Wrighta4d22d72015-09-16 23:19:26 +0100247 private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
Tiger Huang7c610aa2018-10-27 00:01:01 +0800248
249 private OverScroller mOverscroller;
250
251 FlingGestureDetector() {
252 mOverscroller = new OverScroller(mContext);
253 }
254
Michael Wrighta4d22d72015-09-16 23:19:26 +0100255 @Override
256 public boolean onSingleTapUp(MotionEvent e) {
257 if (!mOverscroller.isFinished()) {
258 mOverscroller.forceFinished(true);
259 }
260 return true;
261 }
262 @Override
263 public boolean onFling(MotionEvent down, MotionEvent up,
264 float velocityX, float velocityY) {
265 mOverscroller.computeScrollOffset();
266 long now = SystemClock.uptimeMillis();
267
268 if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
269 mOverscroller.forceFinished(true);
270 }
271 mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
272 Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
273 int duration = mOverscroller.getDuration();
274 if (duration > MAX_FLING_TIME_MILLIS) {
275 duration = MAX_FLING_TIME_MILLIS;
276 }
277 mLastFlingTime = now;
278 mCallbacks.onFling(duration);
279 return true;
280 }
281 }
282
John Spurlock57306e62013-04-22 09:48:49 -0400283 interface Callbacks {
284 void onSwipeFromTop();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400285 void onSwipeFromBottom();
286 void onSwipeFromRight();
Adrian Roos85d202b2016-06-02 16:27:47 -0700287 void onSwipeFromLeft();
Michael Wrighta4d22d72015-09-16 23:19:26 +0100288 void onFling(int durationMs);
Adrian Roos3595be42015-03-05 16:31:15 +0100289 void onDown();
290 void onUpOrCancel();
Jun Mukaid2e7e352015-07-22 17:14:02 -0700291 void onMouseHoverAtTop();
292 void onMouseHoverAtBottom();
293 void onMouseLeaveFromEdge();
John Spurlockad3e6cb2013-04-30 08:47:43 -0400294 void onDebug();
John Spurlock57306e62013-04-22 09:48:49 -0400295 }
296}