• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.internal.policy.PolicyManager;
20 
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.ComponentName;
24 import android.content.ContextWrapper;
25 import android.graphics.drawable.Drawable;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.view.ContextMenu;
31 import android.view.ContextThemeWrapper;
32 import android.view.Gravity;
33 import android.view.KeyEvent;
34 import android.view.LayoutInflater;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.view.MotionEvent;
38 import android.view.View;
39 import android.view.ViewConfiguration;
40 import android.view.ViewGroup;
41 import android.view.Window;
42 import android.view.WindowManager;
43 import android.view.ContextMenu.ContextMenuInfo;
44 import android.view.View.OnCreateContextMenuListener;
45 import android.view.ViewGroup.LayoutParams;
46 import android.view.accessibility.AccessibilityEvent;
47 
48 import java.lang.ref.WeakReference;
49 
50 /**
51  * Base class for Dialogs.
52  *
53  * <p>Note: Activities provide a facility to manage the creation, saving and
54  * restoring of dialogs. See {@link Activity#onCreateDialog(int)},
55  * {@link Activity#onPrepareDialog(int, Dialog)},
56  * {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If
57  * these methods are used, {@link #getOwnerActivity()} will return the Activity
58  * that managed this dialog.
59  *
60  * <p>Often you will want to have a Dialog display on top of the current
61  * input method, because there is no reason for it to accept text.  You can
62  * do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
63  * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming
64  * your Dialog takes input focus, as it the default) with the following code:
65  *
66  * <pre>
67  *     getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
68  *             WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
69  * </pre>
70  */
71 public class Dialog implements DialogInterface, Window.Callback,
72         KeyEvent.Callback, OnCreateContextMenuListener {
73     private Activity mOwnerActivity;
74 
75     final Context mContext;
76     final WindowManager mWindowManager;
77     Window mWindow;
78     View mDecor;
79     /**
80      * This field should be made private, so it is hidden from the SDK.
81      * {@hide}
82      */
83     protected boolean mCancelable = true;
84 
85     private Message mCancelMessage;
86     private Message mDismissMessage;
87     private Message mShowMessage;
88 
89     /**
90      * Whether to cancel the dialog when a touch is received outside of the
91      * window's bounds.
92      */
93     private boolean mCanceledOnTouchOutside = false;
94 
95     private OnKeyListener mOnKeyListener;
96 
97     private boolean mCreated = false;
98     private boolean mShowing = false;
99 
100     private final Thread mUiThread;
101     private final Handler mHandler = new Handler();
102 
103     private static final int DISMISS = 0x43;
104     private static final int CANCEL = 0x44;
105     private static final int SHOW = 0x45;
106 
107     private Handler mListenersHandler;
108 
109     private final Runnable mDismissAction = new Runnable() {
110         public void run() {
111             dismissDialog();
112         }
113     };
114 
115     /**
116      * Create a Dialog window that uses the default dialog frame style.
117      *
118      * @param context The Context the Dialog is to run it.  In particular, it
119      *                uses the window manager and theme in this context to
120      *                present its UI.
121      */
Dialog(Context context)122     public Dialog(Context context) {
123         this(context, 0);
124     }
125 
126     /**
127      * Create a Dialog window that uses a custom dialog style.
128      *
129      * @param context The Context in which the Dialog should run. In particular, it
130      *                uses the window manager and theme from this context to
131      *                present its UI.
132      * @param theme A style resource describing the theme to use for the
133      * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style
134      * and Theme Resources</a> for more information about defining and using
135      * styles.  This theme is applied on top of the current theme in
136      * <var>context</var>.  If 0, the default dialog theme will be used.
137      */
Dialog(Context context, int theme)138     public Dialog(Context context, int theme) {
139         mContext = new ContextThemeWrapper(
140             context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);
141         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
142         Window w = PolicyManager.makeNewWindow(mContext);
143         mWindow = w;
144         w.setCallback(this);
145         w.setWindowManager(mWindowManager, null, null);
146         w.setGravity(Gravity.CENTER);
147         mUiThread = Thread.currentThread();
148         mListenersHandler = new ListenersHandler(this);
149     }
150 
151     /**
152      * @deprecated
153      * @hide
154      */
155     @Deprecated
Dialog(Context context, boolean cancelable, Message cancelCallback)156     protected Dialog(Context context, boolean cancelable,
157             Message cancelCallback) {
158         this(context);
159         mCancelable = cancelable;
160         mCancelMessage = cancelCallback;
161     }
162 
Dialog(Context context, boolean cancelable, OnCancelListener cancelListener)163     protected Dialog(Context context, boolean cancelable,
164             OnCancelListener cancelListener) {
165         this(context);
166         mCancelable = cancelable;
167         setOnCancelListener(cancelListener);
168     }
169 
170     /**
171      * Retrieve the Context this Dialog is running in.
172      *
173      * @return Context The Context that was supplied to the constructor.
174      */
getContext()175     public final Context getContext() {
176         return mContext;
177     }
178 
179     /**
180      * Sets the Activity that owns this dialog. An example use: This Dialog will
181      * use the suggested volume control stream of the Activity.
182      *
183      * @param activity The Activity that owns this dialog.
184      */
setOwnerActivity(Activity activity)185     public final void setOwnerActivity(Activity activity) {
186         mOwnerActivity = activity;
187 
188         getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream());
189     }
190 
191     /**
192      * Returns the Activity that owns this Dialog. For example, if
193      * {@link Activity#showDialog(int)} is used to show this Dialog, that
194      * Activity will be the owner (by default). Depending on how this dialog was
195      * created, this may return null.
196      *
197      * @return The Activity that owns this Dialog.
198      */
getOwnerActivity()199     public final Activity getOwnerActivity() {
200         return mOwnerActivity;
201     }
202 
203     /**
204      * @return Whether the dialog is currently showing.
205      */
isShowing()206     public boolean isShowing() {
207         return mShowing;
208     }
209 
210     /**
211      * Start the dialog and display it on screen.  The window is placed in the
212      * application layer and opaque.  Note that you should not override this
213      * method to do initialization when the dialog is shown, instead implement
214      * that in {@link #onStart}.
215      */
show()216     public void show() {
217         if (mShowing) {
218             if (mDecor != null) {
219                 mDecor.setVisibility(View.VISIBLE);
220             }
221             return;
222         }
223 
224         if (!mCreated) {
225             dispatchOnCreate(null);
226         }
227 
228         onStart();
229         mDecor = mWindow.getDecorView();
230         WindowManager.LayoutParams l = mWindow.getAttributes();
231         if ((l.softInputMode
232                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
233             WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
234             nl.copyFrom(l);
235             nl.softInputMode |=
236                     WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
237             l = nl;
238         }
239 
240         try {
241             mWindowManager.addView(mDecor, l);
242             mShowing = true;
243 
244             sendShowMessage();
245         } finally {
246         }
247     }
248 
249     /**
250      * Hide the dialog, but do not dismiss it.
251      */
hide()252     public void hide() {
253         if (mDecor != null) {
254             mDecor.setVisibility(View.GONE);
255         }
256     }
257 
258     /**
259      * Dismiss this dialog, removing it from the screen. This method can be
260      * invoked safely from any thread.  Note that you should not override this
261      * method to do cleanup when the dialog is dismissed, instead implement
262      * that in {@link #onStop}.
263      */
dismiss()264     public void dismiss() {
265         if (Thread.currentThread() != mUiThread) {
266             mHandler.post(mDismissAction);
267         } else {
268             mDismissAction.run();
269         }
270     }
271 
dismissDialog()272     private void dismissDialog() {
273         if (mDecor == null || !mShowing) {
274             return;
275         }
276 
277         try {
278             mWindowManager.removeView(mDecor);
279         } finally {
280             mDecor = null;
281             mWindow.closeAllPanels();
282             onStop();
283             mShowing = false;
284 
285             sendDismissMessage();
286         }
287     }
288 
sendDismissMessage()289     private void sendDismissMessage() {
290         if (mDismissMessage != null) {
291             // Obtain a new message so this dialog can be re-used
292             Message.obtain(mDismissMessage).sendToTarget();
293         }
294     }
295 
sendShowMessage()296     private void sendShowMessage() {
297         if (mShowMessage != null) {
298             // Obtain a new message so this dialog can be re-used
299             Message.obtain(mShowMessage).sendToTarget();
300         }
301     }
302 
303     // internal method to make sure mcreated is set properly without requiring
304     // users to call through to super in onCreate
dispatchOnCreate(Bundle savedInstanceState)305     void dispatchOnCreate(Bundle savedInstanceState) {
306         if (!mCreated) {
307             onCreate(savedInstanceState);
308             mCreated = true;
309         }
310     }
311 
312     /**
313      * Similar to {@link Activity#onCreate}, you should initialized your dialog
314      * in this method, including calling {@link #setContentView}.
315      * @param savedInstanceState If this dialog is being reinitalized after a
316      *     the hosting activity was previously shut down, holds the result from
317      *     the most recent call to {@link #onSaveInstanceState}, or null if this
318      *     is the first time.
319      */
onCreate(Bundle savedInstanceState)320     protected void onCreate(Bundle savedInstanceState) {
321     }
322 
323     /**
324      * Called when the dialog is starting.
325      */
onStart()326     protected void onStart() {
327     }
328 
329     /**
330      * Called to tell you that you're stopping.
331      */
onStop()332     protected void onStop() {
333     }
334 
335     private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
336     private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy";
337 
338     /**
339      * Saves the state of the dialog into a bundle.
340      *
341      * The default implementation saves the state of its view hierarchy, so you'll
342      * likely want to call through to super if you override this to save additional
343      * state.
344      * @return A bundle with the state of the dialog.
345      */
onSaveInstanceState()346     public Bundle onSaveInstanceState() {
347         Bundle bundle = new Bundle();
348         bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
349         if (mCreated) {
350             bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState());
351         }
352         return bundle;
353     }
354 
355     /**
356      * Restore the state of the dialog from a previously saved bundle.
357      *
358      * The default implementation restores the state of the dialog's view
359      * hierarchy that was saved in the default implementation of {@link #onSaveInstanceState()},
360      * so be sure to call through to super when overriding unless you want to
361      * do all restoring of state yourself.
362      * @param savedInstanceState The state of the dialog previously saved by
363      *     {@link #onSaveInstanceState()}.
364      */
onRestoreInstanceState(Bundle savedInstanceState)365     public void onRestoreInstanceState(Bundle savedInstanceState) {
366         final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
367         if (dialogHierarchyState == null) {
368             // dialog has never been shown, or onCreated, nothing to restore.
369             return;
370         }
371         dispatchOnCreate(savedInstanceState);
372         mWindow.restoreHierarchyState(dialogHierarchyState);
373         if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
374             show();
375         }
376     }
377 
378     /**
379      * Retrieve the current Window for the activity.  This can be used to
380      * directly access parts of the Window API that are not available
381      * through Activity/Screen.
382      *
383      * @return Window The current window, or null if the activity is not
384      *         visual.
385      */
getWindow()386     public Window getWindow() {
387         return mWindow;
388     }
389 
390     /**
391      * Call {@link android.view.Window#getCurrentFocus} on the
392      * Window if this Activity to return the currently focused view.
393      *
394      * @return View The current View with focus or null.
395      *
396      * @see #getWindow
397      * @see android.view.Window#getCurrentFocus
398      */
getCurrentFocus()399     public View getCurrentFocus() {
400         return mWindow != null ? mWindow.getCurrentFocus() : null;
401     }
402 
403     /**
404      * Finds a view that was identified by the id attribute from the XML that
405      * was processed in {@link #onStart}.
406      *
407      * @param id the identifier of the view to find
408      * @return The view if found or null otherwise.
409      */
findViewById(int id)410     public View findViewById(int id) {
411         return mWindow.findViewById(id);
412     }
413 
414     /**
415      * Set the screen content from a layout resource.  The resource will be
416      * inflated, adding all top-level views to the screen.
417      *
418      * @param layoutResID Resource ID to be inflated.
419      */
setContentView(int layoutResID)420     public void setContentView(int layoutResID) {
421         mWindow.setContentView(layoutResID);
422     }
423 
424     /**
425      * Set the screen content to an explicit view.  This view is placed
426      * directly into the screen's view hierarchy.  It can itself be a complex
427      * view hierarhcy.
428      *
429      * @param view The desired content to display.
430      */
setContentView(View view)431     public void setContentView(View view) {
432         mWindow.setContentView(view);
433     }
434 
435     /**
436      * Set the screen content to an explicit view.  This view is placed
437      * directly into the screen's view hierarchy.  It can itself be a complex
438      * view hierarhcy.
439      *
440      * @param view The desired content to display.
441      * @param params Layout parameters for the view.
442      */
setContentView(View view, ViewGroup.LayoutParams params)443     public void setContentView(View view, ViewGroup.LayoutParams params) {
444         mWindow.setContentView(view, params);
445     }
446 
447     /**
448      * Add an additional content view to the screen.  Added after any existing
449      * ones in the screen -- existing views are NOT removed.
450      *
451      * @param view The desired content to display.
452      * @param params Layout parameters for the view.
453      */
addContentView(View view, ViewGroup.LayoutParams params)454     public void addContentView(View view, ViewGroup.LayoutParams params) {
455         mWindow.addContentView(view, params);
456     }
457 
458     /**
459      * Set the title text for this dialog's window.
460      *
461      * @param title The new text to display in the title.
462      */
setTitle(CharSequence title)463     public void setTitle(CharSequence title) {
464         mWindow.setTitle(title);
465         mWindow.getAttributes().setTitle(title);
466     }
467 
468     /**
469      * Set the title text for this dialog's window. The text is retrieved
470      * from the resources with the supplied identifier.
471      *
472      * @param titleId the title's text resource identifier
473      */
setTitle(int titleId)474     public void setTitle(int titleId) {
475         setTitle(mContext.getText(titleId));
476     }
477 
478     /**
479      * A key was pressed down.
480      *
481      * <p>If the focused view didn't want this event, this method is called.
482      *
483      * <p>The default implementation consumed the KEYCODE_BACK to later
484      * handle it in {@link #onKeyUp}.
485      *
486      * @see #onKeyUp
487      * @see android.view.KeyEvent
488      */
onKeyDown(int keyCode, KeyEvent event)489     public boolean onKeyDown(int keyCode, KeyEvent event) {
490         if (keyCode == KeyEvent.KEYCODE_BACK) {
491             event.startTracking();
492             return true;
493         }
494 
495         return false;
496     }
497 
498     /**
499      * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
500      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
501      * the event).
502      */
onKeyLongPress(int keyCode, KeyEvent event)503     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
504         return false;
505     }
506 
507     /**
508      * A key was released.
509      *
510      * <p>The default implementation handles KEYCODE_BACK to close the
511      * dialog.
512      *
513      * @see #onKeyDown
514      * @see KeyEvent
515      */
onKeyUp(int keyCode, KeyEvent event)516     public boolean onKeyUp(int keyCode, KeyEvent event) {
517         if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
518                 && !event.isCanceled()) {
519             onBackPressed();
520             return true;
521         }
522         return false;
523     }
524 
525     /**
526      * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
527      * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
528      * the event).
529      */
onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)530     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
531         return false;
532     }
533 
534     /**
535      * Called when the dialog has detected the user's press of the back
536      * key.  The default implementation simply cancels the dialog (only if
537      * it is cancelable), but you can override this to do whatever you want.
538      */
onBackPressed()539     public void onBackPressed() {
540         if (mCancelable) {
541             cancel();
542         }
543     }
544 
545     /**
546      * Called when a touch screen event was not handled by any of the views
547      * under it. This is most useful to process touch events that happen outside
548      * of your window bounds, where there is no view to receive it.
549      *
550      * @param event The touch screen event being processed.
551      * @return Return true if you have consumed the event, false if you haven't.
552      *         The default implementation will cancel the dialog when a touch
553      *         happens outside of the window bounds.
554      */
onTouchEvent(MotionEvent event)555     public boolean onTouchEvent(MotionEvent event) {
556         if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
557                 && isOutOfBounds(event)) {
558             cancel();
559             return true;
560         }
561 
562         return false;
563     }
564 
isOutOfBounds(MotionEvent event)565     private boolean isOutOfBounds(MotionEvent event) {
566         final int x = (int) event.getX();
567         final int y = (int) event.getY();
568         final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
569         final View decorView = getWindow().getDecorView();
570         return (x < -slop) || (y < -slop)
571                 || (x > (decorView.getWidth()+slop))
572                 || (y > (decorView.getHeight()+slop));
573     }
574 
575     /**
576      * Called when the trackball was moved and not handled by any of the
577      * views inside of the activity.  So, for example, if the trackball moves
578      * while focus is on a button, you will receive a call here because
579      * buttons do not normally do anything with trackball events.  The call
580      * here happens <em>before</em> trackball movements are converted to
581      * DPAD key events, which then get sent back to the view hierarchy, and
582      * will be processed at the point for things like focus navigation.
583      *
584      * @param event The trackball event being processed.
585      *
586      * @return Return true if you have consumed the event, false if you haven't.
587      * The default implementation always returns false.
588      */
onTrackballEvent(MotionEvent event)589     public boolean onTrackballEvent(MotionEvent event) {
590         return false;
591     }
592 
onWindowAttributesChanged(WindowManager.LayoutParams params)593     public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
594         if (mDecor != null) {
595             mWindowManager.updateViewLayout(mDecor, params);
596         }
597     }
598 
onContentChanged()599     public void onContentChanged() {
600     }
601 
onWindowFocusChanged(boolean hasFocus)602     public void onWindowFocusChanged(boolean hasFocus) {
603     }
604 
onAttachedToWindow()605     public void onAttachedToWindow() {
606     }
607 
onDetachedFromWindow()608     public void onDetachedFromWindow() {
609     }
610 
611     /**
612      * Called to process key events.  You can override this to intercept all
613      * key events before they are dispatched to the window.  Be sure to call
614      * this implementation for key events that should be handled normally.
615      *
616      * @param event The key event.
617      *
618      * @return boolean Return true if this event was consumed.
619      */
dispatchKeyEvent(KeyEvent event)620     public boolean dispatchKeyEvent(KeyEvent event) {
621         if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) {
622             return true;
623         }
624         if (mWindow.superDispatchKeyEvent(event)) {
625             return true;
626         }
627         return event.dispatch(this, mDecor != null
628                 ? mDecor.getKeyDispatcherState() : null, this);
629     }
630 
631     /**
632      * Called to process touch screen events.  You can override this to
633      * intercept all touch screen events before they are dispatched to the
634      * window.  Be sure to call this implementation for touch screen events
635      * that should be handled normally.
636      *
637      * @param ev The touch screen event.
638      *
639      * @return boolean Return true if this event was consumed.
640      */
dispatchTouchEvent(MotionEvent ev)641     public boolean dispatchTouchEvent(MotionEvent ev) {
642         if (mWindow.superDispatchTouchEvent(ev)) {
643             return true;
644         }
645         return onTouchEvent(ev);
646     }
647 
648     /**
649      * Called to process trackball events.  You can override this to
650      * intercept all trackball events before they are dispatched to the
651      * window.  Be sure to call this implementation for trackball events
652      * that should be handled normally.
653      *
654      * @param ev The trackball event.
655      *
656      * @return boolean Return true if this event was consumed.
657      */
dispatchTrackballEvent(MotionEvent ev)658     public boolean dispatchTrackballEvent(MotionEvent ev) {
659         if (mWindow.superDispatchTrackballEvent(ev)) {
660             return true;
661         }
662         return onTrackballEvent(ev);
663     }
664 
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)665     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
666         event.setClassName(getClass().getName());
667         event.setPackageName(mContext.getPackageName());
668 
669         LayoutParams params = getWindow().getAttributes();
670         boolean isFullScreen = (params.width == LayoutParams.MATCH_PARENT) &&
671             (params.height == LayoutParams.MATCH_PARENT);
672         event.setFullScreen(isFullScreen);
673 
674         return false;
675     }
676 
677     /**
678      * @see Activity#onCreatePanelView(int)
679      */
onCreatePanelView(int featureId)680     public View onCreatePanelView(int featureId) {
681         return null;
682     }
683 
684     /**
685      * @see Activity#onCreatePanelMenu(int, Menu)
686      */
onCreatePanelMenu(int featureId, Menu menu)687     public boolean onCreatePanelMenu(int featureId, Menu menu) {
688         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
689             return onCreateOptionsMenu(menu);
690         }
691 
692         return false;
693     }
694 
695     /**
696      * @see Activity#onPreparePanel(int, View, Menu)
697      */
onPreparePanel(int featureId, View view, Menu menu)698     public boolean onPreparePanel(int featureId, View view, Menu menu) {
699         if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
700             boolean goforit = onPrepareOptionsMenu(menu);
701             return goforit && menu.hasVisibleItems();
702         }
703         return true;
704     }
705 
706     /**
707      * @see Activity#onMenuOpened(int, Menu)
708      */
onMenuOpened(int featureId, Menu menu)709     public boolean onMenuOpened(int featureId, Menu menu) {
710         return true;
711     }
712 
713     /**
714      * @see Activity#onMenuItemSelected(int, MenuItem)
715      */
onMenuItemSelected(int featureId, MenuItem item)716     public boolean onMenuItemSelected(int featureId, MenuItem item) {
717         return false;
718     }
719 
720     /**
721      * @see Activity#onPanelClosed(int, Menu)
722      */
onPanelClosed(int featureId, Menu menu)723     public void onPanelClosed(int featureId, Menu menu) {
724     }
725 
726     /**
727      * It is usually safe to proxy this call to the owner activity's
728      * {@link Activity#onCreateOptionsMenu(Menu)} if the client desires the same
729      * menu for this Dialog.
730      *
731      * @see Activity#onCreateOptionsMenu(Menu)
732      * @see #getOwnerActivity()
733      */
onCreateOptionsMenu(Menu menu)734     public boolean onCreateOptionsMenu(Menu menu) {
735         return true;
736     }
737 
738     /**
739      * It is usually safe to proxy this call to the owner activity's
740      * {@link Activity#onPrepareOptionsMenu(Menu)} if the client desires the
741      * same menu for this Dialog.
742      *
743      * @see Activity#onPrepareOptionsMenu(Menu)
744      * @see #getOwnerActivity()
745      */
onPrepareOptionsMenu(Menu menu)746     public boolean onPrepareOptionsMenu(Menu menu) {
747         return true;
748     }
749 
750     /**
751      * @see Activity#onOptionsItemSelected(MenuItem)
752      */
onOptionsItemSelected(MenuItem item)753     public boolean onOptionsItemSelected(MenuItem item) {
754         return false;
755     }
756 
757     /**
758      * @see Activity#onOptionsMenuClosed(Menu)
759      */
onOptionsMenuClosed(Menu menu)760     public void onOptionsMenuClosed(Menu menu) {
761     }
762 
763     /**
764      * @see Activity#openOptionsMenu()
765      */
openOptionsMenu()766     public void openOptionsMenu() {
767         mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
768     }
769 
770     /**
771      * @see Activity#closeOptionsMenu()
772      */
closeOptionsMenu()773     public void closeOptionsMenu() {
774         mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
775     }
776 
777     /**
778      * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
779      */
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)780     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
781     }
782 
783     /**
784      * @see Activity#registerForContextMenu(View)
785      */
registerForContextMenu(View view)786     public void registerForContextMenu(View view) {
787         view.setOnCreateContextMenuListener(this);
788     }
789 
790     /**
791      * @see Activity#unregisterForContextMenu(View)
792      */
unregisterForContextMenu(View view)793     public void unregisterForContextMenu(View view) {
794         view.setOnCreateContextMenuListener(null);
795     }
796 
797     /**
798      * @see Activity#openContextMenu(View)
799      */
openContextMenu(View view)800     public void openContextMenu(View view) {
801         view.showContextMenu();
802     }
803 
804     /**
805      * @see Activity#onContextItemSelected(MenuItem)
806      */
onContextItemSelected(MenuItem item)807     public boolean onContextItemSelected(MenuItem item) {
808         return false;
809     }
810 
811     /**
812      * @see Activity#onContextMenuClosed(Menu)
813      */
onContextMenuClosed(Menu menu)814     public void onContextMenuClosed(Menu menu) {
815     }
816 
817     /**
818      * This hook is called when the user signals the desire to start a search.
819      */
onSearchRequested()820     public boolean onSearchRequested() {
821         final SearchManager searchManager = (SearchManager) mContext
822                 .getSystemService(Context.SEARCH_SERVICE);
823 
824         // associate search with owner activity
825         final ComponentName appName = getAssociatedActivity();
826         if (appName != null) {
827             searchManager.startSearch(null, false, appName, null, false);
828             dismiss();
829             return true;
830         } else {
831             return false;
832         }
833     }
834 
835     /**
836      * @return The activity associated with this dialog, or null if there is no assocaited activity.
837      */
getAssociatedActivity()838     private ComponentName getAssociatedActivity() {
839         Activity activity = mOwnerActivity;
840         Context context = getContext();
841         while (activity == null && context != null) {
842             if (context instanceof Activity) {
843                 activity = (Activity) context;  // found it!
844             } else {
845                 context = (context instanceof ContextWrapper) ?
846                         ((ContextWrapper) context).getBaseContext() : // unwrap one level
847                         null;                                         // done
848             }
849         }
850         return activity == null ? null : activity.getComponentName();
851     }
852 
853 
854     /**
855      * Request that key events come to this dialog. Use this if your
856      * dialog has no views with focus, but the dialog still wants
857      * a chance to process key events.
858      *
859      * @param get true if the dialog should receive key events, false otherwise
860      * @see android.view.Window#takeKeyEvents
861      */
takeKeyEvents(boolean get)862     public void takeKeyEvents(boolean get) {
863         mWindow.takeKeyEvents(get);
864     }
865 
866     /**
867      * Enable extended window features.  This is a convenience for calling
868      * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
869      *
870      * @param featureId The desired feature as defined in
871      *                  {@link android.view.Window}.
872      * @return Returns true if the requested feature is supported and now
873      *         enabled.
874      *
875      * @see android.view.Window#requestFeature
876      */
requestWindowFeature(int featureId)877     public final boolean requestWindowFeature(int featureId) {
878         return getWindow().requestFeature(featureId);
879     }
880 
881     /**
882      * Convenience for calling
883      * {@link android.view.Window#setFeatureDrawableResource}.
884      */
setFeatureDrawableResource(int featureId, int resId)885     public final void setFeatureDrawableResource(int featureId, int resId) {
886         getWindow().setFeatureDrawableResource(featureId, resId);
887     }
888 
889     /**
890      * Convenience for calling
891      * {@link android.view.Window#setFeatureDrawableUri}.
892      */
setFeatureDrawableUri(int featureId, Uri uri)893     public final void setFeatureDrawableUri(int featureId, Uri uri) {
894         getWindow().setFeatureDrawableUri(featureId, uri);
895     }
896 
897     /**
898      * Convenience for calling
899      * {@link android.view.Window#setFeatureDrawable(int, Drawable)}.
900      */
setFeatureDrawable(int featureId, Drawable drawable)901     public final void setFeatureDrawable(int featureId, Drawable drawable) {
902         getWindow().setFeatureDrawable(featureId, drawable);
903     }
904 
905     /**
906      * Convenience for calling
907      * {@link android.view.Window#setFeatureDrawableAlpha}.
908      */
setFeatureDrawableAlpha(int featureId, int alpha)909     public final void setFeatureDrawableAlpha(int featureId, int alpha) {
910         getWindow().setFeatureDrawableAlpha(featureId, alpha);
911     }
912 
getLayoutInflater()913     public LayoutInflater getLayoutInflater() {
914         return getWindow().getLayoutInflater();
915     }
916 
917     /**
918      * Sets whether this dialog is cancelable with the
919      * {@link KeyEvent#KEYCODE_BACK BACK} key.
920      */
setCancelable(boolean flag)921     public void setCancelable(boolean flag) {
922         mCancelable = flag;
923     }
924 
925     /**
926      * Sets whether this dialog is canceled when touched outside the window's
927      * bounds. If setting to true, the dialog is set to be cancelable if not
928      * already set.
929      *
930      * @param cancel Whether the dialog should be canceled when touched outside
931      *            the window.
932      */
setCanceledOnTouchOutside(boolean cancel)933     public void setCanceledOnTouchOutside(boolean cancel) {
934         if (cancel && !mCancelable) {
935             mCancelable = true;
936         }
937 
938         mCanceledOnTouchOutside = cancel;
939     }
940 
941     /**
942      * Cancel the dialog.  This is essentially the same as calling {@link #dismiss()}, but it will
943      * also call your {@link DialogInterface.OnCancelListener} (if registered).
944      */
cancel()945     public void cancel() {
946         if (mCancelMessage != null) {
947 
948             // Obtain a new message so this dialog can be re-used
949             Message.obtain(mCancelMessage).sendToTarget();
950         }
951         dismiss();
952     }
953 
954     /**
955      * Set a listener to be invoked when the dialog is canceled.
956      * <p>
957      * This will only be invoked when the dialog is canceled, if the creator
958      * needs to know when it is dismissed in general, use
959      * {@link #setOnDismissListener}.
960      *
961      * @param listener The {@link DialogInterface.OnCancelListener} to use.
962      */
setOnCancelListener(final OnCancelListener listener)963     public void setOnCancelListener(final OnCancelListener listener) {
964         if (listener != null) {
965             mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
966         } else {
967             mCancelMessage = null;
968         }
969     }
970 
971     /**
972      * Set a message to be sent when the dialog is canceled.
973      * @param msg The msg to send when the dialog is canceled.
974      * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener)
975      */
setCancelMessage(final Message msg)976     public void setCancelMessage(final Message msg) {
977         mCancelMessage = msg;
978     }
979 
980     /**
981      * Set a listener to be invoked when the dialog is dismissed.
982      * @param listener The {@link DialogInterface.OnDismissListener} to use.
983      */
setOnDismissListener(final OnDismissListener listener)984     public void setOnDismissListener(final OnDismissListener listener) {
985         if (listener != null) {
986             mDismissMessage = mListenersHandler.obtainMessage(DISMISS, listener);
987         } else {
988             mDismissMessage = null;
989         }
990     }
991 
992     /**
993      * Sets a listener to be invoked when the dialog is shown.
994      * @param listener The {@link DialogInterface.OnShowListener} to use.
995      */
setOnShowListener(OnShowListener listener)996     public void setOnShowListener(OnShowListener listener) {
997         if (listener != null) {
998             mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
999         } else {
1000             mShowMessage = null;
1001         }
1002     }
1003 
1004     /**
1005      * Set a message to be sent when the dialog is dismissed.
1006      * @param msg The msg to send when the dialog is dismissed.
1007      */
setDismissMessage(final Message msg)1008     public void setDismissMessage(final Message msg) {
1009         mDismissMessage = msg;
1010     }
1011 
1012     /**
1013      * By default, this will use the owner Activity's suggested stream type.
1014      *
1015      * @see Activity#setVolumeControlStream(int)
1016      * @see #setOwnerActivity(Activity)
1017      */
setVolumeControlStream(int streamType)1018     public final void setVolumeControlStream(int streamType) {
1019         getWindow().setVolumeControlStream(streamType);
1020     }
1021 
1022     /**
1023      * @see Activity#getVolumeControlStream()
1024      */
getVolumeControlStream()1025     public final int getVolumeControlStream() {
1026         return getWindow().getVolumeControlStream();
1027     }
1028 
1029     /**
1030      * Sets the callback that will be called if a key is dispatched to the dialog.
1031      */
setOnKeyListener(final OnKeyListener onKeyListener)1032     public void setOnKeyListener(final OnKeyListener onKeyListener) {
1033         mOnKeyListener = onKeyListener;
1034     }
1035 
1036     private static final class ListenersHandler extends Handler {
1037         private WeakReference<DialogInterface> mDialog;
1038 
ListenersHandler(Dialog dialog)1039         public ListenersHandler(Dialog dialog) {
1040             mDialog = new WeakReference<DialogInterface>(dialog);
1041         }
1042 
1043         @Override
handleMessage(Message msg)1044         public void handleMessage(Message msg) {
1045             switch (msg.what) {
1046                 case DISMISS:
1047                     ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
1048                     break;
1049                 case CANCEL:
1050                     ((OnCancelListener) msg.obj).onCancel(mDialog.get());
1051                     break;
1052                 case SHOW:
1053                     ((OnShowListener) msg.obj).onShow(mDialog.get());
1054                     break;
1055             }
1056         }
1057     }
1058 }
1059