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