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