1 /* 2 * Copyright (C) 2020 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.systemui.accessibility; 18 19 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS; 20 21 import android.accessibilityservice.AccessibilityService; 22 import android.app.PendingIntent; 23 import android.app.RemoteAction; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.res.Configuration; 29 import android.graphics.drawable.Icon; 30 import android.hardware.input.InputManager; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.PowerManager; 34 import android.os.RemoteException; 35 import android.os.SystemClock; 36 import android.util.Log; 37 import android.view.IWindowManager; 38 import android.view.InputDevice; 39 import android.view.KeyCharacterMap; 40 import android.view.KeyEvent; 41 import android.view.WindowManagerGlobal; 42 import android.view.accessibility.AccessibilityManager; 43 import android.view.accessibility.Flags; 44 45 import com.android.internal.R; 46 import com.android.internal.accessibility.util.AccessibilityUtils; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.util.ScreenshotHelper; 49 import com.android.systemui.CoreStartable; 50 import com.android.systemui.dagger.SysUISingleton; 51 import com.android.systemui.recents.Recents; 52 import com.android.systemui.settings.DisplayTracker; 53 import com.android.systemui.settings.UserTracker; 54 import com.android.systemui.shade.ShadeController; 55 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; 56 import com.android.systemui.statusbar.CommandQueue; 57 import com.android.systemui.statusbar.NotificationShadeWindowController; 58 import com.android.systemui.statusbar.phone.StatusBarWindowCallback; 59 import com.android.systemui.statusbar.policy.ConfigurationController; 60 import com.android.systemui.statusbar.policy.KeyguardStateController; 61 import com.android.systemui.util.Assert; 62 63 import dagger.Lazy; 64 65 import java.util.Locale; 66 import java.util.Optional; 67 68 import javax.inject.Inject; 69 70 /** 71 * Class to register system actions with accessibility framework. 72 */ 73 @SysUISingleton 74 public class SystemActions implements CoreStartable, ConfigurationController.ConfigurationListener { 75 private static final String TAG = "SystemActions"; 76 77 /** 78 * Action ID to go back. 79 */ 80 private static final int SYSTEM_ACTION_ID_BACK = AccessibilityService.GLOBAL_ACTION_BACK; // = 1 81 82 /** 83 * Action ID to go home. 84 */ 85 private static final int SYSTEM_ACTION_ID_HOME = AccessibilityService.GLOBAL_ACTION_HOME; // = 2 86 87 /** 88 * Action ID to toggle showing the overview of recent apps. Will fail on platforms that don't 89 * show recent apps. 90 */ 91 private static final int SYSTEM_ACTION_ID_RECENTS = 92 AccessibilityService.GLOBAL_ACTION_RECENTS; // = 3 93 94 /** 95 * Action ID to open the notifications. 96 */ 97 private static final int SYSTEM_ACTION_ID_NOTIFICATIONS = 98 AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS; // = 4 99 100 /** 101 * Action ID to open the quick settings. 102 */ 103 private static final int SYSTEM_ACTION_ID_QUICK_SETTINGS = 104 AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS; // = 5 105 106 /** 107 * Action ID to open the power long-press dialog. 108 */ 109 private static final int SYSTEM_ACTION_ID_POWER_DIALOG = 110 AccessibilityService.GLOBAL_ACTION_POWER_DIALOG; // = 6 111 112 /** 113 * Action ID to lock the screen 114 */ 115 private static final int SYSTEM_ACTION_ID_LOCK_SCREEN = 116 AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN; // = 8 117 118 /** 119 * Action ID to take a screenshot 120 */ 121 private static final int SYSTEM_ACTION_ID_TAKE_SCREENSHOT = 122 AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9 123 124 /** 125 * Action ID to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and 126 * play/stop media 127 */ 128 private static final int SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK = 129 AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK; // = 10 130 131 /** 132 * Action ID to trigger the accessibility button 133 */ 134 public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON = 135 AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_BUTTON; // 11 136 137 /** 138 * Action ID to show accessibility button's menu of services 139 */ 140 public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER = 141 AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER; // 12 142 143 public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT = 144 AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT; // 13 145 146 public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE = 147 AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15 148 149 /** 150 * Action ID to trigger the dpad up button 151 */ 152 private static final int SYSTEM_ACTION_ID_DPAD_UP = 153 AccessibilityService.GLOBAL_ACTION_DPAD_UP; // 16 154 155 /** 156 * Action ID to trigger the dpad down button 157 */ 158 private static final int SYSTEM_ACTION_ID_DPAD_DOWN = 159 AccessibilityService.GLOBAL_ACTION_DPAD_DOWN; // 17 160 161 /** 162 * Action ID to trigger the dpad left button 163 */ 164 private static final int SYSTEM_ACTION_ID_DPAD_LEFT = 165 AccessibilityService.GLOBAL_ACTION_DPAD_LEFT; // 18 166 167 /** 168 * Action ID to trigger the dpad right button 169 */ 170 private static final int SYSTEM_ACTION_ID_DPAD_RIGHT = 171 AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT; // 19 172 173 /** 174 * Action ID to trigger dpad center keyevent 175 */ 176 private static final int SYSTEM_ACTION_ID_DPAD_CENTER = 177 AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20 178 179 /** 180 * Action ID to trigger menu key event. 181 */ 182 private static final int SYSTEM_ACTION_ID_MENU = 183 AccessibilityService.GLOBAL_ACTION_MENU; // 21 184 185 /** 186 * Action ID to trigger media play/pause key event. 187 */ 188 private static final int SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE = 189 AccessibilityService.GLOBAL_ACTION_MEDIA_PLAY_PAUSE; // 22 190 191 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; 192 193 private final SystemActionsBroadcastReceiver mReceiver; 194 private final Context mContext; 195 private final UserTracker mUserTracker; 196 private final Optional<Recents> mRecentsOptional; 197 private final DisplayTracker mDisplayTracker; 198 private Locale mLocale; 199 private final AccessibilityManager mA11yManager; 200 private final NotificationShadeWindowController mNotificationShadeController; 201 private final KeyguardStateController mKeyguardStateController; 202 private final ShadeController mShadeController; 203 private final Lazy<PanelExpansionInteractor> mPanelExpansionInteractor; 204 private final StatusBarWindowCallback mNotificationShadeCallback; 205 private final ScreenshotHelper mScreenshotHelper; 206 private boolean mDismissNotificationShadeActionRegistered; 207 208 @Inject SystemActions(Context context, UserTracker userTracker, NotificationShadeWindowController notificationShadeController, KeyguardStateController keyguardStateController, ShadeController shadeController, Lazy<PanelExpansionInteractor> panelExpansionInteractor, Optional<Recents> recentsOptional, DisplayTracker displayTracker)209 public SystemActions(Context context, 210 UserTracker userTracker, 211 NotificationShadeWindowController notificationShadeController, 212 KeyguardStateController keyguardStateController, 213 ShadeController shadeController, 214 Lazy<PanelExpansionInteractor> panelExpansionInteractor, 215 Optional<Recents> recentsOptional, 216 DisplayTracker displayTracker) { 217 mContext = context; 218 mUserTracker = userTracker; 219 mKeyguardStateController = keyguardStateController; 220 mShadeController = shadeController; 221 mPanelExpansionInteractor = panelExpansionInteractor; 222 mRecentsOptional = recentsOptional; 223 mDisplayTracker = displayTracker; 224 mReceiver = new SystemActionsBroadcastReceiver(); 225 mLocale = mContext.getResources().getConfiguration().getLocales().get(0); 226 mA11yManager = (AccessibilityManager) mContext.getSystemService( 227 Context.ACCESSIBILITY_SERVICE); 228 mNotificationShadeController = notificationShadeController; 229 // Saving in instance variable since to prevent GC since 230 // NotificationShadeWindowController.registerCallback() only keeps weak references. 231 mNotificationShadeCallback = 232 (keyguardShowing, keyguardOccluded, keyguardGoingAway, bouncerShowing, mDozing, 233 panelExpanded, isDreaming, communalShowing) -> 234 registerOrUnregisterDismissNotificationShadeAction(); 235 mScreenshotHelper = new ScreenshotHelper(mContext); 236 } 237 238 @Override start()239 public void start() { 240 mNotificationShadeController.registerCallback(mNotificationShadeCallback); 241 mContext.registerReceiverForAllUsers( 242 mReceiver, 243 mReceiver.createIntentFilter(), 244 PERMISSION_SELF, 245 null, 246 Context.RECEIVER_EXPORTED); 247 registerActions(); 248 } 249 250 @Override onConfigChanged(Configuration newConfig)251 public void onConfigChanged(Configuration newConfig) { 252 final Locale locale = mContext.getResources().getConfiguration().getLocales().get(0); 253 if (!locale.equals(mLocale)) { 254 mLocale = locale; 255 registerActions(); 256 } 257 } 258 registerActions()259 private void registerActions() { 260 RemoteAction actionBack = createRemoteAction( 261 R.string.accessibility_system_action_back_label, 262 SystemActionsBroadcastReceiver.INTENT_ACTION_BACK); 263 264 RemoteAction actionHome = createRemoteAction( 265 R.string.accessibility_system_action_home_label, 266 SystemActionsBroadcastReceiver.INTENT_ACTION_HOME); 267 268 RemoteAction actionRecents = createRemoteAction( 269 R.string.accessibility_system_action_recents_label, 270 SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS); 271 272 RemoteAction actionNotifications = createRemoteAction( 273 R.string.accessibility_system_action_notifications_label, 274 SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS); 275 276 RemoteAction actionQuickSettings = createRemoteAction( 277 R.string.accessibility_system_action_quick_settings_label, 278 SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS); 279 280 RemoteAction actionPowerDialog = createRemoteAction( 281 R.string.accessibility_system_action_power_dialog_label, 282 SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG); 283 284 RemoteAction actionLockScreen = createRemoteAction( 285 R.string.accessibility_system_action_lock_screen_label, 286 SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN); 287 288 RemoteAction actionTakeScreenshot = createRemoteAction( 289 R.string.accessibility_system_action_screenshot_label, 290 SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT); 291 292 RemoteAction actionHeadsetHook = createRemoteAction( 293 R.string.accessibility_system_action_headset_hook_label, 294 SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK); 295 296 RemoteAction actionAccessibilityShortcut = createRemoteAction( 297 R.string.accessibility_system_action_hardware_a11y_shortcut_label, 298 SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT); 299 300 RemoteAction actionDpadUp = createRemoteAction( 301 R.string.accessibility_system_action_dpad_up_label, 302 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP); 303 304 RemoteAction actionDpadDown = createRemoteAction( 305 R.string.accessibility_system_action_dpad_down_label, 306 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN); 307 308 RemoteAction actionDpadLeft = createRemoteAction( 309 R.string.accessibility_system_action_dpad_left_label, 310 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT); 311 312 RemoteAction actionDpadRight = createRemoteAction( 313 R.string.accessibility_system_action_dpad_right_label, 314 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT); 315 316 RemoteAction actionDpadCenter = createRemoteAction( 317 R.string.accessibility_system_action_dpad_center_label, 318 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER); 319 320 RemoteAction actionMenu = createRemoteAction( 321 R.string.accessibility_system_action_menu_label, 322 SystemActionsBroadcastReceiver.INTENT_ACTION_MENU); 323 324 RemoteAction actionMediaPlayPause = createRemoteAction( 325 R.string.accessibility_system_action_media_play_pause_label, 326 SystemActionsBroadcastReceiver.INTENT_ACTION_MEDIA_PLAY_PAUSE); 327 328 mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK); 329 mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME); 330 mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS); 331 if (mShadeController.isShadeEnabled()) { 332 // These two actions require the shade to be enabled. 333 mA11yManager.registerSystemAction(actionNotifications, SYSTEM_ACTION_ID_NOTIFICATIONS); 334 mA11yManager.registerSystemAction(actionQuickSettings, SYSTEM_ACTION_ID_QUICK_SETTINGS); 335 } 336 mA11yManager.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG); 337 mA11yManager.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN); 338 mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT); 339 mA11yManager.registerSystemAction(actionHeadsetHook, SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK); 340 mA11yManager.registerSystemAction( 341 actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT); 342 mA11yManager.registerSystemAction(actionDpadUp, SYSTEM_ACTION_ID_DPAD_UP); 343 mA11yManager.registerSystemAction(actionDpadDown, SYSTEM_ACTION_ID_DPAD_DOWN); 344 mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT); 345 mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT); 346 mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER); 347 mA11yManager.registerSystemAction(actionMenu, SYSTEM_ACTION_ID_MENU); 348 mA11yManager.registerSystemAction(actionMediaPlayPause, SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE); 349 registerOrUnregisterDismissNotificationShadeAction(); 350 } 351 registerOrUnregisterDismissNotificationShadeAction()352 private void registerOrUnregisterDismissNotificationShadeAction() { 353 Assert.isMainThread(); 354 355 if (mPanelExpansionInteractor.get().isPanelExpanded() 356 && !mKeyguardStateController.isShowing()) { 357 if (!mDismissNotificationShadeActionRegistered) { 358 mA11yManager.registerSystemAction( 359 createRemoteAction( 360 R.string.accessibility_system_action_dismiss_notification_shade, 361 SystemActionsBroadcastReceiver 362 .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE), 363 SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE); 364 mDismissNotificationShadeActionRegistered = true; 365 } 366 } else { 367 if (mDismissNotificationShadeActionRegistered) { 368 mA11yManager.unregisterSystemAction( 369 SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE); 370 mDismissNotificationShadeActionRegistered = false; 371 } 372 } 373 } 374 375 /** 376 * Register a system action. 377 * 378 * @param actionId the action ID to register. 379 */ register(int actionId)380 public void register(int actionId) { 381 int labelId; 382 String intent; 383 switch (actionId) { 384 case SYSTEM_ACTION_ID_BACK: 385 labelId = R.string.accessibility_system_action_back_label; 386 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_BACK; 387 break; 388 case SYSTEM_ACTION_ID_HOME: 389 labelId = R.string.accessibility_system_action_home_label; 390 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HOME; 391 break; 392 case SYSTEM_ACTION_ID_RECENTS: 393 labelId = R.string.accessibility_system_action_recents_label; 394 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS; 395 break; 396 case SYSTEM_ACTION_ID_NOTIFICATIONS: 397 labelId = R.string.accessibility_system_action_notifications_label; 398 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS; 399 break; 400 case SYSTEM_ACTION_ID_QUICK_SETTINGS: 401 labelId = R.string.accessibility_system_action_quick_settings_label; 402 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS; 403 break; 404 case SYSTEM_ACTION_ID_POWER_DIALOG: 405 labelId = R.string.accessibility_system_action_power_dialog_label; 406 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG; 407 break; 408 case SYSTEM_ACTION_ID_LOCK_SCREEN: 409 labelId = R.string.accessibility_system_action_lock_screen_label; 410 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN; 411 break; 412 case SYSTEM_ACTION_ID_TAKE_SCREENSHOT: 413 labelId = R.string.accessibility_system_action_screenshot_label; 414 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT; 415 break; 416 case SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK: 417 labelId = R.string.accessibility_system_action_headset_hook_label; 418 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK; 419 break; 420 case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON: 421 labelId = R.string.accessibility_system_action_on_screen_a11y_shortcut_label; 422 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON; 423 break; 424 case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER: 425 labelId = 426 R.string.accessibility_system_action_on_screen_a11y_shortcut_chooser_label; 427 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER; 428 break; 429 case SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT: 430 labelId = R.string.accessibility_system_action_hardware_a11y_shortcut_label; 431 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT; 432 break; 433 case SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: 434 labelId = R.string.accessibility_system_action_dismiss_notification_shade; 435 intent = SystemActionsBroadcastReceiver 436 .INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE; 437 break; 438 case SYSTEM_ACTION_ID_DPAD_UP: 439 labelId = R.string.accessibility_system_action_dpad_up_label; 440 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP; 441 break; 442 case SYSTEM_ACTION_ID_DPAD_DOWN: 443 labelId = R.string.accessibility_system_action_dpad_down_label; 444 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN; 445 break; 446 case SYSTEM_ACTION_ID_DPAD_LEFT: 447 labelId = R.string.accessibility_system_action_dpad_left_label; 448 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT; 449 break; 450 case SYSTEM_ACTION_ID_DPAD_RIGHT: 451 labelId = R.string.accessibility_system_action_dpad_right_label; 452 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT; 453 break; 454 case SYSTEM_ACTION_ID_DPAD_CENTER: 455 labelId = R.string.accessibility_system_action_dpad_center_label; 456 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER; 457 break; 458 case SYSTEM_ACTION_ID_MENU: 459 labelId = R.string.accessibility_system_action_menu_label; 460 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_MENU; 461 break; 462 case SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE: 463 labelId = R.string.accessibility_system_action_media_play_pause_label; 464 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_MEDIA_PLAY_PAUSE; 465 break; 466 default: 467 return; 468 } 469 mA11yManager.registerSystemAction(createRemoteAction(labelId, intent), actionId); 470 } 471 createRemoteAction(int labelId, String intent)472 private RemoteAction createRemoteAction(int labelId, String intent) { 473 // TODO(b/148087487): update the icon used below to a valid one 474 return new RemoteAction( 475 Icon.createWithResource(mContext, R.drawable.ic_info), 476 mContext.getString(labelId), 477 mContext.getString(labelId), 478 mReceiver.createPendingIntent(mContext, intent)); 479 } 480 481 /** 482 * Unregister a system action. 483 * 484 * @param actionId the action ID to unregister. 485 */ unregister(int actionId)486 public void unregister(int actionId) { 487 mA11yManager.unregisterSystemAction(actionId); 488 } 489 handleBack()490 private void handleBack() { 491 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); 492 } 493 handleHome()494 private void handleHome() { 495 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME); 496 } 497 sendDownAndUpKeyEvents(int keyCode)498 private void sendDownAndUpKeyEvents(int keyCode) { 499 final long downTime = SystemClock.uptimeMillis(); 500 sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime); 501 sendKeyEventIdentityCleared( 502 keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis()); 503 } 504 sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time)505 private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) { 506 KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0, 507 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, 508 InputDevice.SOURCE_KEYBOARD, null); 509 mContext.getSystemService(InputManager.class) 510 .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 511 event.recycle(); 512 } 513 handleRecents()514 private void handleRecents() { 515 mRecentsOptional.ifPresent(Recents::toggleRecentApps); 516 } 517 handleNotifications()518 private void handleNotifications() { 519 mShadeController.animateExpandShade(); 520 } 521 handleQuickSettings()522 private void handleQuickSettings() { 523 mShadeController.animateExpandQs(); 524 } 525 handlePowerDialog()526 private void handlePowerDialog() { 527 IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService(); 528 529 try { 530 windowManager.showGlobalActions(); 531 } catch (RemoteException e) { 532 Log.e(TAG, "failed to display power dialog."); 533 } 534 } 535 handleLockScreen()536 private void handleLockScreen() { 537 IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService(); 538 539 mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(), 540 PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0); 541 try { 542 windowManager.lockNow(null); 543 } catch (RemoteException e) { 544 Log.e(TAG, "failed to lock screen."); 545 } 546 } 547 handleTakeScreenshot()548 private void handleTakeScreenshot() { 549 mScreenshotHelper.takeScreenshot( 550 SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null); 551 } 552 553 @VisibleForTesting handleHeadsetHook()554 void handleHeadsetHook() { 555 if (!AccessibilityUtils.interceptHeadsetHookForActiveCall(mContext)) { 556 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK); 557 } 558 } 559 handleAccessibilityButton()560 private void handleAccessibilityButton() { 561 mA11yManager.notifyAccessibilityButtonClicked( 562 mDisplayTracker.getDefaultDisplayId()); 563 } 564 handleAccessibilityButtonChooser()565 private void handleAccessibilityButtonChooser() { 566 mA11yManager.notifyAccessibilityButtonLongClicked( 567 mDisplayTracker.getDefaultDisplayId()); 568 } 569 handleAccessibilityShortcut()570 private void handleAccessibilityShortcut() { 571 mA11yManager.performAccessibilityShortcut(); 572 } 573 handleAccessibilityDismissNotificationShade()574 private void handleAccessibilityDismissNotificationShade() { 575 mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE); 576 } 577 handleDpadUp()578 private void handleDpadUp() { 579 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP); 580 } 581 handleDpadDown()582 private void handleDpadDown() { 583 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN); 584 } 585 handleDpadLeft()586 private void handleDpadLeft() { 587 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT); 588 } 589 handleDpadRight()590 private void handleDpadRight() { 591 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT); 592 } 593 handleDpadCenter()594 private void handleDpadCenter() { 595 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER); 596 } 597 598 @VisibleForTesting handleMenu()599 void handleMenu() { 600 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MENU); 601 } 602 603 @VisibleForTesting handleMediaPlayPause()604 void handleMediaPlayPause() { 605 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 606 } 607 608 private class SystemActionsBroadcastReceiver extends BroadcastReceiver { 609 private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK"; 610 private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME"; 611 private static final String INTENT_ACTION_RECENTS = "SYSTEM_ACTION_RECENTS"; 612 private static final String INTENT_ACTION_NOTIFICATIONS = "SYSTEM_ACTION_NOTIFICATIONS"; 613 private static final String INTENT_ACTION_QUICK_SETTINGS = "SYSTEM_ACTION_QUICK_SETTINGS"; 614 private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG"; 615 private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN"; 616 private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT"; 617 private static final String INTENT_ACTION_HEADSET_HOOK = "SYSTEM_ACTION_HEADSET_HOOK"; 618 private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON = 619 "SYSTEM_ACTION_ACCESSIBILITY_BUTTON"; 620 private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 621 "SYSTEM_ACTION_ACCESSIBILITY_BUTTON_MENU"; 622 private static final String INTENT_ACTION_ACCESSIBILITY_SHORTCUT = 623 "SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT"; 624 private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE = 625 "SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE"; 626 private static final String INTENT_ACTION_DPAD_UP = "SYSTEM_ACTION_DPAD_UP"; 627 private static final String INTENT_ACTION_DPAD_DOWN = "SYSTEM_ACTION_DPAD_DOWN"; 628 private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT"; 629 private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT"; 630 private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER"; 631 private static final String INTENT_ACTION_MENU = "SYSTEM_ACTION_MENU"; 632 private static final String INTENT_ACTION_MEDIA_PLAY_PAUSE = 633 "SYSTEM_ACTION_MEDIA_PLAY_PAUSE"; 634 createPendingIntent(Context context, String intentAction)635 private PendingIntent createPendingIntent(Context context, String intentAction) { 636 switch (intentAction) { 637 case INTENT_ACTION_BACK: 638 case INTENT_ACTION_HOME: 639 case INTENT_ACTION_RECENTS: 640 case INTENT_ACTION_NOTIFICATIONS: 641 case INTENT_ACTION_QUICK_SETTINGS: 642 case INTENT_ACTION_POWER_DIALOG: 643 case INTENT_ACTION_LOCK_SCREEN: 644 case INTENT_ACTION_TAKE_SCREENSHOT: 645 case INTENT_ACTION_HEADSET_HOOK: 646 case INTENT_ACTION_ACCESSIBILITY_BUTTON: 647 case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER: 648 case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: 649 case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: 650 case INTENT_ACTION_DPAD_UP: 651 case INTENT_ACTION_DPAD_DOWN: 652 case INTENT_ACTION_DPAD_LEFT: 653 case INTENT_ACTION_DPAD_RIGHT: 654 case INTENT_ACTION_DPAD_CENTER: 655 case INTENT_ACTION_MENU: 656 case INTENT_ACTION_MEDIA_PLAY_PAUSE: { 657 Intent intent = new Intent(intentAction); 658 intent.setPackage(context.getPackageName()); 659 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 660 return PendingIntent.getBroadcast(context, 0, intent, 661 PendingIntent.FLAG_IMMUTABLE); 662 } 663 default: 664 break; 665 } 666 return null; 667 } 668 createIntentFilter()669 private IntentFilter createIntentFilter() { 670 IntentFilter intentFilter = new IntentFilter(); 671 intentFilter.addAction(INTENT_ACTION_BACK); 672 intentFilter.addAction(INTENT_ACTION_HOME); 673 intentFilter.addAction(INTENT_ACTION_RECENTS); 674 intentFilter.addAction(INTENT_ACTION_NOTIFICATIONS); 675 intentFilter.addAction(INTENT_ACTION_QUICK_SETTINGS); 676 intentFilter.addAction(INTENT_ACTION_POWER_DIALOG); 677 intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN); 678 intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT); 679 intentFilter.addAction(INTENT_ACTION_HEADSET_HOOK); 680 intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON); 681 intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER); 682 intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT); 683 intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE); 684 intentFilter.addAction(INTENT_ACTION_DPAD_UP); 685 intentFilter.addAction(INTENT_ACTION_DPAD_DOWN); 686 intentFilter.addAction(INTENT_ACTION_DPAD_LEFT); 687 intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT); 688 intentFilter.addAction(INTENT_ACTION_DPAD_CENTER); 689 intentFilter.addAction(INTENT_ACTION_MENU); 690 intentFilter.addAction(INTENT_ACTION_MEDIA_PLAY_PAUSE); 691 return intentFilter; 692 } 693 694 @Override onReceive(Context context, Intent intent)695 public void onReceive(Context context, Intent intent) { 696 String intentAction = intent.getAction(); 697 switch (intentAction) { 698 case INTENT_ACTION_BACK: { 699 handleBack(); 700 break; 701 } 702 case INTENT_ACTION_HOME: { 703 handleHome(); 704 break; 705 } 706 case INTENT_ACTION_RECENTS: { 707 handleRecents(); 708 break; 709 } 710 case INTENT_ACTION_NOTIFICATIONS: { 711 handleNotifications(); 712 break; 713 } 714 case INTENT_ACTION_QUICK_SETTINGS: { 715 handleQuickSettings(); 716 break; 717 } 718 case INTENT_ACTION_POWER_DIALOG: { 719 handlePowerDialog(); 720 break; 721 } 722 case INTENT_ACTION_LOCK_SCREEN: { 723 handleLockScreen(); 724 break; 725 } 726 case INTENT_ACTION_TAKE_SCREENSHOT: { 727 handleTakeScreenshot(); 728 break; 729 } 730 case INTENT_ACTION_HEADSET_HOOK: { 731 handleHeadsetHook(); 732 break; 733 } 734 case INTENT_ACTION_ACCESSIBILITY_BUTTON: { 735 handleAccessibilityButton(); 736 break; 737 } 738 case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER: { 739 handleAccessibilityButtonChooser(); 740 break; 741 } 742 case INTENT_ACTION_ACCESSIBILITY_SHORTCUT: { 743 handleAccessibilityShortcut(); 744 break; 745 } 746 case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: { 747 handleAccessibilityDismissNotificationShade(); 748 break; 749 } 750 case INTENT_ACTION_DPAD_UP: { 751 handleDpadUp(); 752 break; 753 } 754 case INTENT_ACTION_DPAD_DOWN: { 755 handleDpadDown(); 756 break; 757 } 758 case INTENT_ACTION_DPAD_LEFT: { 759 handleDpadLeft(); 760 break; 761 } 762 case INTENT_ACTION_DPAD_RIGHT: { 763 handleDpadRight(); 764 break; 765 } 766 case INTENT_ACTION_DPAD_CENTER: { 767 handleDpadCenter(); 768 break; 769 } 770 case INTENT_ACTION_MENU: { 771 if (Flags.globalActionMenu()) { 772 handleMenu(); 773 } 774 break; 775 } 776 case INTENT_ACTION_MEDIA_PLAY_PAUSE: { 777 if (Flags.globalActionMediaPlayPause()) { 778 handleMediaPlayPause(); 779 } 780 break; 781 } 782 default: 783 break; 784 } 785 } 786 } 787 } 788