SystemGesturesPointerEventListener.java revision 037aa8d434984840691378f3cc7d99d63dcc4076
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.internal.policy.impl; 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 mSwipeEndThreshold; 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 mSwipeEndThreshold = mSwipeStartThreshold * 2; 59 } 60 61 private static <T> T checkNull(String name, T arg) { 62 if (arg == null) { 63 throw new IllegalArgumentException(name + " must not be null"); 64 } 65 return arg; 66 } 67 68 @Override 69 public void onPointerEvent(MotionEvent event) { 70 switch (event.getActionMasked()) { 71 case MotionEvent.ACTION_DOWN: 72 mSwipeFireable = true; 73 mDebugFireable = true; 74 mDownPointers = 0; 75 captureDown(event, 0); 76 break; 77 case MotionEvent.ACTION_POINTER_DOWN: 78 captureDown(event, event.getActionIndex()); 79 if (mDebugFireable) { 80 mDebugFireable = event.getPointerCount() < 5; 81 if (!mDebugFireable) { 82 if (DEBUG) Slog.d(TAG, "Firing debug"); 83 mCallbacks.onDebug(); 84 } 85 } 86 break; 87 case MotionEvent.ACTION_MOVE: 88 if (mSwipeFireable) { 89 final int swipe = detectSwipe(event); 90 mSwipeFireable = swipe == SWIPE_NONE; 91 if (swipe == SWIPE_FROM_TOP) { 92 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop"); 93 mCallbacks.onSwipeFromTop(); 94 } else if (swipe == SWIPE_FROM_BOTTOM) { 95 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom"); 96 mCallbacks.onSwipeFromBottom(); 97 } else if (swipe == SWIPE_FROM_RIGHT) { 98 if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight"); 99 mCallbacks.onSwipeFromRight(); 100 } 101 } 102 break; 103 case MotionEvent.ACTION_UP: 104 case MotionEvent.ACTION_CANCEL: 105 mSwipeFireable = false; 106 mDebugFireable = false; 107 break; 108 default: 109 if (DEBUG) Slog.d(TAG, "Ignoring " + event); 110 } 111 } 112 113 private void captureDown(MotionEvent event, int pointerIndex) { 114 final int pointerId = event.getPointerId(pointerIndex); 115 final int i = findIndex(pointerId); 116 if (DEBUG) Slog.d(TAG, "pointer " + pointerId + 117 " down pointerIndex=" + pointerIndex + " trackingIndex=" + i); 118 if (i != UNTRACKED_POINTER) { 119 mDownX[i] = event.getX(pointerIndex); 120 mDownY[i] = event.getY(pointerIndex); 121 mDownTime[i] = event.getEventTime(); 122 if (DEBUG) Slog.d(TAG, "pointer " + pointerId + 123 " down x=" + mDownX[i] + " y=" + mDownY[i]); 124 } 125 } 126 127 private int findIndex(int pointerId) { 128 for (int i = 0; i < mDownPointers; i++) { 129 if (mDownPointerId[i] == pointerId) { 130 return i; 131 } 132 } 133 if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) { 134 return UNTRACKED_POINTER; 135 } 136 mDownPointerId[mDownPointers++] = pointerId; 137 return mDownPointers - 1; 138 } 139 140 private int detectSwipe(MotionEvent move) { 141 final int historySize = move.getHistorySize(); 142 final int pointerCount = move.getPointerCount(); 143 for (int p = 0; p < pointerCount; p++) { 144 final int pointerId = move.getPointerId(p); 145 final int i = findIndex(pointerId); 146 if (i != UNTRACKED_POINTER) { 147 for (int h = 0; h < historySize; h++) { 148 final long time = move.getHistoricalEventTime(h); 149 final float x = move.getHistoricalX(p, h); 150 final float y = move.getHistoricalY(p, h); 151 final int swipe = detectSwipe(i, time, x, y); 152 if (swipe != SWIPE_NONE) { 153 return swipe; 154 } 155 } 156 final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p)); 157 if (swipe != SWIPE_NONE) { 158 return swipe; 159 } 160 } 161 } 162 return SWIPE_NONE; 163 } 164 165 private int detectSwipe(int i, long time, float x, float y) { 166 final float fromX = mDownX[i]; 167 final float fromY = mDownY[i]; 168 final long elapsed = time - mDownTime[i]; 169 if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i] 170 + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed); 171 if (fromY <= mSwipeStartThreshold 172 && y > fromY + mSwipeEndThreshold 173 && elapsed < SWIPE_TIMEOUT_MS) { 174 return SWIPE_FROM_TOP; 175 } 176 if (fromY >= screenHeight - mSwipeStartThreshold 177 && y < fromY - mSwipeEndThreshold 178 && elapsed < SWIPE_TIMEOUT_MS) { 179 return SWIPE_FROM_BOTTOM; 180 } 181 if (fromX >= screenWidth - mSwipeStartThreshold 182 && x < fromX - mSwipeEndThreshold 183 && elapsed < SWIPE_TIMEOUT_MS) { 184 return SWIPE_FROM_RIGHT; 185 } 186 return SWIPE_NONE; 187 } 188 189 interface Callbacks { 190 void onSwipeFromTop(); 191 void onSwipeFromBottom(); 192 void onSwipeFromRight(); 193 void onDebug(); 194 } 195} 196