1 /*
2  * Copyright 2021 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.globalactions;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
22 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
23 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
24 import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
25 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
26 
27 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
28 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
29 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
30 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
31 
32 import android.animation.Animator;
33 import android.animation.AnimatorListenerAdapter;
34 import android.annotation.Nullable;
35 import android.app.ActivityManager;
36 import android.app.Dialog;
37 import android.app.IActivityManager;
38 import android.app.StatusBarManager;
39 import android.app.WallpaperManager;
40 import android.app.admin.DevicePolicyManager;
41 import android.app.trust.TrustManager;
42 import android.content.BroadcastReceiver;
43 import android.content.Context;
44 import android.content.DialogInterface;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.pm.PackageManager;
48 import android.content.pm.UserInfo;
49 import android.content.res.ColorStateList;
50 import android.content.res.Configuration;
51 import android.content.res.Resources;
52 import android.database.ContentObserver;
53 import android.graphics.Color;
54 import android.graphics.drawable.Drawable;
55 import android.media.AudioManager;
56 import android.os.Binder;
57 import android.os.Bundle;
58 import android.os.Handler;
59 import android.os.IBinder;
60 import android.os.Message;
61 import android.os.RemoteException;
62 import android.os.SystemProperties;
63 import android.os.UserHandle;
64 import android.os.UserManager;
65 import android.os.Vibrator;
66 import android.provider.Settings;
67 import android.service.dreams.IDreamManager;
68 import android.sysprop.TelephonyProperties;
69 import android.telecom.TelecomManager;
70 import android.telephony.ServiceState;
71 import android.telephony.TelephonyCallback;
72 import android.telephony.TelephonyManager;
73 import android.util.ArraySet;
74 import android.util.Log;
75 import android.view.ContextThemeWrapper;
76 import android.view.GestureDetector;
77 import android.view.IWindowManager;
78 import android.view.LayoutInflater;
79 import android.view.MotionEvent;
80 import android.view.View;
81 import android.view.ViewGroup;
82 import android.view.WindowManager;
83 import android.view.accessibility.AccessibilityEvent;
84 import android.view.accessibility.AccessibilityManager;
85 import android.widget.BaseAdapter;
86 import android.widget.ImageView;
87 import android.widget.ImageView.ScaleType;
88 import android.widget.LinearLayout;
89 import android.widget.ListPopupWindow;
90 import android.widget.TextView;
91 
92 import androidx.annotation.NonNull;
93 import androidx.lifecycle.Lifecycle;
94 import androidx.lifecycle.LifecycleOwner;
95 import androidx.lifecycle.LifecycleRegistry;
96 
97 import com.android.internal.R;
98 import com.android.internal.annotations.VisibleForTesting;
99 import com.android.internal.colorextraction.ColorExtractor;
100 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
101 import com.android.internal.logging.MetricsLogger;
102 import com.android.internal.logging.UiEvent;
103 import com.android.internal.logging.UiEventLogger;
104 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
105 import com.android.internal.statusbar.IStatusBarService;
106 import com.android.internal.util.EmergencyAffordanceManager;
107 import com.android.internal.util.ScreenshotHelper;
108 import com.android.internal.widget.LockPatternUtils;
109 import com.android.keyguard.KeyguardUpdateMonitor;
110 import com.android.systemui.MultiListLayout;
111 import com.android.systemui.MultiListLayout.MultiListAdapter;
112 import com.android.systemui.animation.DialogLaunchAnimator;
113 import com.android.systemui.broadcast.BroadcastDispatcher;
114 import com.android.systemui.colorextraction.SysuiColorExtractor;
115 import com.android.systemui.dagger.qualifiers.Background;
116 import com.android.systemui.dagger.qualifiers.Main;
117 import com.android.systemui.model.SysUiState;
118 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
119 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
120 import com.android.systemui.scrim.ScrimDrawable;
121 import com.android.systemui.statusbar.NotificationShadeWindowController;
122 import com.android.systemui.statusbar.phone.StatusBar;
123 import com.android.systemui.statusbar.phone.SystemUIDialog;
124 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
125 import com.android.systemui.statusbar.policy.ConfigurationController;
126 import com.android.systemui.statusbar.policy.KeyguardStateController;
127 import com.android.systemui.telephony.TelephonyListenerManager;
128 import com.android.systemui.util.EmergencyDialerConstants;
129 import com.android.systemui.util.RingerModeTracker;
130 import com.android.systemui.util.settings.GlobalSettings;
131 import com.android.systemui.util.settings.SecureSettings;
132 
133 import java.util.ArrayList;
134 import java.util.List;
135 import java.util.Optional;
136 import java.util.concurrent.Executor;
137 
138 import javax.inject.Inject;
139 
140 /**
141  * Helper to show the global actions dialog.  Each item is an {@link Action} that may show depending
142  * on whether the keyguard is showing, and whether the device is provisioned.
143  */
144 public class GlobalActionsDialogLite implements DialogInterface.OnDismissListener,
145         DialogInterface.OnShowListener,
146         ConfigurationController.ConfigurationListener,
147         GlobalActionsPanelPlugin.Callbacks,
148         LifecycleOwner {
149 
150     public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
151     public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
152     public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
153 
154     private static final String TAG = "GlobalActionsDialogLite";
155 
156     private static final boolean SHOW_SILENT_TOGGLE = true;
157 
158     /* Valid settings for global actions keys.
159      * see config.xml config_globalActionList */
160     @VisibleForTesting
161     static final String GLOBAL_ACTION_KEY_POWER = "power";
162     private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
163     static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
164     private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
165     private static final String GLOBAL_ACTION_KEY_USERS = "users";
166     private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
167     static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
168     private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
169     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
170     static final String GLOBAL_ACTION_KEY_RESTART = "restart";
171     private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
172     static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
173     static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
174 
175     // See NotificationManagerService#scheduleDurationReachedLocked
176     private static final long TOAST_FADE_TIME = 333;
177     // See NotificationManagerService.LONG_DELAY
178     private static final int TOAST_VISIBLE_TIME = 3500;
179 
180     private final Context mContext;
181     private final GlobalActionsManager mWindowManagerFuncs;
182     private final AudioManager mAudioManager;
183     private final IDreamManager mDreamManager;
184     private final DevicePolicyManager mDevicePolicyManager;
185     private final LockPatternUtils mLockPatternUtils;
186     private final TelephonyListenerManager mTelephonyListenerManager;
187     private final KeyguardStateController mKeyguardStateController;
188     private final BroadcastDispatcher mBroadcastDispatcher;
189     protected final GlobalSettings mGlobalSettings;
190     protected final SecureSettings mSecureSettings;
191     protected final Resources mResources;
192     private final ConfigurationController mConfigurationController;
193     private final UserManager mUserManager;
194     private final TrustManager mTrustManager;
195     private final IActivityManager mIActivityManager;
196     private final TelecomManager mTelecomManager;
197     private final MetricsLogger mMetricsLogger;
198     private final UiEventLogger mUiEventLogger;
199     private final SysUiState mSysUiState;
200 
201     // Used for RingerModeTracker
202     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
203 
204     @VisibleForTesting
205     protected final ArrayList<Action> mItems = new ArrayList<>();
206     @VisibleForTesting
207     protected final ArrayList<Action> mOverflowItems = new ArrayList<>();
208     @VisibleForTesting
209     protected final ArrayList<Action> mPowerItems = new ArrayList<>();
210 
211     @VisibleForTesting
212     protected ActionsDialogLite mDialog;
213 
214     private Action mSilentModeAction;
215     private ToggleAction mAirplaneModeOn;
216 
217     protected MyAdapter mAdapter;
218     protected MyOverflowAdapter mOverflowAdapter;
219     protected MyPowerOptionsAdapter mPowerAdapter;
220 
221     private boolean mKeyguardShowing = false;
222     private boolean mDeviceProvisioned = false;
223     private ToggleState mAirplaneState = ToggleState.Off;
224     private boolean mIsWaitingForEcmExit = false;
225     private boolean mHasTelephony;
226     private boolean mHasVibrator;
227     private final boolean mShowSilentToggle;
228     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
229     private final ScreenshotHelper mScreenshotHelper;
230     private final SysuiColorExtractor mSysuiColorExtractor;
231     private final IStatusBarService mStatusBarService;
232     protected final NotificationShadeWindowController mNotificationShadeWindowController;
233     private final IWindowManager mIWindowManager;
234     private final Executor mBackgroundExecutor;
235     private final RingerModeTracker mRingerModeTracker;
236     private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
237     protected Handler mMainHandler;
238     private int mSmallestScreenWidthDp;
239     private final Optional<StatusBar> mStatusBarOptional;
240     private final SystemUIDialogManager mDialogManager;
241     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
242     private final DialogLaunchAnimator mDialogLaunchAnimator;
243 
244     @VisibleForTesting
245     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
246         @UiEvent(doc = "The global actions / power menu surface became visible on the screen.")
247         GA_POWER_MENU_OPEN(337),
248 
249         @UiEvent(doc = "The global actions / power menu surface was dismissed.")
250         GA_POWER_MENU_CLOSE(471),
251 
252         @UiEvent(doc = "The global actions bugreport button was pressed.")
253         GA_BUGREPORT_PRESS(344),
254 
255         @UiEvent(doc = "The global actions bugreport button was long pressed.")
256         GA_BUGREPORT_LONG_PRESS(345),
257 
258         @UiEvent(doc = "The global actions emergency button was pressed.")
259         GA_EMERGENCY_DIALER_PRESS(346),
260 
261         @UiEvent(doc = "The global actions screenshot button was pressed.")
262         GA_SCREENSHOT_PRESS(347),
263 
264         @UiEvent(doc = "The global actions screenshot button was long pressed.")
265         GA_SCREENSHOT_LONG_PRESS(348),
266 
267         @UiEvent(doc = "The global actions power off button was pressed.")
268         GA_SHUTDOWN_PRESS(802),
269 
270         @UiEvent(doc = "The global actions power off button was long pressed.")
271         GA_SHUTDOWN_LONG_PRESS(803),
272 
273         @UiEvent(doc = "The global actions reboot button was pressed.")
274         GA_REBOOT_PRESS(349),
275 
276         @UiEvent(doc = "The global actions reboot button was long pressed.")
277         GA_REBOOT_LONG_PRESS(804),
278 
279         @UiEvent(doc = "The global actions lockdown button was pressed.")
280         GA_LOCKDOWN_PRESS(354), // already created by cwren apparently
281 
282         @UiEvent(doc = "Power menu was opened via quick settings button.")
283         GA_OPEN_QS(805),
284 
285         @UiEvent(doc = "Power menu was opened via power + volume up.")
286         GA_OPEN_POWER_VOLUP(806),
287 
288         @UiEvent(doc = "Power menu was opened via long press on power.")
289         GA_OPEN_LONG_PRESS_POWER(807),
290 
291         @UiEvent(doc = "Power menu was closed via long press on power.")
292         GA_CLOSE_LONG_PRESS_POWER(808),
293 
294         @UiEvent(doc = "Power menu was dismissed by back gesture.")
295         GA_CLOSE_BACK(809),
296 
297         @UiEvent(doc = "Power menu was dismissed by tapping outside dialog.")
298         GA_CLOSE_TAP_OUTSIDE(810),
299 
300         @UiEvent(doc = "Power menu was closed via power + volume up.")
301         GA_CLOSE_POWER_VOLUP(811);
302 
303         private final int mId;
304 
GlobalActionsEvent(int id)305         GlobalActionsEvent(int id) {
306             mId = id;
307         }
308 
309         @Override
getId()310         public int getId() {
311             return mId;
312         }
313     }
314 
315     /**
316      * @param context everything needs a context :(
317      */
318     @Inject
GlobalActionsDialogLite( Context context, GlobalActionsManager windowManagerFuncs, AudioManager audioManager, IDreamManager iDreamManager, DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, TelephonyListenerManager telephonyListenerManager, GlobalSettings globalSettings, SecureSettings secureSettings, @Nullable Vibrator vibrator, @Main Resources resources, ConfigurationController configurationController, KeyguardStateController keyguardStateController, UserManager userManager, TrustManager trustManager, IActivityManager iActivityManager, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, UiEventLogger uiEventLogger, RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler, PackageManager packageManager, Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor, DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager)319     public GlobalActionsDialogLite(
320             Context context,
321             GlobalActionsManager windowManagerFuncs,
322             AudioManager audioManager,
323             IDreamManager iDreamManager,
324             DevicePolicyManager devicePolicyManager,
325             LockPatternUtils lockPatternUtils,
326             BroadcastDispatcher broadcastDispatcher,
327             TelephonyListenerManager telephonyListenerManager,
328             GlobalSettings globalSettings,
329             SecureSettings secureSettings,
330             @Nullable Vibrator vibrator,
331             @Main Resources resources,
332             ConfigurationController configurationController,
333             KeyguardStateController keyguardStateController,
334             UserManager userManager,
335             TrustManager trustManager,
336             IActivityManager iActivityManager,
337             @Nullable TelecomManager telecomManager,
338             MetricsLogger metricsLogger,
339             SysuiColorExtractor colorExtractor,
340             IStatusBarService statusBarService,
341             NotificationShadeWindowController notificationShadeWindowController,
342             IWindowManager iWindowManager,
343             @Background Executor backgroundExecutor,
344             UiEventLogger uiEventLogger,
345             RingerModeTracker ringerModeTracker,
346             SysUiState sysUiState,
347             @Main Handler handler,
348             PackageManager packageManager,
349             Optional<StatusBar> statusBarOptional,
350             KeyguardUpdateMonitor keyguardUpdateMonitor,
351             DialogLaunchAnimator dialogLaunchAnimator,
352             SystemUIDialogManager dialogManager) {
353         mContext = context;
354         mWindowManagerFuncs = windowManagerFuncs;
355         mAudioManager = audioManager;
356         mDreamManager = iDreamManager;
357         mDevicePolicyManager = devicePolicyManager;
358         mLockPatternUtils = lockPatternUtils;
359         mTelephonyListenerManager = telephonyListenerManager;
360         mKeyguardStateController = keyguardStateController;
361         mBroadcastDispatcher = broadcastDispatcher;
362         mGlobalSettings = globalSettings;
363         mSecureSettings = secureSettings;
364         mResources = resources;
365         mConfigurationController = configurationController;
366         mUserManager = userManager;
367         mTrustManager = trustManager;
368         mIActivityManager = iActivityManager;
369         mTelecomManager = telecomManager;
370         mMetricsLogger = metricsLogger;
371         mUiEventLogger = uiEventLogger;
372         mSysuiColorExtractor = colorExtractor;
373         mStatusBarService = statusBarService;
374         mNotificationShadeWindowController = notificationShadeWindowController;
375         mIWindowManager = iWindowManager;
376         mBackgroundExecutor = backgroundExecutor;
377         mRingerModeTracker = ringerModeTracker;
378         mSysUiState = sysUiState;
379         mMainHandler = handler;
380         mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
381         mStatusBarOptional = statusBarOptional;
382         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
383         mDialogLaunchAnimator = dialogLaunchAnimator;
384         mDialogManager = dialogManager;
385 
386         // receive broadcasts
387         IntentFilter filter = new IntentFilter();
388         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
389         filter.addAction(Intent.ACTION_SCREEN_OFF);
390         filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
391         mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
392 
393         mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
394 
395         // get notified of phone state changes
396         mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener);
397         mGlobalSettings.registerContentObserver(
398                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
399                 mAirplaneModeObserver);
400         mHasVibrator = vibrator != null && vibrator.hasVibrator();
401 
402         mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean(
403                 R.bool.config_useFixedVolume);
404         if (mShowSilentToggle) {
405             mRingerModeTracker.getRingerMode().observe(this, ringer ->
406                     mHandler.sendEmptyMessage(MESSAGE_REFRESH)
407             );
408         }
409 
410         mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
411         mScreenshotHelper = new ScreenshotHelper(context);
412 
413         mConfigurationController.addCallback(this);
414     }
415 
416     /**
417      * Clean up callbacks
418      */
destroy()419     public void destroy() {
420         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
421         mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
422         mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver);
423         mConfigurationController.removeCallback(this);
424     }
425 
getContext()426     protected Context getContext() {
427         return mContext;
428     }
429 
getEventLogger()430     protected UiEventLogger getEventLogger() {
431         return mUiEventLogger;
432     }
433 
getStatusBar()434     protected Optional<StatusBar> getStatusBar() {
435         return mStatusBarOptional;
436     }
437 
getKeyguardUpdateMonitor()438     protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
439         return mKeyguardUpdateMonitor;
440     }
441 
442     /**
443      * Show the global actions dialog (creating if necessary) or hide it if it's already showing.
444      *
445      * @param keyguardShowing     True if keyguard is showing
446      * @param isDeviceProvisioned True if device is provisioned
447      * @param view                The view from which we should animate the dialog when showing it
448      */
showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned, @Nullable View view)449     public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
450             @Nullable View view) {
451         mKeyguardShowing = keyguardShowing;
452         mDeviceProvisioned = isDeviceProvisioned;
453         if (mDialog != null && mDialog.isShowing()) {
454             // In order to force global actions to hide on the same affordance press, we must
455             // register a call to onGlobalActionsShown() first to prevent the default actions
456             // menu from showing. This will be followed by a subsequent call to
457             // onGlobalActionsHidden() on dismiss()
458             mWindowManagerFuncs.onGlobalActionsShown();
459             mDialog.dismiss();
460             mDialog = null;
461         } else {
462             handleShow(view);
463         }
464     }
465 
isKeyguardShowing()466     protected boolean isKeyguardShowing() {
467         return mKeyguardShowing;
468     }
469 
isDeviceProvisioned()470     protected boolean isDeviceProvisioned() {
471         return mDeviceProvisioned;
472     }
473 
474     /**
475      * Dismiss the global actions dialog, if it's currently shown
476      */
dismissDialog()477     public void dismissDialog() {
478         mHandler.removeMessages(MESSAGE_DISMISS);
479         mHandler.sendEmptyMessage(MESSAGE_DISMISS);
480     }
481 
awakenIfNecessary()482     protected void awakenIfNecessary() {
483         if (mDreamManager != null) {
484             try {
485                 if (mDreamManager.isDreaming()) {
486                     mDreamManager.awaken();
487                 }
488             } catch (RemoteException e) {
489                 // we tried
490             }
491         }
492     }
493 
handleShow(@ullable View view)494     protected void handleShow(@Nullable View view) {
495         awakenIfNecessary();
496         mDialog = createDialog();
497         prepareDialog();
498 
499         WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
500         attrs.setTitle("ActionsDialog");
501         attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
502         mDialog.getWindow().setAttributes(attrs);
503         // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
504         mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
505 
506         if (view != null) {
507             mDialogLaunchAnimator.showFromView(mDialog, view);
508         } else {
509             mDialog.show();
510         }
511         mWindowManagerFuncs.onGlobalActionsShown();
512     }
513 
514     @VisibleForTesting
shouldShowAction(Action action)515     protected boolean shouldShowAction(Action action) {
516         if (mKeyguardShowing && !action.showDuringKeyguard()) {
517             return false;
518         }
519         if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
520             return false;
521         }
522         return action.shouldShow();
523     }
524 
525     /**
526      * Returns the maximum number of power menu items to show based on which GlobalActions
527      * layout is being used.
528      */
529     @VisibleForTesting
getMaxShownPowerItems()530     protected int getMaxShownPowerItems() {
531         return mResources.getInteger(com.android.systemui.R.integer.power_menu_lite_max_columns)
532                 * mResources.getInteger(com.android.systemui.R.integer.power_menu_lite_max_rows);
533     }
534 
535     /**
536      * Add a power menu action item for to either the main or overflow items lists, depending on
537      * whether controls are enabled and whether the max number of shown items has been reached.
538      */
addActionItem(Action action)539     private void addActionItem(Action action) {
540         if (mItems.size() < getMaxShownPowerItems()) {
541             mItems.add(action);
542         } else {
543             mOverflowItems.add(action);
544         }
545     }
546 
547     @VisibleForTesting
getDefaultActions()548     protected String[] getDefaultActions() {
549         return mResources.getStringArray(R.array.config_globalActionsList);
550     }
551 
addIfShouldShowAction(List<Action> actions, Action action)552     private void addIfShouldShowAction(List<Action> actions, Action action) {
553         if (shouldShowAction(action)) {
554             actions.add(action);
555         }
556     }
557 
558     @VisibleForTesting
createActionItems()559     protected void createActionItems() {
560         // Simple toggle style if there's no vibrator, otherwise use a tri-state
561         if (!mHasVibrator) {
562             mSilentModeAction = new SilentModeToggleAction();
563         } else {
564             mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
565         }
566         mAirplaneModeOn = new AirplaneModeAction();
567         onAirplaneModeChanged();
568 
569         mItems.clear();
570         mOverflowItems.clear();
571         mPowerItems.clear();
572         String[] defaultActions = getDefaultActions();
573 
574         ShutDownAction shutdownAction = new ShutDownAction();
575         RestartAction restartAction = new RestartAction();
576         ArraySet<String> addedKeys = new ArraySet<>();
577         List<Action> tempActions = new ArrayList<>();
578         CurrentUserProvider currentUser = new CurrentUserProvider();
579 
580         // make sure emergency affordance action is first, if needed
581         if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
582             addIfShouldShowAction(tempActions, new EmergencyAffordanceAction());
583             addedKeys.add(GLOBAL_ACTION_KEY_EMERGENCY);
584         }
585 
586         for (int i = 0; i < defaultActions.length; i++) {
587             String actionKey = defaultActions[i];
588             if (addedKeys.contains(actionKey)) {
589                 // If we already have added this, don't add it again.
590                 continue;
591             }
592             if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
593                 addIfShouldShowAction(tempActions, shutdownAction);
594             } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
595                 addIfShouldShowAction(tempActions, mAirplaneModeOn);
596             } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
597                 if (shouldDisplayBugReport(currentUser.get())) {
598                     addIfShouldShowAction(tempActions, new BugReportAction());
599                 }
600             } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
601                 if (mShowSilentToggle) {
602                     addIfShouldShowAction(tempActions, mSilentModeAction);
603                 }
604             } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
605                 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
606                     addUserActions(tempActions, currentUser.get());
607                 }
608             } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
609                 addIfShouldShowAction(tempActions, getSettingsAction());
610             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
611                 if (shouldDisplayLockdown(currentUser.get())) {
612                     addIfShouldShowAction(tempActions, new LockDownAction());
613                 }
614             } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
615                 addIfShouldShowAction(tempActions, getVoiceAssistAction());
616             } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
617                 addIfShouldShowAction(tempActions, getAssistAction());
618             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
619                 addIfShouldShowAction(tempActions, restartAction);
620             } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
621                 addIfShouldShowAction(tempActions, new ScreenshotAction());
622             } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
623                 // TODO(b/206032495): should call mDevicePolicyManager.getLogoutUserId() instead of
624                 // hardcode it to USER_SYSTEM so it properly supports headless system user mode
625                 // (and then call mDevicePolicyManager.clearLogoutUser() after switched)
626                 if (mDevicePolicyManager.isLogoutEnabled()
627                         && currentUser.get() != null
628                         && currentUser.get().id != UserHandle.USER_SYSTEM) {
629                     addIfShouldShowAction(tempActions, new LogoutAction());
630                 }
631             } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
632                 addIfShouldShowAction(tempActions, new EmergencyDialerAction());
633             } else {
634                 Log.e(TAG, "Invalid global action key " + actionKey);
635             }
636             // Add here so we don't add more than one.
637             addedKeys.add(actionKey);
638         }
639 
640         // replace power and restart with a single power options action, if needed
641         if (tempActions.contains(shutdownAction) && tempActions.contains(restartAction)
642                 && tempActions.size() > getMaxShownPowerItems()) {
643             // transfer shutdown and restart to their own list of power actions
644             int powerOptionsIndex = Math.min(tempActions.indexOf(restartAction),
645                     tempActions.indexOf(shutdownAction));
646             tempActions.remove(shutdownAction);
647             tempActions.remove(restartAction);
648             mPowerItems.add(shutdownAction);
649             mPowerItems.add(restartAction);
650 
651             // add the PowerOptionsAction after Emergency, if present
652             tempActions.add(powerOptionsIndex, new PowerOptionsAction());
653         }
654         for (Action action : tempActions) {
655             addActionItem(action);
656         }
657     }
658 
onRefresh()659     protected void onRefresh() {
660         // re-allocate actions between main and overflow lists
661         this.createActionItems();
662     }
663 
initDialogItems()664     protected void initDialogItems() {
665         createActionItems();
666         mAdapter = new MyAdapter();
667         mOverflowAdapter = new MyOverflowAdapter();
668         mPowerAdapter = new MyPowerOptionsAdapter();
669     }
670 
671     /**
672      * Create the global actions dialog.
673      *
674      * @return A new dialog.
675      */
createDialog()676     protected ActionsDialogLite createDialog() {
677         initDialogItems();
678 
679         ActionsDialogLite dialog = new ActionsDialogLite(mContext,
680                 com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
681                 mAdapter, mOverflowAdapter, mSysuiColorExtractor,
682                 mStatusBarService, mNotificationShadeWindowController,
683                 mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
684                 mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
685                 mDialogManager);
686 
687         dialog.setOnDismissListener(this);
688         dialog.setOnShowListener(this);
689 
690         return dialog;
691     }
692 
693     @VisibleForTesting
shouldDisplayLockdown(UserInfo user)694     boolean shouldDisplayLockdown(UserInfo user) {
695         if (user == null) {
696             return false;
697         }
698 
699         int userId = user.id;
700 
701         // Lockdown is meaningless without a place to go.
702         if (!mKeyguardStateController.isMethodSecure()) {
703             return false;
704         }
705 
706         // Only show the lockdown button if the device isn't locked down (for whatever reason).
707         int state = mLockPatternUtils.getStrongAuthForUser(userId);
708         return (state == STRONG_AUTH_NOT_REQUIRED
709                 || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
710     }
711 
712     @VisibleForTesting
shouldDisplayBugReport(UserInfo currentUser)713     boolean shouldDisplayBugReport(UserInfo currentUser) {
714         return mGlobalSettings.getInt(Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0
715                 && (currentUser == null || currentUser.isPrimary());
716     }
717 
718     @Override
onConfigChanged(Configuration newConfig)719     public void onConfigChanged(Configuration newConfig) {
720         if (mDialog != null && mDialog.isShowing()
721                 && (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp)) {
722             mSmallestScreenWidthDp = newConfig.smallestScreenWidthDp;
723             mDialog.refreshDialog();
724         }
725     }
726 
727     /**
728      * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
729      * called when the quick access wallet requests dismissal.
730      */
731     @Override
dismissGlobalActionsMenu()732     public void dismissGlobalActionsMenu() {
733         dismissDialog();
734     }
735 
736     @VisibleForTesting
737     protected final class PowerOptionsAction extends SinglePressAction {
PowerOptionsAction()738         private PowerOptionsAction() {
739             super(com.android.systemui.R.drawable.ic_settings_power,
740                     R.string.global_action_power_options);
741         }
742 
743         @Override
showDuringKeyguard()744         public boolean showDuringKeyguard() {
745             return true;
746         }
747 
748         @Override
showBeforeProvisioning()749         public boolean showBeforeProvisioning() {
750             return true;
751         }
752 
753         @Override
onPress()754         public void onPress() {
755             if (mDialog != null) {
756                 mDialog.showPowerOptionsMenu();
757             }
758         }
759     }
760 
761     @VisibleForTesting
762     final class ShutDownAction extends SinglePressAction implements LongPressAction {
ShutDownAction()763         ShutDownAction() {
764             super(R.drawable.ic_lock_power_off,
765                     R.string.global_action_power_off);
766         }
767 
768         @Override
onLongPress()769         public boolean onLongPress() {
770             mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
771             if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
772                 mWindowManagerFuncs.reboot(true);
773                 return true;
774             }
775             return false;
776         }
777 
778         @Override
showDuringKeyguard()779         public boolean showDuringKeyguard() {
780             return true;
781         }
782 
783         @Override
showBeforeProvisioning()784         public boolean showBeforeProvisioning() {
785             return true;
786         }
787 
788         @Override
onPress()789         public void onPress() {
790             mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
791             // shutdown by making sure radio and power are handled accordingly.
792             mWindowManagerFuncs.shutdown();
793         }
794     }
795 
796     @VisibleForTesting
797     protected abstract class EmergencyAction extends SinglePressAction {
EmergencyAction(int iconResId, int messageResId)798         EmergencyAction(int iconResId, int messageResId) {
799             super(iconResId, messageResId);
800         }
801 
802         @Override
shouldBeSeparated()803         public boolean shouldBeSeparated() {
804             return false;
805         }
806 
807         @Override
create( Context context, View convertView, ViewGroup parent, LayoutInflater inflater)808         public View create(
809                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
810             View v = super.create(context, convertView, parent, inflater);
811             int textColor = getEmergencyTextColor(context);
812             int iconColor = getEmergencyIconColor(context);
813             int backgroundColor = getEmergencyBackgroundColor(context);
814             TextView messageView = v.findViewById(R.id.message);
815             messageView.setTextColor(textColor);
816             messageView.setSelected(true); // necessary for marquee to work
817             ImageView icon = v.findViewById(R.id.icon);
818             icon.getDrawable().setTint(iconColor);
819             icon.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
820             v.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
821             return v;
822         }
823 
824         @Override
showDuringKeyguard()825         public boolean showDuringKeyguard() {
826             return true;
827         }
828 
829         @Override
showBeforeProvisioning()830         public boolean showBeforeProvisioning() {
831             return true;
832         }
833     }
834 
getEmergencyTextColor(Context context)835     protected int getEmergencyTextColor(Context context) {
836         return context.getResources().getColor(
837                 com.android.systemui.R.color.global_actions_lite_text);
838     }
839 
getEmergencyIconColor(Context context)840     protected int getEmergencyIconColor(Context context) {
841         return context.getResources().getColor(
842                 com.android.systemui.R.color.global_actions_lite_emergency_icon);
843     }
844 
getEmergencyBackgroundColor(Context context)845     protected int getEmergencyBackgroundColor(Context context) {
846         return context.getResources().getColor(
847                 com.android.systemui.R.color.global_actions_lite_emergency_background);
848     }
849 
850     private class EmergencyAffordanceAction extends EmergencyAction {
EmergencyAffordanceAction()851         EmergencyAffordanceAction() {
852             super(R.drawable.emergency_icon,
853                     R.string.global_action_emergency);
854         }
855 
856         @Override
onPress()857         public void onPress() {
858             mEmergencyAffordanceManager.performEmergencyCall();
859         }
860     }
861 
862     @VisibleForTesting
863     class EmergencyDialerAction extends EmergencyAction {
EmergencyDialerAction()864         private EmergencyDialerAction() {
865             super(com.android.systemui.R.drawable.ic_emergency_star,
866                     R.string.global_action_emergency);
867         }
868 
869         @Override
onPress()870         public void onPress() {
871             mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
872             mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
873             if (mTelecomManager != null) {
874                 // Close shade so user sees the activity
875                 mStatusBarOptional.ifPresent(StatusBar::collapseShade);
876                 Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
877                         null /* number */);
878                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
879                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
880                         | Intent.FLAG_ACTIVITY_CLEAR_TOP);
881                 intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
882                         EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
883                 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
884             }
885         }
886     }
887 
888     @VisibleForTesting
makeEmergencyDialerActionForTesting()889     EmergencyDialerAction makeEmergencyDialerActionForTesting() {
890         return new EmergencyDialerAction();
891     }
892 
893     @VisibleForTesting
894     final class RestartAction extends SinglePressAction implements LongPressAction {
RestartAction()895         RestartAction() {
896             super(R.drawable.ic_restart, R.string.global_action_restart);
897         }
898 
899         @Override
onLongPress()900         public boolean onLongPress() {
901             mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
902             if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
903                 mWindowManagerFuncs.reboot(true);
904                 return true;
905             }
906             return false;
907         }
908 
909         @Override
showDuringKeyguard()910         public boolean showDuringKeyguard() {
911             return true;
912         }
913 
914         @Override
showBeforeProvisioning()915         public boolean showBeforeProvisioning() {
916             return true;
917         }
918 
919         @Override
onPress()920         public void onPress() {
921             mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS);
922             mWindowManagerFuncs.reboot(false);
923         }
924     }
925 
926     @VisibleForTesting
927     class ScreenshotAction extends SinglePressAction {
ScreenshotAction()928         ScreenshotAction() {
929             super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
930         }
931 
932         @Override
onPress()933         public void onPress() {
934             // Add a little delay before executing, to give the
935             // dialog a chance to go away before it takes a
936             // screenshot.
937             // TODO: instead, omit global action dialog layer
938             mHandler.postDelayed(new Runnable() {
939                 @Override
940                 public void run() {
941                     mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, true, true,
942                             SCREENSHOT_GLOBAL_ACTIONS, mHandler, null);
943                     mMetricsLogger.action(MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
944                     mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_PRESS);
945                 }
946             }, mDialogPressDelay);
947         }
948 
949         @Override
showDuringKeyguard()950         public boolean showDuringKeyguard() {
951             return true;
952         }
953 
954         @Override
showBeforeProvisioning()955         public boolean showBeforeProvisioning() {
956             return false;
957         }
958 
959         @Override
shouldShow()960         public boolean shouldShow() {
961             // Include screenshot in power menu for legacy nav because it is not accessible
962             // through Recents in that mode
963             return is2ButtonNavigationEnabled();
964         }
965 
is2ButtonNavigationEnabled()966         boolean is2ButtonNavigationEnabled() {
967             return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger(
968                     com.android.internal.R.integer.config_navBarInteractionMode);
969         }
970     }
971 
972     @VisibleForTesting
makeScreenshotActionForTesting()973     ScreenshotAction makeScreenshotActionForTesting() {
974         return new ScreenshotAction();
975     }
976 
977     @VisibleForTesting
978     class BugReportAction extends SinglePressAction implements LongPressAction {
979 
BugReportAction()980         BugReportAction() {
981             super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
982         }
983 
984         @Override
onPress()985         public void onPress() {
986             // don't actually trigger the bugreport if we are running stability
987             // tests via monkey
988             if (ActivityManager.isUserAMonkey()) {
989                 return;
990             }
991             // Add a little delay before executing, to give the
992             // dialog a chance to go away before it takes a
993             // screenshot.
994             mHandler.postDelayed(new Runnable() {
995                 @Override
996                 public void run() {
997                     try {
998                         // Take an "interactive" bugreport.
999                         mMetricsLogger.action(
1000                                 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
1001                         mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_PRESS);
1002                         if (!mIActivityManager.launchBugReportHandlerApp()) {
1003                             Log.w(TAG, "Bugreport handler could not be launched");
1004                             mIActivityManager.requestInteractiveBugReport();
1005                         }
1006                         // Close shade so user sees the activity
1007                         mStatusBarOptional.ifPresent(StatusBar::collapseShade);
1008                     } catch (RemoteException e) {
1009                     }
1010                 }
1011             }, mDialogPressDelay);
1012         }
1013 
1014         @Override
onLongPress()1015         public boolean onLongPress() {
1016             // don't actually trigger the bugreport if we are running stability
1017             // tests via monkey
1018             if (ActivityManager.isUserAMonkey()) {
1019                 return false;
1020             }
1021             try {
1022                 // Take a "full" bugreport.
1023                 mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
1024                 mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
1025                 mIActivityManager.requestFullBugReport();
1026                 // Close shade so user sees the activity
1027                 mStatusBarOptional.ifPresent(StatusBar::collapseShade);
1028             } catch (RemoteException e) {
1029             }
1030             return false;
1031         }
1032 
showDuringKeyguard()1033         public boolean showDuringKeyguard() {
1034             return true;
1035         }
1036 
1037         @Override
showBeforeProvisioning()1038         public boolean showBeforeProvisioning() {
1039             return false;
1040         }
1041     }
1042 
1043     @VisibleForTesting
makeBugReportActionForTesting()1044     BugReportAction makeBugReportActionForTesting() {
1045         return new BugReportAction();
1046     }
1047 
1048     private final class LogoutAction extends SinglePressAction {
LogoutAction()1049         private LogoutAction() {
1050             super(R.drawable.ic_logout, R.string.global_action_logout);
1051         }
1052 
1053         @Override
showDuringKeyguard()1054         public boolean showDuringKeyguard() {
1055             return true;
1056         }
1057 
1058         @Override
showBeforeProvisioning()1059         public boolean showBeforeProvisioning() {
1060             return false;
1061         }
1062 
1063         @Override
onPress()1064         public void onPress() {
1065             // Add a little delay before executing, to give the dialog a chance to go away before
1066             // switching user
1067             mHandler.postDelayed(() -> {
1068                 try {
1069                     int currentUserId = getCurrentUser().id;
1070                     mIActivityManager.switchUser(UserHandle.USER_SYSTEM);
1071                     mIActivityManager.stopUser(currentUserId, true /*force*/, null);
1072                 } catch (RemoteException re) {
1073                     Log.e(TAG, "Couldn't logout user " + re);
1074                 }
1075             }, mDialogPressDelay);
1076         }
1077     }
1078 
getSettingsAction()1079     private Action getSettingsAction() {
1080         return new SinglePressAction(R.drawable.ic_settings,
1081                 R.string.global_action_settings) {
1082 
1083             @Override
1084             public void onPress() {
1085                 Intent intent = new Intent(Settings.ACTION_SETTINGS);
1086                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
1087                 mContext.startActivity(intent);
1088             }
1089 
1090             @Override
1091             public boolean showDuringKeyguard() {
1092                 return true;
1093             }
1094 
1095             @Override
1096             public boolean showBeforeProvisioning() {
1097                 return true;
1098             }
1099         };
1100     }
1101 
1102     private Action getAssistAction() {
1103         return new SinglePressAction(R.drawable.ic_action_assist_focused,
1104                 R.string.global_action_assist) {
1105             @Override
1106             public void onPress() {
1107                 Intent intent = new Intent(Intent.ACTION_ASSIST);
1108                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
1109                 mContext.startActivity(intent);
1110             }
1111 
1112             @Override
1113             public boolean showDuringKeyguard() {
1114                 return true;
1115             }
1116 
1117             @Override
1118             public boolean showBeforeProvisioning() {
1119                 return true;
1120             }
1121         };
1122     }
1123 
1124     private Action getVoiceAssistAction() {
1125         return new SinglePressAction(R.drawable.ic_voice_search,
1126                 R.string.global_action_voice_assist) {
1127             @Override
1128             public void onPress() {
1129                 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
1130                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
1131                 mContext.startActivity(intent);
1132             }
1133 
1134             @Override
1135             public boolean showDuringKeyguard() {
1136                 return true;
1137             }
1138 
1139             @Override
1140             public boolean showBeforeProvisioning() {
1141                 return true;
1142             }
1143         };
1144     }
1145 
1146     @VisibleForTesting
1147     class LockDownAction extends SinglePressAction {
1148         LockDownAction() {
1149             super(R.drawable.ic_lock_lockdown, R.string.global_action_lockdown);
1150         }
1151 
1152         @Override
1153         public void onPress() {
1154             mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
1155                     UserHandle.USER_ALL);
1156             mUiEventLogger.log(GlobalActionsEvent.GA_LOCKDOWN_PRESS);
1157             try {
1158                 mIWindowManager.lockNow(null);
1159                 // Lock profiles (if any) on the background thread.
1160                 mBackgroundExecutor.execute(() -> lockProfiles());
1161             } catch (RemoteException e) {
1162                 Log.e(TAG, "Error while trying to lock device.", e);
1163             }
1164         }
1165 
1166         @Override
1167         public boolean showDuringKeyguard() {
1168             return true;
1169         }
1170 
1171         @Override
1172         public boolean showBeforeProvisioning() {
1173             return false;
1174         }
1175     }
1176 
1177     private void lockProfiles() {
1178         final int currentUserId = getCurrentUser().id;
1179         final int[] profileIds = mUserManager.getEnabledProfileIds(currentUserId);
1180         for (final int id : profileIds) {
1181             if (id != currentUserId) {
1182                 mTrustManager.setDeviceLockedForUser(id, true);
1183             }
1184         }
1185     }
1186 
1187     protected UserInfo getCurrentUser() {
1188         try {
1189             return mIActivityManager.getCurrentUser();
1190         } catch (RemoteException re) {
1191             return null;
1192         }
1193     }
1194 
1195     /**
1196      * Non-thread-safe current user provider that caches the result - helpful when a method needs
1197      * to fetch it an indeterminate number of times.
1198      */
1199     private class CurrentUserProvider {
1200         private UserInfo mUserInfo = null;
1201         private boolean mFetched = false;
1202 
1203         @Nullable
1204         UserInfo get() {
1205             if (!mFetched) {
1206                 mFetched = true;
1207                 mUserInfo = getCurrentUser();
1208             }
1209             return mUserInfo;
1210         }
1211     }
1212 
1213     private void addUserActions(List<Action> actions, UserInfo currentUser) {
1214         if (mUserManager.isUserSwitcherEnabled()) {
1215             List<UserInfo> users = mUserManager.getUsers();
1216             for (final UserInfo user : users) {
1217                 if (user.supportsSwitchToByUser()) {
1218                     boolean isCurrentUser = currentUser == null
1219                             ? user.id == 0 : (currentUser.id == user.id);
1220                     Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
1221                             : null;
1222                     SinglePressAction switchToUser = new SinglePressAction(
1223                             R.drawable.ic_menu_cc, icon,
1224                             (user.name != null ? user.name : "Primary")
1225                                     + (isCurrentUser ? " \u2714" : "")) {
1226                         public void onPress() {
1227                             try {
1228                                 mIActivityManager.switchUser(user.id);
1229                             } catch (RemoteException re) {
1230                                 Log.e(TAG, "Couldn't switch user " + re);
1231                             }
1232                         }
1233 
1234                         public boolean showDuringKeyguard() {
1235                             return true;
1236                         }
1237 
1238                         public boolean showBeforeProvisioning() {
1239                             return false;
1240                         }
1241                     };
1242                     addIfShouldShowAction(actions, switchToUser);
1243                 }
1244             }
1245         }
1246     }
1247 
1248     protected void prepareDialog() {
1249         refreshSilentMode();
1250         mAirplaneModeOn.updateState(mAirplaneState);
1251         mAdapter.notifyDataSetChanged();
1252         mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
1253     }
1254 
1255     private void refreshSilentMode() {
1256         if (!mHasVibrator) {
1257             Integer value = mRingerModeTracker.getRingerMode().getValue();
1258             final boolean silentModeOn = value != null && value != AudioManager.RINGER_MODE_NORMAL;
1259             ((ToggleAction) mSilentModeAction).updateState(
1260                     silentModeOn ? ToggleState.On : ToggleState.Off);
1261         }
1262     }
1263 
1264     /**
1265      * {@inheritDoc}
1266      */
1267     @Override
1268     public void onDismiss(DialogInterface dialog) {
1269         if (mDialog == dialog) {
1270             mDialog = null;
1271         }
1272         mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_CLOSE);
1273         mWindowManagerFuncs.onGlobalActionsHidden();
1274         mLifecycle.setCurrentState(Lifecycle.State.CREATED);
1275     }
1276 
1277     /**
1278      * {@inheritDoc}
1279      */
1280     @Override
1281     public void onShow(DialogInterface dialog) {
1282         mMetricsLogger.visible(MetricsEvent.POWER_MENU);
1283         mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_OPEN);
1284     }
1285 
1286     /**
1287      * The adapter used for power menu items shown in the global actions dialog.
1288      */
1289     public class MyAdapter extends MultiListAdapter {
1290         private int countItems(boolean separated) {
1291             int count = 0;
1292             for (int i = 0; i < mItems.size(); i++) {
1293                 final Action action = mItems.get(i);
1294 
1295                 if (action.shouldBeSeparated() == separated) {
1296                     count++;
1297                 }
1298             }
1299             return count;
1300         }
1301 
1302         @Override
1303         public int countSeparatedItems() {
1304             return countItems(true);
1305         }
1306 
1307         @Override
1308         public int countListItems() {
1309             return countItems(false);
1310         }
1311 
1312         @Override
1313         public int getCount() {
1314             return countSeparatedItems() + countListItems();
1315         }
1316 
1317         @Override
1318         public boolean isEnabled(int position) {
1319             return getItem(position).isEnabled();
1320         }
1321 
1322         @Override
1323         public boolean areAllItemsEnabled() {
1324             return false;
1325         }
1326 
1327         @Override
1328         public Action getItem(int position) {
1329             int filteredPos = 0;
1330             for (int i = 0; i < mItems.size(); i++) {
1331                 final Action action = mItems.get(i);
1332                 if (!shouldShowAction(action)) {
1333                     continue;
1334                 }
1335                 if (filteredPos == position) {
1336                     return action;
1337                 }
1338                 filteredPos++;
1339             }
1340 
1341             throw new IllegalArgumentException("position " + position
1342                     + " out of range of showable actions"
1343                     + ", filtered count=" + getCount()
1344                     + ", keyguardshowing=" + mKeyguardShowing
1345                     + ", provisioned=" + mDeviceProvisioned);
1346         }
1347 
1348         /**
1349          * Get the row ID for an item
1350          * @param position The position of the item within the adapter's data set
1351          * @return
1352          */
1353         public long getItemId(int position) {
1354             return position;
1355         }
1356 
1357         @Override
1358         public View getView(int position, View convertView, ViewGroup parent) {
1359             Action action = getItem(position);
1360             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
1361             view.setOnClickListener(v -> onClickItem(position));
1362             if (action instanceof LongPressAction) {
1363                 view.setOnLongClickListener(v -> onLongClickItem(position));
1364             }
1365             return view;
1366         }
1367 
1368         @Override
1369         public boolean onLongClickItem(int position) {
1370             final Action action = mAdapter.getItem(position);
1371             if (action instanceof LongPressAction) {
1372                 if (mDialog != null) {
1373                     // Usually clicking an item shuts down the phone, locks, or starts an activity.
1374                     // We don't want to animate back into the power button when that happens, so we
1375                     // disable the dialog animation before dismissing.
1376                     mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
1377                     mDialog.dismiss();
1378                 } else {
1379                     Log.w(TAG, "Action long-clicked while mDialog is null.");
1380                 }
1381                 return ((LongPressAction) action).onLongPress();
1382             }
1383             return false;
1384         }
1385 
1386         @Override
1387         public void onClickItem(int position) {
1388             Action item = mAdapter.getItem(position);
1389             if (!(item instanceof SilentModeTriStateAction)) {
1390                 if (mDialog != null) {
1391                     // don't dismiss the dialog if we're opening the power options menu
1392                     if (!(item instanceof PowerOptionsAction)) {
1393                         // Usually clicking an item shuts down the phone, locks, or starts an
1394                         // activity. We don't want to animate back into the power button when that
1395                         // happens, so we disable the dialog animation before dismissing.
1396                         mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
1397                         mDialog.dismiss();
1398                     }
1399                 } else {
1400                     Log.w(TAG, "Action clicked while mDialog is null.");
1401                 }
1402                 item.onPress();
1403             }
1404         }
1405 
1406         @Override
1407         public boolean shouldBeSeparated(int position) {
1408             return getItem(position).shouldBeSeparated();
1409         }
1410     }
1411 
1412     /**
1413      * The adapter used for items in the overflow menu.
1414      */
1415     public class MyPowerOptionsAdapter extends BaseAdapter {
1416         @Override
1417         public int getCount() {
1418             return mPowerItems.size();
1419         }
1420 
1421         @Override
1422         public Action getItem(int position) {
1423             return mPowerItems.get(position);
1424         }
1425 
1426         @Override
1427         public long getItemId(int position) {
1428             return position;
1429         }
1430 
1431         @Override
1432         public View getView(int position, View convertView, ViewGroup parent) {
1433             Action action = getItem(position);
1434             if (action == null) {
1435                 Log.w(TAG, "No power options action found at position: " + position);
1436                 return null;
1437             }
1438             int viewLayoutResource = com.android.systemui.R.layout.global_actions_power_item;
1439             View view = convertView != null ? convertView
1440                     : LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
1441             view.setOnClickListener(v -> onClickItem(position));
1442             if (action instanceof LongPressAction) {
1443                 view.setOnLongClickListener(v -> onLongClickItem(position));
1444             }
1445             ImageView icon = view.findViewById(R.id.icon);
1446             TextView messageView = view.findViewById(R.id.message);
1447             messageView.setSelected(true); // necessary for marquee to work
1448 
1449             icon.setImageDrawable(action.getIcon(mContext));
1450             icon.setScaleType(ScaleType.CENTER_CROP);
1451 
1452             if (action.getMessage() != null) {
1453                 messageView.setText(action.getMessage());
1454             } else {
1455                 messageView.setText(action.getMessageResId());
1456             }
1457             return view;
1458         }
1459 
1460         private boolean onLongClickItem(int position) {
1461             final Action action = getItem(position);
1462             if (action instanceof LongPressAction) {
1463                 if (mDialog != null) {
1464                     // Usually clicking an item shuts down the phone, locks, or starts an activity.
1465                     // We don't want to animate back into the power button when that happens, so we
1466                     // disable the dialog animation before dismissing.
1467                     mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
1468                     mDialog.dismiss();
1469                 } else {
1470                     Log.w(TAG, "Action long-clicked while mDialog is null.");
1471                 }
1472                 return ((LongPressAction) action).onLongPress();
1473             }
1474             return false;
1475         }
1476 
1477         private void onClickItem(int position) {
1478             Action item = getItem(position);
1479             if (!(item instanceof SilentModeTriStateAction)) {
1480                 if (mDialog != null) {
1481                     // Usually clicking an item shuts down the phone, locks, or starts an activity.
1482                     // We don't want to animate back into the power button when that happens, so we
1483                     // disable the dialog animation before dismissing.
1484                     mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
1485                     mDialog.dismiss();
1486                 } else {
1487                     Log.w(TAG, "Action clicked while mDialog is null.");
1488                 }
1489                 item.onPress();
1490             }
1491         }
1492     }
1493 
1494     /**
1495      * The adapter used for items in the power options menu, triggered by the PowerOptionsAction.
1496      */
1497     public class MyOverflowAdapter extends BaseAdapter {
1498         @Override
1499         public int getCount() {
1500             return mOverflowItems.size();
1501         }
1502 
1503         @Override
1504         public Action getItem(int position) {
1505             return mOverflowItems.get(position);
1506         }
1507 
1508         @Override
1509         public long getItemId(int position) {
1510             return position;
1511         }
1512 
1513         @Override
1514         public View getView(int position, View convertView, ViewGroup parent) {
1515             Action action = getItem(position);
1516             if (action == null) {
1517                 Log.w(TAG, "No overflow action found at position: " + position);
1518                 return null;
1519             }
1520             int viewLayoutResource = com.android.systemui.R.layout.controls_more_item;
1521             View view = convertView != null ? convertView
1522                     : LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
1523             TextView textView = (TextView) view;
1524             if (action.getMessageResId() != 0) {
1525                 textView.setText(action.getMessageResId());
1526             } else {
1527                 textView.setText(action.getMessage());
1528             }
1529             return textView;
1530         }
1531 
1532         protected boolean onLongClickItem(int position) {
1533             final Action action = getItem(position);
1534             if (action instanceof LongPressAction) {
1535                 if (mDialog != null) {
1536                     // Usually clicking an item shuts down the phone, locks, or starts an activity.
1537                     // We don't want to animate back into the power button when that happens, so we
1538                     // disable the dialog animation before dismissing.
1539                     mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
1540                     mDialog.dismiss();
1541                 } else {
1542                     Log.w(TAG, "Action long-clicked while mDialog is null.");
1543                 }
1544                 return ((LongPressAction) action).onLongPress();
1545             }
1546             return false;
1547         }
1548 
1549         protected void onClickItem(int position) {
1550             Action item = getItem(position);
1551             if (!(item instanceof SilentModeTriStateAction)) {
1552                 if (mDialog != null) {
1553                     // Usually clicking an item shuts down the phone, locks, or starts an activity.
1554                     // We don't want to animate back into the power button when that happens, so we
1555                     // disable the dialog animation before dismissing.
1556                     mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
1557                     mDialog.dismiss();
1558                 } else {
1559                     Log.w(TAG, "Action clicked while mDialog is null.");
1560                 }
1561                 item.onPress();
1562             }
1563         }
1564     }
1565 
1566     // note: the scheme below made more sense when we were planning on having
1567     // 8 different things in the global actions dialog.  seems overkill with
1568     // only 3 items now, but may as well keep this flexible approach so it will
1569     // be easy should someone decide at the last minute to include something
1570     // else, such as 'enable wifi', or 'enable bluetooth'
1571 
1572     /**
1573      * What each item in the global actions dialog must be able to support.
1574      */
1575     public interface Action {
1576         /**
1577          * @return Text that will be announced when dialog is created.  null for none.
1578          */
1579         CharSequence getLabelForAccessibility(Context context);
1580 
1581         /**
1582          * Create the item's view
1583          * @param context
1584          * @param convertView
1585          * @param parent
1586          * @param inflater
1587          * @return
1588          */
1589         View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
1590 
1591         /**
1592          * Handle a regular press
1593          */
1594         void onPress();
1595 
1596         /**
1597          * @return whether this action should appear in the dialog when the keygaurd is showing.
1598          */
1599         boolean showDuringKeyguard();
1600 
1601         /**
1602          * @return whether this action should appear in the dialog before the
1603          * device is provisioned.f
1604          */
1605         boolean showBeforeProvisioning();
1606 
1607         /**
1608          * @return whether this action is enabled
1609          */
1610         boolean isEnabled();
1611 
1612         /**
1613          * @return whether this action should be in a separate section
1614          */
1615         default boolean shouldBeSeparated() {
1616             return false;
1617         }
1618 
1619         /**
1620          * Return the id of the message associated with this action, or 0 if it doesn't have one.
1621          * @return
1622          */
1623         int getMessageResId();
1624 
1625         /**
1626          * Return the icon drawable for this action.
1627          */
1628         Drawable getIcon(Context context);
1629 
1630         /**
1631          * Return the message associated with this action, or null if it doesn't have one.
1632          * @return
1633          */
1634         CharSequence getMessage();
1635 
1636         /**
1637          * @return whether the action should be visible
1638          */
1639         default boolean shouldShow() {
1640             return true;
1641         }
1642     }
1643 
1644     /**
1645      * An action that also supports long press.
1646      */
1647     private interface LongPressAction extends Action {
1648         boolean onLongPress();
1649     }
1650 
1651     /**
1652      * A single press action maintains no state, just responds to a press and takes an action.
1653      */
1654 
1655     private abstract class SinglePressAction implements Action {
1656         private final int mIconResId;
1657         private final Drawable mIcon;
1658         private final int mMessageResId;
1659         private final CharSequence mMessage;
1660 
1661         protected SinglePressAction(int iconResId, int messageResId) {
1662             mIconResId = iconResId;
1663             mMessageResId = messageResId;
1664             mMessage = null;
1665             mIcon = null;
1666         }
1667 
1668         protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
1669             mIconResId = iconResId;
1670             mMessageResId = 0;
1671             mMessage = message;
1672             mIcon = icon;
1673         }
1674 
1675         public boolean isEnabled() {
1676             return true;
1677         }
1678 
1679         public String getStatus() {
1680             return null;
1681         }
1682 
1683         public abstract void onPress();
1684 
1685         public CharSequence getLabelForAccessibility(Context context) {
1686             if (mMessage != null) {
1687                 return mMessage;
1688             } else {
1689                 return context.getString(mMessageResId);
1690             }
1691         }
1692 
1693         public int getMessageResId() {
1694             return mMessageResId;
1695         }
1696 
1697         public CharSequence getMessage() {
1698             return mMessage;
1699         }
1700 
1701         @Override
1702         public Drawable getIcon(Context context) {
1703             if (mIcon != null) {
1704                 return mIcon;
1705             } else {
1706                 return context.getDrawable(mIconResId);
1707             }
1708         }
1709 
1710         public View create(
1711                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
1712             View v = inflater.inflate(getGridItemLayoutResource(), parent, false /* attach */);
1713             // ConstraintLayout flow needs an ID to reference
1714             v.setId(View.generateViewId());
1715 
1716             ImageView icon = v.findViewById(R.id.icon);
1717             TextView messageView = v.findViewById(R.id.message);
1718             messageView.setSelected(true); // necessary for marquee to work
1719 
1720             icon.setImageDrawable(getIcon(context));
1721             icon.setScaleType(ScaleType.CENTER_CROP);
1722 
1723             if (mMessage != null) {
1724                 messageView.setText(mMessage);
1725             } else {
1726                 messageView.setText(mMessageResId);
1727             }
1728 
1729             return v;
1730         }
1731     }
1732 
1733     protected int getGridItemLayoutResource() {
1734         return com.android.systemui.R.layout.global_actions_grid_item_lite;
1735     }
1736 
1737     private enum ToggleState {
1738         Off(false),
1739         TurningOn(true),
1740         TurningOff(true),
1741         On(false);
1742 
1743         private final boolean mInTransition;
1744 
1745         ToggleState(boolean intermediate) {
1746             mInTransition = intermediate;
1747         }
1748 
1749         public boolean inTransition() {
1750             return mInTransition;
1751         }
1752     }
1753 
1754     /**
1755      * A toggle action knows whether it is on or off, and displays an icon and status message
1756      * accordingly.
1757      */
1758     private abstract class ToggleAction implements Action {
1759 
1760         protected ToggleState mState = ToggleState.Off;
1761 
1762         // prefs
1763         protected int mEnabledIconResId;
1764         protected int mDisabledIconResid;
1765         protected int mMessageResId;
1766         protected int mEnabledStatusMessageResId;
1767         protected int mDisabledStatusMessageResId;
1768 
1769         /**
1770          * @param enabledIconResId           The icon for when this action is on.
1771          * @param disabledIconResid          The icon for when this action is off.
1772          * @param message                    The general information message, e.g 'Silent Mode'
1773          * @param enabledStatusMessageResId  The on status message, e.g 'sound disabled'
1774          * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
1775          */
1776         ToggleAction(int enabledIconResId,
1777                 int disabledIconResid,
1778                 int message,
1779                 int enabledStatusMessageResId,
1780                 int disabledStatusMessageResId) {
1781             mEnabledIconResId = enabledIconResId;
1782             mDisabledIconResid = disabledIconResid;
1783             mMessageResId = message;
1784             mEnabledStatusMessageResId = enabledStatusMessageResId;
1785             mDisabledStatusMessageResId = disabledStatusMessageResId;
1786         }
1787 
1788         /**
1789          * Override to make changes to resource IDs just before creating the View.
1790          */
1791         void willCreate() {
1792 
1793         }
1794 
1795         @Override
1796         public CharSequence getLabelForAccessibility(Context context) {
1797             return context.getString(mMessageResId);
1798         }
1799 
1800         private boolean isOn() {
1801             return mState == ToggleState.On || mState == ToggleState.TurningOn;
1802         }
1803 
1804         @Override
1805         public CharSequence getMessage() {
1806             return null;
1807         }
1808         @Override
1809         public int getMessageResId() {
1810             return isOn() ? mEnabledStatusMessageResId : mDisabledStatusMessageResId;
1811         }
1812 
1813         private int getIconResId() {
1814             return isOn() ? mEnabledIconResId : mDisabledIconResid;
1815         }
1816 
1817         @Override
1818         public Drawable getIcon(Context context) {
1819             return context.getDrawable(getIconResId());
1820         }
1821 
1822         public View create(Context context, View convertView, ViewGroup parent,
1823                 LayoutInflater inflater) {
1824             willCreate();
1825 
1826             View v = inflater.inflate(com.android.systemui.R.layout.global_actions_grid_item_v2,
1827                     parent, false /* attach */);
1828             ViewGroup.LayoutParams p = v.getLayoutParams();
1829             p.width = WRAP_CONTENT;
1830             v.setLayoutParams(p);
1831 
1832             ImageView icon = (ImageView) v.findViewById(R.id.icon);
1833             TextView messageView = (TextView) v.findViewById(R.id.message);
1834             final boolean enabled = isEnabled();
1835 
1836             if (messageView != null) {
1837                 messageView.setText(getMessageResId());
1838                 messageView.setEnabled(enabled);
1839                 messageView.setSelected(true); // necessary for marquee to work
1840             }
1841 
1842             if (icon != null) {
1843                 icon.setImageDrawable(context.getDrawable(getIconResId()));
1844                 icon.setEnabled(enabled);
1845             }
1846 
1847             v.setEnabled(enabled);
1848 
1849             return v;
1850         }
1851 
1852         public final void onPress() {
1853             if (mState.inTransition()) {
1854                 Log.w(TAG, "shouldn't be able to toggle when in transition");
1855                 return;
1856             }
1857 
1858             final boolean nowOn = !(mState == ToggleState.On);
1859             onToggle(nowOn);
1860             changeStateFromPress(nowOn);
1861         }
1862 
1863         public boolean isEnabled() {
1864             return !mState.inTransition();
1865         }
1866 
1867         /**
1868          * Implementations may override this if their state can be in on of the intermediate states
1869          * until some notification is received (e.g airplane mode is 'turning off' until we know the
1870          * wireless connections are back online
1871          *
1872          * @param buttonOn Whether the button was turned on or off
1873          */
1874         protected void changeStateFromPress(boolean buttonOn) {
1875             mState = buttonOn ? ToggleState.On : ToggleState.Off;
1876         }
1877 
1878         abstract void onToggle(boolean on);
1879 
1880         public void updateState(ToggleState state) {
1881             mState = state;
1882         }
1883     }
1884 
1885     private class AirplaneModeAction extends ToggleAction {
1886         AirplaneModeAction() {
1887             super(
1888                     R.drawable.ic_lock_airplane_mode,
1889                     R.drawable.ic_lock_airplane_mode_off,
1890                     R.string.global_actions_toggle_airplane_mode,
1891                     R.string.global_actions_airplane_mode_on_status,
1892                     R.string.global_actions_airplane_mode_off_status);
1893         }
1894 
1895         void onToggle(boolean on) {
1896             if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
1897                 mIsWaitingForEcmExit = true;
1898                 // Launch ECM exit dialog
1899                 Intent ecmDialogIntent =
1900                         new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
1901                 ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1902                 mContext.startActivity(ecmDialogIntent);
1903             } else {
1904                 changeAirplaneModeSystemSetting(on);
1905             }
1906         }
1907 
1908         @Override
1909         protected void changeStateFromPress(boolean buttonOn) {
1910             if (!mHasTelephony) return;
1911 
1912             // In ECM mode airplane state cannot be changed
1913             if (!TelephonyProperties.in_ecm_mode().orElse(false)) {
1914                 mState = buttonOn ? ToggleState.TurningOn : ToggleState.TurningOff;
1915                 mAirplaneState = mState;
1916             }
1917         }
1918 
1919         public boolean showDuringKeyguard() {
1920             return true;
1921         }
1922 
1923         public boolean showBeforeProvisioning() {
1924             return false;
1925         }
1926     }
1927 
1928     private class SilentModeToggleAction extends ToggleAction {
1929         SilentModeToggleAction() {
1930             super(R.drawable.ic_audio_vol_mute,
1931                     R.drawable.ic_audio_vol,
1932                     R.string.global_action_toggle_silent_mode,
1933                     R.string.global_action_silent_mode_on_status,
1934                     R.string.global_action_silent_mode_off_status);
1935         }
1936 
1937         void onToggle(boolean on) {
1938             if (on) {
1939                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
1940             } else {
1941                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
1942             }
1943         }
1944 
1945         public boolean showDuringKeyguard() {
1946             return true;
1947         }
1948 
1949         public boolean showBeforeProvisioning() {
1950             return false;
1951         }
1952     }
1953 
1954     private static class SilentModeTriStateAction implements Action, View.OnClickListener {
1955 
1956         private static final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
1957 
1958         private final AudioManager mAudioManager;
1959         private final Handler mHandler;
1960 
1961         SilentModeTriStateAction(AudioManager audioManager, Handler handler) {
1962             mAudioManager = audioManager;
1963             mHandler = handler;
1964         }
1965 
1966         private int ringerModeToIndex(int ringerMode) {
1967             // They just happen to coincide
1968             return ringerMode;
1969         }
1970 
1971         private int indexToRingerMode(int index) {
1972             // They just happen to coincide
1973             return index;
1974         }
1975 
1976         @Override
1977         public CharSequence getLabelForAccessibility(Context context) {
1978             return null;
1979         }
1980 
1981         @Override
1982         public int getMessageResId() {
1983             return 0;
1984         }
1985 
1986         @Override
1987         public CharSequence getMessage() {
1988             return null;
1989         }
1990 
1991         @Override
1992         public Drawable getIcon(Context context) {
1993             return null;
1994         }
1995 
1996 
1997         public View create(Context context, View convertView, ViewGroup parent,
1998                 LayoutInflater inflater) {
1999             View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
2000 
2001             int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
2002             for (int i = 0; i < 3; i++) {
2003                 View itemView = v.findViewById(ITEM_IDS[i]);
2004                 itemView.setSelected(selectedIndex == i);
2005                 // Set up click handler
2006                 itemView.setTag(i);
2007                 itemView.setOnClickListener(this);
2008             }
2009             return v;
2010         }
2011 
2012         public void onPress() {
2013         }
2014 
2015         public boolean showDuringKeyguard() {
2016             return true;
2017         }
2018 
2019         public boolean showBeforeProvisioning() {
2020             return false;
2021         }
2022 
2023         public boolean isEnabled() {
2024             return true;
2025         }
2026 
2027         void willCreate() {
2028         }
2029 
2030         public void onClick(View v) {
2031             if (!(v.getTag() instanceof Integer)) return;
2032 
2033             int index = (Integer) v.getTag();
2034             mAudioManager.setRingerMode(indexToRingerMode(index));
2035             mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
2036         }
2037     }
2038 
2039     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2040         public void onReceive(Context context, Intent intent) {
2041             String action = intent.getAction();
2042             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
2043                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
2044                 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
2045                 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
2046                     mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISMISS, reason));
2047                 }
2048             } else if (TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
2049                 // Airplane mode can be changed after ECM exits if airplane toggle button
2050                 // is pressed during ECM mode
2051                 if (!(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false))
2052                         && mIsWaitingForEcmExit) {
2053                     mIsWaitingForEcmExit = false;
2054                     changeAirplaneModeSystemSetting(true);
2055                 }
2056             }
2057         }
2058     };
2059 
2060     private final TelephonyCallback.ServiceStateListener mPhoneStateListener =
2061             new TelephonyCallback.ServiceStateListener() {
2062         @Override
2063         public void onServiceStateChanged(ServiceState serviceState) {
2064             if (!mHasTelephony) return;
2065             if (mAirplaneModeOn == null) {
2066                 Log.d(TAG, "Service changed before actions created");
2067                 return;
2068             }
2069             final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
2070             mAirplaneState = inAirplaneMode ? ToggleState.On : ToggleState.Off;
2071             mAirplaneModeOn.updateState(mAirplaneState);
2072             mAdapter.notifyDataSetChanged();
2073             mOverflowAdapter.notifyDataSetChanged();
2074             mPowerAdapter.notifyDataSetChanged();
2075         }
2076     };
2077 
2078     private final ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) {
2079         @Override
2080         public void onChange(boolean selfChange) {
2081             onAirplaneModeChanged();
2082         }
2083     };
2084 
2085     private static final int MESSAGE_DISMISS = 0;
2086     private static final int MESSAGE_REFRESH = 1;
2087     private static final int DIALOG_DISMISS_DELAY = 300; // ms
2088     private static final int DIALOG_PRESS_DELAY = 850; // ms
2089 
2090     @VisibleForTesting void setZeroDialogPressDelayForTesting() {
2091         mDialogPressDelay = 0; // ms
2092     }
2093 
2094     private Handler mHandler = new Handler() {
2095         public void handleMessage(Message msg) {
2096             switch (msg.what) {
2097                 case MESSAGE_DISMISS:
2098                     if (mDialog != null) {
2099                         if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
2100                             // Hide instantly.
2101                             mDialog.hide();
2102                             mDialog.dismiss();
2103                         } else {
2104                             mDialog.dismiss();
2105                         }
2106                         mDialog = null;
2107                     }
2108                     break;
2109                 case MESSAGE_REFRESH:
2110                     refreshSilentMode();
2111                     mAdapter.notifyDataSetChanged();
2112                     break;
2113             }
2114         }
2115     };
2116 
2117     private void onAirplaneModeChanged() {
2118         // Let the service state callbacks handle the state.
2119         if (mHasTelephony || mAirplaneModeOn == null) return;
2120 
2121         boolean airplaneModeOn = mGlobalSettings.getInt(
2122                 Settings.Global.AIRPLANE_MODE_ON,
2123                 0) == 1;
2124         mAirplaneState = airplaneModeOn ? ToggleState.On : ToggleState.Off;
2125         mAirplaneModeOn.updateState(mAirplaneState);
2126     }
2127 
2128     /**
2129      * Change the airplane mode system setting
2130      */
2131     private void changeAirplaneModeSystemSetting(boolean on) {
2132         mGlobalSettings.putInt(Settings.Global.AIRPLANE_MODE_ON, on ? 1 : 0);
2133         Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
2134         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
2135         intent.putExtra("state", on);
2136         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
2137         if (!mHasTelephony) {
2138             mAirplaneState = on ? ToggleState.On : ToggleState.Off;
2139         }
2140     }
2141 
2142     @NonNull
2143     @Override
2144     public Lifecycle getLifecycle() {
2145         return mLifecycle;
2146     }
2147 
2148     @VisibleForTesting
2149     static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
2150             ColorExtractor.OnColorsChangedListener {
2151 
2152         protected final Context mContext;
2153         protected MultiListLayout mGlobalActionsLayout;
2154         protected final MyAdapter mAdapter;
2155         protected final MyOverflowAdapter mOverflowAdapter;
2156         protected final MyPowerOptionsAdapter mPowerOptionsAdapter;
2157         protected final IStatusBarService mStatusBarService;
2158         protected final IBinder mToken = new Binder();
2159         protected Drawable mBackgroundDrawable;
2160         protected final SysuiColorExtractor mColorExtractor;
2161         private boolean mKeyguardShowing;
2162         protected float mScrimAlpha;
2163         protected final NotificationShadeWindowController mNotificationShadeWindowController;
2164         protected final SysUiState mSysUiState;
2165         private ListPopupWindow mOverflowPopup;
2166         private Dialog mPowerOptionsDialog;
2167         protected final Runnable mOnRefreshCallback;
2168         private UiEventLogger mUiEventLogger;
2169         private GestureDetector mGestureDetector;
2170         private Optional<StatusBar> mStatusBarOptional;
2171         private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
2172         private LockPatternUtils mLockPatternUtils;
2173 
2174         protected ViewGroup mContainer;
2175 
2176         @VisibleForTesting
2177         protected GestureDetector.SimpleOnGestureListener mGestureListener =
2178                 new GestureDetector.SimpleOnGestureListener() {
2179                     @Override
2180                     public boolean onDown(MotionEvent e) {
2181                         // All gestures begin with this message, so continue listening
2182                         return true;
2183                     }
2184 
2185                     @Override
2186                     public boolean onSingleTapUp(MotionEvent e) {
2187                         // Close without opening shade
2188                         mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
2189                         cancel();
2190                         return false;
2191                     }
2192 
2193                     @Override
2194                     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
2195                             float distanceY) {
2196                         if (distanceY < 0 && distanceY > distanceX
2197                                 && e1.getY() <= mStatusBarOptional.map(
2198                                         StatusBar::getStatusBarHeight).orElse(0)) {
2199                             // Downwards scroll from top
2200                             openShadeAndDismiss();
2201                             return true;
2202                         }
2203                         return false;
2204                     }
2205 
2206                     @Override
2207                     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
2208                             float velocityY) {
2209                         if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
2210                                 && e1.getY() <= mStatusBarOptional.map(
2211                                         StatusBar::getStatusBarHeight).orElse(0)) {
2212                             // Downwards fling from top
2213                             openShadeAndDismiss();
2214                             return true;
2215                         }
2216                         return false;
2217                     }
2218                 };
2219 
2220         ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
2221                 MyOverflowAdapter overflowAdapter,
2222                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
2223                 NotificationShadeWindowController notificationShadeWindowController,
2224                 SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
2225                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
2226                 Optional<StatusBar> statusBarOptional,
2227                 KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
2228                 SystemUIDialogManager systemUiDialogManager) {
2229             // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
2230             // dismiss this dialog when the device is locked.
2231             super(context, themeRes, false /* dismissOnDeviceLock */,
2232                     systemUiDialogManager);
2233             mContext = context;
2234             mAdapter = adapter;
2235             mOverflowAdapter = overflowAdapter;
2236             mPowerOptionsAdapter = powerAdapter;
2237             mColorExtractor = sysuiColorExtractor;
2238             mStatusBarService = statusBarService;
2239             mNotificationShadeWindowController = notificationShadeWindowController;
2240             mSysUiState = sysuiState;
2241             mOnRefreshCallback = onRefreshCallback;
2242             mKeyguardShowing = keyguardShowing;
2243             mUiEventLogger = uiEventLogger;
2244             mStatusBarOptional = statusBarOptional;
2245             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
2246             mLockPatternUtils = lockPatternUtils;
2247             mGestureDetector = new GestureDetector(mContext, mGestureListener);
2248         }
2249 
2250         @Override
2251         protected void onCreate(Bundle savedInstanceState) {
2252             super.onCreate(savedInstanceState);
2253             initializeLayout();
2254         }
2255 
2256         @Override
2257         protected int getWidth() {
2258             return MATCH_PARENT;
2259         }
2260 
2261         @Override
2262         protected int getHeight() {
2263             return MATCH_PARENT;
2264         }
2265 
2266         @Override
2267         public boolean onTouchEvent(MotionEvent event) {
2268             return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
2269         }
2270 
2271         private void openShadeAndDismiss() {
2272             mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
2273             if (mStatusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
2274                 // match existing lockscreen behavior to open QS when swiping from status bar
2275                 mStatusBarOptional.ifPresent(
2276                         statusBar -> statusBar.animateExpandSettingsPanel(null));
2277             } else {
2278                 // otherwise, swiping down should expand notification shade
2279                 mStatusBarOptional.ifPresent(
2280                         statusBar -> statusBar.animateExpandNotificationsPanel());
2281             }
2282             dismiss();
2283         }
2284 
2285         private ListPopupWindow createPowerOverflowPopup() {
2286             GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
2287                     new ContextThemeWrapper(
2288                             mContext,
2289                             com.android.systemui.R.style.Control_ListPopupWindow
2290                     ), false /* isDropDownMode */);
2291             popup.setOnItemClickListener(
2292                     (parent, view, position, id) -> mOverflowAdapter.onClickItem(position));
2293             popup.setOnItemLongClickListener(
2294                     (parent, view, position, id) -> mOverflowAdapter.onLongClickItem(position));
2295             View overflowButton =
2296                     findViewById(com.android.systemui.R.id.global_actions_overflow_button);
2297             popup.setAnchorView(overflowButton);
2298             popup.setAdapter(mOverflowAdapter);
2299             return popup;
2300         }
2301 
2302         public void showPowerOptionsMenu() {
2303             mPowerOptionsDialog = GlobalActionsPowerDialog.create(mContext, mPowerOptionsAdapter);
2304             mPowerOptionsDialog.show();
2305         }
2306 
2307         protected void showPowerOverflowMenu() {
2308             mOverflowPopup = createPowerOverflowPopup();
2309             mOverflowPopup.show();
2310         }
2311 
2312         protected int getLayoutResource() {
2313             return com.android.systemui.R.layout.global_actions_grid_lite;
2314         }
2315 
2316         protected void initializeLayout() {
2317             setContentView(getLayoutResource());
2318             fixNavBarClipping();
2319 
2320             mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
2321             mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
2322                 @Override
2323                 public boolean dispatchPopulateAccessibilityEvent(
2324                         View host, AccessibilityEvent event) {
2325                     // Populate the title here, just as Activity does
2326                     event.getText().add(mContext.getString(R.string.global_actions));
2327                     return true;
2328                 }
2329             });
2330             mGlobalActionsLayout.setRotationListener(this::onRotate);
2331             mGlobalActionsLayout.setAdapter(mAdapter);
2332             mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
2333             mContainer.setOnTouchListener((v, event) -> {
2334                 mGestureDetector.onTouchEvent(event);
2335                 return v.onTouchEvent(event);
2336             });
2337 
2338             View overflowButton = findViewById(
2339                     com.android.systemui.R.id.global_actions_overflow_button);
2340             if (overflowButton != null) {
2341                 if (mOverflowAdapter.getCount() > 0) {
2342                     overflowButton.setOnClickListener((view) -> showPowerOverflowMenu());
2343                     LinearLayout.LayoutParams params =
2344                             (LinearLayout.LayoutParams) mGlobalActionsLayout.getLayoutParams();
2345                     params.setMarginEnd(0);
2346                     mGlobalActionsLayout.setLayoutParams(params);
2347                 } else {
2348                     overflowButton.setVisibility(View.GONE);
2349                     LinearLayout.LayoutParams params =
2350                             (LinearLayout.LayoutParams) mGlobalActionsLayout.getLayoutParams();
2351                     params.setMarginEnd(mContext.getResources().getDimensionPixelSize(
2352                             com.android.systemui.R.dimen.global_actions_side_margin));
2353                     mGlobalActionsLayout.setLayoutParams(params);
2354                 }
2355             }
2356 
2357             if (mBackgroundDrawable == null) {
2358                 mBackgroundDrawable = new ScrimDrawable();
2359                 mScrimAlpha = 1.0f;
2360             }
2361 
2362             // If user entered from the lock screen and smart lock was enabled, disable it
2363             int user = KeyguardUpdateMonitor.getCurrentUser();
2364             boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user);
2365             if (mKeyguardShowing && userHasTrust) {
2366                 mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
2367                 showSmartLockDisabledMessage();
2368             }
2369         }
2370 
2371         protected void fixNavBarClipping() {
2372             ViewGroup content = findViewById(android.R.id.content);
2373             content.setClipChildren(false);
2374             content.setClipToPadding(false);
2375             ViewGroup contentParent = (ViewGroup) content.getParent();
2376             contentParent.setClipChildren(false);
2377             contentParent.setClipToPadding(false);
2378         }
2379 
2380         private void showSmartLockDisabledMessage() {
2381             // Since power menu is the top window, make a Toast-like view that will show up
2382             View message = LayoutInflater.from(mContext)
2383                     .inflate(com.android.systemui.R.layout.global_actions_toast, mContainer, false);
2384 
2385             // Set up animation
2386             AccessibilityManager mAccessibilityManager =
2387                     (AccessibilityManager) getContext().getSystemService(
2388                             Context.ACCESSIBILITY_SERVICE);
2389             final int visibleTime = mAccessibilityManager.getRecommendedTimeoutMillis(
2390                     TOAST_VISIBLE_TIME, AccessibilityManager.FLAG_CONTENT_TEXT);
2391             message.setVisibility(View.VISIBLE);
2392             message.setAlpha(0f);
2393             mContainer.addView(message);
2394 
2395             // Fade in
2396             message.animate()
2397                     .alpha(1f)
2398                     .setDuration(TOAST_FADE_TIME)
2399                     .setListener(new AnimatorListenerAdapter() {
2400                         @Override
2401                         public void onAnimationEnd(Animator animation) {
2402                             // Then fade out
2403                             message.animate()
2404                                     .alpha(0f)
2405                                     .setDuration(TOAST_FADE_TIME)
2406                                     .setStartDelay(visibleTime)
2407                                     .setListener(null);
2408                         }
2409                     });
2410         }
2411 
2412         @Override
2413         protected void onStart() {
2414             super.onStart();
2415             mGlobalActionsLayout.updateList();
2416 
2417             if (mBackgroundDrawable instanceof ScrimDrawable) {
2418                 mColorExtractor.addOnColorsChangedListener(this);
2419                 GradientColors colors = mColorExtractor.getNeutralColors();
2420                 updateColors(colors, false /* animate */);
2421             }
2422         }
2423 
2424         /**
2425          * Updates background and system bars according to current GradientColors.
2426          *
2427          * @param colors  Colors and hints to use.
2428          * @param animate Interpolates gradient if true, just sets otherwise.
2429          */
2430         private void updateColors(GradientColors colors, boolean animate) {
2431             if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
2432                 return;
2433             }
2434             ((ScrimDrawable) mBackgroundDrawable).setColor(Color.BLACK, animate);
2435             View decorView = getWindow().getDecorView();
2436             if (colors.supportsDarkText()) {
2437                 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
2438                         | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
2439             } else {
2440                 decorView.setSystemUiVisibility(0);
2441             }
2442         }
2443 
2444         @Override
2445         protected void onStop() {
2446             super.onStop();
2447             mColorExtractor.removeOnColorsChangedListener(this);
2448         }
2449 
2450         @Override
2451         public void onBackPressed() {
2452             super.onBackPressed();
2453             mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK);
2454         }
2455 
2456         @Override
2457         public void show() {
2458             super.show();
2459             mNotificationShadeWindowController.setRequestTopUi(true, TAG);
2460             mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
2461                     .commitUpdate(mContext.getDisplayId());
2462         }
2463 
2464         @Override
2465         public void dismiss() {
2466             dismissOverflow();
2467             dismissPowerOptions();
2468 
2469             mNotificationShadeWindowController.setRequestTopUi(false, TAG);
2470             mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
2471                     .commitUpdate(mContext.getDisplayId());
2472 
2473             super.dismiss();
2474         }
2475 
2476         protected final void dismissOverflow() {
2477             if (mOverflowPopup != null) {
2478                 mOverflowPopup.dismiss();
2479             }
2480         }
2481 
2482         protected final void dismissPowerOptions() {
2483             if (mPowerOptionsDialog != null) {
2484                 mPowerOptionsDialog.dismiss();
2485             }
2486         }
2487 
2488         protected final void setRotationSuggestionsEnabled(boolean enabled) {
2489             try {
2490                 final int userId = Binder.getCallingUserHandle().getIdentifier();
2491                 final int what = enabled
2492                         ? StatusBarManager.DISABLE2_NONE
2493                         : StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
2494                 mStatusBarService.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
2495             } catch (RemoteException ex) {
2496                 throw ex.rethrowFromSystemServer();
2497             }
2498         }
2499 
2500         @Override
2501         public void onColorsChanged(ColorExtractor extractor, int which) {
2502             if (mKeyguardShowing) {
2503                 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
2504                     updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
2505                             true /* animate */);
2506                 }
2507             } else {
2508                 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
2509                     updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
2510                             true /* animate */);
2511                 }
2512             }
2513         }
2514 
2515         public void setKeyguardShowing(boolean keyguardShowing) {
2516             mKeyguardShowing = keyguardShowing;
2517         }
2518 
2519         public void refreshDialog() {
2520             mOnRefreshCallback.run();
2521 
2522             // Dismiss the dropdown menus.
2523             dismissOverflow();
2524             dismissPowerOptions();
2525 
2526             // Update the list as the max number of items per row has probably changed.
2527             mGlobalActionsLayout.updateList();
2528         }
2529 
2530         public void onRotate(int from, int to) {
2531             refreshDialog();
2532         }
2533     }
2534 }
2535