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