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