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