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