1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.phone; 18 19 import static com.android.systemui.Flags.predictiveBackAnimateDialogs; 20 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.res.Configuration; 28 import android.graphics.Insets; 29 import android.graphics.drawable.Drawable; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.SystemProperties; 34 import android.os.UserHandle; 35 import android.util.TypedValue; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.view.ViewRootImpl; 39 import android.view.Window; 40 import android.view.WindowInsets.Type; 41 import android.view.WindowManager; 42 import android.view.WindowManager.LayoutParams; 43 44 import androidx.annotation.Nullable; 45 import androidx.annotation.StyleRes; 46 47 import com.android.systemui.Dependency; 48 import com.android.systemui.animation.DialogTransitionAnimator; 49 import com.android.systemui.broadcast.BroadcastDispatcher; 50 import com.android.systemui.dagger.qualifiers.Application; 51 import com.android.systemui.model.SysUiState; 52 import com.android.systemui.res.R; 53 import com.android.systemui.shared.system.QuickStepContract; 54 import com.android.systemui.util.DialogKt; 55 56 import java.util.ArrayList; 57 import java.util.List; 58 59 import javax.inject.Inject; 60 61 /** 62 * Class for dialogs that should appear over panels and keyguard. 63 * 64 * DO NOT SUBCLASS THIS. See {@link DialogDelegate} for an interface that enables 65 * customizing behavior via composition instead of inheritance. Clients should implement the 66 * Delegate class and then pass their implementation into the SystemUIDialog constructor. 67 * 68 * Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to 69 * listeners on whether this dialog is showing. 70 * 71 * The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast, 72 * and dismisses itself when it receives the broadcast. 73 */ 74 public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigChangedCallback { 75 public static final int DEFAULT_THEME = R.style.Theme_SystemUI_Dialog; 76 // TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on. 77 private static final String FLAG_TABLET_DIALOG_WIDTH = 78 "persist.systemui.flag_tablet_dialog_width"; 79 public static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true; 80 81 private final Context mContext; 82 private final DialogDelegate<SystemUIDialog> mDelegate; 83 @Nullable 84 private final DismissReceiver mDismissReceiver; 85 private final Handler mHandler = new Handler(); 86 private final SystemUIDialogManager mDialogManager; 87 private final SysUiState mSysUiState; 88 89 private int mLastWidth = Integer.MIN_VALUE; 90 private int mLastHeight = Integer.MIN_VALUE; 91 private int mLastConfigurationWidthDp = -1; 92 private int mLastConfigurationHeightDp = -1; 93 94 private final List<Runnable> mOnCreateRunnables = new ArrayList<>(); 95 96 /** 97 * @deprecated Don't subclass SystemUIDialog. Please subclass {@link Delegate} and pass it to 98 * {@link Factory#create(Delegate)} to create a custom dialog. 99 */ 100 @Deprecated SystemUIDialog(Context context)101 public SystemUIDialog(Context context) { 102 this(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK); 103 } 104 SystemUIDialog(Context context, int theme)105 public SystemUIDialog(Context context, int theme) { 106 this(context, theme, DEFAULT_DISMISS_ON_DEVICE_LOCK); 107 } 108 SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock)109 public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) { 110 // TODO(b/219008720): Remove those calls to Dependency.get by introducing a 111 // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set 112 // the content and attach listeners. 113 this(context, theme, dismissOnDeviceLock, 114 Dependency.get(SystemUIDialogManager.class), 115 Dependency.get(SysUiState.class), 116 Dependency.get(BroadcastDispatcher.class), 117 Dependency.get(DialogTransitionAnimator.class)); 118 } 119 120 public static class Factory { 121 private final Context mContext; 122 private final SystemUIDialogManager mSystemUIDialogManager; 123 private final SysUiState mSysUiState; 124 private final BroadcastDispatcher mBroadcastDispatcher; 125 private final DialogTransitionAnimator mDialogTransitionAnimator; 126 127 @Inject Factory( @pplication Context context, SystemUIDialogManager systemUIDialogManager, SysUiState sysUiState, BroadcastDispatcher broadcastDispatcher, DialogTransitionAnimator dialogTransitionAnimator)128 public Factory( 129 @Application Context context, 130 SystemUIDialogManager systemUIDialogManager, 131 SysUiState sysUiState, 132 BroadcastDispatcher broadcastDispatcher, 133 DialogTransitionAnimator dialogTransitionAnimator) { 134 mContext = context; 135 mSystemUIDialogManager = systemUIDialogManager; 136 mSysUiState = sysUiState; 137 mBroadcastDispatcher = broadcastDispatcher; 138 mDialogTransitionAnimator = dialogTransitionAnimator; 139 } 140 141 /** 142 * Creates a new instance of {@link SystemUIDialog} with no customized behavior. 143 * 144 * When you just need a dialog, call this. 145 */ create()146 public SystemUIDialog create() { 147 return create(new DialogDelegate<>() { 148 }, mContext, DEFAULT_THEME); 149 } 150 151 /** 152 * Creates a new instance of {@link SystemUIDialog} with no customized behavior. 153 * 154 * When you just need a dialog created with a specific {@link Context}, call this. 155 */ create(Context context)156 public SystemUIDialog create(Context context) { 157 return create(new DialogDelegate<>() { 158 }, context, DEFAULT_THEME); 159 } 160 161 /** 162 * Creates a new instance of {@link SystemUIDialog} with {@code delegate} as the {@link 163 * Delegate}. 164 * 165 * When you need to customize the dialog, pass it a delegate. 166 */ create(Delegate delegate, Context context)167 public SystemUIDialog create(Delegate delegate, Context context) { 168 return create(delegate, context, DEFAULT_THEME); 169 } 170 create(Delegate delegate, Context context, @StyleRes int theme)171 public SystemUIDialog create(Delegate delegate, Context context, @StyleRes int theme) { 172 return create((DialogDelegate<SystemUIDialog>) delegate, context, theme); 173 } 174 create(Delegate delegate)175 public SystemUIDialog create(Delegate delegate) { 176 return create(delegate, mContext); 177 } 178 create(DialogDelegate<SystemUIDialog> dialogDelegate, Context context, @StyleRes int theme)179 private SystemUIDialog create(DialogDelegate<SystemUIDialog> dialogDelegate, 180 Context context, @StyleRes int theme) { 181 return new SystemUIDialog( 182 context, 183 theme, 184 DEFAULT_DISMISS_ON_DEVICE_LOCK, 185 mSystemUIDialogManager, 186 mSysUiState, 187 mBroadcastDispatcher, 188 mDialogTransitionAnimator, 189 dialogDelegate); 190 } 191 } 192 SystemUIDialog( Context context, int theme, boolean dismissOnDeviceLock, SystemUIDialogManager dialogManager, SysUiState sysUiState, BroadcastDispatcher broadcastDispatcher, DialogTransitionAnimator dialogTransitionAnimator)193 public SystemUIDialog( 194 Context context, 195 int theme, 196 boolean dismissOnDeviceLock, 197 SystemUIDialogManager dialogManager, 198 SysUiState sysUiState, 199 BroadcastDispatcher broadcastDispatcher, 200 DialogTransitionAnimator dialogTransitionAnimator) { 201 this( 202 context, 203 theme, 204 dismissOnDeviceLock, 205 dialogManager, 206 sysUiState, 207 broadcastDispatcher, 208 dialogTransitionAnimator, 209 new DialogDelegate<>() { 210 }); 211 } 212 SystemUIDialog( Context context, int theme, boolean dismissOnDeviceLock, SystemUIDialogManager dialogManager, SysUiState sysUiState, BroadcastDispatcher broadcastDispatcher, DialogTransitionAnimator dialogTransitionAnimator, Delegate delegate)213 public SystemUIDialog( 214 Context context, 215 int theme, 216 boolean dismissOnDeviceLock, 217 SystemUIDialogManager dialogManager, 218 SysUiState sysUiState, 219 BroadcastDispatcher broadcastDispatcher, 220 DialogTransitionAnimator dialogTransitionAnimator, 221 Delegate delegate) { 222 this( 223 context, 224 theme, 225 dismissOnDeviceLock, 226 dialogManager, 227 sysUiState, 228 broadcastDispatcher, 229 dialogTransitionAnimator, 230 (DialogDelegate<SystemUIDialog>) delegate); 231 } 232 SystemUIDialog( Context context, int theme, boolean dismissOnDeviceLock, SystemUIDialogManager dialogManager, SysUiState sysUiState, BroadcastDispatcher broadcastDispatcher, DialogTransitionAnimator dialogTransitionAnimator, DialogDelegate<SystemUIDialog> delegate)233 public SystemUIDialog( 234 Context context, 235 int theme, 236 boolean dismissOnDeviceLock, 237 SystemUIDialogManager dialogManager, 238 SysUiState sysUiState, 239 BroadcastDispatcher broadcastDispatcher, 240 DialogTransitionAnimator dialogTransitionAnimator, 241 DialogDelegate<SystemUIDialog> delegate) { 242 super(context, theme); 243 mContext = context; 244 mDelegate = delegate; 245 246 applyFlags(this); 247 WindowManager.LayoutParams attrs = getWindow().getAttributes(); 248 attrs.setTitle(getClass().getSimpleName()); 249 getWindow().setAttributes(attrs); 250 251 mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this, broadcastDispatcher, 252 dialogTransitionAnimator) : null; 253 mDialogManager = dialogManager; 254 mSysUiState = sysUiState; 255 } 256 257 @Override onCreate(Bundle savedInstanceState)258 protected void onCreate(Bundle savedInstanceState) { 259 mDelegate.beforeCreate(this, savedInstanceState); 260 super.onCreate(savedInstanceState); 261 mDelegate.onCreate(this, savedInstanceState); 262 263 Configuration config = getContext().getResources().getConfiguration(); 264 mLastConfigurationWidthDp = config.screenWidthDp; 265 mLastConfigurationHeightDp = config.screenHeightDp; 266 updateWindowSize(); 267 268 for (int i = 0; i < mOnCreateRunnables.size(); i++) { 269 mOnCreateRunnables.get(i).run(); 270 } 271 if (predictiveBackAnimateDialogs()) { 272 View targetView = getWindow().getDecorView(); 273 DialogKt.registerAnimationOnBackInvoked( 274 /* dialog = */ this, 275 /* targetView = */ targetView, 276 /* backAnimationSpec= */mDelegate.getBackAnimationSpec( 277 () -> targetView.getResources().getDisplayMetrics()) 278 ); 279 } 280 } 281 updateWindowSize()282 private void updateWindowSize() { 283 // Only the thread that created this dialog can update its window size. 284 if (Looper.myLooper() != mHandler.getLooper()) { 285 mHandler.post(this::updateWindowSize); 286 return; 287 } 288 289 int width = getWidth(); 290 int height = getHeight(); 291 if (width == mLastWidth && height == mLastHeight) { 292 return; 293 } 294 295 mLastWidth = width; 296 mLastHeight = height; 297 getWindow().setLayout(width, height); 298 } 299 300 @Override onConfigurationChanged(Configuration configuration)301 public void onConfigurationChanged(Configuration configuration) { 302 if (mLastConfigurationWidthDp != configuration.screenWidthDp 303 || mLastConfigurationHeightDp != configuration.screenHeightDp) { 304 mLastConfigurationWidthDp = configuration.screenWidthDp; 305 mLastConfigurationHeightDp = configuration.compatScreenWidthDp; 306 307 updateWindowSize(); 308 } 309 310 mDelegate.onConfigurationChanged(this, configuration); 311 } 312 313 /** 314 * Return this dialog width. This method will be invoked when this dialog is created and when 315 * the device configuration changes, and the result will be used to resize this dialog window. 316 */ getWidth()317 protected int getWidth() { 318 return mDelegate.getWidth(this); 319 } 320 321 /** 322 * Return this dialog height. This method will be invoked when this dialog is created and when 323 * the device configuration changes, and the result will be used to resize this dialog window. 324 */ getHeight()325 protected int getHeight() { 326 return mDelegate.getHeight(this); 327 } 328 329 @Override onStart()330 protected final void onStart() { 331 super.onStart(); 332 333 if (mDismissReceiver != null) { 334 mDismissReceiver.register(); 335 } 336 337 // Listen for configuration changes to resize this dialog window. This is mostly necessary 338 // for foldables that often go from large <=> small screen when folding/unfolding. 339 ViewRootImpl.addConfigCallback(this); 340 mDialogManager.setShowing(this, true); 341 mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true) 342 .commitUpdate(mContext.getDisplayId()); 343 344 start(); 345 } 346 347 /** 348 * Called when {@link #onStart} is called. Subclasses wishing to override {@link #onStart()} 349 * should override this method instead. 350 */ start()351 protected void start() { 352 mDelegate.onStart(this); 353 } 354 355 @Override onStop()356 protected final void onStop() { 357 super.onStop(); 358 359 if (mDismissReceiver != null) { 360 mDismissReceiver.unregister(); 361 } 362 363 ViewRootImpl.removeConfigCallback(this); 364 mDialogManager.setShowing(this, false); 365 mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, false) 366 .commitUpdate(mContext.getDisplayId()); 367 368 stop(); 369 } 370 371 /** 372 * Called when {@link #onStop} is called. Subclasses wishing to override {@link #onStop()} 373 * should override this method instead. 374 */ stop()375 protected void stop() { 376 mDelegate.onStop(this); 377 } 378 379 @Override onWindowFocusChanged(boolean hasFocus)380 public void onWindowFocusChanged(boolean hasFocus) { 381 super.onWindowFocusChanged(hasFocus); 382 mDelegate.onWindowFocusChanged(this, hasFocus); 383 } 384 setShowForAllUsers(boolean show)385 public void setShowForAllUsers(boolean show) { 386 setShowForAllUsers(this, show); 387 } 388 setMessage(int resId)389 public void setMessage(int resId) { 390 setMessage(mContext.getString(resId)); 391 } 392 393 /** 394 * Set a listener to be invoked when the positive button of the dialog is pressed. The dialog 395 * will automatically be dismissed when the button is clicked. 396 */ setPositiveButton(int resId, OnClickListener onClick)397 public void setPositiveButton(int resId, OnClickListener onClick) { 398 setPositiveButton(resId, onClick, true /* dismissOnClick */); 399 } 400 401 /** 402 * Set a listener to be invoked when the positive button of the dialog is pressed. The dialog 403 * will be dismissed when the button is clicked iff {@code dismissOnClick} is true. 404 */ setPositiveButton(int resId, OnClickListener onClick, boolean dismissOnClick)405 public void setPositiveButton(int resId, OnClickListener onClick, boolean dismissOnClick) { 406 setButton(BUTTON_POSITIVE, resId, onClick, dismissOnClick); 407 } 408 409 /** 410 * Set a listener to be invoked when the negative button of the dialog is pressed. The dialog 411 * will automatically be dismissed when the button is clicked. 412 */ setNegativeButton(int resId, OnClickListener onClick)413 public void setNegativeButton(int resId, OnClickListener onClick) { 414 setNegativeButton(resId, onClick, true /* dismissOnClick */); 415 } 416 417 /** 418 * Set a listener to be invoked when the negative button of the dialog is pressed. The dialog 419 * will be dismissed when the button is clicked iff {@code dismissOnClick} is true. 420 */ setNegativeButton(int resId, OnClickListener onClick, boolean dismissOnClick)421 public void setNegativeButton(int resId, OnClickListener onClick, boolean dismissOnClick) { 422 setButton(BUTTON_NEGATIVE, resId, onClick, dismissOnClick); 423 } 424 425 /** 426 * Set a listener to be invoked when the neutral button of the dialog is pressed. The dialog 427 * will automatically be dismissed when the button is clicked. 428 */ setNeutralButton(int resId, OnClickListener onClick)429 public void setNeutralButton(int resId, OnClickListener onClick) { 430 setNeutralButton(resId, onClick, true /* dismissOnClick */); 431 } 432 433 /** 434 * Set a listener to be invoked when the neutral button of the dialog is pressed. The dialog 435 * will be dismissed when the button is clicked iff {@code dismissOnClick} is true. 436 */ setNeutralButton(int resId, OnClickListener onClick, boolean dismissOnClick)437 public void setNeutralButton(int resId, OnClickListener onClick, boolean dismissOnClick) { 438 setButton(BUTTON_NEUTRAL, resId, onClick, dismissOnClick); 439 } 440 setButton(int whichButton, int resId, OnClickListener onClick, boolean dismissOnClick)441 private void setButton(int whichButton, int resId, OnClickListener onClick, 442 boolean dismissOnClick) { 443 if (dismissOnClick) { 444 setButton(whichButton, mContext.getString(resId), onClick); 445 } else { 446 // Set a null OnClickListener to make sure the button is still created and shown. 447 setButton(whichButton, mContext.getString(resId), (OnClickListener) null); 448 449 // When the dialog is created, set the click listener but don't dismiss the dialog when 450 // it is clicked. 451 mOnCreateRunnables.add(() -> getButton(whichButton).setOnClickListener( 452 view -> onClick.onClick(this, whichButton))); 453 } 454 } 455 setShowForAllUsers(Dialog dialog, boolean show)456 public static void setShowForAllUsers(Dialog dialog, boolean show) { 457 if (show) { 458 dialog.getWindow().getAttributes().privateFlags |= 459 WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; 460 } else { 461 dialog.getWindow().getAttributes().privateFlags &= 462 ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; 463 } 464 } 465 466 /** 467 * Ensure the window type is set properly to show over all other screens 468 */ setWindowOnTop(Dialog dialog, boolean isKeyguardShowing)469 public static void setWindowOnTop(Dialog dialog, boolean isKeyguardShowing) { 470 final Window window = dialog.getWindow(); 471 window.setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); 472 if (isKeyguardShowing) { 473 window.getAttributes().setFitInsetsTypes( 474 window.getAttributes().getFitInsetsTypes() & ~Type.statusBars()); 475 } 476 } 477 applyFlags(AlertDialog dialog)478 public static AlertDialog applyFlags(AlertDialog dialog) { 479 final Window window = dialog.getWindow(); 480 window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); 481 window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 482 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 483 window.getAttributes().setFitInsetsTypes( 484 window.getAttributes().getFitInsetsTypes() & ~Type.statusBars()); 485 return dialog; 486 } 487 488 /** 489 * Registers a listener that dismisses the given dialog when it receives 490 * the screen off / close system dialogs broadcast. 491 * <p> 492 * <strong>Note:</strong> Don't call dialog.setOnDismissListener() after 493 * calling this because it causes a leak of BroadcastReceiver. Instead, call the version that 494 * takes an extra Runnable as a parameter. 495 * 496 * @param dialog The dialog to be associated with the listener. 497 */ registerDismissListener(Dialog dialog)498 public static void registerDismissListener(Dialog dialog) { 499 registerDismissListener(dialog, null); 500 } 501 502 /** 503 * Registers a listener that dismisses the given dialog when it receives 504 * the screen off / close system dialogs broadcast. 505 * <p> 506 * <strong>Note:</strong> Don't call dialog.setOnDismissListener() after 507 * calling this because it causes a leak of BroadcastReceiver. 508 * 509 * @param dialog The dialog to be associated with the listener. 510 * @param dismissAction An action to run when the dialog is dismissed. 511 */ registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction)512 public static void registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction) { 513 // TODO(b/219008720): Remove those calls to Dependency.get. 514 DismissReceiver dismissReceiver = new DismissReceiver(dialog, 515 Dependency.get(BroadcastDispatcher.class), 516 Dependency.get(DialogTransitionAnimator.class)); 517 dialog.setOnDismissListener(d -> { 518 dismissReceiver.unregister(); 519 if (dismissAction != null) dismissAction.run(); 520 }); 521 dismissReceiver.register(); 522 } 523 524 /** Set an appropriate size to {@code dialog} depending on the current configuration. */ setDialogSize(Dialog dialog)525 public static void setDialogSize(Dialog dialog) { 526 // We need to create the dialog first, otherwise the size will be overridden when it is 527 // created. 528 dialog.create(); 529 dialog.getWindow().setLayout(getDefaultDialogWidth(dialog), getDefaultDialogHeight()); 530 } 531 getDefaultDialogWidth(Dialog dialog)532 static int getDefaultDialogWidth(Dialog dialog) { 533 Context context = dialog.getContext(); 534 int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0); 535 if (flagValue == -1) { 536 // The width of bottom sheets (624dp). 537 return calculateDialogWidthWithInsets(dialog, 624); 538 } else if (flagValue == -2) { 539 // The suggested small width for all dialogs (348dp) 540 return calculateDialogWidthWithInsets(dialog, 348); 541 } else if (flagValue > 0) { 542 // Any given width. 543 return calculateDialogWidthWithInsets(dialog, flagValue); 544 } else { 545 // By default we use the same width as the notification shade in portrait mode. 546 int width = context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width); 547 if (width > 0) { 548 // If we are neither WRAP_CONTENT or MATCH_PARENT, add the background insets so that 549 // the dialog is the desired width. 550 width += getHorizontalInsets(dialog); 551 } 552 return width; 553 } 554 } 555 556 /** 557 * Return the pixel width {@param dialog} should be so that it is {@param widthInDp} wide, 558 * taking its background insets into consideration. 559 */ calculateDialogWidthWithInsets(Dialog dialog, int widthInDp)560 private static int calculateDialogWidthWithInsets(Dialog dialog, int widthInDp) { 561 float widthInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, widthInDp, 562 dialog.getContext().getResources().getDisplayMetrics()); 563 return Math.round(widthInPixels + getHorizontalInsets(dialog)); 564 } 565 getHorizontalInsets(Dialog dialog)566 private static int getHorizontalInsets(Dialog dialog) { 567 View decorView = dialog.getWindow().getDecorView(); 568 if (decorView == null) { 569 return 0; 570 } 571 572 // We first look for the background on the dialogContentWithBackground added by 573 // DialogTransitionAnimator. If it's not there, we use the background of the DecorView. 574 View viewWithBackground = decorView.findViewByPredicate( 575 view -> view.getTag( 576 com.android.systemui.animation.R.id.tag_dialog_background) != null); 577 Drawable background = viewWithBackground != null ? viewWithBackground.getBackground() 578 : decorView.getBackground(); 579 Insets insets = background != null ? background.getOpticalInsets() : Insets.NONE; 580 return insets.left + insets.right; 581 } 582 getDefaultDialogHeight()583 static int getDefaultDialogHeight() { 584 return ViewGroup.LayoutParams.WRAP_CONTENT; 585 } 586 587 private static class DismissReceiver extends BroadcastReceiver { 588 private static final IntentFilter INTENT_FILTER = new IntentFilter(); 589 590 static { 591 INTENT_FILTER.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 592 INTENT_FILTER.addAction(Intent.ACTION_SCREEN_OFF); 593 } 594 595 private final Dialog mDialog; 596 private boolean mRegistered; 597 private final BroadcastDispatcher mBroadcastDispatcher; 598 private final DialogTransitionAnimator mDialogTransitionAnimator; 599 DismissReceiver(Dialog dialog, BroadcastDispatcher broadcastDispatcher, DialogTransitionAnimator dialogTransitionAnimator)600 DismissReceiver(Dialog dialog, BroadcastDispatcher broadcastDispatcher, 601 DialogTransitionAnimator dialogTransitionAnimator) { 602 mDialog = dialog; 603 mBroadcastDispatcher = broadcastDispatcher; 604 mDialogTransitionAnimator = dialogTransitionAnimator; 605 } 606 register()607 void register() { 608 mBroadcastDispatcher.registerReceiver(this, INTENT_FILTER, null, UserHandle.CURRENT); 609 mRegistered = true; 610 } 611 unregister()612 void unregister() { 613 if (mRegistered) { 614 mBroadcastDispatcher.unregisterReceiver(this); 615 mRegistered = false; 616 } 617 } 618 619 @Override onReceive(Context context, Intent intent)620 public void onReceive(Context context, Intent intent) { 621 // These broadcast are usually received when locking the device, swiping up to home 622 // (which collapses the shade), etc. In those cases, we usually don't want to animate 623 // back into the view. 624 mDialogTransitionAnimator.disableAllCurrentDialogsExitAnimations(); 625 mDialog.dismiss(); 626 } 627 } 628 629 /** 630 * A delegate class that should be implemented in place of sublcassing {@link SystemUIDialog}. 631 * 632 * Implement this interface and then pass an instance of your implementation to 633 * {@link SystemUIDialog.Factory#create(Delegate)}. 634 */ 635 public interface Delegate extends DialogDelegate<SystemUIDialog> { 636 /** 637 * Returns a new {@link SystemUIDialog} which has been passed this Delegate in its 638 * construction. 639 */ createDialog()640 SystemUIDialog createDialog(); 641 } 642 } 643