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