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 */ Dialog(Context context)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 */ Dialog(Context context, int theme)147 public Dialog(Context context, int theme) { 148 this(context, theme, true); 149 } 150 Dialog(Context context, int theme, boolean createContextThemeWrapper)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 Dialog(Context context, boolean cancelable, Message cancelCallback)179 protected Dialog(Context context, boolean cancelable, 180 Message cancelCallback) { 181 this(context); 182 mCancelable = cancelable; 183 mCancelMessage = cancelCallback; 184 } 185 Dialog(Context context, boolean cancelable, OnCancelListener cancelListener)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 */ getContext()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 */ getActionBar()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 */ setOwnerActivity(Activity activity)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 */ getOwnerActivity()231 public final Activity getOwnerActivity() { 232 return mOwnerActivity; 233 } 234 235 /** 236 * @return Whether the dialog is currently showing. 237 */ isShowing()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 */ create()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 */ show()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 */ hide()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 dismiss()322 public void dismiss() { 323 if (Looper.myLooper() == mHandler.getLooper()) { 324 dismissDialog(); 325 } else { 326 mHandler.post(mDismissAction); 327 } 328 } 329 dismissDialog()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 sendDismissMessage()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 sendShowMessage()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 dispatchOnCreate(Bundle savedInstanceState)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 */ onCreate(Bundle savedInstanceState)386 protected void onCreate(Bundle savedInstanceState) { 387 } 388 389 /** 390 * Called when the dialog is starting. 391 */ onStart()392 protected void onStart() { 393 if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); 394 } 395 396 /** 397 * Called to tell you that you're stopping. 398 */ onStop()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 */ onSaveInstanceState()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 */ onRestoreInstanceState(Bundle savedInstanceState)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 */ getWindow()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 */ getCurrentFocus()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 */ findViewById(int id)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 */ setContentView(int layoutResID)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 */ setContentView(View view)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 */ setContentView(View view, ViewGroup.LayoutParams params)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 */ addContentView(View view, ViewGroup.LayoutParams params)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 */ setTitle(CharSequence title)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 */ setTitle(int titleId)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 */ onKeyDown(int keyCode, KeyEvent event)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 */ onKeyLongPress(int keyCode, KeyEvent event)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 */ onKeyUp(int keyCode, KeyEvent event)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 */ onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)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 */ onBackPressed()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 */ onKeyShortcut(int keyCode, KeyEvent event)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 */ onTouchEvent(MotionEvent event)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 */ onTrackballEvent(MotionEvent event)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 */ onGenericMotionEvent(MotionEvent event)690 public boolean onGenericMotionEvent(MotionEvent event) { 691 return false; 692 } 693 onWindowAttributesChanged(WindowManager.LayoutParams params)694 public void onWindowAttributesChanged(WindowManager.LayoutParams params) { 695 if (mDecor != null) { 696 mWindowManager.updateViewLayout(mDecor, params); 697 } 698 } 699 onContentChanged()700 public void onContentChanged() { 701 } 702 onWindowFocusChanged(boolean hasFocus)703 public void onWindowFocusChanged(boolean hasFocus) { 704 } 705 onAttachedToWindow()706 public void onAttachedToWindow() { 707 } 708 onDetachedFromWindow()709 public void onDetachedFromWindow() { 710 } 711 712 /** @hide */ 713 @Override onWindowDismissed()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 */ dispatchKeyEvent(KeyEvent event)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 */ dispatchKeyShortcutEvent(KeyEvent event)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 */ dispatchTouchEvent(MotionEvent ev)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 */ dispatchTrackballEvent(MotionEvent ev)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 */ dispatchGenericMotionEvent(MotionEvent ev)798 public boolean dispatchGenericMotionEvent(MotionEvent ev) { 799 if (mWindow.superDispatchGenericMotionEvent(ev)) { 800 return true; 801 } 802 return onGenericMotionEvent(ev); 803 } 804 dispatchPopulateAccessibilityEvent(AccessibilityEvent event)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 */ onCreatePanelView(int featureId)820 public View onCreatePanelView(int featureId) { 821 return null; 822 } 823 824 /** 825 * @see Activity#onCreatePanelMenu(int, Menu) 826 */ onCreatePanelMenu(int featureId, Menu menu)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 */ onPreparePanel(int featureId, View view, Menu menu)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 */ onMenuOpened(int featureId, Menu menu)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 */ onMenuItemSelected(int featureId, MenuItem item)859 public boolean onMenuItemSelected(int featureId, MenuItem item) { 860 return false; 861 } 862 863 /** 864 * @see Activity#onPanelClosed(int, Menu) 865 */ onPanelClosed(int featureId, Menu menu)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 */ onCreateOptionsMenu(Menu menu)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 */ onPrepareOptionsMenu(Menu menu)892 public boolean onPrepareOptionsMenu(Menu menu) { 893 return true; 894 } 895 896 /** 897 * @see Activity#onOptionsItemSelected(MenuItem) 898 */ onOptionsItemSelected(MenuItem item)899 public boolean onOptionsItemSelected(MenuItem item) { 900 return false; 901 } 902 903 /** 904 * @see Activity#onOptionsMenuClosed(Menu) 905 */ onOptionsMenuClosed(Menu menu)906 public void onOptionsMenuClosed(Menu menu) { 907 } 908 909 /** 910 * @see Activity#openOptionsMenu() 911 */ openOptionsMenu()912 public void openOptionsMenu() { 913 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { 914 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); 915 } 916 } 917 918 /** 919 * @see Activity#closeOptionsMenu() 920 */ closeOptionsMenu()921 public void closeOptionsMenu() { 922 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { 923 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); 924 } 925 } 926 927 /** 928 * @see Activity#invalidateOptionsMenu() 929 */ invalidateOptionsMenu()930 public void invalidateOptionsMenu() { 931 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) { 932 mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); 933 } 934 } 935 936 /** 937 * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) 938 */ onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)939 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 940 } 941 942 /** 943 * @see Activity#registerForContextMenu(View) 944 */ registerForContextMenu(View view)945 public void registerForContextMenu(View view) { 946 view.setOnCreateContextMenuListener(this); 947 } 948 949 /** 950 * @see Activity#unregisterForContextMenu(View) 951 */ unregisterForContextMenu(View view)952 public void unregisterForContextMenu(View view) { 953 view.setOnCreateContextMenuListener(null); 954 } 955 956 /** 957 * @see Activity#openContextMenu(View) 958 */ openContextMenu(View view)959 public void openContextMenu(View view) { 960 view.showContextMenu(); 961 } 962 963 /** 964 * @see Activity#onContextItemSelected(MenuItem) 965 */ onContextItemSelected(MenuItem item)966 public boolean onContextItemSelected(MenuItem item) { 967 return false; 968 } 969 970 /** 971 * @see Activity#onContextMenuClosed(Menu) 972 */ onContextMenuClosed(Menu menu)973 public void onContextMenuClosed(Menu menu) { 974 } 975 976 /** 977 * This hook is called when the user signals the desire to start a search. 978 */ onSearchRequested()979 public boolean onSearchRequested() { 980 final SearchManager searchManager = (SearchManager) mContext 981 .getSystemService(Context.SEARCH_SERVICE); 982 983 // associate search with owner activity 984 final ComponentName appName = getAssociatedActivity(); 985 if (appName != null && searchManager.getSearchableInfo(appName) != null) { 986 searchManager.startSearch(null, false, appName, null, false); 987 dismiss(); 988 return true; 989 } else { 990 return false; 991 } 992 } 993 onWindowStartingActionMode(ActionMode.Callback callback)994 public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { 995 if (mActionBar != null) { 996 return mActionBar.startActionMode(callback); 997 } 998 return null; 999 } 1000 1001 /** 1002 * {@inheritDoc} 1003 * 1004 * Note that if you override this method you should always call through 1005 * to the superclass implementation by calling super.onActionModeStarted(mode). 1006 */ onActionModeStarted(ActionMode mode)1007 public void onActionModeStarted(ActionMode mode) { 1008 mActionMode = mode; 1009 } 1010 1011 /** 1012 * {@inheritDoc} 1013 * 1014 * Note that if you override this method you should always call through 1015 * to the superclass implementation by calling super.onActionModeFinished(mode). 1016 */ onActionModeFinished(ActionMode mode)1017 public void onActionModeFinished(ActionMode mode) { 1018 if (mode == mActionMode) { 1019 mActionMode = null; 1020 } 1021 } 1022 1023 /** 1024 * @return The activity associated with this dialog, or null if there is no associated activity. 1025 */ getAssociatedActivity()1026 private ComponentName getAssociatedActivity() { 1027 Activity activity = mOwnerActivity; 1028 Context context = getContext(); 1029 while (activity == null && context != null) { 1030 if (context instanceof Activity) { 1031 activity = (Activity) context; // found it! 1032 } else { 1033 context = (context instanceof ContextWrapper) ? 1034 ((ContextWrapper) context).getBaseContext() : // unwrap one level 1035 null; // done 1036 } 1037 } 1038 return activity == null ? null : activity.getComponentName(); 1039 } 1040 1041 1042 /** 1043 * Request that key events come to this dialog. Use this if your 1044 * dialog has no views with focus, but the dialog still wants 1045 * a chance to process key events. 1046 * 1047 * @param get true if the dialog should receive key events, false otherwise 1048 * @see android.view.Window#takeKeyEvents 1049 */ takeKeyEvents(boolean get)1050 public void takeKeyEvents(boolean get) { 1051 mWindow.takeKeyEvents(get); 1052 } 1053 1054 /** 1055 * Enable extended window features. This is a convenience for calling 1056 * {@link android.view.Window#requestFeature getWindow().requestFeature()}. 1057 * 1058 * @param featureId The desired feature as defined in 1059 * {@link android.view.Window}. 1060 * @return Returns true if the requested feature is supported and now 1061 * enabled. 1062 * 1063 * @see android.view.Window#requestFeature 1064 */ requestWindowFeature(int featureId)1065 public final boolean requestWindowFeature(int featureId) { 1066 return getWindow().requestFeature(featureId); 1067 } 1068 1069 /** 1070 * Convenience for calling 1071 * {@link android.view.Window#setFeatureDrawableResource}. 1072 */ setFeatureDrawableResource(int featureId, int resId)1073 public final void setFeatureDrawableResource(int featureId, int resId) { 1074 getWindow().setFeatureDrawableResource(featureId, resId); 1075 } 1076 1077 /** 1078 * Convenience for calling 1079 * {@link android.view.Window#setFeatureDrawableUri}. 1080 */ setFeatureDrawableUri(int featureId, Uri uri)1081 public final void setFeatureDrawableUri(int featureId, Uri uri) { 1082 getWindow().setFeatureDrawableUri(featureId, uri); 1083 } 1084 1085 /** 1086 * Convenience for calling 1087 * {@link android.view.Window#setFeatureDrawable(int, Drawable)}. 1088 */ setFeatureDrawable(int featureId, Drawable drawable)1089 public final void setFeatureDrawable(int featureId, Drawable drawable) { 1090 getWindow().setFeatureDrawable(featureId, drawable); 1091 } 1092 1093 /** 1094 * Convenience for calling 1095 * {@link android.view.Window#setFeatureDrawableAlpha}. 1096 */ setFeatureDrawableAlpha(int featureId, int alpha)1097 public final void setFeatureDrawableAlpha(int featureId, int alpha) { 1098 getWindow().setFeatureDrawableAlpha(featureId, alpha); 1099 } 1100 getLayoutInflater()1101 public LayoutInflater getLayoutInflater() { 1102 return getWindow().getLayoutInflater(); 1103 } 1104 1105 /** 1106 * Sets whether this dialog is cancelable with the 1107 * {@link KeyEvent#KEYCODE_BACK BACK} key. 1108 */ setCancelable(boolean flag)1109 public void setCancelable(boolean flag) { 1110 mCancelable = flag; 1111 } 1112 1113 /** 1114 * Sets whether this dialog is canceled when touched outside the window's 1115 * bounds. If setting to true, the dialog is set to be cancelable if not 1116 * already set. 1117 * 1118 * @param cancel Whether the dialog should be canceled when touched outside 1119 * the window. 1120 */ setCanceledOnTouchOutside(boolean cancel)1121 public void setCanceledOnTouchOutside(boolean cancel) { 1122 if (cancel && !mCancelable) { 1123 mCancelable = true; 1124 } 1125 1126 mWindow.setCloseOnTouchOutside(cancel); 1127 } 1128 1129 /** 1130 * Cancel the dialog. This is essentially the same as calling {@link #dismiss()}, but it will 1131 * also call your {@link DialogInterface.OnCancelListener} (if registered). 1132 */ cancel()1133 public void cancel() { 1134 if (!mCanceled && mCancelMessage != null) { 1135 mCanceled = true; 1136 // Obtain a new message so this dialog can be re-used 1137 Message.obtain(mCancelMessage).sendToTarget(); 1138 } 1139 dismiss(); 1140 } 1141 1142 /** 1143 * Set a listener to be invoked when the dialog is canceled. 1144 * 1145 * <p>This will only be invoked when the dialog is canceled. 1146 * Cancel events alone will not capture all ways that 1147 * the dialog might be dismissed. If the creator needs 1148 * to know when a dialog is dismissed in general, use 1149 * {@link #setOnDismissListener}.</p> 1150 * 1151 * @param listener The {@link DialogInterface.OnCancelListener} to use. 1152 */ setOnCancelListener(final OnCancelListener listener)1153 public void setOnCancelListener(final OnCancelListener listener) { 1154 if (mCancelAndDismissTaken != null) { 1155 throw new IllegalStateException( 1156 "OnCancelListener is already taken by " 1157 + mCancelAndDismissTaken + " and can not be replaced."); 1158 } 1159 if (listener != null) { 1160 mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener); 1161 } else { 1162 mCancelMessage = null; 1163 } 1164 } 1165 1166 /** 1167 * Set a message to be sent when the dialog is canceled. 1168 * @param msg The msg to send when the dialog is canceled. 1169 * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener) 1170 */ setCancelMessage(final Message msg)1171 public void setCancelMessage(final Message msg) { 1172 mCancelMessage = msg; 1173 } 1174 1175 /** 1176 * Set a listener to be invoked when the dialog is dismissed. 1177 * @param listener The {@link DialogInterface.OnDismissListener} to use. 1178 */ setOnDismissListener(final OnDismissListener listener)1179 public void setOnDismissListener(final OnDismissListener listener) { 1180 if (mCancelAndDismissTaken != null) { 1181 throw new IllegalStateException( 1182 "OnDismissListener is already taken by " 1183 + mCancelAndDismissTaken + " and can not be replaced."); 1184 } 1185 if (listener != null) { 1186 mDismissMessage = mListenersHandler.obtainMessage(DISMISS, listener); 1187 } else { 1188 mDismissMessage = null; 1189 } 1190 } 1191 1192 /** 1193 * Sets a listener to be invoked when the dialog is shown. 1194 * @param listener The {@link DialogInterface.OnShowListener} to use. 1195 */ setOnShowListener(OnShowListener listener)1196 public void setOnShowListener(OnShowListener listener) { 1197 if (listener != null) { 1198 mShowMessage = mListenersHandler.obtainMessage(SHOW, listener); 1199 } else { 1200 mShowMessage = null; 1201 } 1202 } 1203 1204 /** 1205 * Set a message to be sent when the dialog is dismissed. 1206 * @param msg The msg to send when the dialog is dismissed. 1207 */ setDismissMessage(final Message msg)1208 public void setDismissMessage(final Message msg) { 1209 mDismissMessage = msg; 1210 } 1211 1212 /** @hide */ takeCancelAndDismissListeners(String msg, final OnCancelListener cancel, final OnDismissListener dismiss)1213 public boolean takeCancelAndDismissListeners(String msg, final OnCancelListener cancel, 1214 final OnDismissListener dismiss) { 1215 if (mCancelAndDismissTaken != null) { 1216 mCancelAndDismissTaken = null; 1217 } else if (mCancelMessage != null || mDismissMessage != null) { 1218 return false; 1219 } 1220 1221 setOnCancelListener(cancel); 1222 setOnDismissListener(dismiss); 1223 mCancelAndDismissTaken = msg; 1224 1225 return true; 1226 } 1227 1228 /** 1229 * By default, this will use the owner Activity's suggested stream type. 1230 * 1231 * @see Activity#setVolumeControlStream(int) 1232 * @see #setOwnerActivity(Activity) 1233 */ setVolumeControlStream(int streamType)1234 public final void setVolumeControlStream(int streamType) { 1235 getWindow().setVolumeControlStream(streamType); 1236 } 1237 1238 /** 1239 * @see Activity#getVolumeControlStream() 1240 */ getVolumeControlStream()1241 public final int getVolumeControlStream() { 1242 return getWindow().getVolumeControlStream(); 1243 } 1244 1245 /** 1246 * Sets the callback that will be called if a key is dispatched to the dialog. 1247 */ setOnKeyListener(final OnKeyListener onKeyListener)1248 public void setOnKeyListener(final OnKeyListener onKeyListener) { 1249 mOnKeyListener = onKeyListener; 1250 } 1251 1252 private static final class ListenersHandler extends Handler { 1253 private WeakReference<DialogInterface> mDialog; 1254 ListenersHandler(Dialog dialog)1255 public ListenersHandler(Dialog dialog) { 1256 mDialog = new WeakReference<DialogInterface>(dialog); 1257 } 1258 1259 @Override handleMessage(Message msg)1260 public void handleMessage(Message msg) { 1261 switch (msg.what) { 1262 case DISMISS: 1263 ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); 1264 break; 1265 case CANCEL: 1266 ((OnCancelListener) msg.obj).onCancel(mDialog.get()); 1267 break; 1268 case SHOW: 1269 ((OnShowListener) msg.obj).onShow(mDialog.get()); 1270 break; 1271 } 1272 } 1273 } 1274 } 1275