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 1757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockpackage com.android.internal.policy.impl; 1857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 1957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.content.Context; 2057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.util.Slog; 2157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlockimport android.view.MotionEvent; 22037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.view.WindowManagerPolicy.PointerEventListener; 2357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 2457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock/* 2557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * Listens for system-wide input gestures, firing callbacks when detected. 2657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock * @hide 2757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock */ 28037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerpublic class SystemGesturesPointerEventListener implements PointerEventListener { 2957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private static final String TAG = "SystemGestures"; 3057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private static final boolean DEBUG = false; 3157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private static final long SWIPE_TIMEOUT_MS = 500; 3257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private static final int MAX_TRACKED_POINTERS = 32; // max per input system 3357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private static final int UNTRACKED_POINTER = -1; 3457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 35ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private static final int SWIPE_NONE = 0; 36ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private static final int SWIPE_FROM_TOP = 1; 37ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private static final int SWIPE_FROM_BOTTOM = 2; 38ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private static final int SWIPE_FROM_RIGHT = 3; 39ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock 4057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private final int mSwipeStartThreshold; 419ba21fdc9ddf1d132215d29054b55af416561367John Spurlock private final int mSwipeDistanceThreshold; 4257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private final Callbacks mCallbacks; 4357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS]; 44ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private final float[] mDownX = new float[MAX_TRACKED_POINTERS]; 4557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private final float[] mDownY = new float[MAX_TRACKED_POINTERS]; 4657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private final long[] mDownTime = new long[MAX_TRACKED_POINTERS]; 4757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 48ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock int screenHeight; 49ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock int screenWidth; 5057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private int mDownPointers; 51ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private boolean mSwipeFireable; 52ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private boolean mDebugFireable; 5357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 54037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) { 5557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock mCallbacks = checkNull("callbacks", callbacks); 5657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock mSwipeStartThreshold = checkNull("context", context).getResources() 5757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 589ba21fdc9ddf1d132215d29054b55af416561367John Spurlock mSwipeDistanceThreshold = mSwipeStartThreshold; 599ba21fdc9ddf1d132215d29054b55af416561367John Spurlock if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold 609ba21fdc9ddf1d132215d29054b55af416561367John Spurlock + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold); 6157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 6257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 6357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private static <T> T checkNull(String name, T arg) { 6457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (arg == null) { 6557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock throw new IllegalArgumentException(name + " must not be null"); 6657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 6757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock return arg; 6857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 6957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 7057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock @Override 71037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner public void onPointerEvent(MotionEvent event) { 7257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock switch (event.getActionMasked()) { 7357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock case MotionEvent.ACTION_DOWN: 74ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mSwipeFireable = true; 75ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mDebugFireable = true; 7657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock mDownPointers = 0; 7757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock captureDown(event, 0); 7857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock break; 7957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock case MotionEvent.ACTION_POINTER_DOWN: 8057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock captureDown(event, event.getActionIndex()); 81ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (mDebugFireable) { 82ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mDebugFireable = event.getPointerCount() < 5; 83ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (!mDebugFireable) { 84ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (DEBUG) Slog.d(TAG, "Firing debug"); 85ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mCallbacks.onDebug(); 86ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } 87ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } 8857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock break; 8957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock case MotionEvent.ACTION_MOVE: 90ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (mSwipeFireable) { 91ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock final int swipe = detectSwipe(event); 92ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mSwipeFireable = swipe == SWIPE_NONE; 93ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (swipe == SWIPE_FROM_TOP) { 94ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop"); 95ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mCallbacks.onSwipeFromTop(); 96ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } else if (swipe == SWIPE_FROM_BOTTOM) { 97ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom"); 98ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mCallbacks.onSwipeFromBottom(); 99ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } else if (swipe == SWIPE_FROM_RIGHT) { 100ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight"); 101ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mCallbacks.onSwipeFromRight(); 102ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } 10357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 10457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock break; 10557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock case MotionEvent.ACTION_UP: 10657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock case MotionEvent.ACTION_CANCEL: 107ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mSwipeFireable = false; 108ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mDebugFireable = false; 10957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock break; 11057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock default: 11157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (DEBUG) Slog.d(TAG, "Ignoring " + event); 11257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 11357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 11457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 11557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private void captureDown(MotionEvent event, int pointerIndex) { 11657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final int pointerId = event.getPointerId(pointerIndex); 11757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final int i = findIndex(pointerId); 11857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (DEBUG) Slog.d(TAG, "pointer " + pointerId + 11957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock " down pointerIndex=" + pointerIndex + " trackingIndex=" + i); 12057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (i != UNTRACKED_POINTER) { 121ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock mDownX[i] = event.getX(pointerIndex); 12257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock mDownY[i] = event.getY(pointerIndex); 12357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock mDownTime[i] = event.getEventTime(); 124ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (DEBUG) Slog.d(TAG, "pointer " + pointerId + 125ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock " down x=" + mDownX[i] + " y=" + mDownY[i]); 12657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 12757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 12857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 12957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock private int findIndex(int pointerId) { 13057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock for (int i = 0; i < mDownPointers; i++) { 13157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (mDownPointerId[i] == pointerId) { 13257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock return i; 13357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 13457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 13557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) { 13657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock return UNTRACKED_POINTER; 13757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 13857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock mDownPointerId[mDownPointers++] = pointerId; 13957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock return mDownPointers - 1; 14057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 14157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 142ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private int detectSwipe(MotionEvent move) { 14357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final int historySize = move.getHistorySize(); 14457306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final int pointerCount = move.getPointerCount(); 14557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock for (int p = 0; p < pointerCount; p++) { 14657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final int pointerId = move.getPointerId(p); 14757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final int i = findIndex(pointerId); 14857306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (i != UNTRACKED_POINTER) { 14957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock for (int h = 0; h < historySize; h++) { 15057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final long time = move.getHistoricalEventTime(h); 151ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock final float x = move.getHistoricalX(p, h); 15257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final float y = move.getHistoricalY(p, h); 153ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock final int swipe = detectSwipe(i, time, x, y); 154ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (swipe != SWIPE_NONE) { 155ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return swipe; 15657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 15757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 158ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p)); 159ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (swipe != SWIPE_NONE) { 160ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return swipe; 16157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 16257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 16357306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 164ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return SWIPE_NONE; 16557306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 16657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 167ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock private int detectSwipe(int i, long time, float x, float y) { 168ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock final float fromX = mDownX[i]; 16957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final float fromY = mDownY[i]; 17057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock final long elapsed = time - mDownTime[i]; 17157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i] 172ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed); 173ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (fromY <= mSwipeStartThreshold 1749ba21fdc9ddf1d132215d29054b55af416561367John Spurlock && y > fromY + mSwipeDistanceThreshold 175ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock && elapsed < SWIPE_TIMEOUT_MS) { 176ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return SWIPE_FROM_TOP; 177ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } 178ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (fromY >= screenHeight - mSwipeStartThreshold 1799ba21fdc9ddf1d132215d29054b55af416561367John Spurlock && y < fromY - mSwipeDistanceThreshold 180ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock && elapsed < SWIPE_TIMEOUT_MS) { 181ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return SWIPE_FROM_BOTTOM; 182ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } 183ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock if (fromX >= screenWidth - mSwipeStartThreshold 1849ba21fdc9ddf1d132215d29054b55af416561367John Spurlock && x < fromX - mSwipeDistanceThreshold 185ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock && elapsed < SWIPE_TIMEOUT_MS) { 186ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return SWIPE_FROM_RIGHT; 187ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock } 188ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock return SWIPE_NONE; 18957306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 19057306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock 19157306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock interface Callbacks { 19257306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock void onSwipeFromTop(); 193ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock void onSwipeFromBottom(); 194ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock void onSwipeFromRight(); 195ad3e6cb4db99ad33fcfc61f236d37cd83446866dJohn Spurlock void onDebug(); 19657306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock } 19757306e6b79a87518b5739fc5717cd1cd47c75eaeJohn Spurlock} 198