Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.policy;
     18 
     19 import com.android.internal.app.AlertController;
     20 import com.android.internal.globalactions.Action;
     21 import com.android.internal.globalactions.ActionsAdapter;
     22 import com.android.internal.globalactions.ActionsDialog;
     23 import com.android.internal.globalactions.LongPressAction;
     24 import com.android.internal.globalactions.SinglePressAction;
     25 import com.android.internal.globalactions.ToggleAction;
     26 import com.android.internal.logging.MetricsLogger;
     27 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     28 import com.android.internal.R;
     29 import com.android.internal.telephony.TelephonyIntents;
     30 import com.android.internal.telephony.TelephonyProperties;
     31 import com.android.internal.util.EmergencyAffordanceManager;
     32 import com.android.internal.widget.LockPatternUtils;
     33 import com.android.server.policy.PowerAction;
     34 import com.android.server.policy.RestartAction;
     35 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
     36 
     37 import android.app.ActivityManager;
     38 import android.content.BroadcastReceiver;
     39 import android.content.Context;
     40 import android.content.DialogInterface;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.pm.UserInfo;
     44 import android.database.ContentObserver;
     45 import android.graphics.drawable.Drawable;
     46 import android.media.AudioManager;
     47 import android.net.ConnectivityManager;
     48 import android.os.Build;
     49 import android.os.Handler;
     50 import android.os.Message;
     51 import android.os.RemoteException;
     52 import android.os.ServiceManager;
     53 import android.os.SystemProperties;
     54 import android.os.UserHandle;
     55 import android.os.UserManager;
     56 import android.os.Vibrator;
     57 import android.provider.Settings;
     58 import android.service.dreams.DreamService;
     59 import android.service.dreams.IDreamManager;
     60 import android.telephony.PhoneStateListener;
     61 import android.telephony.ServiceState;
     62 import android.telephony.TelephonyManager;
     63 import android.util.ArraySet;
     64 import android.util.Log;
     65 import android.view.LayoutInflater;
     66 import android.view.View;
     67 import android.view.ViewGroup;
     68 import android.view.WindowManager;
     69 import android.view.WindowManagerGlobal;
     70 import android.widget.AdapterView;
     71 
     72 import java.util.ArrayList;
     73 import java.util.List;
     74 
     75 /**
     76  * Helper to show the global actions dialog.  Each item is an {@link Action} that
     77  * may show depending on whether the keyguard is showing, and whether the device
     78  * is provisioned.
     79  */
     80 class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener  {
     81 
     82     private static final String TAG = "LegacyGlobalActions";
     83 
     84     private static final boolean SHOW_SILENT_TOGGLE = true;
     85 
     86     /* Valid settings for global actions keys.
     87      * see config.xml config_globalActionList */
     88     private static final String GLOBAL_ACTION_KEY_POWER = "power";
     89     private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
     90     private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
     91     private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
     92     private static final String GLOBAL_ACTION_KEY_USERS = "users";
     93     private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
     94     private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
     95     private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
     96     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
     97     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
     98 
     99     private final Context mContext;
    100     private final WindowManagerFuncs mWindowManagerFuncs;
    101     private final AudioManager mAudioManager;
    102     private final IDreamManager mDreamManager;
    103     private final Runnable mOnDismiss;
    104 
    105     private ArrayList<Action> mItems;
    106     private ActionsDialog mDialog;
    107 
    108     private Action mSilentModeAction;
    109     private ToggleAction mAirplaneModeOn;
    110 
    111     private ActionsAdapter mAdapter;
    112 
    113     private boolean mKeyguardShowing = false;
    114     private boolean mDeviceProvisioned = false;
    115     private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
    116     private boolean mIsWaitingForEcmExit = false;
    117     private boolean mHasTelephony;
    118     private boolean mHasVibrator;
    119     private final boolean mShowSilentToggle;
    120     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
    121 
    122     /**
    123      * @param context everything needs a context :(
    124      */
    125     public LegacyGlobalActions(Context context, WindowManagerFuncs windowManagerFuncs,
    126             Runnable onDismiss) {
    127         mContext = context;
    128         mWindowManagerFuncs = windowManagerFuncs;
    129         mOnDismiss = onDismiss;
    130         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    131         mDreamManager = IDreamManager.Stub.asInterface(
    132                 ServiceManager.getService(DreamService.DREAM_SERVICE));
    133 
    134         // receive broadcasts
    135         IntentFilter filter = new IntentFilter();
    136         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    137         filter.addAction(Intent.ACTION_SCREEN_OFF);
    138         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
    139         context.registerReceiver(mBroadcastReceiver, filter);
    140 
    141         ConnectivityManager cm = (ConnectivityManager)
    142                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
    143         mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
    144 
    145         // get notified of phone state changes
    146         TelephonyManager telephonyManager =
    147                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    148         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
    149         mContext.getContentResolver().registerContentObserver(
    150                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
    151                 mAirplaneModeObserver);
    152         Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
    153         mHasVibrator = vibrator != null && vibrator.hasVibrator();
    154 
    155         mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
    156                 com.android.internal.R.bool.config_useFixedVolume);
    157 
    158         mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
    159     }
    160 
    161     /**
    162      * Show the global actions dialog (creating if necessary)
    163      * @param keyguardShowing True if keyguard is showing
    164      */
    165     public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
    166         mKeyguardShowing = keyguardShowing;
    167         mDeviceProvisioned = isDeviceProvisioned;
    168         if (mDialog != null) {
    169             mDialog.dismiss();
    170             mDialog = null;
    171             // Show delayed, so that the dismiss of the previous dialog completes
    172             mHandler.sendEmptyMessage(MESSAGE_SHOW);
    173         } else {
    174             handleShow();
    175         }
    176     }
    177 
    178     private void awakenIfNecessary() {
    179         if (mDreamManager != null) {
    180             try {
    181                 if (mDreamManager.isDreaming()) {
    182                     mDreamManager.awaken();
    183                 }
    184             } catch (RemoteException e) {
    185                 // we tried
    186             }
    187         }
    188     }
    189 
    190     private void handleShow() {
    191         awakenIfNecessary();
    192         mDialog = createDialog();
    193         prepareDialog();
    194 
    195         // If we only have 1 item and it's a simple press action, just do this action.
    196         if (mAdapter.getCount() == 1
    197                 && mAdapter.getItem(0) instanceof SinglePressAction
    198                 && !(mAdapter.getItem(0) instanceof LongPressAction)) {
    199             ((SinglePressAction) mAdapter.getItem(0)).onPress();
    200         } else {
    201             if (mDialog != null) {
    202                 WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
    203                 attrs.setTitle("LegacyGlobalActions");
    204                 mDialog.getWindow().setAttributes(attrs);
    205                 mDialog.show();
    206                 mDialog.getWindow().getDecorView().setSystemUiVisibility(
    207                         View.STATUS_BAR_DISABLE_EXPAND);
    208             }
    209         }
    210     }
    211 
    212     /**
    213      * Create the global actions dialog.
    214      * @return A new dialog.
    215      */
    216     private ActionsDialog createDialog() {
    217         // Simple toggle style if there's no vibrator, otherwise use a tri-state
    218         if (!mHasVibrator) {
    219             mSilentModeAction = new SilentModeToggleAction();
    220         } else {
    221             mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
    222         }
    223         mAirplaneModeOn = new ToggleAction(
    224                 R.drawable.ic_lock_airplane_mode,
    225                 R.drawable.ic_lock_airplane_mode_off,
    226                 R.string.global_actions_toggle_airplane_mode,
    227                 R.string.global_actions_airplane_mode_on_status,
    228                 R.string.global_actions_airplane_mode_off_status) {
    229 
    230             @Override
    231             public void onToggle(boolean on) {
    232                 if (mHasTelephony && Boolean.parseBoolean(
    233                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
    234                     mIsWaitingForEcmExit = true;
    235                     // Launch ECM exit dialog
    236                     Intent ecmDialogIntent =
    237                             new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
    238                     ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    239                     mContext.startActivity(ecmDialogIntent);
    240                 } else {
    241                     changeAirplaneModeSystemSetting(on);
    242                 }
    243             }
    244 
    245             @Override
    246             protected void changeStateFromPress(boolean buttonOn) {
    247                 if (!mHasTelephony) return;
    248 
    249                 // In ECM mode airplane state cannot be changed
    250                 if (!(Boolean.parseBoolean(
    251                         SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
    252                     mState = buttonOn ? State.TurningOn : State.TurningOff;
    253                     mAirplaneState = mState;
    254                 }
    255             }
    256 
    257             @Override
    258             public boolean showDuringKeyguard() {
    259                 return true;
    260             }
    261 
    262             @Override
    263             public boolean showBeforeProvisioning() {
    264                 return false;
    265             }
    266         };
    267         onAirplaneModeChanged();
    268 
    269         mItems = new ArrayList<Action>();
    270         String[] defaultActions = mContext.getResources().getStringArray(
    271                 com.android.internal.R.array.config_globalActionsList);
    272 
    273         ArraySet<String> addedKeys = new ArraySet<String>();
    274         for (int i = 0; i < defaultActions.length; i++) {
    275             String actionKey = defaultActions[i];
    276             if (addedKeys.contains(actionKey)) {
    277                 // If we already have added this, don't add it again.
    278                 continue;
    279             }
    280             if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
    281                 mItems.add(new PowerAction(mContext, mWindowManagerFuncs));
    282             } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
    283                 mItems.add(mAirplaneModeOn);
    284             } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
    285                 if (Settings.Global.getInt(mContext.getContentResolver(),
    286                         Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
    287                     mItems.add(new BugReportAction());
    288                 }
    289             } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
    290                 if (mShowSilentToggle) {
    291                     mItems.add(mSilentModeAction);
    292                 }
    293             } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
    294                 if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
    295                     addUsersToMenu(mItems);
    296                 }
    297             } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
    298                 mItems.add(getSettingsAction());
    299             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
    300                 mItems.add(getLockdownAction());
    301             } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
    302                 mItems.add(getVoiceAssistAction());
    303             } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
    304                 mItems.add(getAssistAction());
    305             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
    306                 mItems.add(new RestartAction(mContext, mWindowManagerFuncs));
    307             } else {
    308                 Log.e(TAG, "Invalid global action key " + actionKey);
    309             }
    310             // Add here so we don't add more than one.
    311             addedKeys.add(actionKey);
    312         }
    313 
    314         if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
    315             mItems.add(getEmergencyAction());
    316         }
    317 
    318         mAdapter = new ActionsAdapter(mContext, mItems,
    319                 () -> mDeviceProvisioned, () -> mKeyguardShowing);
    320 
    321         AlertController.AlertParams params = new AlertController.AlertParams(mContext);
    322         params.mAdapter = mAdapter;
    323         params.mOnClickListener = this;
    324         params.mForceInverseBackground = true;
    325 
    326         ActionsDialog dialog = new ActionsDialog(mContext, params);
    327         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
    328 
    329         dialog.getListView().setItemsCanFocus(true);
    330         dialog.getListView().setLongClickable(true);
    331         dialog.getListView().setOnItemLongClickListener(
    332                 new AdapterView.OnItemLongClickListener() {
    333                     @Override
    334                     public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
    335                             long id) {
    336                         final Action action = mAdapter.getItem(position);
    337                         if (action instanceof LongPressAction) {
    338                             return ((LongPressAction) action).onLongPress();
    339                         }
    340                         return false;
    341                     }
    342         });
    343         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    344 
    345         dialog.setOnDismissListener(this);
    346 
    347         return dialog;
    348     }
    349 
    350     private class BugReportAction extends SinglePressAction implements LongPressAction {
    351 
    352         public BugReportAction() {
    353             super(com.android.internal.R.drawable.ic_lock_bugreport, R.string.bugreport_title);
    354         }
    355 
    356         @Override
    357         public void onPress() {
    358             // don't actually trigger the bugreport if we are running stability
    359             // tests via monkey
    360             if (ActivityManager.isUserAMonkey()) {
    361                 return;
    362             }
    363             // Add a little delay before executing, to give the
    364             // dialog a chance to go away before it takes a
    365             // screenshot.
    366             mHandler.postDelayed(new Runnable() {
    367                 @Override
    368                 public void run() {
    369                     try {
    370                         // Take an "interactive" bugreport.
    371                         MetricsLogger.action(mContext,
    372                                 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
    373                         ActivityManager.getService().requestBugReport(
    374                                 ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
    375                     } catch (RemoteException e) {
    376                     }
    377                 }
    378             }, 500);
    379         }
    380 
    381         @Override
    382         public boolean onLongPress() {
    383             // don't actually trigger the bugreport if we are running stability
    384             // tests via monkey
    385             if (ActivityManager.isUserAMonkey()) {
    386                 return false;
    387             }
    388             try {
    389                 // Take a "full" bugreport.
    390                 MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
    391                 ActivityManager.getService().requestBugReport(
    392                         ActivityManager.BUGREPORT_OPTION_FULL);
    393             } catch (RemoteException e) {
    394             }
    395             return false;
    396         }
    397 
    398         @Override
    399         public boolean showDuringKeyguard() {
    400             return true;
    401         }
    402 
    403         @Override
    404         public boolean showBeforeProvisioning() {
    405             return false;
    406         }
    407 
    408         @Override
    409         public String getStatus() {
    410             return mContext.getString(
    411                     com.android.internal.R.string.bugreport_status,
    412                     Build.VERSION.RELEASE,
    413                     Build.ID);
    414         }
    415     }
    416 
    417     private Action getSettingsAction() {
    418         return new SinglePressAction(com.android.internal.R.drawable.ic_settings,
    419                 R.string.global_action_settings) {
    420 
    421             @Override
    422             public void onPress() {
    423                 Intent intent = new Intent(Settings.ACTION_SETTINGS);
    424                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    425                 mContext.startActivity(intent);
    426             }
    427 
    428             @Override
    429             public boolean showDuringKeyguard() {
    430                 return true;
    431             }
    432 
    433             @Override
    434             public boolean showBeforeProvisioning() {
    435                 return true;
    436             }
    437         };
    438     }
    439 
    440     private Action getEmergencyAction() {
    441         return new SinglePressAction(com.android.internal.R.drawable.emergency_icon,
    442                 R.string.global_action_emergency) {
    443             @Override
    444             public void onPress() {
    445                 mEmergencyAffordanceManager.performEmergencyCall();
    446             }
    447 
    448             @Override
    449             public boolean showDuringKeyguard() {
    450                 return true;
    451             }
    452 
    453             @Override
    454             public boolean showBeforeProvisioning() {
    455                 return true;
    456             }
    457         };
    458     }
    459 
    460     private Action getAssistAction() {
    461         return new SinglePressAction(com.android.internal.R.drawable.ic_action_assist_focused,
    462                 R.string.global_action_assist) {
    463             @Override
    464             public void onPress() {
    465                 Intent intent = new Intent(Intent.ACTION_ASSIST);
    466                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    467                 mContext.startActivity(intent);
    468             }
    469 
    470             @Override
    471             public boolean showDuringKeyguard() {
    472                 return true;
    473             }
    474 
    475             @Override
    476             public boolean showBeforeProvisioning() {
    477                 return true;
    478             }
    479         };
    480     }
    481 
    482     private Action getVoiceAssistAction() {
    483         return new SinglePressAction(com.android.internal.R.drawable.ic_voice_search,
    484                 R.string.global_action_voice_assist) {
    485             @Override
    486             public void onPress() {
    487                 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
    488                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    489                 mContext.startActivity(intent);
    490             }
    491 
    492             @Override
    493             public boolean showDuringKeyguard() {
    494                 return true;
    495             }
    496 
    497             @Override
    498             public boolean showBeforeProvisioning() {
    499                 return true;
    500             }
    501         };
    502     }
    503 
    504     private Action getLockdownAction() {
    505         return new SinglePressAction(com.android.internal.R.drawable.ic_lock_lock,
    506                 R.string.global_action_lockdown) {
    507 
    508             @Override
    509             public void onPress() {
    510                 new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
    511                 try {
    512                     WindowManagerGlobal.getWindowManagerService().lockNow(null);
    513                 } catch (RemoteException e) {
    514                     Log.e(TAG, "Error while trying to lock device.", e);
    515                 }
    516             }
    517 
    518             @Override
    519             public boolean showDuringKeyguard() {
    520                 return true;
    521             }
    522 
    523             @Override
    524             public boolean showBeforeProvisioning() {
    525                 return false;
    526             }
    527         };
    528     }
    529 
    530     private UserInfo getCurrentUser() {
    531         try {
    532             return ActivityManager.getService().getCurrentUser();
    533         } catch (RemoteException re) {
    534             return null;
    535         }
    536     }
    537 
    538     private boolean isCurrentUserOwner() {
    539         UserInfo currentUser = getCurrentUser();
    540         return currentUser == null || currentUser.isPrimary();
    541     }
    542 
    543     private void addUsersToMenu(ArrayList<Action> items) {
    544         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    545         if (um.isUserSwitcherEnabled()) {
    546             List<UserInfo> users = um.getUsers();
    547             UserInfo currentUser = getCurrentUser();
    548             for (final UserInfo user : users) {
    549                 if (user.supportsSwitchToByUser()) {
    550                     boolean isCurrentUser = currentUser == null
    551                             ? user.id == 0 : (currentUser.id == user.id);
    552                     Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
    553                             : null;
    554                     SinglePressAction switchToUser = new SinglePressAction(
    555                             com.android.internal.R.drawable.ic_menu_cc, icon,
    556                             (user.name != null ? user.name : "Primary")
    557                             + (isCurrentUser ? " \u2714" : "")) {
    558                         @Override
    559                         public void onPress() {
    560                             try {
    561                                 ActivityManager.getService().switchUser(user.id);
    562                             } catch (RemoteException re) {
    563                                 Log.e(TAG, "Couldn't switch user " + re);
    564                             }
    565                         }
    566 
    567                         @Override
    568                         public boolean showDuringKeyguard() {
    569                             return true;
    570                         }
    571 
    572                         @Override
    573                         public boolean showBeforeProvisioning() {
    574                             return false;
    575                         }
    576                     };
    577                     items.add(switchToUser);
    578                 }
    579             }
    580         }
    581     }
    582 
    583     private void prepareDialog() {
    584         refreshSilentMode();
    585         mAirplaneModeOn.updateState(mAirplaneState);
    586         mAdapter.notifyDataSetChanged();
    587         mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    588         if (mShowSilentToggle) {
    589             IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
    590             mContext.registerReceiver(mRingerModeReceiver, filter);
    591         }
    592     }
    593 
    594     private void refreshSilentMode() {
    595         if (!mHasVibrator) {
    596             final boolean silentModeOn =
    597                     mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
    598             ((ToggleAction)mSilentModeAction).updateState(
    599                     silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
    600         }
    601     }
    602 
    603     /** {@inheritDoc} */
    604     @Override
    605     public void onDismiss(DialogInterface dialog) {
    606         if (mOnDismiss != null) {
    607             mOnDismiss.run();
    608         }
    609         if (mShowSilentToggle) {
    610             try {
    611                 mContext.unregisterReceiver(mRingerModeReceiver);
    612             } catch (IllegalArgumentException ie) {
    613                 // ignore this
    614                 Log.w(TAG, ie);
    615             }
    616         }
    617     }
    618 
    619     /** {@inheritDoc} */
    620     @Override
    621     public void onClick(DialogInterface dialog, int which) {
    622         if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
    623             dialog.dismiss();
    624         }
    625         mAdapter.getItem(which).onPress();
    626     }
    627 
    628     // note: the scheme below made more sense when we were planning on having
    629     // 8 different things in the global actions dialog.  seems overkill with
    630     // only 3 items now, but may as well keep this flexible approach so it will
    631     // be easy should someone decide at the last minute to include something
    632     // else, such as 'enable wifi', or 'enable bluetooth'
    633 
    634     private class SilentModeToggleAction extends ToggleAction {
    635         public SilentModeToggleAction() {
    636             super(R.drawable.ic_audio_vol_mute,
    637                     R.drawable.ic_audio_vol,
    638                     R.string.global_action_toggle_silent_mode,
    639                     R.string.global_action_silent_mode_on_status,
    640                     R.string.global_action_silent_mode_off_status);
    641         }
    642 
    643         @Override
    644         public void onToggle(boolean on) {
    645             if (on) {
    646                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
    647             } else {
    648                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
    649             }
    650         }
    651 
    652         @Override
    653         public boolean showDuringKeyguard() {
    654             return true;
    655         }
    656 
    657         @Override
    658         public boolean showBeforeProvisioning() {
    659             return false;
    660         }
    661     }
    662 
    663     private static class SilentModeTriStateAction implements Action, View.OnClickListener {
    664 
    665         private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3 };
    666 
    667         private final AudioManager mAudioManager;
    668         private final Handler mHandler;
    669         private final Context mContext;
    670 
    671         SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
    672             mAudioManager = audioManager;
    673             mHandler = handler;
    674             mContext = context;
    675         }
    676 
    677         private int ringerModeToIndex(int ringerMode) {
    678             // They just happen to coincide
    679             return ringerMode;
    680         }
    681 
    682         private int indexToRingerMode(int index) {
    683             // They just happen to coincide
    684             return index;
    685         }
    686 
    687         @Override
    688         public CharSequence getLabelForAccessibility(Context context) {
    689             return null;
    690         }
    691 
    692         @Override
    693         public View create(Context context, View convertView, ViewGroup parent,
    694                 LayoutInflater inflater) {
    695             View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
    696 
    697             int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
    698             for (int i = 0; i < 3; i++) {
    699                 View itemView = v.findViewById(ITEM_IDS[i]);
    700                 itemView.setSelected(selectedIndex == i);
    701                 // Set up click handler
    702                 itemView.setTag(i);
    703                 itemView.setOnClickListener(this);
    704             }
    705             return v;
    706         }
    707 
    708         @Override
    709         public void onPress() {
    710         }
    711 
    712         @Override
    713         public boolean showDuringKeyguard() {
    714             return true;
    715         }
    716 
    717         @Override
    718         public boolean showBeforeProvisioning() {
    719             return false;
    720         }
    721 
    722         @Override
    723         public boolean isEnabled() {
    724             return true;
    725         }
    726 
    727         void willCreate() {
    728         }
    729 
    730         @Override
    731         public void onClick(View v) {
    732             if (!(v.getTag() instanceof Integer)) return;
    733 
    734             int index = (Integer) v.getTag();
    735             mAudioManager.setRingerMode(indexToRingerMode(index));
    736             mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
    737         }
    738     }
    739 
    740     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    741         @Override
    742         public void onReceive(Context context, Intent intent) {
    743             String action = intent.getAction();
    744             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
    745                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
    746                 String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
    747                 if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
    748                     mHandler.sendEmptyMessage(MESSAGE_DISMISS);
    749                 }
    750             } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
    751                 // Airplane mode can be changed after ECM exits if airplane toggle button
    752                 // is pressed during ECM mode
    753                 if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
    754                         mIsWaitingForEcmExit) {
    755                     mIsWaitingForEcmExit = false;
    756                     changeAirplaneModeSystemSetting(true);
    757                 }
    758             }
    759         }
    760     };
    761 
    762     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    763         @Override
    764         public void onServiceStateChanged(ServiceState serviceState) {
    765             if (!mHasTelephony) return;
    766             final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
    767             mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
    768             mAirplaneModeOn.updateState(mAirplaneState);
    769             mAdapter.notifyDataSetChanged();
    770         }
    771     };
    772 
    773     private BroadcastReceiver mRingerModeReceiver = new BroadcastReceiver() {
    774         @Override
    775         public void onReceive(Context context, Intent intent) {
    776             if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
    777                 mHandler.sendEmptyMessage(MESSAGE_REFRESH);
    778             }
    779         }
    780     };
    781 
    782     private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
    783         @Override
    784         public void onChange(boolean selfChange) {
    785             onAirplaneModeChanged();
    786         }
    787     };
    788 
    789     private static final int MESSAGE_DISMISS = 0;
    790     private static final int MESSAGE_REFRESH = 1;
    791     private static final int MESSAGE_SHOW = 2;
    792     private static final int DIALOG_DISMISS_DELAY = 300; // ms
    793 
    794     private Handler mHandler = new Handler() {
    795         @Override
    796         public void handleMessage(Message msg) {
    797             switch (msg.what) {
    798             case MESSAGE_DISMISS:
    799                 if (mDialog != null) {
    800                     mDialog.dismiss();
    801                     mDialog = null;
    802                 }
    803                 break;
    804             case MESSAGE_REFRESH:
    805                 refreshSilentMode();
    806                 mAdapter.notifyDataSetChanged();
    807                 break;
    808             case MESSAGE_SHOW:
    809                 handleShow();
    810                 break;
    811             }
    812         }
    813     };
    814 
    815     private void onAirplaneModeChanged() {
    816         // Let the service state callbacks handle the state.
    817         if (mHasTelephony) return;
    818 
    819         boolean airplaneModeOn = Settings.Global.getInt(
    820                 mContext.getContentResolver(),
    821                 Settings.Global.AIRPLANE_MODE_ON,
    822                 0) == 1;
    823         mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
    824         mAirplaneModeOn.updateState(mAirplaneState);
    825     }
    826 
    827     /**
    828      * Change the airplane mode system setting
    829      */
    830     private void changeAirplaneModeSystemSetting(boolean on) {
    831         Settings.Global.putInt(
    832                 mContext.getContentResolver(),
    833                 Settings.Global.AIRPLANE_MODE_ON,
    834                 on ? 1 : 0);
    835         Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    836         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    837         intent.putExtra("state", on);
    838         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    839         if (!mHasTelephony) {
    840             mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
    841         }
    842     }
    843 }
    844