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