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