Home | History | Annotate | Download | only in globalactions
      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.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
     19 
     20 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
     21 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
     22 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
     23 
     24 import android.app.ActivityManager;
     25 import android.app.Dialog;
     26 import android.app.KeyguardManager;
     27 import android.app.PendingIntent;
     28 import android.app.StatusBarManager;
     29 import android.app.WallpaperManager;
     30 import android.app.admin.DevicePolicyManager;
     31 import android.app.trust.TrustManager;
     32 import android.content.BroadcastReceiver;
     33 import android.content.Context;
     34 import android.content.DialogInterface;
     35 import android.content.Intent;
     36 import android.content.IntentFilter;
     37 import android.content.pm.UserInfo;
     38 import android.database.ContentObserver;
     39 import android.graphics.drawable.Drawable;
     40 import android.media.AudioManager;
     41 import android.net.ConnectivityManager;
     42 import android.os.Binder;
     43 import android.os.Handler;
     44 import android.os.IBinder;
     45 import android.os.Message;
     46 import android.os.RemoteException;
     47 import android.os.ServiceManager;
     48 import android.os.SystemProperties;
     49 import android.os.UserHandle;
     50 import android.os.UserManager;
     51 import android.os.Vibrator;
     52 import android.provider.Settings;
     53 import android.service.dreams.DreamService;
     54 import android.service.dreams.IDreamManager;
     55 import android.telephony.PhoneStateListener;
     56 import android.telephony.ServiceState;
     57 import android.telephony.TelephonyManager;
     58 import android.text.TextUtils;
     59 import android.util.ArraySet;
     60 import android.util.FeatureFlagUtils;
     61 import android.util.Log;
     62 import android.view.ContextThemeWrapper;
     63 import android.view.LayoutInflater;
     64 import android.view.View;
     65 import android.view.ViewGroup;
     66 import android.view.Window;
     67 import android.view.WindowManager;
     68 import android.view.WindowManagerGlobal;
     69 import android.view.accessibility.AccessibilityEvent;
     70 import android.widget.FrameLayout;
     71 import android.widget.ImageView;
     72 import android.widget.ImageView.ScaleType;
     73 import android.widget.TextView;
     74 
     75 import com.android.internal.R;
     76 import com.android.internal.colorextraction.ColorExtractor;
     77 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
     78 import com.android.internal.colorextraction.drawable.ScrimDrawable;
     79 import com.android.internal.logging.MetricsLogger;
     80 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     81 import com.android.internal.statusbar.IStatusBarService;
     82 import com.android.internal.telephony.TelephonyIntents;
     83 import com.android.internal.telephony.TelephonyProperties;
     84 import com.android.internal.util.EmergencyAffordanceManager;
     85 import com.android.internal.util.ScreenRecordHelper;
     86 import com.android.internal.util.ScreenshotHelper;
     87 import com.android.internal.view.RotationPolicy;
     88 import com.android.internal.widget.LockPatternUtils;
     89 import com.android.systemui.Dependency;
     90 import com.android.systemui.Interpolators;
     91 import com.android.systemui.MultiListLayout;
     92 import com.android.systemui.MultiListLayout.MultiListAdapter;
     93 import com.android.systemui.colorextraction.SysuiColorExtractor;
     94 import com.android.systemui.plugins.ActivityStarter;
     95 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
     96 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
     97 import com.android.systemui.statusbar.phone.ScrimController;
     98 import com.android.systemui.statusbar.phone.UnlockMethodCache;
     99 import com.android.systemui.statusbar.policy.ConfigurationController;
    100 import com.android.systemui.util.EmergencyDialerConstants;
    101 import com.android.systemui.util.leak.RotationUtils;
    102 import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
    103 
    104 import java.util.ArrayList;
    105 import java.util.List;
    106 
    107 /**
    108  * Helper to show the global actions dialog.  Each item is an {@link Action} that
    109  * may show depending on whether the keyguard is showing, and whether the device
    110  * is provisioned.
    111  */
    112 public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
    113         DialogInterface.OnShowListener, ConfigurationController.ConfigurationListener {
    114 
    115     static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
    116     static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
    117     static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
    118 
    119     private static final String TAG = "GlobalActionsDialog";
    120 
    121     private static final boolean SHOW_SILENT_TOGGLE = true;
    122 
    123     /* Valid settings for global actions keys.
    124      * see config.xml config_globalActionList */
    125     private static final String GLOBAL_ACTION_KEY_POWER = "power";
    126     private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
    127     private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
    128     private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
    129     private static final String GLOBAL_ACTION_KEY_USERS = "users";
    130     private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
    131     private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
    132     private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
    133     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
    134     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
    135     private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
    136     private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
    137     private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
    138 
    139     private final Context mContext;
    140     private final GlobalActionsManager mWindowManagerFuncs;
    141     private final AudioManager mAudioManager;
    142     private final IDreamManager mDreamManager;
    143     private final DevicePolicyManager mDevicePolicyManager;
    144     private final LockPatternUtils mLockPatternUtils;
    145     private final KeyguardManager mKeyguardManager;
    146 
    147     private ArrayList<Action> mItems;
    148     private ActionsDialog mDialog;
    149 
    150     private Action mSilentModeAction;
    151     private ToggleAction mAirplaneModeOn;
    152 
    153     private MyAdapter mAdapter;
    154 
    155     private boolean mKeyguardShowing = false;
    156     private boolean mDeviceProvisioned = false;
    157     private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
    158     private boolean mIsWaitingForEcmExit = false;
    159     private boolean mHasTelephony;
    160     private boolean mHasVibrator;
    161     private boolean mHasLogoutButton;
    162     private boolean mHasLockdownButton;
    163     private final boolean mShowSilentToggle;
    164     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
    165     private final ScreenshotHelper mScreenshotHelper;
    166     private final ScreenRecordHelper mScreenRecordHelper;
    167     private final ActivityStarter mActivityStarter;
    168     private GlobalActionsPanelPlugin mPanelPlugin;
    169 
    170     /**
    171      * @param context everything needs a context :(
    172      */
    173     public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
    174         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
    175         mWindowManagerFuncs = windowManagerFuncs;
    176         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    177         mDreamManager = IDreamManager.Stub.asInterface(
    178                 ServiceManager.getService(DreamService.DREAM_SERVICE));
    179         mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
    180                 Context.DEVICE_POLICY_SERVICE);
    181         mLockPatternUtils = new LockPatternUtils(mContext);
    182         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
    183 
    184         // receive broadcasts
    185         IntentFilter filter = new IntentFilter();
    186         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    187         filter.addAction(Intent.ACTION_SCREEN_OFF);
    188         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
    189         context.registerReceiver(mBroadcastReceiver, filter);
    190 
    191         ConnectivityManager cm = (ConnectivityManager)
    192                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
    193         mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
    194 
    195         // get notified of phone state changes
    196         TelephonyManager telephonyManager =
    197                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    198         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
    199         mContext.getContentResolver().registerContentObserver(
    200                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
    201                 mAirplaneModeObserver);
    202         Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
    203         mHasVibrator = vibrator != null && vibrator.hasVibrator();
    204 
    205         mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
    206                 R.bool.config_useFixedVolume);
    207 
    208         mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
    209         mScreenshotHelper = new ScreenshotHelper(context);
    210         mScreenRecordHelper = new ScreenRecordHelper(context);
    211 
    212         Dependency.get(ConfigurationController.class).addCallback(this);
    213 
    214         mActivityStarter = Dependency.get(ActivityStarter.class);
    215         UnlockMethodCache unlockMethodCache = UnlockMethodCache.getInstance(context);
    216         unlockMethodCache.addListener(
    217                 () -> {
    218                     if (mDialog != null && mDialog.mPanelController != null) {
    219                         boolean locked = !unlockMethodCache.canSkipBouncer();
    220                         mDialog.mPanelController.onDeviceLockStateChanged(locked);
    221                     }
    222                 });
    223     }
    224 
    225     /**
    226      * Show the global actions dialog (creating if necessary)
    227      *
    228      * @param keyguardShowing True if keyguard is showing
    229      */
    230     public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
    231             GlobalActionsPanelPlugin panelPlugin) {
    232         mKeyguardShowing = keyguardShowing;
    233         mDeviceProvisioned = isDeviceProvisioned;
    234         mPanelPlugin = panelPlugin;
    235         if (mDialog != null) {
    236             mDialog.dismiss();
    237             mDialog = null;
    238             // Show delayed, so that the dismiss of the previous dialog completes
    239             mHandler.sendEmptyMessage(MESSAGE_SHOW);
    240         } else {
    241             handleShow();
    242         }
    243     }
    244 
    245     /**
    246      * Dismiss the global actions dialog, if it's currently shown
    247      */
    248     public void dismissDialog() {
    249         mHandler.removeMessages(MESSAGE_DISMISS);
    250         mHandler.sendEmptyMessage(MESSAGE_DISMISS);
    251     }
    252 
    253     private void awakenIfNecessary() {
    254         if (mDreamManager != null) {
    255             try {
    256                 if (mDreamManager.isDreaming()) {
    257                     mDreamManager.awaken();
    258                 }
    259             } catch (RemoteException e) {
    260                 // we tried
    261             }
    262         }
    263     }
    264 
    265     private void handleShow() {
    266         awakenIfNecessary();
    267         mDialog = createDialog();
    268         prepareDialog();
    269 
    270         // If we only have 1 item and it's a simple press action, just do this action.
    271         if (mAdapter.getCount() == 1
    272                 && mAdapter.getItem(0) instanceof SinglePressAction
    273                 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
    274             ((SinglePressAction) mAdapter.getItem(0)).onPress();
    275         } else {
    276             WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
    277             attrs.setTitle("ActionsDialog");
    278             attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
    279             mDialog.getWindow().setAttributes(attrs);
    280             mDialog.show();
    281             mWindowManagerFuncs.onGlobalActionsShown();
    282         }
    283     }
    284 
    285     /**
    286      * Create the global actions dialog.
    287      *
    288      * @return A new dialog.
    289      */
    290     private ActionsDialog createDialog() {
    291         // Simple toggle style if there's no vibrator, otherwise use a tri-state
    292         if (!mHasVibrator) {
    293             mSilentModeAction = new SilentModeToggleAction();
    294         } else {
    295             mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
    296         }
    297         mAirplaneModeOn = new ToggleAction(
    298                 R.drawable.ic_lock_airplane_mode,
    299                 R.drawable.ic_lock_airplane_mode_off,
    300                 R.string.global_actions_toggle_airplane_mode,
    301                 R.string.global_actions_airplane_mode_on_status,
    302                 R.string.global_actions_airplane_mode_off_status) {
    303 
    304             void onToggle(boolean on) {
    305                 if (mHasTelephony && Boolean.parseBoolean(
    306                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
    307                     mIsWaitingForEcmExit = true;
    308                     // Launch ECM exit dialog
    309                     Intent ecmDialogIntent =
    310                             new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
    311                     ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    312                     mContext.startActivity(ecmDialogIntent);
    313                 } else {
    314                     changeAirplaneModeSystemSetting(on);
    315                 }
    316             }
    317 
    318             @Override
    319             protected void changeStateFromPress(boolean buttonOn) {
    320                 if (!mHasTelephony) return;
    321 
    322                 // In ECM mode airplane state cannot be changed
    323                 if (!(Boolean.parseBoolean(
    324                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
    325                     mState = buttonOn ? State.TurningOn : State.TurningOff;
    326                     mAirplaneState = mState;
    327                 }
    328             }
    329 
    330             public boolean showDuringKeyguard() {
    331                 return true;
    332             }
    333 
    334             public boolean showBeforeProvisioning() {
    335                 return false;
    336             }
    337         };
    338         onAirplaneModeChanged();
    339 
    340         mItems = new ArrayList<Action>();
    341         String[] defaultActions = mContext.getResources().getStringArray(
    342                 R.array.config_globalActionsList);
    343 
    344         ArraySet<String> addedKeys = new ArraySet<String>();
    345         mHasLogoutButton = false;
    346         mHasLockdownButton = false;
    347         for (int i = 0; i < defaultActions.length; i++) {
    348             String actionKey = defaultActions[i];
    349             if (addedKeys.contains(actionKey)) {
    350                 // If we already have added this, don't add it again.
    351                 continue;
    352             }
    353             if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
    354                 mItems.add(new PowerAction());
    355             } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
    356                 mItems.add(mAirplaneModeOn);
    357             } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
    358                 if (Settings.Global.getInt(mContext.getContentResolver(),
    359                         Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
    360                     mItems.add(new BugReportAction());
    361                 }
    362             } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
    363                 if (mShowSilentToggle) {
    364                     mItems.add(mSilentModeAction);
    365                 }
    366             } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
    367                 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
    368                     addUsersToMenu(mItems);
    369                 }
    370             } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
    371                 mItems.add(getSettingsAction());
    372             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
    373                 if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
    374                             Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
    375                         && shouldDisplayLockdown()) {
    376                     mItems.add(getLockdownAction());
    377                     mHasLockdownButton = true;
    378                 }
    379             } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
    380                 mItems.add(getVoiceAssistAction());
    381             } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
    382                 mItems.add(getAssistAction());
    383             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
    384                 mItems.add(new RestartAction());
    385             } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
    386                 mItems.add(new ScreenshotAction());
    387             } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
    388                 if (mDevicePolicyManager.isLogoutEnabled()
    389                         && getCurrentUser().id != UserHandle.USER_SYSTEM) {
    390                     mItems.add(new LogoutAction());
    391                     mHasLogoutButton = true;
    392                 }
    393             } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
    394                 if (!mEmergencyAffordanceManager.needsEmergencyAffordance()) {
    395                     mItems.add(new EmergencyDialerAction());
    396                 }
    397             } else {
    398                 Log.e(TAG, "Invalid global action key " + actionKey);
    399             }
    400             // Add here so we don't add more than one.
    401             addedKeys.add(actionKey);
    402         }
    403 
    404         if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
    405             mItems.add(new EmergencyAffordanceAction());
    406         }
    407 
    408         mAdapter = new MyAdapter();
    409 
    410         GlobalActionsPanelPlugin.PanelViewController panelViewController =
    411                 mPanelPlugin != null
    412                         ? mPanelPlugin.onPanelShown(
    413                                 new GlobalActionsPanelPlugin.Callbacks() {
    414                                     @Override
    415                                     public void dismissGlobalActionsMenu() {
    416                                         if (mDialog != null) {
    417                                             mDialog.dismiss();
    418                                         }
    419                                     }
    420 
    421                                     @Override
    422                                     public void startPendingIntentDismissingKeyguard(
    423                                             PendingIntent intent) {
    424                                         mActivityStarter
    425                                                 .startPendingIntentDismissingKeyguard(intent);
    426                                     }
    427                                 },
    428                                 mKeyguardManager.isDeviceLocked())
    429                         : null;
    430 
    431         ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController);
    432         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
    433         dialog.setKeyguardShowing(mKeyguardShowing);
    434 
    435         dialog.setOnDismissListener(this);
    436         dialog.setOnShowListener(this);
    437 
    438         return dialog;
    439     }
    440 
    441     private boolean shouldDisplayLockdown() {
    442         int userId = getCurrentUser().id;
    443         // Lockdown is meaningless without a place to go.
    444         if (!mKeyguardManager.isDeviceSecure(userId)) {
    445             return false;
    446         }
    447 
    448         // Only show the lockdown button if the device isn't locked down (for whatever reason).
    449         int state = mLockPatternUtils.getStrongAuthForUser(userId);
    450         return (state == STRONG_AUTH_NOT_REQUIRED
    451                 || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
    452     }
    453 
    454     @Override
    455     public void onUiModeChanged() {
    456         mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
    457         if (mDialog != null && mDialog.isShowing()) {
    458             mDialog.refreshDialog();
    459         }
    460     }
    461 
    462     public void destroy() {
    463         Dependency.get(ConfigurationController.class).removeCallback(this);
    464     }
    465 
    466     private final class PowerAction extends SinglePressAction implements LongPressAction {
    467         private PowerAction() {
    468             super(R.drawable.ic_lock_power_off,
    469                     R.string.global_action_power_off);
    470         }
    471 
    472         @Override
    473         public boolean onLongPress() {
    474             UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    475             if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
    476                 mWindowManagerFuncs.reboot(true);
    477                 return true;
    478             }
    479             return false;
    480         }
    481 
    482         @Override
    483         public boolean showDuringKeyguard() {
    484             return true;
    485         }
    486 
    487         @Override
    488         public boolean showBeforeProvisioning() {
    489             return true;
    490         }
    491 
    492         @Override
    493         public void onPress() {
    494             // shutdown by making sure radio and power are handled accordingly.
    495             mWindowManagerFuncs.shutdown();
    496         }
    497     }
    498 
    499     private abstract class EmergencyAction extends SinglePressAction {
    500         EmergencyAction(int iconResId, int messageResId) {
    501             super(iconResId, messageResId);
    502         }
    503 
    504         @Override
    505         public boolean shouldBeSeparated() {
    506             return shouldUseSeparatedView();
    507         }
    508 
    509         @Override
    510         public View create(
    511                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
    512             View v = super.create(context, convertView, parent, inflater);
    513             int textColor;
    514             if (shouldBeSeparated()) {
    515                 textColor = v.getResources().getColor(
    516                         com.android.systemui.R.color.global_actions_alert_text);
    517             } else {
    518                 textColor = v.getResources().getColor(
    519                         com.android.systemui.R.color.global_actions_text);
    520             }
    521             TextView messageView = v.findViewById(R.id.message);
    522             messageView.setTextColor(textColor);
    523             messageView.setSelected(true); // necessary for marquee to work
    524             ImageView icon = (ImageView) v.findViewById(R.id.icon);
    525             icon.getDrawable().setTint(textColor);
    526             return v;
    527         }
    528 
    529         @Override
    530         public boolean showDuringKeyguard() {
    531             return true;
    532         }
    533 
    534         @Override
    535         public boolean showBeforeProvisioning() {
    536             return true;
    537         }
    538     }
    539 
    540     private class EmergencyAffordanceAction extends EmergencyAction {
    541         EmergencyAffordanceAction() {
    542             super(R.drawable.emergency_icon,
    543                     R.string.global_action_emergency);
    544         }
    545 
    546         @Override
    547         public void onPress() {
    548             mEmergencyAffordanceManager.performEmergencyCall();
    549         }
    550     }
    551 
    552     private class EmergencyDialerAction extends EmergencyAction {
    553         private EmergencyDialerAction() {
    554             super(com.android.systemui.R.drawable.ic_emergency_star,
    555                     R.string.global_action_emergency);
    556         }
    557 
    558         @Override
    559         public void onPress() {
    560             MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
    561             Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL);
    562             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    563                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    564                     | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    565             intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
    566                     EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
    567             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
    568         }
    569     }
    570 
    571     private final class RestartAction extends SinglePressAction implements LongPressAction {
    572         private RestartAction() {
    573             super(R.drawable.ic_restart, R.string.global_action_restart);
    574         }
    575 
    576         @Override
    577         public boolean onLongPress() {
    578             UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    579             if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
    580                 mWindowManagerFuncs.reboot(true);
    581                 return true;
    582             }
    583             return false;
    584         }
    585 
    586         @Override
    587         public boolean showDuringKeyguard() {
    588             return true;
    589         }
    590 
    591         @Override
    592         public boolean showBeforeProvisioning() {
    593             return true;
    594         }
    595 
    596         @Override
    597         public void onPress() {
    598             mWindowManagerFuncs.reboot(false);
    599         }
    600     }
    601 
    602     private class ScreenshotAction extends SinglePressAction implements LongPressAction {
    603         public ScreenshotAction() {
    604             super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
    605         }
    606 
    607         @Override
    608         public void onPress() {
    609             // Add a little delay before executing, to give the
    610             // dialog a chance to go away before it takes a
    611             // screenshot.
    612             // TODO: instead, omit global action dialog layer
    613             mHandler.postDelayed(new Runnable() {
    614                 @Override
    615                 public void run() {
    616                     mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
    617                     MetricsLogger.action(mContext,
    618                             MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
    619                 }
    620             }, 500);
    621         }
    622 
    623         @Override
    624         public boolean showDuringKeyguard() {
    625             return true;
    626         }
    627 
    628         @Override
    629         public boolean showBeforeProvisioning() {
    630             return false;
    631         }
    632 
    633         @Override
    634         public boolean onLongPress() {
    635             if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
    636                 mScreenRecordHelper.launchRecordPrompt();
    637             } else {
    638                 onPress();
    639             }
    640             return true;
    641         }
    642     }
    643 
    644     private class BugReportAction extends SinglePressAction implements LongPressAction {
    645 
    646         public BugReportAction() {
    647             super(R.drawable.ic_lock_bugreport, R.string.bugreport_title);
    648         }
    649 
    650         @Override
    651         public void onPress() {
    652             // don't actually trigger the bugreport if we are running stability
    653             // tests via monkey
    654             if (ActivityManager.isUserAMonkey()) {
    655                 return;
    656             }
    657             // Add a little delay before executing, to give the
    658             // dialog a chance to go away before it takes a
    659             // screenshot.
    660             mHandler.postDelayed(new Runnable() {
    661                 @Override
    662                 public void run() {
    663                     try {
    664                         // Take an "interactive" bugreport.
    665                         MetricsLogger.action(mContext,
    666                                 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
    667                         ActivityManager.getService().requestBugReport(
    668                                 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
    669                     } catch (RemoteException e) {
    670                     }
    671                 }
    672             }, 500);
    673         }
    674 
    675         @Override
    676         public boolean onLongPress() {
    677             // don't actually trigger the bugreport if we are running stability
    678             // tests via monkey
    679             if (ActivityManager.isUserAMonkey()) {
    680                 return false;
    681             }
    682             try {
    683                 // Take a "full" bugreport.
    684                 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
    685                 ActivityManager.getService().requestBugReport(
    686                         ActivityManager.BUGREPORT_OPTION_FULL);
    687             } catch (RemoteException e) {
    688             }
    689             return false;
    690         }
    691 
    692         public boolean showDuringKeyguard() {
    693             return true;
    694         }
    695 
    696         @Override
    697         public boolean showBeforeProvisioning() {
    698             return false;
    699         }
    700     }
    701 
    702     private final class LogoutAction extends SinglePressAction {
    703         private LogoutAction() {
    704             super(R.drawable.ic_logout, R.string.global_action_logout);
    705         }
    706 
    707         @Override
    708         public boolean showDuringKeyguard() {
    709             return true;
    710         }
    711 
    712         @Override
    713         public boolean showBeforeProvisioning() {
    714             return false;
    715         }
    716 
    717         @Override
    718         public void onPress() {
    719             // Add a little delay before executing, to give the dialog a chance to go away before
    720             // switching user
    721             mHandler.postDelayed(() -> {
    722                 try {
    723                     int currentUserId = getCurrentUser().id;
    724                     ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
    725                     ActivityManager.getService().stopUser(currentUserId, true /*force*/, null);
    726                 } catch (RemoteException re) {
    727                     Log.e(TAG, "Couldn't logout user " + re);
    728                 }
    729             }, 500);
    730         }
    731     }
    732 
    733     private Action getSettingsAction() {
    734         return new SinglePressAction(R.drawable.ic_settings,
    735                 R.string.global_action_settings) {
    736 
    737             @Override
    738             public void onPress() {
    739                 Intent intent = new Intent(Settings.ACTION_SETTINGS);
    740                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    741                 mContext.startActivity(intent);
    742             }
    743 
    744             @Override
    745             public boolean showDuringKeyguard() {
    746                 return true;
    747             }
    748 
    749             @Override
    750             public boolean showBeforeProvisioning() {
    751                 return true;
    752             }
    753         };
    754     }
    755 
    756     private Action getAssistAction() {
    757         return new SinglePressAction(R.drawable.ic_action_assist_focused,
    758                 R.string.global_action_assist) {
    759             @Override
    760             public void onPress() {
    761                 Intent intent = new Intent(Intent.ACTION_ASSIST);
    762                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    763                 mContext.startActivity(intent);
    764             }
    765 
    766             @Override
    767             public boolean showDuringKeyguard() {
    768                 return true;
    769             }
    770 
    771             @Override
    772             public boolean showBeforeProvisioning() {
    773                 return true;
    774             }
    775         };
    776     }
    777 
    778     private Action getVoiceAssistAction() {
    779         return new SinglePressAction(R.drawable.ic_voice_search,
    780                 R.string.global_action_voice_assist) {
    781             @Override
    782             public void onPress() {
    783                 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
    784                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    785                 mContext.startActivity(intent);
    786             }
    787 
    788             @Override
    789             public boolean showDuringKeyguard() {
    790                 return true;
    791             }
    792 
    793             @Override
    794             public boolean showBeforeProvisioning() {
    795                 return true;
    796             }
    797         };
    798     }
    799 
    800     private Action getLockdownAction() {
    801         return new SinglePressAction(R.drawable.ic_lock_lockdown,
    802                 R.string.global_action_lockdown) {
    803 
    804             @Override
    805             public void onPress() {
    806                 new LockPatternUtils(mContext)
    807                         .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
    808                                 UserHandle.USER_ALL);
    809                 try {
    810                     WindowManagerGlobal.getWindowManagerService().lockNow(null);
    811                     // Lock profiles (if any) on the background thread.
    812                     final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
    813                     bgHandler.post(() -> lockProfiles());
    814                 } catch (RemoteException e) {
    815                     Log.e(TAG, "Error while trying to lock device.", e);
    816                 }
    817             }
    818 
    819             @Override
    820             public boolean showDuringKeyguard() {
    821                 return true;
    822             }
    823 
    824             @Override
    825             public boolean showBeforeProvisioning() {
    826                 return false;
    827             }
    828         };
    829     }
    830 
    831     private void lockProfiles() {
    832         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    833         final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
    834         final int currentUserId = getCurrentUser().id;
    835         final int[] profileIds = um.getEnabledProfileIds(currentUserId);
    836         for (final int id : profileIds) {
    837             if (id != currentUserId) {
    838                 tm.setDeviceLockedForUser(id, true);
    839             }
    840         }
    841     }
    842 
    843     private UserInfo getCurrentUser() {
    844         try {
    845             return ActivityManager.getService().getCurrentUser();
    846         } catch (RemoteException re) {
    847             return null;
    848         }
    849     }
    850 
    851     private boolean isCurrentUserOwner() {
    852         UserInfo currentUser = getCurrentUser();
    853         return currentUser == null || currentUser.isPrimary();
    854     }
    855 
    856     private void addUsersToMenu(ArrayList<Action> items) {
    857         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    858         if (um.isUserSwitcherEnabled()) {
    859             List<UserInfo> users = um.getUsers();
    860             UserInfo currentUser = getCurrentUser();
    861             for (final UserInfo user : users) {
    862                 if (user.supportsSwitchToByUser()) {
    863                     boolean isCurrentUser = currentUser == null
    864                             ? user.id == 0 : (currentUser.id == user.id);
    865                     Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
    866                             : null;
    867                     SinglePressAction switchToUser = new SinglePressAction(
    868                             R.drawable.ic_menu_cc, icon,
    869                             (user.name != null ? user.name : "Primary")
    870                                     + (isCurrentUser ? " \u2714" : "")) {
    871                         public void onPress() {
    872                             try {
    873                                 ActivityManager.getService().switchUser(user.id);
    874                             } catch (RemoteException re) {
    875                                 Log.e(TAG, "Couldn't switch user " + re);
    876                             }
    877                         }
    878 
    879                         public boolean showDuringKeyguard() {
    880                             return true;
    881                         }
    882 
    883                         public boolean showBeforeProvisioning() {
    884                             return false;
    885                         }
    886                     };
    887                     items.add(switchToUser);
    888                 }
    889             }
    890         }
    891     }
    892 
    893     private void prepareDialog() {
    894         refreshSilentMode();
    895         mAirplaneModeOn.updateState(mAirplaneState);
    896         mAdapter.notifyDataSetChanged();
    897         if (mShowSilentToggle) {
    898             IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
    899             mContext.registerReceiver(mRingerModeReceiver, filter);
    900         }
    901     }
    902 
    903     private void refreshSilentMode() {
    904         if (!mHasVibrator) {
    905             final boolean silentModeOn =
    906                     mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
    907             ((ToggleAction) mSilentModeAction).updateState(
    908                     silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
    909         }
    910     }
    911 
    912     /** {@inheritDoc} */
    913     public void onDismiss(DialogInterface dialog) {
    914         mWindowManagerFuncs.onGlobalActionsHidden();
    915         if (mShowSilentToggle) {
    916             try {
    917                 mContext.unregisterReceiver(mRingerModeReceiver);
    918             } catch (IllegalArgumentException ie) {
    919                 // ignore this
    920                 Log.w(TAG, ie);
    921             }
    922         }
    923     }
    924 
    925     /** {@inheritDoc} */
    926     public void onShow(DialogInterface dialog) {
    927         MetricsLogger.visible(mContext, MetricsEvent.POWER_MENU);
    928     }
    929 
    930     /**
    931      * The adapter used for the list within the global actions dialog, taking
    932      * into account whether the keyguard is showing via
    933      * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
    934      * the device is provisioned
    935      * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
    936      */
    937     public class MyAdapter extends MultiListAdapter {
    938         private int countItems(boolean separated) {
    939             int count = 0;
    940             for (int i = 0; i < mItems.size(); i++) {
    941                 final Action action = mItems.get(i);
    942 
    943                 if (shouldBeShown(action) && action.shouldBeSeparated() == separated) {
    944                     count++;
    945                 }
    946             }
    947             return count;
    948         }
    949 
    950         private boolean shouldBeShown(Action action) {
    951             if (mKeyguardShowing && !action.showDuringKeyguard()) {
    952                 return false;
    953             }
    954             if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
    955                 return false;
    956             }
    957             return true;
    958         }
    959 
    960         @Override
    961         public int countSeparatedItems() {
    962             return countItems(true);
    963         }
    964 
    965         @Override
    966         public int countListItems() {
    967             return countItems(false);
    968         }
    969 
    970         @Override
    971         public int getCount() {
    972             return countSeparatedItems() + countListItems();
    973         }
    974 
    975         @Override
    976         public boolean isEnabled(int position) {
    977             return getItem(position).isEnabled();
    978         }
    979 
    980         @Override
    981         public boolean areAllItemsEnabled() {
    982             return false;
    983         }
    984 
    985         @Override
    986         public Action getItem(int position) {
    987             int filteredPos = 0;
    988             for (int i = 0; i < mItems.size(); i++) {
    989                 final Action action = mItems.get(i);
    990                 if (!shouldBeShown(action)) {
    991                     continue;
    992                 }
    993                 if (filteredPos == position) {
    994                     return action;
    995                 }
    996                 filteredPos++;
    997             }
    998 
    999             throw new IllegalArgumentException("position " + position
   1000                     + " out of range of showable actions"
   1001                     + ", filtered count=" + getCount()
   1002                     + ", keyguardshowing=" + mKeyguardShowing
   1003                     + ", provisioned=" + mDeviceProvisioned);
   1004         }
   1005 
   1006 
   1007         public long getItemId(int position) {
   1008             return position;
   1009         }
   1010 
   1011         @Override
   1012         public View getView(int position, View convertView, ViewGroup parent) {
   1013             Action action = getItem(position);
   1014             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
   1015             view.setOnClickListener(v -> onClickItem(position));
   1016             view.setOnLongClickListener(v -> onLongClickItem(position));
   1017             return view;
   1018         }
   1019 
   1020         @Override
   1021         public boolean onLongClickItem(int position) {
   1022             final Action action = mAdapter.getItem(position);
   1023             if (action instanceof LongPressAction) {
   1024                 mDialog.dismiss();
   1025                 return ((LongPressAction) action).onLongPress();
   1026             }
   1027             return false;
   1028         }
   1029 
   1030         @Override
   1031         public void onClickItem(int position) {
   1032             Action item = mAdapter.getItem(position);
   1033             if (!(item instanceof SilentModeTriStateAction)) {
   1034                 mDialog.dismiss();
   1035             }
   1036             item.onPress();
   1037         }
   1038 
   1039         @Override
   1040         public boolean shouldBeSeparated(int position) {
   1041             return getItem(position).shouldBeSeparated();
   1042         }
   1043     }
   1044 
   1045     // note: the scheme below made more sense when we were planning on having
   1046     // 8 different things in the global actions dialog.  seems overkill with
   1047     // only 3 items now, but may as well keep this flexible approach so it will
   1048     // be easy should someone decide at the last minute to include something
   1049     // else, such as 'enable wifi', or 'enable bluetooth'
   1050 
   1051     /**
   1052      * What each item in the global actions dialog must be able to support.
   1053      */
   1054     public interface Action {
   1055         /**
   1056          * @return Text that will be announced when dialog is created.  null
   1057          * for none.
   1058          */
   1059         CharSequence getLabelForAccessibility(Context context);
   1060 
   1061         View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
   1062 
   1063         void onPress();
   1064 
   1065         /**
   1066          * @return whether this action should appear in the dialog when the keygaurd
   1067          * is showing.
   1068          */
   1069         boolean showDuringKeyguard();
   1070 
   1071         /**
   1072          * @return whether this action should appear in the dialog before the
   1073          * device is provisioned.onlongpress
   1074          *
   1075          */
   1076         boolean showBeforeProvisioning();
   1077 
   1078         boolean isEnabled();
   1079 
   1080         default boolean shouldBeSeparated() {
   1081             return false;
   1082         }
   1083     }
   1084 
   1085     /**
   1086      * An action that also supports long press.
   1087      */
   1088     private interface LongPressAction extends Action {
   1089         boolean onLongPress();
   1090     }
   1091 
   1092     /**
   1093      * A single press action maintains no state, just responds to a press
   1094      * and takes an action.
   1095      */
   1096     private static abstract class SinglePressAction implements Action {
   1097         private final int mIconResId;
   1098         private final Drawable mIcon;
   1099         private final int mMessageResId;
   1100         private final CharSequence mMessage;
   1101 
   1102         protected SinglePressAction(int iconResId, int messageResId) {
   1103             mIconResId = iconResId;
   1104             mMessageResId = messageResId;
   1105             mMessage = null;
   1106             mIcon = null;
   1107         }
   1108 
   1109         protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) {
   1110             mIconResId = iconResId;
   1111             mMessageResId = 0;
   1112             mMessage = message;
   1113             mIcon = icon;
   1114         }
   1115 
   1116         public boolean isEnabled() {
   1117             return true;
   1118         }
   1119 
   1120         public String getStatus() {
   1121             return null;
   1122         }
   1123 
   1124         abstract public void onPress();
   1125 
   1126         public CharSequence getLabelForAccessibility(Context context) {
   1127             if (mMessage != null) {
   1128                 return mMessage;
   1129             } else {
   1130                 return context.getString(mMessageResId);
   1131             }
   1132         }
   1133 
   1134         protected int getActionLayoutId(Context context) {
   1135             return com.android.systemui.R.layout.global_actions_grid_item;
   1136         }
   1137 
   1138         public View create(
   1139                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
   1140             View v = inflater.inflate(getActionLayoutId(context), parent,
   1141                     false);
   1142 
   1143             ImageView icon = (ImageView) v.findViewById(R.id.icon);
   1144             TextView messageView = (TextView) v.findViewById(R.id.message);
   1145             messageView.setSelected(true); // necessary for marquee to work
   1146 
   1147             TextView statusView = (TextView) v.findViewById(R.id.status);
   1148             final String status = getStatus();
   1149             if (!TextUtils.isEmpty(status)) {
   1150                 statusView.setText(status);
   1151             } else {
   1152                 statusView.setVisibility(View.GONE);
   1153             }
   1154             if (mIcon != null) {
   1155                 icon.setImageDrawable(mIcon);
   1156                 icon.setScaleType(ScaleType.CENTER_CROP);
   1157             } else if (mIconResId != 0) {
   1158                 icon.setImageDrawable(context.getDrawable(mIconResId));
   1159             }
   1160             if (mMessage != null) {
   1161                 messageView.setText(mMessage);
   1162             } else {
   1163                 messageView.setText(mMessageResId);
   1164             }
   1165 
   1166             return v;
   1167         }
   1168     }
   1169 
   1170     /**
   1171      * A toggle action knows whether it is on or off, and displays an icon
   1172      * and status message accordingly.
   1173      */
   1174     private static abstract class ToggleAction implements Action {
   1175 
   1176         enum State {
   1177             Off(false),
   1178             TurningOn(true),
   1179             TurningOff(true),
   1180             On(false);
   1181 
   1182             private final boolean inTransition;
   1183 
   1184             State(boolean intermediate) {
   1185                 inTransition = intermediate;
   1186             }
   1187 
   1188             public boolean inTransition() {
   1189                 return inTransition;
   1190             }
   1191         }
   1192 
   1193         protected State mState = State.Off;
   1194 
   1195         // prefs
   1196         protected int mEnabledIconResId;
   1197         protected int mDisabledIconResid;
   1198         protected int mMessageResId;
   1199         protected int mEnabledStatusMessageResId;
   1200         protected int mDisabledStatusMessageResId;
   1201 
   1202         /**
   1203          * @param enabledIconResId           The icon for when this action is on.
   1204          * @param disabledIconResid          The icon for when this action is off.
   1205          * @param message                    The general information message, e.g 'Silent Mode'
   1206          * @param enabledStatusMessageResId  The on status message, e.g 'sound disabled'
   1207          * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
   1208          */
   1209         public ToggleAction(int enabledIconResId,
   1210                 int disabledIconResid,
   1211                 int message,
   1212                 int enabledStatusMessageResId,
   1213                 int disabledStatusMessageResId) {
   1214             mEnabledIconResId = enabledIconResId;
   1215             mDisabledIconResid = disabledIconResid;
   1216             mMessageResId = message;
   1217             mEnabledStatusMessageResId = enabledStatusMessageResId;
   1218             mDisabledStatusMessageResId = disabledStatusMessageResId;
   1219         }
   1220 
   1221         /**
   1222          * Override to make changes to resource IDs just before creating the
   1223          * View.
   1224          */
   1225         void willCreate() {
   1226 
   1227         }
   1228 
   1229         @Override
   1230         public CharSequence getLabelForAccessibility(Context context) {
   1231             return context.getString(mMessageResId);
   1232         }
   1233 
   1234         public View create(Context context, View convertView, ViewGroup parent,
   1235                 LayoutInflater inflater) {
   1236             willCreate();
   1237 
   1238             View v = inflater.inflate(R
   1239                     .layout.global_actions_item, parent, false);
   1240 
   1241             ImageView icon = (ImageView) v.findViewById(R.id.icon);
   1242             TextView messageView = (TextView) v.findViewById(R.id.message);
   1243             TextView statusView = (TextView) v.findViewById(R.id.status);
   1244             final boolean enabled = isEnabled();
   1245 
   1246             if (messageView != null) {
   1247                 messageView.setText(mMessageResId);
   1248                 messageView.setEnabled(enabled);
   1249                 messageView.setSelected(true); // necessary for marquee to work
   1250             }
   1251 
   1252             boolean on = ((mState == State.On) || (mState == State.TurningOn));
   1253             if (icon != null) {
   1254                 icon.setImageDrawable(context.getDrawable(
   1255                         (on ? mEnabledIconResId : mDisabledIconResid)));
   1256                 icon.setEnabled(enabled);
   1257             }
   1258 
   1259             if (statusView != null) {
   1260                 statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
   1261                 statusView.setVisibility(View.VISIBLE);
   1262                 statusView.setEnabled(enabled);
   1263             }
   1264             v.setEnabled(enabled);
   1265 
   1266             return v;
   1267         }
   1268 
   1269         public final void onPress() {
   1270             if (mState.inTransition()) {
   1271                 Log.w(TAG, "shouldn't be able to toggle when in transition");
   1272                 return;
   1273             }
   1274 
   1275             final boolean nowOn = !(mState == State.On);
   1276             onToggle(nowOn);
   1277             changeStateFromPress(nowOn);
   1278         }
   1279 
   1280         public boolean isEnabled() {
   1281             return !mState.inTransition();
   1282         }
   1283 
   1284         /**
   1285          * Implementations may override this if their state can be in on of the intermediate
   1286          * states until some notification is received (e.g airplane mode is 'turning off' until
   1287          * we know the wireless connections are back online
   1288          *
   1289          * @param buttonOn Whether the button was turned on or off
   1290          */
   1291         protected void changeStateFromPress(boolean buttonOn) {
   1292             mState = buttonOn ? State.On : State.Off;
   1293         }
   1294 
   1295         abstract void onToggle(boolean on);
   1296 
   1297         public void updateState(State state) {
   1298             mState = state;
   1299         }
   1300     }
   1301 
   1302     private class SilentModeToggleAction extends ToggleAction {
   1303         public SilentModeToggleAction() {
   1304             super(R.drawable.ic_audio_vol_mute,
   1305                     R.drawable.ic_audio_vol,
   1306                     R.string.global_action_toggle_silent_mode,
   1307                     R.string.global_action_silent_mode_on_status,
   1308                     R.string.global_action_silent_mode_off_status);
   1309         }
   1310 
   1311         void onToggle(boolean on) {
   1312             if (on) {
   1313                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
   1314             } else {
   1315                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
   1316             }
   1317         }
   1318 
   1319         public boolean showDuringKeyguard() {
   1320             return true;
   1321         }
   1322 
   1323         public boolean showBeforeProvisioning() {
   1324             return false;
   1325         }
   1326     }
   1327 
   1328     private static class SilentModeTriStateAction implements Action, View.OnClickListener {
   1329 
   1330         private final int[] ITEM_IDS = {R.id.option1, R.id.option2, R.id.option3};
   1331 
   1332         private final AudioManager mAudioManager;
   1333         private final Handler mHandler;
   1334 
   1335         SilentModeTriStateAction(AudioManager audioManager, Handler handler) {
   1336             mAudioManager = audioManager;
   1337             mHandler = handler;
   1338         }
   1339 
   1340         private int ringerModeToIndex(int ringerMode) {
   1341             // They just happen to coincide
   1342             return ringerMode;
   1343         }
   1344 
   1345         private int indexToRingerMode(int index) {
   1346             // They just happen to coincide
   1347             return index;
   1348         }
   1349 
   1350         @Override
   1351         public CharSequence getLabelForAccessibility(Context context) {
   1352             return null;
   1353         }
   1354 
   1355         public View create(Context context, View convertView, ViewGroup parent,
   1356                 LayoutInflater inflater) {
   1357             View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
   1358 
   1359             int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
   1360             for (int i = 0; i < 3; i++) {
   1361                 View itemView = v.findViewById(ITEM_IDS[i]);
   1362                 itemView.setSelected(selectedIndex == i);
   1363                 // Set up click handler
   1364                 itemView.setTag(i);
   1365                 itemView.setOnClickListener(this);
   1366             }
   1367             return v;
   1368         }
   1369 
   1370         public void onPress() {
   1371         }
   1372 
   1373         public boolean showDuringKeyguard() {
   1374             return true;
   1375         }
   1376 
   1377         public boolean showBeforeProvisioning() {
   1378             return false;
   1379         }
   1380 
   1381         public boolean isEnabled() {
   1382             return true;
   1383         }
   1384 
   1385         void willCreate() {
   1386         }
   1387 
   1388         public void onClick(View v) {
   1389             if (!(v.getTag() instanceof Integer)) return;
   1390 
   1391             int index = (Integer) v.getTag();
   1392             mAudioManager.setRingerMode(indexToRingerMode(index));
   1393             mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
   1394         }
   1395     }
   1396 
   1397     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
   1398         public void onReceive(Context context, Intent intent) {
   1399             String action = intent.getAction();
   1400             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
   1401                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
   1402                 String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
   1403                 if (!SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
   1404                     mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISMISS, reason));
   1405                 }
   1406             } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
   1407                 // Airplane mode can be changed after ECM exits if airplane toggle button
   1408                 // is pressed during ECM mode
   1409                 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
   1410                         mIsWaitingForEcmExit) {
   1411                     mIsWaitingForEcmExit = false;
   1412                     changeAirplaneModeSystemSetting(true);
   1413                 }
   1414             }
   1415         }
   1416     };
   1417 
   1418     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
   1419         @Override
   1420         public void onServiceStateChanged(ServiceState serviceState) {
   1421             if (!mHasTelephony) return;
   1422             final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
   1423             mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
   1424             mAirplaneModeOn.updateState(mAirplaneState);
   1425             mAdapter.notifyDataSetChanged();
   1426         }
   1427     };
   1428 
   1429     private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
   1430         @Override
   1431         public void onReceive(Context context, Intent intent) {
   1432             if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
   1433                 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
   1434             }
   1435         }
   1436     };
   1437 
   1438     private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
   1439         @Override
   1440         public void onChange(boolean selfChange) {
   1441             onAirplaneModeChanged();
   1442         }
   1443     };
   1444 
   1445     private static final int MESSAGE_DISMISS = 0;
   1446     private static final int MESSAGE_REFRESH = 1;
   1447     private static final int MESSAGE_SHOW = 2;
   1448     private static final int DIALOG_DISMISS_DELAY = 300; // ms
   1449 
   1450     private Handler mHandler = new Handler() {
   1451         public void handleMessage(Message msg) {
   1452             switch (msg.what) {
   1453                 case MESSAGE_DISMISS:
   1454                     if (mDialog != null) {
   1455                         if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
   1456                             mDialog.dismissImmediately();
   1457                         } else {
   1458                             mDialog.dismiss();
   1459                         }
   1460                         mDialog = null;
   1461                     }
   1462                     break;
   1463                 case MESSAGE_REFRESH:
   1464                     refreshSilentMode();
   1465                     mAdapter.notifyDataSetChanged();
   1466                     break;
   1467                 case MESSAGE_SHOW:
   1468                     handleShow();
   1469                     break;
   1470             }
   1471         }
   1472     };
   1473 
   1474     private void onAirplaneModeChanged() {
   1475         // Let the service state callbacks handle the state.
   1476         if (mHasTelephony) return;
   1477 
   1478         boolean airplaneModeOn = Settings.Global.getInt(
   1479                 mContext.getContentResolver(),
   1480                 Settings.Global.AIRPLANE_MODE_ON,
   1481                 0) == 1;
   1482         mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
   1483         mAirplaneModeOn.updateState(mAirplaneState);
   1484     }
   1485 
   1486     /**
   1487      * Change the airplane mode system setting
   1488      */
   1489     private void changeAirplaneModeSystemSetting(boolean on) {
   1490         Settings.Global.putInt(
   1491                 mContext.getContentResolver(),
   1492                 Settings.Global.AIRPLANE_MODE_ON,
   1493                 on ? 1 : 0);
   1494         Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
   1495         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1496         intent.putExtra("state", on);
   1497         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   1498         if (!mHasTelephony) {
   1499             mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
   1500         }
   1501     }
   1502 
   1503     private static final class ActionsDialog extends Dialog implements DialogInterface,
   1504             ColorExtractor.OnColorsChangedListener {
   1505 
   1506         private final Context mContext;
   1507         private final MyAdapter mAdapter;
   1508         private final IStatusBarService mStatusBarService;
   1509         private final IBinder mToken = new Binder();
   1510         private MultiListLayout mGlobalActionsLayout;
   1511         private Drawable mBackgroundDrawable;
   1512         private final SysuiColorExtractor mColorExtractor;
   1513         private final GlobalActionsPanelPlugin.PanelViewController mPanelController;
   1514         private boolean mKeyguardShowing;
   1515         private boolean mShowing;
   1516         private float mScrimAlpha;
   1517         private ResetOrientationData mResetOrientationData;
   1518 
   1519         ActionsDialog(Context context, MyAdapter adapter,
   1520                 GlobalActionsPanelPlugin.PanelViewController plugin) {
   1521             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
   1522             mContext = context;
   1523             mAdapter = adapter;
   1524             mColorExtractor = Dependency.get(SysuiColorExtractor.class);
   1525             mStatusBarService = Dependency.get(IStatusBarService.class);
   1526 
   1527             // Window initialization
   1528             Window window = getWindow();
   1529             window.requestFeature(Window.FEATURE_NO_TITLE);
   1530             // Inflate the decor view, so the attributes below are not overwritten by the theme.
   1531             window.getDecorView();
   1532             window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
   1533                     | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
   1534                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
   1535             window.setLayout(MATCH_PARENT, MATCH_PARENT);
   1536             window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
   1537             window.addFlags(
   1538                     WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
   1539                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
   1540                     | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
   1541                     | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
   1542                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
   1543                     | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
   1544             window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
   1545             setTitle(R.string.global_actions);
   1546 
   1547             mPanelController = plugin;
   1548             initializeLayout();
   1549         }
   1550 
   1551         private boolean shouldUsePanel() {
   1552             return mPanelController != null && mPanelController.getPanelContent() != null;
   1553         }
   1554 
   1555         private void initializePanel() {
   1556             int rotation = RotationUtils.getRotation(mContext);
   1557             boolean rotationLocked = RotationPolicy.isRotationLocked(mContext);
   1558             if (rotation != RotationUtils.ROTATION_NONE) {
   1559                 if (rotationLocked) {
   1560                     if (mResetOrientationData == null) {
   1561                         mResetOrientationData = new ResetOrientationData();
   1562                         mResetOrientationData.locked = true;
   1563                         mResetOrientationData.rotation = rotation;
   1564                     }
   1565 
   1566                     // Unlock rotation, so user can choose to rotate to portrait to see the panel.
   1567                     // This call is posted so that the rotation does not change until post-layout,
   1568                     // otherwise onConfigurationChanged() may not get invoked.
   1569                     mGlobalActionsLayout.post(() ->
   1570                             RotationPolicy.setRotationLockAtAngle(
   1571                                     mContext, false, RotationUtils.ROTATION_NONE));
   1572                 }
   1573             } else {
   1574                 if (!rotationLocked) {
   1575                     if (mResetOrientationData == null) {
   1576                         mResetOrientationData = new ResetOrientationData();
   1577                         mResetOrientationData.locked = false;
   1578                     }
   1579 
   1580                     // Lock to portrait, so the user doesn't accidentally hide the panel.
   1581                     // This call is posted so that the rotation does not change until post-layout,
   1582                     // otherwise onConfigurationChanged() may not get invoked.
   1583                     mGlobalActionsLayout.post(() ->
   1584                             RotationPolicy.setRotationLockAtAngle(
   1585                                     mContext, true, RotationUtils.ROTATION_NONE));
   1586                 }
   1587 
   1588                 // Disable rotation suggestions, if enabled
   1589                 setRotationSuggestionsEnabled(false);
   1590 
   1591                 FrameLayout panelContainer =
   1592                         findViewById(com.android.systemui.R.id.global_actions_panel_container);
   1593                 FrameLayout.LayoutParams panelParams =
   1594                         new FrameLayout.LayoutParams(
   1595                                 FrameLayout.LayoutParams.MATCH_PARENT,
   1596                                 FrameLayout.LayoutParams.MATCH_PARENT);
   1597                 panelContainer.addView(mPanelController.getPanelContent(), panelParams);
   1598                 mBackgroundDrawable = mPanelController.getBackgroundDrawable();
   1599                 mScrimAlpha = 1f;
   1600             }
   1601         }
   1602 
   1603         private void initializeLayout() {
   1604             setContentView(getGlobalActionsLayoutId(mContext));
   1605             fixNavBarClipping();
   1606             mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
   1607             mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
   1608             ((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss());
   1609             mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
   1610                 @Override
   1611                 public boolean dispatchPopulateAccessibilityEvent(
   1612                         View host, AccessibilityEvent event) {
   1613                     // Populate the title here, just as Activity does
   1614                     event.getText().add(mContext.getString(R.string.global_actions));
   1615                     return true;
   1616                 }
   1617             });
   1618             mGlobalActionsLayout.setRotationListener(this::onRotate);
   1619             mGlobalActionsLayout.setAdapter(mAdapter);
   1620 
   1621             if (shouldUsePanel()) {
   1622                 initializePanel();
   1623             }
   1624             if (mBackgroundDrawable == null) {
   1625                 mBackgroundDrawable = new ScrimDrawable();
   1626                 mScrimAlpha = ScrimController.GRADIENT_SCRIM_ALPHA;
   1627             }
   1628             getWindow().setBackgroundDrawable(mBackgroundDrawable);
   1629         }
   1630 
   1631         private void fixNavBarClipping() {
   1632             ViewGroup content = findViewById(android.R.id.content);
   1633             content.setClipChildren(false);
   1634             content.setClipToPadding(false);
   1635             ViewGroup contentParent = (ViewGroup) content.getParent();
   1636             contentParent.setClipChildren(false);
   1637             contentParent.setClipToPadding(false);
   1638         }
   1639 
   1640         private int getGlobalActionsLayoutId(Context context) {
   1641             int rotation = RotationUtils.getRotation(context);
   1642             boolean useGridLayout = isForceGridEnabled(context)
   1643                     || (shouldUsePanel() && rotation == RotationUtils.ROTATION_NONE);
   1644             if (rotation == RotationUtils.ROTATION_SEASCAPE) {
   1645                 if (useGridLayout) {
   1646                     return com.android.systemui.R.layout.global_actions_grid_seascape;
   1647                 } else {
   1648                     return com.android.systemui.R.layout.global_actions_column_seascape;
   1649                 }
   1650             } else {
   1651                 if (useGridLayout) {
   1652                     return com.android.systemui.R.layout.global_actions_grid;
   1653                 } else {
   1654                     return com.android.systemui.R.layout.global_actions_column;
   1655                 }
   1656             }
   1657         }
   1658 
   1659         @Override
   1660         protected void onStart() {
   1661             super.setCanceledOnTouchOutside(true);
   1662             super.onStart();
   1663             mGlobalActionsLayout.updateList();
   1664 
   1665             if (mBackgroundDrawable instanceof ScrimDrawable) {
   1666                 mColorExtractor.addOnColorsChangedListener(this);
   1667                 GradientColors colors = mColorExtractor.getNeutralColors();
   1668                 updateColors(colors, false /* animate */);
   1669             }
   1670         }
   1671 
   1672         /**
   1673          * Updates background and system bars according to current GradientColors.
   1674          * @param colors Colors and hints to use.
   1675          * @param animate Interpolates gradient if true, just sets otherwise.
   1676          */
   1677         private void updateColors(GradientColors colors, boolean animate) {
   1678             if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
   1679                 return;
   1680             }
   1681             ((ScrimDrawable) mBackgroundDrawable).setColor(colors.getMainColor(), animate);
   1682             View decorView = getWindow().getDecorView();
   1683             if (colors.supportsDarkText()) {
   1684                 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
   1685                         View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
   1686             } else {
   1687                 decorView.setSystemUiVisibility(0);
   1688             }
   1689         }
   1690 
   1691         @Override
   1692         protected void onStop() {
   1693             super.onStop();
   1694             mColorExtractor.removeOnColorsChangedListener(this);
   1695         }
   1696 
   1697         @Override
   1698         public void show() {
   1699             super.show();
   1700             mShowing = true;
   1701             mBackgroundDrawable.setAlpha(0);
   1702             mGlobalActionsLayout.setTranslationX(mGlobalActionsLayout.getAnimationOffsetX());
   1703             mGlobalActionsLayout.setTranslationY(mGlobalActionsLayout.getAnimationOffsetY());
   1704             mGlobalActionsLayout.setAlpha(0);
   1705             mGlobalActionsLayout.animate()
   1706                     .alpha(1)
   1707                     .translationX(0)
   1708                     .translationY(0)
   1709                     .setDuration(300)
   1710                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
   1711                     .setUpdateListener(animation -> {
   1712                         int alpha = (int) ((Float) animation.getAnimatedValue()
   1713                                 * mScrimAlpha * 255);
   1714                         mBackgroundDrawable.setAlpha(alpha);
   1715                     })
   1716                     .start();
   1717         }
   1718 
   1719         @Override
   1720         public void dismiss() {
   1721             if (!mShowing) {
   1722                 return;
   1723             }
   1724             mShowing = false;
   1725             mGlobalActionsLayout.setTranslationX(0);
   1726             mGlobalActionsLayout.setTranslationY(0);
   1727             mGlobalActionsLayout.setAlpha(1);
   1728             mGlobalActionsLayout.animate()
   1729                     .alpha(0)
   1730                     .translationX(mGlobalActionsLayout.getAnimationOffsetX())
   1731                     .translationY(mGlobalActionsLayout.getAnimationOffsetY())
   1732                     .setDuration(300)
   1733                     .withEndAction(super::dismiss)
   1734                     .setInterpolator(new LogAccelerateInterpolator())
   1735                     .setUpdateListener(animation -> {
   1736                         int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
   1737                                 * mScrimAlpha * 255);
   1738                         mBackgroundDrawable.setAlpha(alpha);
   1739                     })
   1740                     .start();
   1741             dismissPanel();
   1742             resetOrientation();
   1743         }
   1744 
   1745         void dismissImmediately() {
   1746             super.dismiss();
   1747             mShowing = false;
   1748             dismissPanel();
   1749             resetOrientation();
   1750         }
   1751 
   1752         private void dismissPanel() {
   1753             if (mPanelController != null) {
   1754                 mPanelController.onDismissed();
   1755             }
   1756         }
   1757 
   1758         private void setRotationSuggestionsEnabled(boolean enabled) {
   1759             try {
   1760                 final int userId = Binder.getCallingUserHandle().getIdentifier();
   1761                 final int what = enabled
   1762                         ? StatusBarManager.DISABLE2_NONE
   1763                         : StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
   1764                 mStatusBarService.disable2ForUser(what, mToken, mContext.getPackageName(), userId);
   1765             } catch (RemoteException ex) {
   1766                 throw ex.rethrowFromSystemServer();
   1767             }
   1768         }
   1769 
   1770         private void resetOrientation() {
   1771             if (mResetOrientationData != null) {
   1772                 RotationPolicy.setRotationLockAtAngle(mContext, mResetOrientationData.locked,
   1773                         mResetOrientationData.rotation);
   1774             }
   1775             setRotationSuggestionsEnabled(true);
   1776         }
   1777 
   1778         @Override
   1779         public void onColorsChanged(ColorExtractor extractor, int which) {
   1780             if (mKeyguardShowing) {
   1781                 if ((WallpaperManager.FLAG_LOCK & which) != 0) {
   1782                     updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK),
   1783                             true /* animate */);
   1784                 }
   1785             } else {
   1786                 if ((WallpaperManager.FLAG_SYSTEM & which) != 0) {
   1787                     updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM),
   1788                             true /* animate */);
   1789                 }
   1790             }
   1791         }
   1792 
   1793         public void setKeyguardShowing(boolean keyguardShowing) {
   1794             mKeyguardShowing = keyguardShowing;
   1795         }
   1796 
   1797         public void refreshDialog() {
   1798             initializeLayout();
   1799             mGlobalActionsLayout.updateList();
   1800         }
   1801 
   1802         public void onRotate(int from, int to) {
   1803             if (mShowing) {
   1804                 refreshDialog();
   1805             }
   1806         }
   1807 
   1808         private static class ResetOrientationData {
   1809             public boolean locked;
   1810             public int rotation;
   1811         }
   1812     }
   1813 
   1814     /**
   1815      * Determines whether or not debug mode has been activated for the Global Actions Panel.
   1816      */
   1817     private static boolean isPanelDebugModeEnabled(Context context) {
   1818         return Settings.Secure.getInt(context.getContentResolver(),
   1819                 Settings.Secure.GLOBAL_ACTIONS_PANEL_DEBUG_ENABLED, 0) == 1;
   1820     }
   1821 
   1822     /**
   1823      * Determines whether or not the Global Actions menu should be forced to
   1824      * use the newer grid-style layout.
   1825      */
   1826     private static boolean isForceGridEnabled(Context context) {
   1827         return isPanelDebugModeEnabled(context);
   1828     }
   1829 
   1830     /**
   1831      * Determines whether the Global Actions menu should use a separated view for emergency actions.
   1832      */
   1833     private static boolean shouldUseSeparatedView() {
   1834         return true;
   1835     }
   1836 }
   1837