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