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