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 com.android.server.accessibility.gestures.TouchExplorer.DEBUG; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.os.Handler; 24 import android.util.Slog; 25 import android.view.MotionEvent; 26 import android.view.ViewConfiguration; 27 28 /** 29 * This class describes a common base for gesture matchers. A gesture matcher checks a series of 30 * motion events against a single gesture. Coordinating the individual gesture matchers is done by 31 * the GestureManifold. To create a new Gesture, extend this class and override the onDown, onMove, 32 * onUp, etc methods as necessary. If you don't override a method your matcher will do nothing in 33 * response to that type of event. Finally, be sure to give your gesture a name by overriding 34 * getGestureName(). 35 * @hide 36 */ 37 public abstract class GestureMatcher { 38 // Potential states for this individual gesture matcher. 39 // In STATE_CLEAR, this matcher is accepting new motion events but has not formally signaled 40 // that there is enough data to judge that a gesture has started. 41 public static final int STATE_CLEAR = 0; 42 // In STATE_GESTURE_STARTED, this matcher continues to accept motion events and it has signaled 43 // to the gesture manifold that what looks like the specified gesture has started. 44 public static final int STATE_GESTURE_STARTED = 1; 45 // In STATE_GESTURE_COMPLETED, this matcher has successfully matched the specified gesture. and 46 // will not accept motion events until it is cleared. 47 public static final int STATE_GESTURE_COMPLETED = 2; 48 // In STATE_GESTURE_CANCELED, this matcher will not accept new motion events because it is 49 // impossible that this set of motion events will match the specified gesture. 50 public static final int STATE_GESTURE_CANCELED = 3; 51 52 @IntDef({STATE_CLEAR, STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELED}) 53 public @interface State {} 54 55 @State private int mState = STATE_CLEAR; 56 // The id number of the gesture that gets passed to accessibility services. 57 private final int mGestureId; 58 // handler for asynchronous operations like timeouts 59 private final Handler mHandler; 60 61 private StateChangeListener mListener; 62 63 // Use this to transition to new states after a delay. 64 // e.g. cancel or complete after some timeout. 65 // Convenience functions for tapTimeout and doubleTapTimeout are already defined here. 66 protected final DelayedTransition mDelayedTransition; 67 GestureMatcher(int gestureId, Handler handler, StateChangeListener listener)68 protected GestureMatcher(int gestureId, Handler handler, StateChangeListener listener) { 69 mGestureId = gestureId; 70 mHandler = handler; 71 mDelayedTransition = new DelayedTransition(); 72 mListener = listener; 73 } 74 75 /** 76 * Resets all state information for this matcher. Subclasses that include their own state 77 * information should override this method to reset their own state information and call 78 * super.clear(). 79 */ clear()80 public void clear() { 81 mState = STATE_CLEAR; 82 cancelPendingTransitions(); 83 } 84 getState()85 public final int getState() { 86 return mState; 87 } 88 89 /** 90 * Transitions to a new state and notifies any listeners. Note that any pending transitions are 91 * canceled. 92 */ setState( @tate int state, MotionEvent event, MotionEvent rawEvent, int policyFlags)93 private void setState( 94 @State int state, MotionEvent event, MotionEvent rawEvent, int policyFlags) { 95 mState = state; 96 cancelPendingTransitions(); 97 if (mListener != null) { 98 mListener.onStateChanged(mGestureId, mState, event, rawEvent, policyFlags); 99 } 100 } 101 102 /** Indicates that there is evidence to suggest that this gesture has started. */ startGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags)103 protected final void startGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 104 setState(STATE_GESTURE_STARTED, event, rawEvent, policyFlags); 105 } 106 107 /** Indicates this stream of motion events can no longer match this gesture. */ cancelGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags)108 protected final void cancelGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 109 setState(STATE_GESTURE_CANCELED, event, rawEvent, policyFlags); 110 } 111 112 /** Indicates this gesture is completed. */ completeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags)113 protected final void completeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 114 setState(STATE_GESTURE_COMPLETED, event, rawEvent, policyFlags); 115 } 116 setListener(@onNull StateChangeListener listener)117 public final void setListener(@NonNull StateChangeListener listener) { 118 mListener = listener; 119 } 120 getGestureId()121 public int getGestureId() { 122 return mGestureId; 123 } 124 125 /** 126 * Process a motion event and attempt to match it to this gesture. 127 * 128 * @param event the event as passed in from the event stream. 129 * @param rawEvent the original un-modified event. Useful for calculating movements in physical 130 * space. 131 * @param policyFlags the policy flags as passed in from the event stream. 132 * @return the state of this matcher. 133 */ onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)134 public final int onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 135 if (mState == STATE_GESTURE_CANCELED || mState == STATE_GESTURE_COMPLETED) { 136 return mState; 137 } 138 switch (event.getActionMasked()) { 139 case MotionEvent.ACTION_DOWN: 140 onDown(event, rawEvent, policyFlags); 141 break; 142 case MotionEvent.ACTION_POINTER_DOWN: 143 onPointerDown(event, rawEvent, policyFlags); 144 break; 145 case MotionEvent.ACTION_MOVE: 146 onMove(event, rawEvent, policyFlags); 147 break; 148 case MotionEvent.ACTION_POINTER_UP: 149 onPointerUp(event, rawEvent, policyFlags); 150 break; 151 case MotionEvent.ACTION_UP: 152 onUp(event, rawEvent, policyFlags); 153 break; 154 default: 155 // Cancel because of invalid event. 156 setState(STATE_GESTURE_CANCELED, event, rawEvent, policyFlags); 157 break; 158 } 159 return mState; 160 } 161 162 /** 163 * Matchers override this method to respond to ACTION_DOWN events. ACTION_DOWN events indicate 164 * the first finger has touched the screen. If not overridden the default response is to do 165 * nothing. 166 */ onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags)167 protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {} 168 169 /** 170 * Matchers override this method to respond to ACTION_POINTER_DOWN events. ACTION_POINTER_DOWN 171 * indicates that more than one finger has touched the screen. If not overridden the default 172 * response is to do nothing. 173 * 174 * @param event the event as passed in from the event stream. 175 * @param rawEvent the original un-modified event. Useful for calculating movements in physical 176 * space. 177 * @param policyFlags the policy flags as passed in from the event stream. 178 */ onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags)179 protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {} 180 181 /** 182 * Matchers override this method to respond to ACTION_MOVE events. ACTION_MOVE indicates that 183 * one or fingers has moved. If not overridden the default response is to do nothing. 184 * 185 * @param event the event as passed in from the event stream. 186 * @param rawEvent the original un-modified event. Useful for calculating movements in physical 187 * space. 188 * @param policyFlags the policy flags as passed in from the event stream. 189 */ onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags)190 protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {} 191 192 /** 193 * Matchers override this method to respond to ACTION_POINTER_UP events. ACTION_POINTER_UP 194 * indicates that a finger has lifted from the screen but at least one finger continues to touch 195 * the screen. If not overridden the default response is to do nothing. 196 * 197 * @param event the event as passed in from the event stream. 198 * @param rawEvent the original un-modified event. Useful for calculating movements in physical 199 * space. 200 * @param policyFlags the policy flags as passed in from the event stream. 201 */ onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags)202 protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {} 203 204 /** 205 * Matchers override this method to respond to ACTION_UP events. ACTION_UP indicates that there 206 * are no more fingers touching the screen. If not overridden the default response is to do 207 * nothing. 208 * 209 * @param event the event as passed in from the event stream. 210 * @param rawEvent the original un-modified event. Useful for calculating movements in physical 211 * space. 212 * @param policyFlags the policy flags as passed in from the event stream. 213 */ onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags)214 protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {} 215 216 /** Cancels this matcher after the tap timeout. Any pending state transitions are removed. */ cancelAfterTapTimeout(MotionEvent event, MotionEvent rawEvent, int policyFlags)217 protected void cancelAfterTapTimeout(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 218 cancelAfter(ViewConfiguration.getTapTimeout(), event, rawEvent, policyFlags); 219 } 220 221 /** Cancels this matcher after the double tap timeout. Any pending cancelations are removed. */ cancelAfterDoubleTapTimeout( MotionEvent event, MotionEvent rawEvent, int policyFlags)222 protected final void cancelAfterDoubleTapTimeout( 223 MotionEvent event, MotionEvent rawEvent, int policyFlags) { 224 cancelAfter(ViewConfiguration.getDoubleTapTimeout(), event, rawEvent, policyFlags); 225 } 226 227 /** 228 * Cancels this matcher after the specified timeout. Any pending cancelations are removed. Used 229 * to prevent this matcher from accepting motion events until it is cleared. 230 */ cancelAfter( long timeout, MotionEvent event, MotionEvent rawEvent, int policyFlags)231 protected final void cancelAfter( 232 long timeout, MotionEvent event, MotionEvent rawEvent, int policyFlags) { 233 mDelayedTransition.cancel(); 234 mDelayedTransition.post(STATE_GESTURE_CANCELED, timeout, event, rawEvent, policyFlags); 235 } 236 237 /** Cancels any delayed transitions between states scheduled for this matcher. */ cancelPendingTransitions()238 protected final void cancelPendingTransitions() { 239 mDelayedTransition.cancel(); 240 } 241 242 /** 243 * Signals that this gesture has been completed after the tap timeout has expired. Used to 244 * ensure that there is no conflict with another gesture or for gestures that explicitly require 245 * a hold. 246 */ completeAfterLongPressTimeout( MotionEvent event, MotionEvent rawEvent, int policyFlags)247 protected final void completeAfterLongPressTimeout( 248 MotionEvent event, MotionEvent rawEvent, int policyFlags) { 249 completeAfter(ViewConfiguration.getLongPressTimeout(), event, rawEvent, policyFlags); 250 } 251 252 /** 253 * Signals that this gesture has been completed after the tap timeout has expired. Used to 254 * ensure that there is no conflict with another gesture or for gestures that explicitly require 255 * a hold. 256 */ completeAfterTapTimeout( MotionEvent event, MotionEvent rawEvent, int policyFlags)257 protected final void completeAfterTapTimeout( 258 MotionEvent event, MotionEvent rawEvent, int policyFlags) { 259 completeAfter(ViewConfiguration.getTapTimeout(), event, rawEvent, policyFlags); 260 } 261 262 /** 263 * Signals that this gesture has been completed after the specified timeout has expired. Used to 264 * ensure that there is no conflict with another gesture or for gestures that explicitly require 265 * a hold. 266 */ completeAfter( long timeout, MotionEvent event, MotionEvent rawEvent, int policyFlags)267 protected final void completeAfter( 268 long timeout, MotionEvent event, MotionEvent rawEvent, int policyFlags) { 269 mDelayedTransition.cancel(); 270 mDelayedTransition.post(STATE_GESTURE_COMPLETED, timeout, event, rawEvent, policyFlags); 271 } 272 273 /** 274 * Signals that this gesture has been completed after the double-tap timeout has expired. Used 275 * to ensure that there is no conflict with another gesture or for gestures that explicitly 276 * require a hold. 277 */ completeAfterDoubleTapTimeout( MotionEvent event, MotionEvent rawEvent, int policyFlags)278 protected final void completeAfterDoubleTapTimeout( 279 MotionEvent event, MotionEvent rawEvent, int policyFlags) { 280 completeAfter(ViewConfiguration.getDoubleTapTimeout(), event, rawEvent, policyFlags); 281 } 282 getStateSymbolicName(@tate int state)283 static String getStateSymbolicName(@State int state) { 284 switch (state) { 285 case STATE_CLEAR: 286 return "STATE_CLEAR"; 287 case STATE_GESTURE_STARTED: 288 return "STATE_GESTURE_STARTED"; 289 case STATE_GESTURE_COMPLETED: 290 return "STATE_GESTURE_COMPLETED"; 291 case STATE_GESTURE_CANCELED: 292 return "STATE_GESTURE_CANCELED"; 293 default: 294 return "Unknown state: " + state; 295 } 296 } 297 298 /** 299 * Returns a readable name for this matcher that can be displayed to the user and in system 300 * logs. 301 */ getGestureName()302 protected abstract String getGestureName(); 303 304 /** 305 * Returns a String representation of this matcher. Each matcher can override this method to add 306 * extra state information to the string representation. 307 */ toString()308 public String toString() { 309 return getGestureName() + ":" + getStateSymbolicName(mState); 310 } 311 312 /** This class allows matchers to transition between states on a delay. */ 313 protected final class DelayedTransition implements Runnable { 314 315 private static final String LOG_TAG = "GestureMatcher.DelayedTransition"; 316 int mTargetState; 317 MotionEvent mEvent; 318 MotionEvent mRawEvent; 319 int mPolicyFlags; 320 cancel()321 public void cancel() { 322 // Avoid meaningless debug messages. 323 if (DEBUG && isPending()) { 324 Slog.d( 325 LOG_TAG, 326 getGestureName() 327 + ": canceling delayed transition to " 328 + getStateSymbolicName(mTargetState)); 329 } 330 mHandler.removeCallbacks(this); 331 recycleEvent(); 332 } 333 post( int state, long delay, MotionEvent event, MotionEvent rawEvent, int policyFlags)334 public void post( 335 int state, long delay, MotionEvent event, MotionEvent rawEvent, int policyFlags) { 336 // Recycle the old event first if necessary, to handle duplicate calls to post. 337 recycleEvent(); 338 mTargetState = state; 339 if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { 340 mEvent = event.copy(); 341 mRawEvent = rawEvent.copy(); 342 } else { 343 mEvent = event; 344 mRawEvent = rawEvent; 345 } 346 mPolicyFlags = policyFlags; 347 mHandler.postDelayed(this, delay); 348 if (DEBUG) { 349 Slog.d( 350 LOG_TAG, 351 getGestureName() 352 + ": posting delayed transition to " 353 + getStateSymbolicName(mTargetState)); 354 } 355 } 356 isPending()357 public boolean isPending() { 358 return mHandler.hasCallbacks(this); 359 } 360 forceSendAndRemove()361 public void forceSendAndRemove() { 362 if (isPending()) { 363 run(); 364 cancel(); 365 } 366 } 367 368 @Override run()369 public void run() { 370 if (DEBUG) { 371 Slog.d( 372 LOG_TAG, 373 getGestureName() 374 + ": executing delayed transition to " 375 + getStateSymbolicName(mTargetState)); 376 } 377 setState(mTargetState, mEvent, mRawEvent, mPolicyFlags); 378 recycleEvent(); 379 } 380 recycleEvent()381 private void recycleEvent() { 382 if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { 383 if (mEvent == null || mRawEvent == null) { 384 return; 385 } 386 mEvent.recycle(); 387 mRawEvent.recycle(); 388 mEvent = null; 389 mRawEvent = null; 390 } 391 } 392 } 393 394 /** Interface to allow a class to listen for state changes in a specific gesture matcher */ 395 public interface StateChangeListener { 396 onStateChanged( int gestureId, int state, MotionEvent event, MotionEvent rawEvent, int policyFlags)397 void onStateChanged( 398 int gestureId, int state, MotionEvent event, MotionEvent rawEvent, int policyFlags); 399 } 400 } 401