• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.accessibility.gestures;
18 
19 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
20 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
21 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
22 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
23 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
24 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
25 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
26 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
27 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
28 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
29 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
30 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
31 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD;
32 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
33 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
34 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
35 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
36 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
37 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD;
38 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
39 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
40 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
41 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
42 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
43 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT;
44 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_UP;
45 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP;
46 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
47 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
48 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
49 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT;
50 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT;
51 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP;
52 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT;
53 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN;
54 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT;
55 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP;
56 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT;
57 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN;
58 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT;
59 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP;
60 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP;
61 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN;
62 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT;
63 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT;
64 
65 import static com.android.server.accessibility.gestures.Swipe.DOWN;
66 import static com.android.server.accessibility.gestures.Swipe.LEFT;
67 import static com.android.server.accessibility.gestures.Swipe.RIGHT;
68 import static com.android.server.accessibility.gestures.Swipe.UP;
69 import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
70 
71 import android.accessibilityservice.AccessibilityGestureEvent;
72 import android.content.Context;
73 import android.os.Handler;
74 import android.util.Slog;
75 import android.view.MotionEvent;
76 
77 import java.util.ArrayList;
78 import java.util.List;
79 
80 /**
81  * This class coordinates a series of individual gesture matchers to serve as a unified gesture
82  * detector. Gesture matchers are tied to a single gesture. It calls listener callback functions
83  * when a gesture starts or completes.
84  */
85 public class GestureManifold implements GestureMatcher.StateChangeListener {
86 
87     private static final String LOG_TAG = "GestureManifold";
88 
89     private final List<GestureMatcher> mGestures = new ArrayList<>();
90     private final Context mContext;
91     // Handler for performing asynchronous operations.
92     private final Handler mHandler;
93     // Listener to be notified of gesture start and end.
94     private Listener mListener;
95     // Whether double tap and double tap and hold will be dispatched to the service or handled in
96     // the framework.
97     private boolean mServiceHandlesDoubleTap = false;
98     // Whether multi-finger gestures are enabled.
99     boolean mMultiFingerGesturesEnabled;
100     // Whether the two-finger passthrough is enabled when multi-finger gestures are enabled.
101     private boolean mTwoFingerPassthroughEnabled;
102     // Whether to send the motion events during gesture dispatch.
103     private boolean mSendMotionEventsEnabled = false;
104     // A list of all the multi-finger gestures, for easy adding and removal.
105     private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>();
106     // A list of two-finger swipes, for easy adding and removal when turning on or off two-finger
107     // passthrough.
108     private final List<GestureMatcher> mTwoFingerSwipes = new ArrayList<>();
109     // The list of motion events for the current gesture.
110     private List<MotionEvent> mEvents = new ArrayList<>();
111     // Shared state information.
112     private TouchState mState;
113 
GestureManifold(Context context, Listener listener, TouchState state, Handler handler)114     public GestureManifold(Context context, Listener listener, TouchState state, Handler handler) {
115         mContext = context;
116         mHandler = handler;
117         mListener = listener;
118         mState = state;
119         mMultiFingerGesturesEnabled = false;
120         mTwoFingerPassthroughEnabled = false;
121         // Set up gestures.
122         // Start with double tap.
123         mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
124         mGestures.add(new MultiTapAndHold(context, 2, GESTURE_DOUBLE_TAP_AND_HOLD, this));
125         // Second-finger double tap.
126         mGestures.add(new SecondFingerMultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
127         // One-direction swipes.
128         mGestures.add(new Swipe(context, RIGHT, GESTURE_SWIPE_RIGHT, this));
129         mGestures.add(new Swipe(context, LEFT, GESTURE_SWIPE_LEFT, this));
130         mGestures.add(new Swipe(context, UP, GESTURE_SWIPE_UP, this));
131         mGestures.add(new Swipe(context, DOWN, GESTURE_SWIPE_DOWN, this));
132         // Two-direction swipes.
133         mGestures.add(new Swipe(context, LEFT, RIGHT, GESTURE_SWIPE_LEFT_AND_RIGHT, this));
134         mGestures.add(new Swipe(context, LEFT, UP, GESTURE_SWIPE_LEFT_AND_UP, this));
135         mGestures.add(new Swipe(context, LEFT, DOWN, GESTURE_SWIPE_LEFT_AND_DOWN, this));
136         mGestures.add(new Swipe(context, RIGHT, UP, GESTURE_SWIPE_RIGHT_AND_UP, this));
137         mGestures.add(new Swipe(context, RIGHT, DOWN, GESTURE_SWIPE_RIGHT_AND_DOWN, this));
138         mGestures.add(new Swipe(context, RIGHT, LEFT, GESTURE_SWIPE_RIGHT_AND_LEFT, this));
139         mGestures.add(new Swipe(context, DOWN, UP, GESTURE_SWIPE_DOWN_AND_UP, this));
140         mGestures.add(new Swipe(context, DOWN, LEFT, GESTURE_SWIPE_DOWN_AND_LEFT, this));
141         mGestures.add(new Swipe(context, DOWN, RIGHT, GESTURE_SWIPE_DOWN_AND_RIGHT, this));
142         mGestures.add(new Swipe(context, UP, DOWN, GESTURE_SWIPE_UP_AND_DOWN, this));
143         mGestures.add(new Swipe(context, UP, LEFT, GESTURE_SWIPE_UP_AND_LEFT, this));
144         mGestures.add(new Swipe(context, UP, RIGHT, GESTURE_SWIPE_UP_AND_RIGHT, this));
145         // Set up multi-finger gestures to be enabled later.
146         // Two-finger taps.
147         mMultiFingerGestures.add(
148                 new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
149         mMultiFingerGestures.add(
150                 new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
151         mMultiFingerGestures.add(
152                 new MultiFingerMultiTapAndHold(
153                         mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
154         mMultiFingerGestures.add(
155                 new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
156         mMultiFingerGestures.add(
157                 new MultiFingerMultiTapAndHold(
158                         mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, this));
159         // Three-finger taps.
160         mMultiFingerGestures.add(
161                 new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this));
162         mMultiFingerGestures.add(
163                 new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
164         mMultiFingerGestures.add(
165                 new MultiFingerMultiTapAndHold(
166                         mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, this));
167         mMultiFingerGestures.add(
168                 new MultiFingerMultiTapAndHold(
169                         mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this));
170         mMultiFingerGestures.add(
171                 new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
172         mMultiFingerGestures.add(
173                 new MultiFingerMultiTapAndHold(
174                         mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD, this));
175         mMultiFingerGestures.add(
176                 new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
177         // Four-finger taps.
178         mMultiFingerGestures.add(
179                 new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this));
180         mMultiFingerGestures.add(
181                 new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this));
182         mMultiFingerGestures.add(
183                 new MultiFingerMultiTapAndHold(
184                         mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, this));
185         mMultiFingerGestures.add(
186                 new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
187         // Two-finger swipes.
188         mTwoFingerSwipes.add(
189                 new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
190         mTwoFingerSwipes.add(
191                 new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this));
192         mTwoFingerSwipes.add(
193                 new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this));
194         mTwoFingerSwipes.add(new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
195         mMultiFingerGestures.addAll(mTwoFingerSwipes);
196         // Three-finger swipes.
197         mMultiFingerGestures.add(
198                 new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this));
199         mMultiFingerGestures.add(
200                 new MultiFingerSwipe(context, 3, LEFT, GESTURE_3_FINGER_SWIPE_LEFT, this));
201         mMultiFingerGestures.add(
202                 new MultiFingerSwipe(context, 3, RIGHT, GESTURE_3_FINGER_SWIPE_RIGHT, this));
203         mMultiFingerGestures.add(
204                 new MultiFingerSwipe(context, 3, UP, GESTURE_3_FINGER_SWIPE_UP, this));
205         // Four-finger swipes.
206         mMultiFingerGestures.add(
207                 new MultiFingerSwipe(context, 4, DOWN, GESTURE_4_FINGER_SWIPE_DOWN, this));
208         mMultiFingerGestures.add(
209                 new MultiFingerSwipe(context, 4, LEFT, GESTURE_4_FINGER_SWIPE_LEFT, this));
210         mMultiFingerGestures.add(
211                 new MultiFingerSwipe(context, 4, RIGHT, GESTURE_4_FINGER_SWIPE_RIGHT, this));
212         mMultiFingerGestures.add(
213                 new MultiFingerSwipe(context, 4, UP, GESTURE_4_FINGER_SWIPE_UP, this));
214     }
215 
216     /**
217      * Processes a motion event.
218      *
219      * @param event The event as received from the previous entry in the event stream.
220      * @param rawEvent The event without any transformations e.g. magnification.
221      * @param policyFlags
222      * @return True if the event has been appropriately handled by the gesture manifold and related
223      *     callback functions, false if it should be handled further by the calling function.
224      */
onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)225     public boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
226         if (mState.isClear()) {
227             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
228                 // Validity safeguard: if touch state is clear, then matchers should always be clear
229                 // before processing the next down event.
230                 clear();
231             } else {
232                 // If for some reason other events come through while in the clear state they could
233                 // compromise the state of particular matchers, so we just ignore them.
234                 return false;
235             }
236         }
237         if (mSendMotionEventsEnabled) {
238             mEvents.add(MotionEvent.obtainNoHistory(rawEvent));
239         }
240         for (GestureMatcher matcher : mGestures) {
241             if (matcher.getState() != GestureMatcher.STATE_GESTURE_CANCELED) {
242                 if (DEBUG) {
243                     Slog.d(LOG_TAG, matcher.toString());
244                 }
245                 matcher.onMotionEvent(event, rawEvent, policyFlags);
246                 if (DEBUG) {
247                     Slog.d(LOG_TAG, matcher.toString());
248                 }
249                 if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) {
250                     // Here we just return. The actual gesture dispatch is done in
251                     // onStateChanged().
252                     // No need to process this event any further.
253                     return true;
254                 }
255             }
256         }
257         return false;
258     }
259 
clear()260     public void clear() {
261         for (GestureMatcher matcher : mGestures) {
262             matcher.clear();
263         }
264         if (mEvents != null) {
265             while (mEvents.size() > 0) {
266                 mEvents.remove(0).recycle();
267             }
268         }
269     }
270 
271     /**
272      * Listener that receives notifications of the state of the gesture detector. Listener functions
273      * are called as a result of onMotionEvent(). The current MotionEvent in the context of these
274      * functions is the event passed into onMotionEvent.
275      */
276     public interface Listener {
277         /**
278          * When FLAG_SERVICE_HANDLES_DOUBLE_TAP is enabled, this method is not called; double-tap
279          * and hold is dispatched via onGestureCompleted. Otherwise, this method is called when the
280          * user has performed a double tap and then held down the second tap.
281          */
onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags)282         void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags);
283 
284         /**
285          * When FLAG_SERVICE_HANDLES_DOUBLE_TAP is enabled, this method is not called; double-tap is
286          * dispatched via onGestureCompleted. Otherwise, this method is called when the user lifts
287          * their finger on the second tap of a double tap.
288          *
289          * @return true if the event is consumed, else false
290          */
onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags)291         boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags);
292 
293         /**
294          * Called when the system has decided the event stream is a potential gesture.
295          *
296          * @return true if the event is consumed, else false
297          */
onGestureStarted()298         boolean onGestureStarted();
299 
300         /**
301          * Called when an event stream is recognized as a gesture.
302          *
303          * @param gestureEvent Information about the gesture.
304          * @return true if the event is consumed, else false
305          */
onGestureCompleted(AccessibilityGestureEvent gestureEvent)306         boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent);
307 
308         /**
309          * Called when the system has decided an event stream doesn't match any known gesture.
310          *
311          * @param event The most recent MotionEvent received.
312          * @param policyFlags The policy flags of the most recent event.
313          * @return true if the event is consumed, else false
314          */
onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags)315         boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags);
316     }
317 
318     @Override
onStateChanged( int gestureId, int state, MotionEvent event, MotionEvent rawEvent, int policyFlags)319     public void onStateChanged(
320             int gestureId, int state, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
321         if (state == GestureMatcher.STATE_GESTURE_STARTED && !mState.isGestureDetecting()) {
322             if (gestureId == GESTURE_DOUBLE_TAP || gestureId == GESTURE_DOUBLE_TAP_AND_HOLD) {
323                 if (mServiceHandlesDoubleTap) {
324                     mListener.onGestureStarted();
325                 }
326             } else {
327                 mListener.onGestureStarted();
328             }
329         } else if (state == GestureMatcher.STATE_GESTURE_COMPLETED) {
330             onGestureCompleted(gestureId, event, rawEvent, policyFlags);
331         } else if (state == GestureMatcher.STATE_GESTURE_CANCELED && mState.isGestureDetecting()) {
332             // We only want to call the cancelation callback if there are no other pending
333             // detectors.
334             for (GestureMatcher matcher : mGestures) {
335                 if (matcher.getState() == GestureMatcher.STATE_GESTURE_STARTED) {
336                     return;
337                 }
338             }
339             if (DEBUG) {
340                 Slog.d(LOG_TAG, "Cancelling.");
341             }
342             mListener.onGestureCancelled(event, rawEvent, policyFlags);
343         }
344     }
345 
onGestureCompleted( int gestureId, MotionEvent event, MotionEvent rawEvent, int policyFlags)346     private void onGestureCompleted(
347             int gestureId, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
348         // Note that gestures that complete immediately call clear() from onMotionEvent.
349         // Gestures that complete on a delay call clear() here.
350         switch (gestureId) {
351             case GESTURE_DOUBLE_TAP:
352                 if (mServiceHandlesDoubleTap) {
353                     AccessibilityGestureEvent gestureEvent =
354                             new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
355                     mListener.onGestureCompleted(gestureEvent);
356                 } else {
357                     mListener.onDoubleTap(event, rawEvent, policyFlags);
358                 }
359                 break;
360             case GESTURE_DOUBLE_TAP_AND_HOLD:
361                 if (mServiceHandlesDoubleTap) {
362                     AccessibilityGestureEvent gestureEvent =
363                             new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
364                     mListener.onGestureCompleted(gestureEvent);
365                 } else {
366                     mListener.onDoubleTapAndHold(event, rawEvent, policyFlags);
367                 }
368                 break;
369             default:
370                 AccessibilityGestureEvent gestureEvent =
371                         new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
372                 mListener.onGestureCompleted(gestureEvent);
373                 break;
374         }
375         clear();
376     }
377 
isMultiFingerGesturesEnabled()378     public boolean isMultiFingerGesturesEnabled() {
379         return mMultiFingerGesturesEnabled;
380     }
381 
setMultiFingerGesturesEnabled(boolean mode)382     public void setMultiFingerGesturesEnabled(boolean mode) {
383         if (mMultiFingerGesturesEnabled != mode) {
384             mMultiFingerGesturesEnabled = mode;
385             if (mode) {
386                 mGestures.addAll(mMultiFingerGestures);
387             } else {
388                 mGestures.removeAll(mMultiFingerGestures);
389             }
390         }
391     }
392 
isTwoFingerPassthroughEnabled()393     public boolean isTwoFingerPassthroughEnabled() {
394         return mTwoFingerPassthroughEnabled;
395     }
396 
setTwoFingerPassthroughEnabled(boolean mode)397     public void setTwoFingerPassthroughEnabled(boolean mode) {
398         if (mTwoFingerPassthroughEnabled != mode) {
399             mTwoFingerPassthroughEnabled = mode;
400             if (!mode) {
401                 mMultiFingerGestures.addAll(mTwoFingerSwipes);
402                 if (mMultiFingerGesturesEnabled) {
403                     mGestures.addAll(mTwoFingerSwipes);
404                 }
405             } else {
406                 mMultiFingerGestures.removeAll(mTwoFingerSwipes);
407                 mGestures.removeAll(mTwoFingerSwipes);
408             }
409         }
410     }
411 
setServiceHandlesDoubleTap(boolean mode)412     public void setServiceHandlesDoubleTap(boolean mode) {
413         mServiceHandlesDoubleTap = mode;
414     }
415 
isServiceHandlesDoubleTapEnabled()416     public boolean isServiceHandlesDoubleTapEnabled() {
417         return mServiceHandlesDoubleTap;
418     }
419 
setSendMotionEventsEnabled(boolean mode)420     public void setSendMotionEventsEnabled(boolean mode) {
421         mSendMotionEventsEnabled = mode;
422         if (!mode) {
423             while (mEvents.size() > 0) {
424                 mEvents.remove(0).recycle();
425             }
426         }
427     }
428 
isSendMotionEventsEnabled()429     public boolean isSendMotionEventsEnabled() {
430         return mSendMotionEventsEnabled;
431     }
432 
433     /**
434      * Returns the current list of motion events. It is the caller's responsibility to copy the list
435      * if they want it to persist after a call to clear().
436      */
getMotionEvents()437     public List<MotionEvent> getMotionEvents() {
438         return mEvents;
439     }
440 }
441