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