OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / policy / SystemGesturesPointerEventListener.java
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
17 package com.android.server.policy;
18
19 import android.content.Context;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.SystemClock;
23 import android.util.Slog;
24 import android.view.GestureDetector;
25 import android.view.InputDevice;
26 import android.view.MotionEvent;
27 import android.view.WindowManagerPolicy.PointerEventListener;
28 import android.widget.OverScroller;
29
30 /*
31  * Listens for system-wide input gestures, firing callbacks when detected.
32  * @hide
33  */
34 public class SystemGesturesPointerEventListener implements PointerEventListener {
35     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;
40     private static final int MAX_FLING_TIME_MILLIS = 5000;
41
42     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;
46
47     private final Context mContext;
48     private final int mSwipeStartThreshold;
49     private final int mSwipeDistanceThreshold;
50     private final Callbacks mCallbacks;
51     private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
52     private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
53     private final float[] mDownY = new float[MAX_TRACKED_POINTERS];
54     private final long[] mDownTime = new long[MAX_TRACKED_POINTERS];
55
56     private GestureDetector mGestureDetector;
57     private OverScroller mOverscroller;
58
59     int screenHeight;
60     int screenWidth;
61     private int mDownPointers;
62     private boolean mSwipeFireable;
63     private boolean mDebugFireable;
64     private boolean mMouseHoveringAtEdge;
65     private long mLastFlingTime;
66
67     public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
68         mContext = context;
69         mCallbacks = checkNull("callbacks", callbacks);
70         mSwipeStartThreshold = checkNull("context", context).getResources()
71                 .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
72         mSwipeDistanceThreshold = mSwipeStartThreshold;
73         if (DEBUG) Slog.d(TAG,  "mSwipeStartThreshold=" + mSwipeStartThreshold
74                 + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
75     }
76
77     private static <T> T checkNull(String name, T arg) {
78         if (arg == null) {
79             throw new IllegalArgumentException(name + " must not be null");
80         }
81         return arg;
82     }
83
84     public void systemReady() {
85         Handler h = new Handler(Looper.myLooper());
86         mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
87         mOverscroller = new OverScroller(mContext);
88     }
89
90     @Override
91     public void onPointerEvent(MotionEvent event) {
92         if (mGestureDetector != null && event.isTouchEvent()) {
93             mGestureDetector.onTouchEvent(event);
94         }
95         switch (event.getActionMasked()) {
96             case MotionEvent.ACTION_DOWN:
97                 mSwipeFireable = true;
98                 mDebugFireable = true;
99                 mDownPointers = 0;
100                 captureDown(event, 0);
101                 if (mMouseHoveringAtEdge) {
102                     mMouseHoveringAtEdge = false;
103                     mCallbacks.onMouseLeaveFromEdge();
104                 }
105                 mCallbacks.onDown();
106                 break;
107             case MotionEvent.ACTION_POINTER_DOWN:
108                 captureDown(event, event.getActionIndex());
109                 if (mDebugFireable) {
110                     mDebugFireable = event.getPointerCount() < 5;
111                     if (!mDebugFireable) {
112                         if (DEBUG) Slog.d(TAG, "Firing debug");
113                         mCallbacks.onDebug();
114                     }
115                 }
116                 break;
117             case MotionEvent.ACTION_MOVE:
118                 if (mSwipeFireable) {
119                     final int swipe = detectSwipe(event);
120                     mSwipeFireable = swipe == SWIPE_NONE;
121                     if (swipe == SWIPE_FROM_TOP) {
122                         if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
123                         mCallbacks.onSwipeFromTop();
124                     } else if (swipe == SWIPE_FROM_BOTTOM) {
125                         if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
126                         mCallbacks.onSwipeFromBottom();
127                     } else if (swipe == SWIPE_FROM_RIGHT) {
128                         if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
129                         mCallbacks.onSwipeFromRight();
130                     }
131                 }
132                 break;
133             case MotionEvent.ACTION_HOVER_MOVE:
134                 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
135                     if (!mMouseHoveringAtEdge && event.getY() == 0) {
136                         mCallbacks.onMouseHoverAtTop();
137                         mMouseHoveringAtEdge = true;
138                     } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
139                         mCallbacks.onMouseHoverAtBottom();
140                         mMouseHoveringAtEdge = true;
141                     } else if (mMouseHoveringAtEdge
142                             && (event.getY() > 0 && event.getY() < screenHeight - 1)) {
143                         mCallbacks.onMouseLeaveFromEdge();
144                         mMouseHoveringAtEdge = false;
145                     }
146                 }
147                 break;
148             case MotionEvent.ACTION_UP:
149             case MotionEvent.ACTION_CANCEL:
150                 mSwipeFireable = false;
151                 mDebugFireable = false;
152                 mCallbacks.onUpOrCancel();
153                 break;
154             default:
155                 if (DEBUG) Slog.d(TAG, "Ignoring " + event);
156         }
157     }
158
159     private void captureDown(MotionEvent event, int pointerIndex) {
160         final int pointerId = event.getPointerId(pointerIndex);
161         final int i = findIndex(pointerId);
162         if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
163                 " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
164         if (i != UNTRACKED_POINTER) {
165             mDownX[i] = event.getX(pointerIndex);
166             mDownY[i] = event.getY(pointerIndex);
167             mDownTime[i] = event.getEventTime();
168             if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
169                     " down x=" + mDownX[i] + " y=" + mDownY[i]);
170         }
171     }
172
173     private int findIndex(int pointerId) {
174         for (int i = 0; i < mDownPointers; i++) {
175             if (mDownPointerId[i] == pointerId) {
176                 return i;
177             }
178         }
179         if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
180             return UNTRACKED_POINTER;
181         }
182         mDownPointerId[mDownPointers++] = pointerId;
183         return mDownPointers - 1;
184     }
185
186     private int detectSwipe(MotionEvent move) {
187         final int historySize = move.getHistorySize();
188         final int pointerCount = move.getPointerCount();
189         for (int p = 0; p < pointerCount; p++) {
190             final int pointerId = move.getPointerId(p);
191             final int i = findIndex(pointerId);
192             if (i != UNTRACKED_POINTER) {
193                 for (int h = 0; h < historySize; h++) {
194                     final long time = move.getHistoricalEventTime(h);
195                     final float x = move.getHistoricalX(p, h);
196                     final float y = move.getHistoricalY(p,  h);
197                     final int swipe = detectSwipe(i, time, x, y);
198                     if (swipe != SWIPE_NONE) {
199                         return swipe;
200                     }
201                 }
202                 final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
203                 if (swipe != SWIPE_NONE) {
204                     return swipe;
205                 }
206             }
207         }
208         return SWIPE_NONE;
209     }
210
211     private int detectSwipe(int i, long time, float x, float y) {
212         final float fromX = mDownX[i];
213         final float fromY = mDownY[i];
214         final long elapsed = time - mDownTime[i];
215         if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
216                 + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
217         if (fromY <= mSwipeStartThreshold
218                 && y > fromY + mSwipeDistanceThreshold
219                 && elapsed < SWIPE_TIMEOUT_MS) {
220             return SWIPE_FROM_TOP;
221         }
222         if (fromY >= screenHeight - mSwipeStartThreshold
223                 && y < fromY - mSwipeDistanceThreshold
224                 && elapsed < SWIPE_TIMEOUT_MS) {
225             return SWIPE_FROM_BOTTOM;
226         }
227         if (fromX >= screenWidth - mSwipeStartThreshold
228                 && x < fromX - mSwipeDistanceThreshold
229                 && elapsed < SWIPE_TIMEOUT_MS) {
230             return SWIPE_FROM_RIGHT;
231         }
232         return SWIPE_NONE;
233     }
234
235     private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
236         @Override
237         public boolean onSingleTapUp(MotionEvent e) {
238             if (!mOverscroller.isFinished()) {
239                 mOverscroller.forceFinished(true);
240             }
241             return true;
242         }
243         @Override
244         public boolean onFling(MotionEvent down, MotionEvent up,
245                 float velocityX, float velocityY) {
246             mOverscroller.computeScrollOffset();
247             long now = SystemClock.uptimeMillis();
248
249             if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
250                 mOverscroller.forceFinished(true);
251             }
252             mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
253                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
254             int duration = mOverscroller.getDuration();
255             if (duration > MAX_FLING_TIME_MILLIS) {
256                 duration = MAX_FLING_TIME_MILLIS;
257             }
258             mLastFlingTime = now;
259             mCallbacks.onFling(duration);
260             return true;
261         }
262     }
263
264     interface Callbacks {
265         void onSwipeFromTop();
266         void onSwipeFromBottom();
267         void onSwipeFromRight();
268         void onFling(int durationMs);
269         void onDown();
270         void onUpOrCancel();
271         void onMouseHoverAtTop();
272         void onMouseHoverAtBottom();
273         void onMouseLeaveFromEdge();
274         void onDebug();
275     }
276 }