1 /*
2  * Copyright (C) 2006 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 android.app;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.DrawableRes;
21 import android.annotation.IdRes;
22 import android.annotation.LayoutRes;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.StringRes;
26 import android.annotation.StyleRes;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.ContextWrapper;
30 import android.content.DialogInterface;
31 import android.content.pm.ApplicationInfo;
32 import android.content.res.Configuration;
33 import android.content.res.ResourceId;
34 import android.graphics.drawable.Drawable;
35 import android.net.Uri;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.util.Log;
41 import android.util.TypedValue;
42 import android.view.ActionMode;
43 import android.view.ContextMenu;
44 import android.view.ContextMenu.ContextMenuInfo;
45 import android.view.ContextThemeWrapper;
46 import android.view.Gravity;
47 import android.view.KeyEvent;
48 import android.view.LayoutInflater;
49 import android.view.Menu;
50 import android.view.MenuItem;
51 import android.view.MotionEvent;
52 import android.view.SearchEvent;
53 import android.view.View;
54 import android.view.View.OnCreateContextMenuListener;
55 import android.view.ViewGroup;
56 import android.view.ViewGroup.LayoutParams;
57 import android.view.Window;
58 import android.view.WindowManager;
59 import android.view.accessibility.AccessibilityEvent;
60 
61 import com.android.internal.R;
62 import com.android.internal.app.WindowDecorActionBar;
63 import com.android.internal.policy.PhoneWindow;
64 
65 import java.lang.ref.WeakReference;
66 
67 /**
68  * Base class for Dialogs.
69  *
70  * <p>Note: Activities provide a facility to manage the creation, saving and
71  * restoring of dialogs. See {@link Activity#onCreateDialog(int)},
72  * {@link Activity#onPrepareDialog(int, Dialog)},
73  * {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If
74  * these methods are used, {@link #getOwnerActivity()} will return the Activity
75  * that managed this dialog.
76  *
77  * <p>Often you will want to have a Dialog display on top of the current
78  * input method, because there is no reason for it to accept text.  You can
79  * do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
80  * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming
81  * your Dialog takes input focus, as it the default) with the following code:
82  *
83  * <pre>
84  * getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
85  *         WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);</pre>
86  *
87  * <div class="special reference">
88  * <h3>Developer Guides</h3>
89  * <p>For more information about creating dialogs, read the
90  * <a href="{@docRoot}guide/topics/ui/dialogs.html">Dialogs</a> developer guide.</p>
91  * </div>
92  */
93 public class Dialog implements DialogInterface, Window.Callback,
94         KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
95     private static final String TAG = "Dialog";
96     private Activity mOwnerActivity;
97 
98     private final WindowManager mWindowManager;
99 
100     final Context mContext;
101     final Window mWindow;
102 
103     View mDecor;
104 
105     private ActionBar mActionBar;
106     /**
107      * This field should be made private, so it is hidden from the SDK.
108      * {@hide}
109      */
110     protected boolean mCancelable = true;
111 
112     private String mCancelAndDismissTaken;
113     private Message mCancelMessage;
114     private Message mDismissMessage;
115     private Message mShowMessage;
116 
117     private OnKeyListener mOnKeyListener;
118 
119     private boolean mCreated = false;
120     private boolean mShowing = false;
121     private boolean mCanceled = false;
122 
123     private final Handler mHandler = new Handler();
124 
125     private static final int DISMISS = 0x43;
126     private static final int CANCEL = 0x44;
127     private static final int SHOW = 0x45;
128 
129     private final Handler mListenersHandler;
130 
131     private SearchEvent mSearchEvent;
132 
133     private ActionMode mActionMode;
134 
135     private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
136 
137     private final Runnable mDismissAction = this::dismissDialog;
138 
139     /**
140      * Creates a dialog window that uses the default dialog theme.
141      * <p>
142      * The supplied {@code context} is used to obtain the window manager and
143      * base theme used to present the dialog.
144      *
145      * @param context the context in which the dialog should run
146      * @see android.R.styleable#Theme_dialogTheme
147      */
Dialog(@onNull Context context)148     public Dialog(@NonNull Context context) {
149         this(context, 0, true);
150     }
151 
152     /**
153      * Creates a dialog window that uses a custom dialog style.
154      * <p>
155      * The supplied {@code context} is used to obtain the window manager and
156      * base theme used to present the dialog.
157      * <p>
158      * The supplied {@code theme} is applied on top of the context's theme. See
159      * <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
160      * Style and Theme Resources</a> for more information about defining and
161      * using styles.
162      *
163      * @param context the context in which the dialog should run
164      * @param themeResId a style resource describing the theme to use for the
165      *              window, or {@code 0} to use the default dialog theme
166      */
Dialog(@onNull Context context, @StyleRes int themeResId)167     public Dialog(@NonNull Context context, @StyleRes int themeResId) {
168         this(context, themeResId, true);
169     }
170 
Dialog(@onNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper)171     Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
172         if (createContextThemeWrapper) {
173             if (themeResId == ResourceId.ID_NULL) {
174                 final TypedValue outValue = new TypedValue();
175                 context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
176                 themeResId = outValue.resourceId;
177             }
178             mContext = new ContextThemeWrapper(context, themeResId);
179         } else {
180             mContext = context;
181         }
182 
183         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
184 
185         final Window w = new PhoneWindow(mContext);
186         mWindow = w;
187         w.setCallback(this);
188         w.setOnWindowDismissedCallback(this);
189         w.setOnWindowSwipeDismissedCallback(() -> {
190             if (mCancelable) {
191                 cancel();
192             }
193         });
194         w.setWindowManager(mWindowManager, null, null);
195         w.setGravity(Gravity.CENTER);
196 
197         mListenersHandler = new ListenersHandler(this);
198     }
199 
200     /**
201      * @deprecated
202      * @hide
203      */
204     @Deprecated
Dialog(@onNull Context context, boolean cancelable, @Nullable Message cancelCallback)205     protected Dialog(@NonNull Context context, boolean cancelable,
206             @Nullable Message cancelCallback) {
207         this(context);
208         mCancelable = cancelable;
209         updateWindowForCancelable();
210         mCancelMessage = cancelCallback;
211     }
212 
Dialog(@onNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener)213     protected Dialog(@NonNull Context context, boolean cancelable,
214             @Nullable OnCancelListener cancelListener) {
215         this(context);
216         mCancelable = cancelable;
217         updateWindowForCancelable();
218         setOnCancelListener(cancelListener);
219     }
220 
221     /**
222      * Retrieve the Context this Dialog is running in.
223      *
224      * @return Context The Context used by the Dialog.
225      */
getContext()226     public final @NonNull Context getContext() {
227         return mContext;
228     }
229 
230     /**
231      * Retrieve the {@link ActionBar} attached to this dialog, if present.
232      *
233      * @return The ActionBar attached to the dialog or null if no ActionBar is present.
234      */
getActionBar()235     public @Nullable ActionBar getActionBar() {
236         return mActionBar;
237     }
238 
239     /**
240      * Sets the Activity that owns this dialog. An example use: This Dialog will
241      * use the suggested volume control stream of the Activity.
242      *
243      * @param activity The Activity that owns this dialog.
244      */
setOwnerActivity(@onNull Activity activity)245     public final void setOwnerActivity(@NonNull Activity activity) {
246         mOwnerActivity = activity;
247 
248         getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream());
249     }
250 
251     /**
252      * Returns the Activity that owns this Dialog. For example, if
253      * {@link Activity#showDialog(int)} is used to show this Dialog, that
254      * Activity will be the owner (by default). Depending on how this dialog was
255      * created, this may return null.
256      *
257      * @return The Activity that owns this Dialog.
258      */
getOwnerActivity()259     public final @Nullable Activity getOwnerActivity() {
260         return mOwnerActivity;
261     }
262 
263     /**
264      * @return Whether the dialog is currently showing.
265      */
isShowing()266     public boolean isShowing() {
267         return mShowing;
268     }
269 
270     /**
271      * Forces immediate creation of the dialog.
272      * <p>
273      * Note that you should not override this method to perform dialog creation.
274      * Rather, override {@link #onCreate(Bundle)}.
275      */
create()276     public void create() {
277         if (!mCreated) {
278             dispatchOnCreate(null);
279         }
280     }
281 
282     /**
283      * Start the dialog and display it on screen.  The window is placed in the
284      * application layer and opaque.  Note that you should not override this
285      * method to do initialization when the dialog is shown, instead implement
286      * that in {@link #onStart}.
287      */
show()288     public void show() {
289         if (mShowing) {
290             if (mDecor != null) {
291                 if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
292                     mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
293                 }
294                 mDecor.setVisibility(View.VISIBLE);
295             }
296             return;
297         }
298 
299         mCanceled = false;
300 
301         if (!mCreated) {
302             dispatchOnCreate(null);
303         } else {
304             // Fill the DecorView in on any configuration changes that
305             // may have occured while it was removed from the WindowManager.
306             final Configuration config = mContext.getResources().getConfiguration();
307             mWindow.getDecorView().dispatchConfigurationChanged(config);
308         }
309 
310         onStart();
311         mDecor = mWindow.getDecorView();
312 
313         if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
314             final ApplicationInfo info = mContext.getApplicationInfo();
315             mWindow.setDefaultIcon(info.icon);
316             mWindow.setDefaultLogo(info.logo);
317             mActionBar = new WindowDecorActionBar(this);
318         }
319 
320         WindowManager.LayoutParams l = mWindow.getAttributes();
321         boolean restoreSoftInputMode = false;
322         if ((l.softInputMode
323                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
324             l.softInputMode |=
325                     WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
326             restoreSoftInputMode = true;
327         }
328 
329         mWindowManager.addView(mDecor, l);
330         if (restoreSoftInputMode) {
331             l.softInputMode &=
332                     ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
333         }
334 
335         mShowing = true;
336 
337         sendShowMessage();
338     }
339 
340     /**
341      * Hide the dialog, but do not dismiss it.
342      */
hide()343     public void hide() {
344         if (mDecor != null) {
345             mDecor.setVisibility(View.GONE);
346         }
347     }
348 
349     /**
350      * Dismiss this dialog, removing it from the screen. This method can be
351      * invoked safely from any thread.  Note that you should not override this
352      * method to do cleanup when the dialog is dismissed, instead implement
353      * that in {@link #onStop}.
354      */
355     @Override
dismiss()356     public void dismiss() {
357         if (Looper.myLooper() == mHandler.getLooper()) {
358             dismissDialog();
359         } else {
360             mHandler.post(mDismissAction);
361         }
362     }
363 
dismissDialog()364     void dismissDialog() {
365         if (mDecor == null || !mShowing) {
366             return;
367         }
368 
369         if (mWindow.isDestroyed()) {
370             Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
371             return;
372         }
373 
374         try {
375             mWindowManager.removeViewImmediate(mDecor);
376         } finally {
377             if (mActionMode != null) {
378                 mActionMode.finish();
379             }
380             mDecor = null;
381             mWindow.closeAllPanels();
382             onStop();
383             mShowing = false;
384 
385             sendDismissMessage();
386         }
387     }
388 
sendDismissMessage()389     private void sendDismissMessage() {
390         if (mDismissMessage != null) {
391             // Obtain a new message so this dialog can be re-used
392             Message.obtain(mDismissMessage).sendToTarget();
393         }
394     }
395 
sendShowMessage()396     private void sendShowMessage() {
397         if (mShowMessage != null) {
398             // Obtain a new message so this dialog can be re-used
399             Message.obtain(mShowMessage).sendToTarget();
400         }
401     }
402 
403     // internal method to make sure mCreated is set properly without requiring
404     // users to call through to super in onCreate
dispatchOnCreate(Bundle savedInstanceState)405     void dispatchOnCreate(Bundle savedInstanceState) {
406         if (!mCreated) {
407             onCreate(savedInstanceState);
408             mCreated = true;
409         }
410     }
411 
412     /**
413      * Similar to {@link Activity#onCreate}, you should initialize your dialog
414      * in this method, including calling {@link #setContentView}.
415      * @param savedInstanceState If this dialog is being reinitialized after a
416      *     the hosting activity was previously shut down, holds the result from
417      *     the most recent call to {@link #onSaveInstanceState}, or null if this
418      *     is the first time.
419      */
onCreate(Bundle savedInstanceState)420     protected void onCreate(Bundle savedInstanceState) {
421     }
422 
423     /**
424      * Called when the dialog is starting.
425      */
onStart()426     protected void onStart() {
427         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
428     }
429 
430     /**
431      * Called to tell you that you're stopping.
432      */
onStop()433     protected void onStop() {
434         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
435     }
436 
437     private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
438     private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy";
439 
440     /**
441      * Saves the state of the dialog into a bundle.
442      *
443      * The default implementation saves the state of its view hierarchy, so you'll
444      * likely want to call through to super if you override this to save additional
445      * state.
446      * @return A bundle with the state of the dialog.
447      */
onSaveInstanceState()448     public @NonNull Bundle onSaveInstanceState() {
449         Bundle bundle = new Bundle();
450         bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
451         if (mCreated) {
452             bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState());
453         }
454         return bundle;
455     }
456 
457     /**
458      * Restore the state of the dialog from a previously saved bundle.
459      *
460      * The default implementation restores the state of the dialog's view
461      * hierarchy that was saved in the default implementation of {@link #onSaveInstanceState()},
462      * so be sure to call through to super when overriding unless you want to
463      * do all restoring of state yourself.
464      * @param savedInstanceState The state of the dialog previously saved by
465      *     {@link #onSaveInstanceState()}.
466      */
onRestoreInstanceState(@onNull Bundle savedInstanceState)467     public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
468         final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
469         if (dialogHierarchyState == null) {
470             // dialog has never been shown, or onCreated, nothing to restore.
471             return;
472         }
473         dispatchOnCreate(savedInstanceState);
474         mWindow.restoreHierarchyState(dialogHierarchyState);
475         if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
476             show();
477         }
478     }
479 
480     /**
481      * Retrieve the current Window for the activity.  This can be used to
482      * directly access parts of the Window API that are not available
483      * through Activity/Screen.
484      *
485      * @return Window The current window, or null if the activity is not
486      *         visual.
487      */
getWindow()488     public @Nullable Window getWindow() {
489         return mWindow;
490     }
491 
492     /**
493      * Call {@link android.view.Window#getCurrentFocus} on the
494      * Window if this Activity to return the currently focused view.
495      *
496      * @return View The current View with focus or null.
497      *
498      * @see #getWindow
499      * @see android.view.Window#getCurrentFocus
500      */
getCurrentFocus()501     public @Nullable View getCurrentFocus() {
502         return mWindow != null ? mWindow.getCurrentFocus() : null;
503     }
504 
505     /**
506      * Finds the first descendant view with the given ID or {@code null} if the
507      * ID is invalid (< 0), there is no matching view in the hierarchy, or the
508      * dialog has not yet been fully created (for example, via {@link #show()}
509      * or {@link #create()}).
510      * <p>
511      * <strong>Note:</strong> In most cases -- depending on compiler support --
512      * the resulting view is automatically cast to the target class type. If
513      * the target class type is unconstrained, an explicit cast may be
514      * necessary.
515      *
516      * @param id the ID to search for
517      * @return a view with given ID if found, or {@code null} otherwise
518      * @see View#findViewById(int)
519      * @see Dialog#requireViewById(int)
520      */
521     @Nullable
findViewById(@dRes int id)522     public <T extends View> T findViewById(@IdRes int id) {
523         return mWindow.findViewById(id);
524     }
525 
526     /**
527      * Finds the first descendant view with the given ID or throws an IllegalArgumentException if
528      * the ID is invalid (< 0), there is no matching view in the hierarchy, or the dialog has not
529      * yet been fully created (for example, via {@link #show()} or {@link #create()}).
530      * <p>
531      * <strong>Note:</strong> In most cases -- depending on compiler support --
532      * the resulting view is automatically cast to the target class type. If
533      * the target class type is unconstrained, an explicit cast may be
534      * necessary.
535      *
536      * @param id the ID to search for
537      * @return a view with given ID
538      * @see View#requireViewById(int)
539      * @see Dialog#findViewById(int)
540      */
541     @NonNull
requireViewById(@dRes int id)542     public final <T extends View> T requireViewById(@IdRes int id) {
543         T view = findViewById(id);
544         if (view == null) {
545             throw new IllegalArgumentException("ID does not reference a View inside this Dialog");
546         }
547         return view;
548     }
549 
550     /**
551      * Set the screen content from a layout resource.  The resource will be
552      * inflated, adding all top-level views to the screen.
553      *
554      * @param layoutResID Resource ID to be inflated.
555      */
setContentView(@ayoutRes int layoutResID)556     public void setContentView(@LayoutRes int layoutResID) {
557         mWindow.setContentView(layoutResID);
558     }
559 
560     /**
561      * Set the screen content to an explicit view.  This view is placed
562      * directly into the screen's view hierarchy.  It can itself be a complex
563      * view hierarchy.
564      *
565      * @param view The desired content to display.
566      */
setContentView(@onNull View view)567     public void setContentView(@NonNull View view) {
568         mWindow.setContentView(view);
569     }
570 
571     /**
572      * Set the screen content to an explicit view.  This view is placed
573      * directly into the screen's view hierarchy.  It can itself be a complex
574      * view hierarchy.
575      *
576      * @param view The desired content to display.
577      * @param params Layout parameters for the view.
578      */
setContentView(@onNull View view, @Nullable ViewGroup.LayoutParams params)579     public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
580         mWindow.setContentView(view, params);
581     }
582 
583     /**
584      * Add an additional content view to the screen.  Added after any existing
585      * ones in the screen -- existing views are NOT removed.
586      *
587      * @param view The desired content to display.
588      * @param params Layout parameters for the view.
589      */
addContentView(@onNull View view, @Nullable ViewGroup.LayoutParams params)590     public void addContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
591         mWindow.addContentView(view, params);
592     }
593 
594     /**
595      * Set the title text for this dialog's window.
596      *
597      * @param title The new text to display in the title.
598      */
setTitle(@ullable CharSequence title)599     public void setTitle(@Nullable CharSequence title) {
600         mWindow.setTitle(title);
601         mWindow.getAttributes().setTitle(title);
602     }
603 
604     /**
605      * Set the title text for this dialog's window. The text is retrieved
606      * from the resources with the supplied identifier.
607      *
608      * @param titleId the title's text resource identifier
609      */
setTitle(@tringRes int titleId)610     public void setTitle(@StringRes int titleId) {
611         setTitle(mContext.getText(titleId));
612     }
613 
614     /**
615      * A key was pressed down.
616      * <p>
617      * If the focused view didn't want this event, this method is called.
618      * <p>
619      * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
620      * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
621      * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
622      *
623      * @see #onKeyUp
624      * @see android.view.KeyEvent
625      */
626     @Override
onKeyDown(int keyCode, @NonNull KeyEvent event)627     public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
628         if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
629             event.startTracking();
630             return true;
631         }
632 
633         return false;
634     }
635 
636     /**
637      * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
638      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
639      * the event).
640      */
641     @Override
onKeyLongPress(int keyCode, @NonNull KeyEvent event)642     public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
643         return false;
644     }
645 
646     /**
647      * A key was released.
648      * <p>
649      * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
650      * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
651      * KEYCODE_ESCAPE} to close the dialog.
652      *
653      * @see #onKeyDown
654      * @see android.view.KeyEvent
655      */
656     @Override
onKeyUp(int keyCode, @NonNull KeyEvent event)657     public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
658         if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
659                 && event.isTracking()
660                 && !event.isCanceled()) {
661             onBackPressed();
662             return true;
663         }
664         return false;
665     }
666 
667     /**
668      * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
669      * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
670      * the event).
671      */
672     @Override
onKeyMultiple(int keyCode, int repeatCount, @NonNull KeyEvent event)673     public boolean onKeyMultiple(int keyCode, int repeatCount, @NonNull KeyEvent event) {
674         return false;
675     }
676 
677     /**
678      * Called when the dialog has detected the user's press of the back
679      * key.  The default implementation simply cancels the dialog (only if
680      * it is cancelable), but you can override this to do whatever you want.
681      */
onBackPressed()682     public void onBackPressed() {
683         if (mCancelable) {
684             cancel();
685         }
686     }
687 
688     /**
689      * Called when a key shortcut event is not handled by any of the views in the Dialog.
690      * Override this method to implement global key shortcuts for the Dialog.
691      * Key shortcuts can also be implemented by setting the
692      * {@link MenuItem#setShortcut(char, char) shortcut} property of menu items.
693      *
694      * @param keyCode The value in event.getKeyCode().
695      * @param event Description of the key event.
696      * @return True if the key shortcut was handled.
697      */
onKeyShortcut(int keyCode, @NonNull KeyEvent event)698     public boolean onKeyShortcut(int keyCode, @NonNull KeyEvent event) {
699         return false;
700     }
701 
702     /**
703      * Called when a touch screen event was not handled by any of the views
704      * under it. This is most useful to process touch events that happen outside
705      * of your window bounds, where there is no view to receive it.
706      *
707      * @param event The touch screen event being processed.
708      * @return Return true if you have consumed the event, false if you haven't.
709      *         The default implementation will cancel the dialog when a touch
710      *         happens outside of the window bounds.
711      */
onTouchEvent(@onNull MotionEvent event)712     public boolean onTouchEvent(@NonNull MotionEvent event) {
713         if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) {
714             cancel();
715             return true;
716         }
717 
718         return false;
719     }
720 
721     /**
722      * Called when the trackball was moved and not handled by any of the
723      * views inside of the activity.  So, for example, if the trackball moves
724      * while focus is on a button, you will receive a call here because
725      * buttons do not normally do anything with trackball events.  The call
726      * here happens <em>before</em> trackball movements are converted to
727      * DPAD key events, which then get sent back to the view hierarchy, and
728      * will be processed at the point for things like focus navigation.
729      *
730      * @param event The trackball event being processed.
731      *
732      * @return Return true if you have consumed the event, false if you haven't.
733      * The default implementation always returns false.
734      */
onTrackballEvent(@onNull MotionEvent event)735     public boolean onTrackballEvent(@NonNull MotionEvent event) {
736         return false;
737     }
738 
739     /**
740      * Called when a generic motion event was not handled by any of the
741      * views inside of the dialog.
742      * <p>
743      * Generic motion events describe joystick movements, mouse hovers, track pad
744      * touches, scroll wheel movements and other input events.  The
745      * {@link MotionEvent#getSource() source} of the motion event specifies
746      * the class of input that was received.  Implementations of this method
747      * must examine the bits in the source before processing the event.
748      * The following code example shows how this is done.
749      * </p><p>
750      * Generic motion events with source class
751      * {@link android.view.InputDevice#SOURCE_CLASS_POINTER}
752      * are delivered to the view under the pointer.  All other generic motion events are
753      * delivered to the focused view.
754      * </p><p>
755      * See {@link View#onGenericMotionEvent(MotionEvent)} for an example of how to
756      * handle this event.
757      * </p>
758      *
759      * @param event The generic motion event being processed.
760      *
761      * @return Return true if you have consumed the event, false if you haven't.
762      * The default implementation always returns false.
763      */
onGenericMotionEvent(@onNull MotionEvent event)764     public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
765         return false;
766     }
767 
768     @Override
onWindowAttributesChanged(WindowManager.LayoutParams params)769     public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
770         if (mDecor != null) {
771             mWindowManager.updateViewLayout(mDecor, params);
772         }
773     }
774 
775     @Override
onContentChanged()776     public void onContentChanged() {
777     }
778 
779     @Override
onWindowFocusChanged(boolean hasFocus)780     public void onWindowFocusChanged(boolean hasFocus) {
781     }
782 
783     @Override
onAttachedToWindow()784     public void onAttachedToWindow() {
785     }
786 
787     @Override
onDetachedFromWindow()788     public void onDetachedFromWindow() {
789     }
790 
791     /** @hide */
792     @Override
onWindowDismissed(boolean finishTask, boolean suppressWindowTransition)793     public void onWindowDismissed(boolean finishTask, boolean suppressWindowTransition) {
794         dismiss();
795     }
796 
797     /**
798      * Called to process key events.  You can override this to intercept all
799      * key events before they are dispatched to the window.  Be sure to call
800      * this implementation for key events that should be handled normally.
801      *
802      * @param event The key event.
803      *
804      * @return boolean Return true if this event was consumed.
805      */
806     @Override
dispatchKeyEvent(@onNull KeyEvent event)807     public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
808         if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) {
809             return true;
810         }
811         if (mWindow.superDispatchKeyEvent(event)) {
812             return true;
813         }
814         return event.dispatch(this, mDecor != null
815                 ? mDecor.getKeyDispatcherState() : null, this);
816     }
817 
818     /**
819      * Called to process a key shortcut event.
820      * You can override this to intercept all key shortcut events before they are
821      * dispatched to the window.  Be sure to call this implementation for key shortcut
822      * events that should be handled normally.
823      *
824      * @param event The key shortcut event.
825      * @return True if this event was consumed.
826      */
827     @Override
dispatchKeyShortcutEvent(@onNull KeyEvent event)828     public boolean dispatchKeyShortcutEvent(@NonNull KeyEvent event) {
829         if (mWindow.superDispatchKeyShortcutEvent(event)) {
830             return true;
831         }
832         return onKeyShortcut(event.getKeyCode(), event);
833     }
834 
835     /**
836      * Called to process touch screen events.  You can override this to
837      * intercept all touch screen events before they are dispatched to the
838      * window.  Be sure to call this implementation for touch screen events
839      * that should be handled normally.
840      *
841      * @param ev The touch screen event.
842      *
843      * @return boolean Return true if this event was consumed.
844      */
845     @Override
dispatchTouchEvent(@onNull MotionEvent ev)846     public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
847         if (mWindow.superDispatchTouchEvent(ev)) {
848             return true;
849         }
850         return onTouchEvent(ev);
851     }
852 
853     /**
854      * Called to process trackball events.  You can override this to
855      * intercept all trackball events before they are dispatched to the
856      * window.  Be sure to call this implementation for trackball events
857      * that should be handled normally.
858      *
859      * @param ev The trackball event.
860      *
861      * @return boolean Return true if this event was consumed.
862      */
863     @Override
dispatchTrackballEvent(@onNull MotionEvent ev)864     public boolean dispatchTrackballEvent(@NonNull MotionEvent ev) {
865         if (mWindow.superDispatchTrackballEvent(ev)) {
866             return true;
867         }
868         return onTrackballEvent(ev);
869     }
870 
871     /**
872      * Called to process generic motion events.  You can override this to
873      * intercept all generic motion events before they are dispatched to the
874      * window.  Be sure to call this implementation for generic motion events
875      * that should be handled normally.
876      *
877      * @param ev The generic motion event.
878      *
879      * @return boolean Return true if this event was consumed.
880      */
881     @Override
dispatchGenericMotionEvent(@onNull MotionEvent ev)882     public boolean dispatchGenericMotionEvent(@NonNull MotionEvent ev) {
883         if (mWindow.superDispatchGenericMotionEvent(ev)) {
884             return true;
885         }
886         return onGenericMotionEvent(ev);
887     }
888 
889     @Override
dispatchPopulateAccessibilityEvent(@onNull AccessibilityEvent event)890     public boolean dispatchPopulateAccessibilityEvent(@NonNull AccessibilityEvent event) {
891         event.setClassName(getClass().getName());
892         event.setPackageName(mContext.getPackageName());
893 
894         LayoutParams params = getWindow().getAttributes();
895         boolean isFullScreen = (params.width == LayoutParams.MATCH_PARENT) &&
896             (params.height == LayoutParams.MATCH_PARENT);
897         event.setFullScreen(isFullScreen);
898 
899         return false;
900     }
901 
902     /**
903      * @see Activity#onCreatePanelView(int)
904      */
905     @Override
onCreatePanelView(int featureId)906     public View onCreatePanelView(int featureId) {
907         return null;
908     }
909 
910     /**
911      * @see Activity#onCreatePanelMenu(int, Menu)
912      */
913     @Override
onCreatePanelMenu(int featureId, @NonNull Menu menu)914     public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
915         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
916             return onCreateOptionsMenu(menu);
917         }
918 
919         return false;
920     }
921 
922     /**
923      * @see Activity#onPreparePanel(int, View, Menu)
924      */
925     @Override
onPreparePanel(int featureId, View view, Menu menu)926     public boolean onPreparePanel(int featureId, View view, Menu menu) {
927         if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
928             return onPrepareOptionsMenu(menu) && menu.hasVisibleItems();
929         }
930         return true;
931     }
932 
933     /**
934      * @see Activity#onMenuOpened(int, Menu)
935      */
936     @Override
onMenuOpened(int featureId, Menu menu)937     public boolean onMenuOpened(int featureId, Menu menu) {
938         if (featureId == Window.FEATURE_ACTION_BAR) {
939             mActionBar.dispatchMenuVisibilityChanged(true);
940         }
941         return true;
942     }
943 
944     /**
945      * @see Activity#onMenuItemSelected(int, MenuItem)
946      */
947     @Override
onMenuItemSelected(int featureId, MenuItem item)948     public boolean onMenuItemSelected(int featureId, MenuItem item) {
949         return false;
950     }
951 
952     /**
953      * @see Activity#onPanelClosed(int, Menu)
954      */
955     @Override
onPanelClosed(int featureId, Menu menu)956     public void onPanelClosed(int featureId, Menu menu) {
957         if (featureId == Window.FEATURE_ACTION_BAR) {
958             mActionBar.dispatchMenuVisibilityChanged(false);
959         }
960     }
961 
962     /**
963      * It is usually safe to proxy this call to the owner activity's
964      * {@link Activity#onCreateOptionsMenu(Menu)} if the client desires the same
965      * menu for this Dialog.
966      *
967      * @see Activity#onCreateOptionsMenu(Menu)
968      * @see #getOwnerActivity()
969      */
onCreateOptionsMenu(@onNull Menu menu)970     public boolean onCreateOptionsMenu(@NonNull Menu menu) {
971         return true;
972     }
973 
974     /**
975      * It is usually safe to proxy this call to the owner activity's
976      * {@link Activity#onPrepareOptionsMenu(Menu)} if the client desires the
977      * same menu for this Dialog.
978      *
979      * @see Activity#onPrepareOptionsMenu(Menu)
980      * @see #getOwnerActivity()
981      */
onPrepareOptionsMenu(@onNull Menu menu)982     public boolean onPrepareOptionsMenu(@NonNull Menu menu) {
983         return true;
984     }
985 
986     /**
987      * @see Activity#onOptionsItemSelected(MenuItem)
988      */
onOptionsItemSelected(@onNull MenuItem item)989     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
990         return false;
991     }
992 
993     /**
994      * @see Activity#onOptionsMenuClosed(Menu)
995      */
onOptionsMenuClosed(@onNull Menu menu)996     public void onOptionsMenuClosed(@NonNull Menu menu) {
997     }
998 
999     /**
1000      * @see Activity#openOptionsMenu()
1001      */
openOptionsMenu()1002     public void openOptionsMenu() {
1003         if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
1004             mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
1005         }
1006     }
1007 
1008     /**
1009      * @see Activity#closeOptionsMenu()
1010      */
closeOptionsMenu()1011     public void closeOptionsMenu() {
1012         if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
1013             mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1014         }
1015     }
1016 
1017     /**
1018      * @see Activity#invalidateOptionsMenu()
1019      */
invalidateOptionsMenu()1020     public void invalidateOptionsMenu() {
1021         if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
1022             mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
1023         }
1024     }
1025 
1026     /**
1027      * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
1028      */
1029     @Override
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)1030     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
1031     }
1032 
1033     /**
1034      * @see Activity#registerForContextMenu(View)
1035      */
registerForContextMenu(@onNull View view)1036     public void registerForContextMenu(@NonNull View view) {
1037         view.setOnCreateContextMenuListener(this);
1038     }
1039 
1040     /**
1041      * @see Activity#unregisterForContextMenu(View)
1042      */
unregisterForContextMenu(@onNull View view)1043     public void unregisterForContextMenu(@NonNull View view) {
1044         view.setOnCreateContextMenuListener(null);
1045     }
1046 
1047     /**
1048      * @see Activity#openContextMenu(View)
1049      */
openContextMenu(@onNull View view)1050     public void openContextMenu(@NonNull View view) {
1051         view.showContextMenu();
1052     }
1053 
1054     /**
1055      * @see Activity#onContextItemSelected(MenuItem)
1056      */
onContextItemSelected(@onNull MenuItem item)1057     public boolean onContextItemSelected(@NonNull MenuItem item) {
1058         return false;
1059     }
1060 
1061     /**
1062      * @see Activity#onContextMenuClosed(Menu)
1063      */
onContextMenuClosed(@onNull Menu menu)1064     public void onContextMenuClosed(@NonNull Menu menu) {
1065     }
1066 
1067     /**
1068      * This hook is called when the user signals the desire to start a search.
1069      */
1070     @Override
onSearchRequested(@onNull SearchEvent searchEvent)1071     public boolean onSearchRequested(@NonNull SearchEvent searchEvent) {
1072         mSearchEvent = searchEvent;
1073         return onSearchRequested();
1074     }
1075 
1076     /**
1077      * This hook is called when the user signals the desire to start a search.
1078      */
1079     @Override
onSearchRequested()1080     public boolean onSearchRequested() {
1081         final SearchManager searchManager = (SearchManager) mContext
1082                 .getSystemService(Context.SEARCH_SERVICE);
1083 
1084         // associate search with owner activity
1085         final ComponentName appName = getAssociatedActivity();
1086         if (appName != null && searchManager.getSearchableInfo(appName) != null) {
1087             searchManager.startSearch(null, false, appName, null, false);
1088             dismiss();
1089             return true;
1090         } else {
1091             return false;
1092         }
1093     }
1094 
1095     /**
1096      * During the onSearchRequested() callbacks, this function will return the
1097      * {@link SearchEvent} that triggered the callback, if it exists.
1098      *
1099      * @return SearchEvent The SearchEvent that triggered the {@link
1100      *                    #onSearchRequested} callback.
1101      */
getSearchEvent()1102     public final @Nullable SearchEvent getSearchEvent() {
1103         return mSearchEvent;
1104     }
1105 
1106     @Override
onWindowStartingActionMode(ActionMode.Callback callback)1107     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
1108         if (mActionBar != null && mActionModeTypeStarting == ActionMode.TYPE_PRIMARY) {
1109             return mActionBar.startActionMode(callback);
1110         }
1111         return null;
1112     }
1113 
1114     @Override
onWindowStartingActionMode(ActionMode.Callback callback, int type)1115     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
1116         try {
1117             mActionModeTypeStarting = type;
1118             return onWindowStartingActionMode(callback);
1119         } finally {
1120             mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
1121         }
1122     }
1123 
1124     /**
1125      * {@inheritDoc}
1126      *
1127      * Note that if you override this method you should always call through
1128      * to the superclass implementation by calling super.onActionModeStarted(mode).
1129      */
1130     @Override
1131     @CallSuper
onActionModeStarted(ActionMode mode)1132     public void onActionModeStarted(ActionMode mode) {
1133         mActionMode = mode;
1134     }
1135 
1136     /**
1137      * {@inheritDoc}
1138      *
1139      * Note that if you override this method you should always call through
1140      * to the superclass implementation by calling super.onActionModeFinished(mode).
1141      */
1142     @Override
1143     @CallSuper
onActionModeFinished(ActionMode mode)1144     public void onActionModeFinished(ActionMode mode) {
1145         if (mode == mActionMode) {
1146             mActionMode = null;
1147         }
1148     }
1149 
1150     /**
1151      * @return The activity associated with this dialog, or null if there is no associated activity.
1152      */
getAssociatedActivity()1153     private ComponentName getAssociatedActivity() {
1154         Activity activity = mOwnerActivity;
1155         Context context = getContext();
1156         while (activity == null && context != null) {
1157             if (context instanceof Activity) {
1158                 activity = (Activity) context;  // found it!
1159             } else {
1160                 context = (context instanceof ContextWrapper) ?
1161                         ((ContextWrapper) context).getBaseContext() : // unwrap one level
1162                         null;                                         // done
1163             }
1164         }
1165         return activity == null ? null : activity.getComponentName();
1166     }
1167 
1168 
1169     /**
1170      * Request that key events come to this dialog. Use this if your
1171      * dialog has no views with focus, but the dialog still wants
1172      * a chance to process key events.
1173      *
1174      * @param get true if the dialog should receive key events, false otherwise
1175      * @see android.view.Window#takeKeyEvents
1176      */
takeKeyEvents(boolean get)1177     public void takeKeyEvents(boolean get) {
1178         mWindow.takeKeyEvents(get);
1179     }
1180 
1181     /**
1182      * Enable extended window features.  This is a convenience for calling
1183      * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
1184      *
1185      * @param featureId The desired feature as defined in
1186      *                  {@link android.view.Window}.
1187      * @return Returns true if the requested feature is supported and now
1188      *         enabled.
1189      *
1190      * @see android.view.Window#requestFeature
1191      */
requestWindowFeature(int featureId)1192     public final boolean requestWindowFeature(int featureId) {
1193         return getWindow().requestFeature(featureId);
1194     }
1195 
1196     /**
1197      * Convenience for calling
1198      * {@link android.view.Window#setFeatureDrawableResource}.
1199      */
setFeatureDrawableResource(int featureId, @DrawableRes int resId)1200     public final void setFeatureDrawableResource(int featureId, @DrawableRes int resId) {
1201         getWindow().setFeatureDrawableResource(featureId, resId);
1202     }
1203 
1204     /**
1205      * Convenience for calling
1206      * {@link android.view.Window#setFeatureDrawableUri}.
1207      */
setFeatureDrawableUri(int featureId, @Nullable Uri uri)1208     public final void setFeatureDrawableUri(int featureId, @Nullable Uri uri) {
1209         getWindow().setFeatureDrawableUri(featureId, uri);
1210     }
1211 
1212     /**
1213      * Convenience for calling
1214      * {@link android.view.Window#setFeatureDrawable(int, Drawable)}.
1215      */
setFeatureDrawable(int featureId, @Nullable Drawable drawable)1216     public final void setFeatureDrawable(int featureId, @Nullable Drawable drawable) {
1217         getWindow().setFeatureDrawable(featureId, drawable);
1218     }
1219 
1220     /**
1221      * Convenience for calling
1222      * {@link android.view.Window#setFeatureDrawableAlpha}.
1223      */
setFeatureDrawableAlpha(int featureId, int alpha)1224     public final void setFeatureDrawableAlpha(int featureId, int alpha) {
1225         getWindow().setFeatureDrawableAlpha(featureId, alpha);
1226     }
1227 
getLayoutInflater()1228     public @NonNull LayoutInflater getLayoutInflater() {
1229         return getWindow().getLayoutInflater();
1230     }
1231 
1232     /**
1233      * Sets whether this dialog is cancelable with the
1234      * {@link KeyEvent#KEYCODE_BACK BACK} key.
1235      */
setCancelable(boolean flag)1236     public void setCancelable(boolean flag) {
1237         mCancelable = flag;
1238         updateWindowForCancelable();
1239     }
1240 
1241     /**
1242      * Sets whether this dialog is canceled when touched outside the window's
1243      * bounds. If setting to true, the dialog is set to be cancelable if not
1244      * already set.
1245      *
1246      * @param cancel Whether the dialog should be canceled when touched outside
1247      *            the window.
1248      */
setCanceledOnTouchOutside(boolean cancel)1249     public void setCanceledOnTouchOutside(boolean cancel) {
1250         if (cancel && !mCancelable) {
1251             mCancelable = true;
1252             updateWindowForCancelable();
1253         }
1254 
1255         mWindow.setCloseOnTouchOutside(cancel);
1256     }
1257 
1258     /**
1259      * Cancel the dialog.  This is essentially the same as calling {@link #dismiss()}, but it will
1260      * also call your {@link DialogInterface.OnCancelListener} (if registered).
1261      */
1262     @Override
cancel()1263     public void cancel() {
1264         if (!mCanceled && mCancelMessage != null) {
1265             mCanceled = true;
1266             // Obtain a new message so this dialog can be re-used
1267             Message.obtain(mCancelMessage).sendToTarget();
1268         }
1269         dismiss();
1270     }
1271 
1272     /**
1273      * Set a listener to be invoked when the dialog is canceled.
1274      *
1275      * <p>This will only be invoked when the dialog is canceled.
1276      * Cancel events alone will not capture all ways that
1277      * the dialog might be dismissed. If the creator needs
1278      * to know when a dialog is dismissed in general, use
1279      * {@link #setOnDismissListener}.</p>
1280      *
1281      * @param listener The {@link DialogInterface.OnCancelListener} to use.
1282      */
setOnCancelListener(@ullable OnCancelListener listener)1283     public void setOnCancelListener(@Nullable OnCancelListener listener) {
1284         if (mCancelAndDismissTaken != null) {
1285             throw new IllegalStateException(
1286                     "OnCancelListener is already taken by "
1287                     + mCancelAndDismissTaken + " and can not be replaced.");
1288         }
1289         if (listener != null) {
1290             mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
1291         } else {
1292             mCancelMessage = null;
1293         }
1294     }
1295 
1296     /**
1297      * Set a message to be sent when the dialog is canceled.
1298      * @param msg The msg to send when the dialog is canceled.
1299      * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener)
1300      */
setCancelMessage(@ullable Message msg)1301     public void setCancelMessage(@Nullable Message msg) {
1302         mCancelMessage = msg;
1303     }
1304 
1305     /**
1306      * Set a listener to be invoked when the dialog is dismissed.
1307      * @param listener The {@link DialogInterface.OnDismissListener} to use.
1308      */
setOnDismissListener(@ullable OnDismissListener listener)1309     public void setOnDismissListener(@Nullable OnDismissListener listener) {
1310         if (mCancelAndDismissTaken != null) {
1311             throw new IllegalStateException(
1312                     "OnDismissListener is already taken by "
1313                     + mCancelAndDismissTaken + " and can not be replaced.");
1314         }
1315         if (listener != null) {
1316             mDismissMessage = mListenersHandler.obtainMessage(DISMISS, listener);
1317         } else {
1318             mDismissMessage = null;
1319         }
1320     }
1321 
1322     /**
1323      * Sets a listener to be invoked when the dialog is shown.
1324      * @param listener The {@link DialogInterface.OnShowListener} to use.
1325      */
setOnShowListener(@ullable OnShowListener listener)1326     public void setOnShowListener(@Nullable OnShowListener listener) {
1327         if (listener != null) {
1328             mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
1329         } else {
1330             mShowMessage = null;
1331         }
1332     }
1333 
1334     /**
1335      * Set a message to be sent when the dialog is dismissed.
1336      * @param msg The msg to send when the dialog is dismissed.
1337      */
setDismissMessage(@ullable Message msg)1338     public void setDismissMessage(@Nullable Message msg) {
1339         mDismissMessage = msg;
1340     }
1341 
1342     /** @hide */
takeCancelAndDismissListeners(@ullable String msg, @Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss)1343     public boolean takeCancelAndDismissListeners(@Nullable String msg,
1344             @Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss) {
1345         if (mCancelAndDismissTaken != null) {
1346             mCancelAndDismissTaken = null;
1347         } else if (mCancelMessage != null || mDismissMessage != null) {
1348             return false;
1349         }
1350 
1351         setOnCancelListener(cancel);
1352         setOnDismissListener(dismiss);
1353         mCancelAndDismissTaken = msg;
1354 
1355         return true;
1356     }
1357 
1358     /**
1359      * By default, this will use the owner Activity's suggested stream type.
1360      *
1361      * @see Activity#setVolumeControlStream(int)
1362      * @see #setOwnerActivity(Activity)
1363      */
setVolumeControlStream(int streamType)1364     public final void setVolumeControlStream(int streamType) {
1365         getWindow().setVolumeControlStream(streamType);
1366     }
1367 
1368     /**
1369      * @see Activity#getVolumeControlStream()
1370      */
getVolumeControlStream()1371     public final int getVolumeControlStream() {
1372         return getWindow().getVolumeControlStream();
1373     }
1374 
1375     /**
1376      * Sets the callback that will be called if a key is dispatched to the dialog.
1377      */
setOnKeyListener(@ullable OnKeyListener onKeyListener)1378     public void setOnKeyListener(@Nullable OnKeyListener onKeyListener) {
1379         mOnKeyListener = onKeyListener;
1380     }
1381 
1382     private static final class ListenersHandler extends Handler {
1383         private final WeakReference<DialogInterface> mDialog;
1384 
ListenersHandler(Dialog dialog)1385         public ListenersHandler(Dialog dialog) {
1386             mDialog = new WeakReference<>(dialog);
1387         }
1388 
1389         @Override
handleMessage(Message msg)1390         public void handleMessage(Message msg) {
1391             switch (msg.what) {
1392                 case DISMISS:
1393                     ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
1394                     break;
1395                 case CANCEL:
1396                     ((OnCancelListener) msg.obj).onCancel(mDialog.get());
1397                     break;
1398                 case SHOW:
1399                     ((OnShowListener) msg.obj).onShow(mDialog.get());
1400                     break;
1401             }
1402         }
1403     }
1404 
updateWindowForCancelable()1405     private void updateWindowForCancelable() {
1406         mWindow.setCloseOnSwipeEnabled(mCancelable);
1407     }
1408 }
1409