1 /* 2 * Copyright (C) 2011 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; 18 19 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 20 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; 21 22 import android.annotation.MainThread; 23 import android.content.Context; 24 import android.graphics.Region; 25 import android.os.PowerManager; 26 import android.provider.Settings; 27 import android.util.Slog; 28 import android.util.SparseArray; 29 import android.util.SparseBooleanArray; 30 import android.view.Display; 31 import android.view.InputDevice; 32 import android.view.InputEvent; 33 import android.view.InputFilter; 34 import android.view.KeyEvent; 35 import android.view.MotionEvent; 36 import android.view.accessibility.AccessibilityEvent; 37 38 import com.android.server.LocalServices; 39 import com.android.server.accessibility.gestures.TouchExplorer; 40 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; 41 import com.android.server.accessibility.magnification.MagnificationGestureHandler; 42 import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; 43 import com.android.server.accessibility.magnification.WindowMagnificationPromptController; 44 import com.android.server.policy.WindowManagerPolicy; 45 46 import java.util.ArrayList; 47 48 /** 49 * This class is an input filter for implementing accessibility features such 50 * as display magnification and explore by touch. 51 * 52 * NOTE: This class has to be created and poked only from the main thread. 53 */ 54 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { 55 56 private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); 57 58 private static final boolean DEBUG = false; 59 60 /** 61 * Flag for enabling the screen magnification feature. 62 * 63 * @see #setUserAndEnabledFeatures(int, int) 64 */ 65 static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001; 66 67 /** 68 * Flag for enabling the touch exploration feature. 69 * 70 * @see #setUserAndEnabledFeatures(int, int) 71 */ 72 static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002; 73 74 /** 75 * Flag for enabling the filtering key events feature. 76 * 77 * @see #setUserAndEnabledFeatures(int, int) 78 */ 79 static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004; 80 81 /** 82 * Flag for enabling "Automatically click on mouse stop" feature. 83 * 84 * @see #setUserAndEnabledFeatures(int, int) 85 */ 86 static final int FLAG_FEATURE_AUTOCLICK = 0x00000008; 87 88 /** 89 * Flag for enabling motion event injection. 90 * 91 * @see #setUserAndEnabledFeatures(int, int) 92 */ 93 static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010; 94 95 /** 96 * Flag for enabling the feature to control the screen magnifier. If 97 * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored 98 * as the screen magnifier feature performs a super set of the work 99 * performed by this feature. 100 * 101 * @see #setUserAndEnabledFeatures(int, int) 102 */ 103 static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020; 104 105 /** 106 * Flag for enabling the feature to trigger the screen magnifier 107 * from another on-device interaction. 108 */ 109 static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040; 110 111 /** 112 * Flag for dispatching double tap and double tap and hold to the service. 113 * 114 * @see #setUserAndEnabledFeatures(int, int) 115 */ 116 static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x00000080; 117 118 /** 119 * Flag for enabling multi-finger gestures. 120 * 121 * @see #setUserAndEnabledFeatures(int, int) 122 */ 123 static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100; 124 125 /** 126 * Flag for enabling two-finger passthrough when multi-finger gestures are enabled. 127 * 128 * @see #setUserAndEnabledFeatures(int, int) 129 */ 130 static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200; 131 132 /** 133 * Flag for including motion events when dispatching a gesture. 134 * 135 * @see #setUserAndEnabledFeatures(int, int) 136 */ 137 static final int FLAG_SEND_MOTION_EVENTS = 0x00000400; 138 139 static final int FEATURES_AFFECTING_MOTION_EVENTS = 140 FLAG_FEATURE_INJECT_MOTION_EVENTS 141 | FLAG_FEATURE_AUTOCLICK 142 | FLAG_FEATURE_TOUCH_EXPLORATION 143 | FLAG_FEATURE_SCREEN_MAGNIFIER 144 | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER 145 | FLAG_SERVICE_HANDLES_DOUBLE_TAP 146 | FLAG_REQUEST_MULTI_FINGER_GESTURES 147 | FLAG_REQUEST_2_FINGER_PASSTHROUGH; 148 149 private final Context mContext; 150 151 private final PowerManager mPm; 152 153 private final AccessibilityManagerService mAms; 154 155 private final SparseArray<EventStreamTransformation> mEventHandler; 156 157 private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0); 158 159 private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler = 160 new SparseArray<>(0); 161 162 private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0); 163 164 private AutoclickController mAutoclickController; 165 166 private KeyboardInterceptor mKeyboardInterceptor; 167 168 private boolean mInstalled; 169 170 private int mUserId; 171 172 private int mEnabledFeatures; 173 174 private EventStreamState mMouseStreamState; 175 176 private EventStreamState mTouchScreenStreamState; 177 178 private EventStreamState mKeyboardStreamState; 179 AccessibilityInputFilter(Context context, AccessibilityManagerService service)180 AccessibilityInputFilter(Context context, AccessibilityManagerService service) { 181 this(context, service, new SparseArray<>(0)); 182 } 183 AccessibilityInputFilter(Context context, AccessibilityManagerService service, SparseArray<EventStreamTransformation> eventHandler)184 AccessibilityInputFilter(Context context, AccessibilityManagerService service, 185 SparseArray<EventStreamTransformation> eventHandler) { 186 super(context.getMainLooper()); 187 mContext = context; 188 mAms = service; 189 mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 190 mEventHandler = eventHandler; 191 } 192 193 @Override onInstalled()194 public void onInstalled() { 195 if (DEBUG) { 196 Slog.d(TAG, "Accessibility input filter installed."); 197 } 198 mInstalled = true; 199 disableFeatures(); 200 enableFeatures(); 201 super.onInstalled(); 202 } 203 204 @Override onUninstalled()205 public void onUninstalled() { 206 if (DEBUG) { 207 Slog.d(TAG, "Accessibility input filter uninstalled."); 208 } 209 mInstalled = false; 210 disableFeatures(); 211 super.onUninstalled(); 212 } 213 onDisplayChanged()214 void onDisplayChanged() { 215 if (mInstalled) { 216 disableFeatures(); 217 enableFeatures(); 218 } 219 } 220 221 @Override onInputEvent(InputEvent event, int policyFlags)222 public void onInputEvent(InputEvent event, int policyFlags) { 223 if (DEBUG) { 224 Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" 225 + Integer.toHexString(policyFlags)); 226 } 227 228 if (mEventHandler.size() == 0) { 229 if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event); 230 super.onInputEvent(event, policyFlags); 231 return; 232 } 233 234 EventStreamState state = getEventStreamState(event); 235 if (state == null) { 236 super.onInputEvent(event, policyFlags); 237 return; 238 } 239 240 int eventSource = event.getSource(); 241 if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) { 242 state.reset(); 243 clearEventsForAllEventHandlers(eventSource); 244 super.onInputEvent(event, policyFlags); 245 return; 246 } 247 248 if (state.updateInputSource(event.getSource())) { 249 clearEventsForAllEventHandlers(eventSource); 250 } 251 252 if (!state.inputSourceValid()) { 253 super.onInputEvent(event, policyFlags); 254 return; 255 } 256 257 if (event instanceof MotionEvent) { 258 if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) { 259 MotionEvent motionEvent = (MotionEvent) event; 260 processMotionEvent(state, motionEvent, policyFlags); 261 return; 262 } else { 263 super.onInputEvent(event, policyFlags); 264 } 265 } else if (event instanceof KeyEvent) { 266 KeyEvent keyEvent = (KeyEvent) event; 267 processKeyEvent(state, keyEvent, policyFlags); 268 } 269 } 270 271 /** 272 * Gets current event stream state associated with an input event. 273 * @return The event stream state that should be used for the event. Null if the event should 274 * not be handled by #AccessibilityInputFilter. 275 */ getEventStreamState(InputEvent event)276 private EventStreamState getEventStreamState(InputEvent event) { 277 if (event instanceof MotionEvent) { 278 if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { 279 if (mTouchScreenStreamState == null) { 280 mTouchScreenStreamState = new TouchScreenEventStreamState(); 281 } 282 return mTouchScreenStreamState; 283 } 284 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { 285 if (mMouseStreamState == null) { 286 mMouseStreamState = new MouseEventStreamState(); 287 } 288 return mMouseStreamState; 289 } 290 } else if (event instanceof KeyEvent) { 291 if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) { 292 if (mKeyboardStreamState == null) { 293 mKeyboardStreamState = new KeyboardEventStreamState(); 294 } 295 return mKeyboardStreamState; 296 } 297 } 298 return null; 299 } 300 clearEventsForAllEventHandlers(int eventSource)301 private void clearEventsForAllEventHandlers(int eventSource) { 302 for (int i = 0; i < mEventHandler.size(); i++) { 303 final EventStreamTransformation eventHandler = mEventHandler.valueAt(i); 304 if (eventHandler != null) { 305 eventHandler.clearEvents(eventSource); 306 } 307 } 308 } 309 processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags)310 private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) { 311 if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) { 312 super.onInputEvent(event, policyFlags); 313 return; 314 } 315 316 if (!state.shouldProcessMotionEvent(event)) { 317 return; 318 } 319 320 handleMotionEvent(event, policyFlags); 321 } 322 processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags)323 private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) { 324 if (!state.shouldProcessKeyEvent(event)) { 325 super.onInputEvent(event, policyFlags); 326 return; 327 } 328 // Since the display id of KeyEvent always would be -1 and there is only one 329 // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of 330 // DEFAULT_DISPLAY to handle. 331 mEventHandler.get(Display.DEFAULT_DISPLAY).onKeyEvent(event, policyFlags); 332 } 333 handleMotionEvent(MotionEvent event, int policyFlags)334 private void handleMotionEvent(MotionEvent event, int policyFlags) { 335 if (DEBUG) { 336 Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags); 337 } 338 mPm.userActivity(event.getEventTime(), false); 339 MotionEvent transformedEvent = MotionEvent.obtain(event); 340 final int displayId = event.getDisplayId(); 341 mEventHandler.get(isDisplayIdValid(displayId) ? displayId : Display.DEFAULT_DISPLAY) 342 .onMotionEvent(transformedEvent, event, policyFlags); 343 transformedEvent.recycle(); 344 } 345 isDisplayIdValid(int displayId)346 private boolean isDisplayIdValid(int displayId) { 347 return mEventHandler.get(displayId) != null; 348 } 349 350 @Override onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, int policyFlags)351 public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, 352 int policyFlags) { 353 sendInputEvent(transformedEvent, policyFlags); 354 } 355 356 @Override onKeyEvent(KeyEvent event, int policyFlags)357 public void onKeyEvent(KeyEvent event, int policyFlags) { 358 sendInputEvent(event, policyFlags); 359 } 360 361 @Override onAccessibilityEvent(AccessibilityEvent event)362 public void onAccessibilityEvent(AccessibilityEvent event) { 363 // TODO Implement this to inject the accessibility event 364 // into the accessibility manager service similarly 365 // to how this is done for input events. 366 } 367 368 @Override setNext(EventStreamTransformation sink)369 public void setNext(EventStreamTransformation sink) { 370 /* do nothing */ 371 } 372 373 @Override getNext()374 public EventStreamTransformation getNext() { 375 return null; 376 } 377 378 @Override clearEvents(int inputSource)379 public void clearEvents(int inputSource) { 380 /* do nothing */ 381 } 382 setUserAndEnabledFeatures(int userId, int enabledFeatures)383 void setUserAndEnabledFeatures(int userId, int enabledFeatures) { 384 if (DEBUG) { 385 Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x" 386 + Integer.toHexString(enabledFeatures) + ")"); 387 } 388 if (mEnabledFeatures == enabledFeatures && mUserId == userId) { 389 return; 390 } 391 if (mInstalled) { 392 disableFeatures(); 393 } 394 mUserId = userId; 395 mEnabledFeatures = enabledFeatures; 396 if (mInstalled) { 397 enableFeatures(); 398 } 399 } 400 notifyAccessibilityEvent(AccessibilityEvent event)401 void notifyAccessibilityEvent(AccessibilityEvent event) { 402 for (int i = 0; i < mEventHandler.size(); i++) { 403 final EventStreamTransformation eventHandler = mEventHandler.valueAt(i); 404 if (eventHandler != null) { 405 eventHandler.onAccessibilityEvent(event); 406 } 407 } 408 } 409 notifyAccessibilityButtonClicked(int displayId)410 void notifyAccessibilityButtonClicked(int displayId) { 411 if (mMagnificationGestureHandler.size() != 0) { 412 final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId); 413 if (handler != null) { 414 handler.notifyShortcutTriggered(); 415 } 416 } 417 } 418 enableFeatures()419 private void enableFeatures() { 420 if (DEBUG) Slog.i(TAG, "enableFeatures()"); 421 422 resetStreamState(); 423 424 final ArrayList<Display> displaysList = mAms.getValidDisplayList(); 425 426 if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) { 427 mAutoclickController = new AutoclickController(mContext, mUserId); 428 addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController); 429 } 430 431 for (int i = displaysList.size() - 1; i >= 0; i--) { 432 final int displayId = displaysList.get(i).getDisplayId(); 433 final Context displayContext = mContext.createDisplayContext(displaysList.get(i)); 434 435 if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { 436 TouchExplorer explorer = new TouchExplorer(displayContext, mAms); 437 if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) { 438 explorer.setServiceHandlesDoubleTap(true); 439 } 440 if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) { 441 explorer.setMultiFingerGesturesEnabled(true); 442 } 443 if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) { 444 explorer.setTwoFingerPassthroughEnabled(true); 445 } 446 if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) { 447 explorer.setSendMotionEventsEnabled(true); 448 } 449 addFirstEventHandler(displayId, explorer); 450 mTouchExplorer.put(displayId, explorer); 451 } 452 453 if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 454 || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) 455 || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { 456 final MagnificationGestureHandler magnificationGestureHandler = 457 createMagnificationGestureHandler(displayId, 458 displayContext); 459 addFirstEventHandler(displayId, magnificationGestureHandler); 460 mMagnificationGestureHandler.put(displayId, magnificationGestureHandler); 461 } 462 463 if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { 464 MotionEventInjector injector = new MotionEventInjector( 465 mContext.getMainLooper()); 466 addFirstEventHandler(displayId, injector); 467 mMotionEventInjectors.put(displayId, injector); 468 } 469 } 470 471 if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { 472 mAms.setMotionEventInjectors(mMotionEventInjectors); 473 } 474 475 if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) { 476 mKeyboardInterceptor = new KeyboardInterceptor(mAms, 477 LocalServices.getService(WindowManagerPolicy.class)); 478 // Since the display id of KeyEvent always would be -1 and it would be dispatched to 479 // the display with input focus directly, we only need one KeyboardInterceptor for 480 // default display. 481 addFirstEventHandler(Display.DEFAULT_DISPLAY, mKeyboardInterceptor); 482 } 483 } 484 485 /** 486 * Adds an event handler to the event handler chain for giving display. The handler is added at 487 * the beginning of the chain. 488 * 489 * @param displayId The logical display id. 490 * @param handler The handler to be added to the event handlers list. 491 */ addFirstEventHandler(int displayId, EventStreamTransformation handler)492 private void addFirstEventHandler(int displayId, EventStreamTransformation handler) { 493 EventStreamTransformation eventHandler = mEventHandler.get(displayId); 494 if (eventHandler != null) { 495 handler.setNext(eventHandler); 496 } else { 497 handler.setNext(this); 498 } 499 eventHandler = handler; 500 mEventHandler.put(displayId, eventHandler); 501 } 502 503 /** 504 * Adds an event handler to the event handler chain for all displays. The handler is added at 505 * the beginning of the chain. 506 * 507 * @param displayList The list of displays 508 * @param handler The handler to be added to the event handlers list. 509 */ addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList, EventStreamTransformation handler)510 private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList, 511 EventStreamTransformation handler) { 512 for (int i = 0; i < displayList.size(); i++) { 513 final int displayId = displayList.get(i).getDisplayId(); 514 addFirstEventHandler(displayId, handler); 515 } 516 } 517 disableFeatures()518 private void disableFeatures() { 519 for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) { 520 final MotionEventInjector injector = mMotionEventInjectors.valueAt(i); 521 if (injector != null) { 522 injector.onDestroy(); 523 } 524 } 525 mAms.setMotionEventInjectors(null); 526 mMotionEventInjectors.clear(); 527 if (mAutoclickController != null) { 528 mAutoclickController.onDestroy(); 529 mAutoclickController = null; 530 } 531 for (int i = mTouchExplorer.size() - 1; i >= 0; i--) { 532 final TouchExplorer explorer = mTouchExplorer.valueAt(i); 533 if (explorer != null) { 534 explorer.onDestroy(); 535 } 536 } 537 mTouchExplorer.clear(); 538 for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) { 539 final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i); 540 if (handler != null) { 541 handler.onDestroy(); 542 } 543 } 544 mMagnificationGestureHandler.clear(); 545 if (mKeyboardInterceptor != null) { 546 mKeyboardInterceptor.onDestroy(); 547 mKeyboardInterceptor = null; 548 } 549 550 mEventHandler.clear(); 551 resetStreamState(); 552 } 553 createMagnificationGestureHandler( int displayId, Context displayContext)554 private MagnificationGestureHandler createMagnificationGestureHandler( 555 int displayId, Context displayContext) { 556 final boolean detectControlGestures = (mEnabledFeatures 557 & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0; 558 final boolean triggerable = (mEnabledFeatures 559 & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0; 560 MagnificationGestureHandler magnificationGestureHandler; 561 if (mAms.getMagnificationMode(displayId) 562 == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { 563 final Context uiContext = displayContext.createWindowContext( 564 TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */); 565 magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext, 566 mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(), 567 detectControlGestures, triggerable, 568 displayId); 569 } else { 570 final Context uiContext = displayContext.createWindowContext( 571 TYPE_MAGNIFICATION_OVERLAY, null /* options */); 572 magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext, 573 mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(), 574 detectControlGestures, triggerable, 575 new WindowMagnificationPromptController(displayContext, mUserId), displayId); 576 } 577 return magnificationGestureHandler; 578 } 579 resetStreamState()580 void resetStreamState() { 581 if (mTouchScreenStreamState != null) { 582 mTouchScreenStreamState.reset(); 583 } 584 if (mMouseStreamState != null) { 585 mMouseStreamState.reset(); 586 } 587 if (mKeyboardStreamState != null) { 588 mKeyboardStreamState.reset(); 589 } 590 } 591 592 @Override onDestroy()593 public void onDestroy() { 594 /* ignore */ 595 } 596 597 /** 598 * Called to refresh the magnification mode on the given display. 599 * It's responsible for changing {@link MagnificationGestureHandler} based on the current mode. 600 * 601 * @param display The logical display 602 */ 603 @MainThread refreshMagnificationMode(Display display)604 public void refreshMagnificationMode(Display display) { 605 final int displayId = display.getDisplayId(); 606 final MagnificationGestureHandler magnificationGestureHandler = 607 mMagnificationGestureHandler.get(displayId); 608 if (magnificationGestureHandler == null) { 609 return; 610 } 611 if (magnificationGestureHandler.getMode() == mAms.getMagnificationMode(displayId)) { 612 return; 613 } 614 magnificationGestureHandler.onDestroy(); 615 final MagnificationGestureHandler currentMagnificationGestureHandler = 616 createMagnificationGestureHandler(displayId, 617 mContext.createDisplayContext(display)); 618 switchEventStreamTransformation(displayId, magnificationGestureHandler, 619 currentMagnificationGestureHandler); 620 mMagnificationGestureHandler.put(displayId, currentMagnificationGestureHandler); 621 } 622 623 @MainThread switchEventStreamTransformation(int displayId, EventStreamTransformation oldStreamTransformation, EventStreamTransformation currentStreamTransformation)624 private void switchEventStreamTransformation(int displayId, 625 EventStreamTransformation oldStreamTransformation, 626 EventStreamTransformation currentStreamTransformation) { 627 EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId); 628 if (eventStreamTransformation == null) { 629 return; 630 } 631 if (eventStreamTransformation == oldStreamTransformation) { 632 currentStreamTransformation.setNext(oldStreamTransformation.getNext()); 633 mEventHandler.put(displayId, currentStreamTransformation); 634 } else { 635 while (eventStreamTransformation != null) { 636 if (eventStreamTransformation.getNext() == oldStreamTransformation) { 637 eventStreamTransformation.setNext(currentStreamTransformation); 638 currentStreamTransformation.setNext(oldStreamTransformation.getNext()); 639 return; 640 } else { 641 eventStreamTransformation = eventStreamTransformation.getNext(); 642 } 643 } 644 } 645 } 646 647 /** 648 * Keeps state of event streams observed for an input device with a certain source. 649 * Provides information about whether motion and key events should be processed by accessibility 650 * #EventStreamTransformations. Base implementation describes behaviour for event sources that 651 * whose events should not be handled by a11y event stream transformations. 652 */ 653 private static class EventStreamState { 654 private int mSource; 655 EventStreamState()656 EventStreamState() { 657 mSource = -1; 658 } 659 660 /** 661 * Updates the input source of the device associated with the state. If the source changes, 662 * resets internal state. 663 * 664 * @param source Updated input source. 665 * @return Whether the input source has changed. 666 */ updateInputSource(int source)667 public boolean updateInputSource(int source) { 668 if (mSource == source) { 669 return false; 670 } 671 // Reset clears internal state, so make sure it's called before |mSource| is updated. 672 reset(); 673 mSource = source; 674 return true; 675 } 676 677 /** 678 * @return Whether input source is valid. 679 */ inputSourceValid()680 public boolean inputSourceValid() { 681 return mSource >= 0; 682 } 683 684 /** 685 * Resets the event stream state. 686 */ reset()687 public void reset() { 688 mSource = -1; 689 } 690 691 /** 692 * @return Whether scroll events for device should be handled by event transformations. 693 */ shouldProcessScroll()694 public boolean shouldProcessScroll() { 695 return false; 696 } 697 698 /** 699 * @param event An observed motion event. 700 * @return Whether the event should be handled by event transformations. 701 */ shouldProcessMotionEvent(MotionEvent event)702 public boolean shouldProcessMotionEvent(MotionEvent event) { 703 return false; 704 } 705 706 /** 707 * @param event An observed key event. 708 * @return Whether the event should be handled by event transformations. 709 */ shouldProcessKeyEvent(KeyEvent event)710 public boolean shouldProcessKeyEvent(KeyEvent event) { 711 return false; 712 } 713 } 714 715 /** 716 * Keeps state of stream of events from a mouse device. 717 */ 718 private static class MouseEventStreamState extends EventStreamState { 719 private boolean mMotionSequenceStarted; 720 MouseEventStreamState()721 public MouseEventStreamState() { 722 reset(); 723 } 724 725 @Override reset()726 final public void reset() { 727 super.reset(); 728 mMotionSequenceStarted = false; 729 } 730 731 @Override shouldProcessScroll()732 final public boolean shouldProcessScroll() { 733 return true; 734 } 735 736 @Override shouldProcessMotionEvent(MotionEvent event)737 final public boolean shouldProcessMotionEvent(MotionEvent event) { 738 if (mMotionSequenceStarted) { 739 return true; 740 } 741 // Wait for down or move event to start processing mouse events. 742 int action = event.getActionMasked(); 743 mMotionSequenceStarted = 744 action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE; 745 return mMotionSequenceStarted; 746 } 747 } 748 749 /** 750 * Keeps state of stream of events from a touch screen device. 751 */ 752 private static class TouchScreenEventStreamState extends EventStreamState { 753 private boolean mTouchSequenceStarted; 754 private boolean mHoverSequenceStarted; 755 TouchScreenEventStreamState()756 public TouchScreenEventStreamState() { 757 reset(); 758 } 759 760 @Override reset()761 final public void reset() { 762 super.reset(); 763 mTouchSequenceStarted = false; 764 mHoverSequenceStarted = false; 765 } 766 767 @Override shouldProcessMotionEvent(MotionEvent event)768 final public boolean shouldProcessMotionEvent(MotionEvent event) { 769 // Wait for a down touch event to start processing. 770 if (event.isTouchEvent()) { 771 if (mTouchSequenceStarted) { 772 return true; 773 } 774 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN; 775 return mTouchSequenceStarted; 776 } 777 778 // Wait for an enter hover event to start processing. 779 if (mHoverSequenceStarted) { 780 return true; 781 } 782 mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER; 783 return mHoverSequenceStarted; 784 } 785 } 786 787 /** 788 * Keeps state of streams of events from all keyboard devices. 789 */ 790 private static class KeyboardEventStreamState extends EventStreamState { 791 private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray(); 792 KeyboardEventStreamState()793 public KeyboardEventStreamState() { 794 reset(); 795 } 796 797 @Override reset()798 final public void reset() { 799 super.reset(); 800 mEventSequenceStartedMap.clear(); 801 } 802 803 /* 804 * Key events from different devices may be interleaved. For example, the volume up and 805 * down keys can come from different input sources. 806 */ 807 @Override updateInputSource(int deviceId)808 public boolean updateInputSource(int deviceId) { 809 return false; 810 } 811 812 // We manage all input source simultaneously; there is no concept of validity. 813 @Override inputSourceValid()814 public boolean inputSourceValid() { 815 return true; 816 } 817 818 @Override shouldProcessKeyEvent(KeyEvent event)819 final public boolean shouldProcessKeyEvent(KeyEvent event) { 820 // For each keyboard device, wait for a down event from a device to start processing 821 int deviceId = event.getDeviceId(); 822 if (mEventSequenceStartedMap.get(deviceId, false)) { 823 return true; 824 } 825 boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN; 826 mEventSequenceStartedMap.put(deviceId, shouldProcess); 827 return shouldProcess; 828 } 829 } 830 setGestureDetectionPassthroughRegion(int displayId, Region region)831 public void setGestureDetectionPassthroughRegion(int displayId, Region region) { 832 if (region != null && mTouchExplorer.contains(displayId)) { 833 mTouchExplorer.get(displayId).setGestureDetectionPassthroughRegion(region); 834 } 835 } 836 setTouchExplorationPassthroughRegion(int displayId, Region region)837 public void setTouchExplorationPassthroughRegion(int displayId, Region region) { 838 if (region != null && mTouchExplorer.contains(displayId)) { 839 mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region); 840 } 841 } 842 } 843