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