SystemGesturesPointerEventListener.java revision 3595be4d19caaa7ddfbff0b979d135aaf5ac20b1
1/* 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 17package com.android.server.policy; 18 19import android.content.Context; 20import android.util.Slog; 21import android.view.MotionEvent; 22import android.view.WindowManagerPolicy.PointerEventListener; 23 24/* 25 * Listens for system-wide input gestures, firing callbacks when detected. 26 * @hide 27 */ 28public class SystemGesturesPointerEventListener implements PointerEventListener { 29 private static final String TAG = "SystemGestures"; 30 private static final boolean DEBUG = false; 31 private static final long SWIPE_TIMEOUT_MS = 500; 32 private static final int MAX_TRACKED_POINTERS = 32; // max per input system 33 private static final int UNTRACKED_POINTER = -1; 34 35 private static final int SWIPE_NONE = 0; 36 private static final int SWIPE_FROM_TOP = 1; 37 private static final int SWIPE_FROM_BOTTOM = 2; 38 private static final int SWIPE_FROM_RIGHT = 3; 39 40 private final int mSwipeStartThreshold; 41 private final int mSwipeDistanceThreshold; 42 private final Callbacks mCallbacks; 43 private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS]; 44 private final float[] mDownX = new float[MAX_TRACKED_POINTERS]; 45 private final float[] mDownY = new float[MAX_TRACKED_POINTERS]; 46 private final long[] mDownTime = new long[MAX_TRACKED_POINTERS]; 47 48 int screenHeight; 49 int screenWidth; 50 private int mDownPointers; 51 private boolean mSwipeFireable; 52 private boolean mDebugFireable; 53 54 public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) { 55 mCallbacks = checkNull("callbacks", callbacks); 56 mSwipeStartThreshold = checkNull("context", context).getResources() 57 .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 58 mSwipeDistanceThreshold = mSwipeStartThreshold; 59 if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold 60 + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold); 61 } 62 63 private static <T> T checkNull(String name, T arg) { 64 if (arg == null) { 65 throw new IllegalArgumentException(name + " must not be null"); 66 } 67 return arg; 68 } 69 70 @Override 71 public void onPointerEvent(MotionEvent event) { 72 switch (event.getActionMasked()) { 73 case MotionEvent.ACTION_DOWN: 74 mSwipeFireable = true; 75 mDebugFireable = true; 76 mDownPointers = 0; 77 captureDown(event, 0); 78 mCallbacks.onDown(); 79 break; 80 case MotionEvent.ACTION_POINTER_DOWN: 81 captureDown(event, event.getActionIndex()); 82 if (mDebugFireable) { 83 mDebugFireable = event.getPointerCount() < 5; 84 if (!mDebugFireable) { 85 if (DEBUG) Slog.d(TAG, "Firing debug"); 86 mCallbacks.onDebug(); 87 } 88 } 89 break; 90 case MotionEvent.ACTION_MOVE: 91 if (mSwipeFireable) { 92 final int swipe = detectSwipe(event); 93 mSwipeFireable = swipe == SWIPE_NONE; 94 if (swipe == SWIPE_FROM_TOP) { 95 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop"); 96 mCallbacks.onSwipeFromTop(); 97 } else if (swipe == SWIPE_FROM_BOTTOM) { 98 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom"); 99 mCallbacks.onSwipeFromBottom(); 100 } else if (swipe == SWIPE_FROM_RIGHT) { 101 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight"); 102 mCallbacks.onSwipeFromRight(); 103 } 104 } 105 break; 106 case MotionEvent.ACTION_UP: 107 case MotionEvent.ACTION_CANCEL: 108 mSwipeFireable = false; 109 mDebugFireable = false; 110 mCallbacks.onUpOrCancel(); 111 break; 112 default: 113 if (DEBUG) Slog.d(TAG, "Ignoring " + event); 114 } 115 } 116 117 private void captureDown(MotionEvent event, int pointerIndex) { 118 final int pointerId = event.getPointerId(pointerIndex); 119 final int i = findIndex(pointerId); 120 if (DEBUG) Slog.d(TAG, "pointer " + pointerId + 121 " down pointerIndex=" + pointerIndex + " trackingIndex=" + i); 122 if (i != UNTRACKED_POINTER) { 123 mDownX[i] = event.getX(pointerIndex); 124 mDownY[i] = event.getY(pointerIndex); 125 mDownTime[i] = event.getEventTime(); 126 if (DEBUG) Slog.d(TAG, "pointer " + pointerId + 127 " down x=" + mDownX[i] + " y=" + mDownY[i]); 128 } 129 } 130 131 private int findIndex(int pointerId) { 132 for (int i = 0; i < mDownPointers; i++) { 133 if (mDownPointerId[i] == pointerId) { 134 return i; 135 } 136 } 137 if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) { 138 return UNTRACKED_POINTER; 139 } 140 mDownPointerId[mDownPointers++] = pointerId; 141 return mDownPointers - 1; 142 } 143 144 private int detectSwipe(MotionEvent move) { 145 final int historySize = move.getHistorySize(); 146 final int pointerCount = move.getPointerCount(); 147 for (int p = 0; p < pointerCount; p++) { 148 final int pointerId = move.getPointerId(p); 149 final int i = findIndex(pointerId); 150 if (i != UNTRACKED_POINTER) { 151 for (int h = 0; h < historySize; h++) { 152 final long time = move.getHistoricalEventTime(h); 153 final float x = move.getHistoricalX(p, h); 154 final float y = move.getHistoricalY(p, h); 155 final int swipe = detectSwipe(i, time, x, y); 156 if (swipe != SWIPE_NONE) { 157 return swipe; 158 } 159 } 160 final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p)); 161 if (swipe != SWIPE_NONE) { 162 return swipe; 163 } 164 } 165 } 166 return SWIPE_NONE; 167 } 168 169 private int detectSwipe(int i, long time, float x, float y) { 170 final float fromX = mDownX[i]; 171 final float fromY = mDownY[i]; 172 final long elapsed = time - mDownTime[i]; 173 if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i] 174 + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed); 175 if (fromY <= mSwipeStartThreshold 176 && y > fromY + mSwipeDistanceThreshold 177 && elapsed < SWIPE_TIMEOUT_MS) { 178 return SWIPE_FROM_TOP; 179 } 180 if (fromY >= screenHeight - mSwipeStartThreshold 181 && y < fromY - mSwipeDistanceThreshold 182 && elapsed < SWIPE_TIMEOUT_MS) { 183 return SWIPE_FROM_BOTTOM; 184 } 185 if (fromX >= screenWidth - mSwipeStartThreshold 186 && x < fromX - mSwipeDistanceThreshold 187 && elapsed < SWIPE_TIMEOUT_MS) { 188 return SWIPE_FROM_RIGHT; 189 } 190 return SWIPE_NONE; 191 } 192 193 interface Callbacks { 194 void onSwipeFromTop(); 195 void onSwipeFromBottom(); 196 void onSwipeFromRight(); 197 void onDown(); 198 void onUpOrCancel(); 199 void onDebug(); 200 } 201} 202