Dialog.java revision 75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec
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 com.android.internal.policy.PolicyManager; 20 21import android.content.Context; 22import android.content.DialogInterface; 23import android.graphics.drawable.Drawable; 24import android.net.Uri; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.Message; 28import android.util.Config; 29import android.util.Log; 30import android.view.ContextMenu; 31import android.view.ContextThemeWrapper; 32import android.view.Gravity; 33import android.view.KeyEvent; 34import android.view.LayoutInflater; 35import android.view.Menu; 36import android.view.MenuItem; 37import android.view.MotionEvent; 38import android.view.View; 39import android.view.ViewConfiguration; 40import android.view.ViewGroup; 41import android.view.Window; 42import android.view.WindowManager; 43import android.view.ContextMenu.ContextMenuInfo; 44import android.view.View.OnCreateContextMenuListener; 45import android.view.ViewGroup.LayoutParams; 46import android.view.accessibility.AccessibilityEvent; 47 48import java.lang.ref.WeakReference; 49 50/** 51 * Base class for Dialogs. 52 * 53 * <p>Note: Activities provide a facility to manage the creation, saving and 54 * restoring of dialogs. See {@link Activity#onCreateDialog(int)}, 55 * {@link Activity#onPrepareDialog(int, Dialog)}, 56 * {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If 57 * these methods are used, {@link #getOwnerActivity()} will return the Activity 58 * that managed this dialog. 59 * 60 * <p>Often you will want to have a Dialog display on top of the current 61 * input method, because there is no reason for it to accept text. You can 62 * do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM 63 * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming 64 * your Dialog takes input focus, as it the default) with the following code: 65 * 66 * <pre> 67 * getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 68 * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 69 * </pre> 70 */ 71public class Dialog implements DialogInterface, Window.Callback, 72 KeyEvent.Callback, OnCreateContextMenuListener { 73 private static final String LOG_TAG = "Dialog"; 74 75 private Activity mOwnerActivity; 76 77 final Context mContext; 78 final WindowManager mWindowManager; 79 Window mWindow; 80 View mDecor; 81 /** 82 * This field should be made private, so it is hidden from the SDK. 83 * {@hide} 84 */ 85 protected boolean mCancelable = true; 86 87 private Message mCancelMessage; 88 private Message mDismissMessage; 89 90 /** 91 * Whether to cancel the dialog when a touch is received outside of the 92 * window's bounds. 93 */ 94 private boolean mCanceledOnTouchOutside = false; 95 96 private OnKeyListener mOnKeyListener; 97 98 private boolean mCreated = false; 99 private boolean mShowing = false; 100 101 private final Thread mUiThread; 102 private final Handler mHandler = new Handler(); 103 104 private final Runnable mDismissAction = new Runnable() { 105 public void run() { 106 dismissDialog(); 107 } 108 }; 109 110 /** 111 * Create a Dialog window that uses the default dialog frame style. 112 * 113 * @param context The Context the Dialog is to run it. In particular, it 114 * uses the window manager and theme in this context to 115 * present its UI. 116 */ 117 public Dialog(Context context) { 118 this(context, 0); 119 } 120 121 /** 122 * Create a Dialog window that uses a custom dialog style. 123 * 124 * @param context The Context in which the Dialog should run. In particular, it 125 * uses the window manager and theme from this context to 126 * present its UI. 127 * @param theme A style resource describing the theme to use for the 128 * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style 129 * and Theme Resources</a> for more information about defining and using 130 * styles. This theme is applied on top of the current theme in 131 * <var>context</var>. If 0, the default dialog theme will be used. 132 */ 133 public Dialog(Context context, int theme) { 134 mContext = new ContextThemeWrapper( 135 context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme); 136 mWindowManager = (WindowManager)context.getSystemService("window"); 137 Window w = PolicyManager.makeNewWindow(mContext); 138 mWindow = w; 139 w.setCallback(this); 140 w.setWindowManager(mWindowManager, null, null); 141 w.setGravity(Gravity.CENTER); 142 mUiThread = Thread.currentThread(); 143 mDismissCancelHandler = new DismissCancelHandler(this); 144 } 145 146 /** 147 * @deprecated 148 * @hide 149 */ 150 @Deprecated 151 protected Dialog(Context context, boolean cancelable, 152 Message cancelCallback) { 153 this(context); 154 mCancelable = cancelable; 155 mCancelMessage = cancelCallback; 156 } 157 158 protected Dialog(Context context, boolean cancelable, 159 OnCancelListener cancelListener) { 160 this(context); 161 mCancelable = cancelable; 162 setOnCancelListener(cancelListener); 163 } 164 165 /** 166 * Retrieve the Context this Dialog is running in. 167 * 168 * @return Context The Context that was supplied to the constructor. 169 */ 170 public final Context getContext() { 171 return mContext; 172 } 173 174 /** 175 * Sets the Activity that owns this dialog. An example use: This Dialog will 176 * use the suggested volume control stream of the Activity. 177 * 178 * @param activity The Activity that owns this dialog. 179 */ 180 public final void setOwnerActivity(Activity activity) { 181 mOwnerActivity = activity; 182 183 getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream()); 184 } 185 186 /** 187 * Returns the Activity that owns this Dialog. For example, if 188 * {@link Activity#showDialog(int)} is used to show this Dialog, that 189 * Activity will be the owner (by default). Depending on how this dialog was 190 * created, this may return null. 191 * 192 * @return The Activity that owns this Dialog. 193 */ 194 public final Activity getOwnerActivity() { 195 return mOwnerActivity; 196 } 197 198 /** 199 * @return Whether the dialog is currently showing. 200 */ 201 public boolean isShowing() { 202 return mShowing; 203 } 204 205 /** 206 * Start the dialog and display it on screen. The window is placed in the 207 * application layer and opaque. Note that you should not override this 208 * method to do initialization when the dialog is shown, instead implement 209 * that in {@link #onStart}. 210 */ 211 public void show() { 212 if (mShowing) { 213 if (Config.LOGV) Log.v(LOG_TAG, 214 "[Dialog] start: already showing, ignore"); 215 if (mDecor != null) { 216 mDecor.setVisibility(View.VISIBLE); 217 } 218 return; 219 } 220 221 if (!mCreated) { 222 dispatchOnCreate(null); 223 } 224 225 onStart(); 226 mDecor = mWindow.getDecorView(); 227 WindowManager.LayoutParams l = mWindow.getAttributes(); 228 if ((l.softInputMode 229 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { 230 WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); 231 nl.copyFrom(l); 232 nl.softInputMode |= 233 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; 234 l = nl; 235 } 236 mWindowManager.addView(mDecor, l); 237 mShowing = true; 238 } 239 240 /** 241 * Hide the dialog, but do not dismiss it. 242 */ 243 public void hide() { 244 if (mDecor != null) { 245 mDecor.setVisibility(View.GONE); 246 } 247 } 248 249 /** 250 * Dismiss this dialog, removing it from the screen. This method can be 251 * invoked safely from any thread. Note that you should not override this 252 * method to do cleanup when the dialog is dismissed, instead implement 253 * that in {@link #onStop}. 254 */ 255 public void dismiss() { 256 if (Thread.currentThread() != mUiThread) { 257 mHandler.post(mDismissAction); 258 } else { 259 mDismissAction.run(); 260 } 261 } 262 263 private void dismissDialog() { 264 if (mDecor == null) { 265 if (Config.LOGV) Log.v(LOG_TAG, 266 "[Dialog] dismiss: already dismissed, ignore"); 267 return; 268 } 269 if (!mShowing) { 270 if (Config.LOGV) Log.v(LOG_TAG, 271 "[Dialog] dismiss: not showing, ignore"); 272 return; 273 } 274 275 mWindowManager.removeView(mDecor); 276 277 mDecor = null; 278 mWindow.closeAllPanels(); 279 onStop(); 280 mShowing = false; 281 282 sendDismissMessage(); 283 } 284 285 private void sendDismissMessage() { 286 if (mDismissMessage != null) { 287 // Obtain a new message so this dialog can be re-used 288 Message.obtain(mDismissMessage).sendToTarget(); 289 } 290 } 291 292 // internal method to make sure mcreated is set properly without requiring 293 // users to call through to super in onCreate 294 void dispatchOnCreate(Bundle savedInstanceState) { 295 onCreate(savedInstanceState); 296 mCreated = true; 297 } 298 299 /** 300 * Similar to {@link Activity#onCreate}, you should initialized your dialog 301 * in this method, including calling {@link #setContentView}. 302 * @param savedInstanceState If this dialog is being reinitalized after a 303 * the hosting activity was previously shut down, holds the result from 304 * the most recent call to {@link #onSaveInstanceState}, or null if this 305 * is the first time. 306 */ 307 protected void onCreate(Bundle savedInstanceState) { 308 } 309 310 /** 311 * Called when the dialog is starting. 312 */ 313 protected void onStart() { 314 } 315 316 /** 317 * Called to tell you that you're stopping. 318 */ 319 protected void onStop() { 320 } 321 322 private static final String DIALOG_SHOWING_TAG = "android:dialogShowing"; 323 private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy"; 324 325 /** 326 * Saves the state of the dialog into a bundle. 327 * 328 * The default implementation saves the state of its view hierarchy, so you'll 329 * likely want to call through to super if you override this to save additional 330 * state. 331 * @return A bundle with the state of the dialog. 332 */ 333 public Bundle onSaveInstanceState() { 334 Bundle bundle = new Bundle(); 335 bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing); 336 if (mCreated) { 337 bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState()); 338 } 339 return bundle; 340 } 341 342 /** 343 * Restore the state of the dialog from a previously saved bundle. 344 * 345 * The default implementation restores the state of the dialog's view 346 * hierarchy that was saved in the default implementation of {@link #onSaveInstanceState()}, 347 * so be sure to call through to super when overriding unless you want to 348 * do all restoring of state yourself. 349 * @param savedInstanceState The state of the dialog previously saved by 350 * {@link #onSaveInstanceState()}. 351 */ 352 public void onRestoreInstanceState(Bundle savedInstanceState) { 353 final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG); 354 if (dialogHierarchyState == null) { 355 // dialog has never been shown, or onCreated, nothing to restore. 356 return; 357 } 358 dispatchOnCreate(savedInstanceState); 359 mWindow.restoreHierarchyState(dialogHierarchyState); 360 if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) { 361 show(); 362 } 363 } 364 365 /** 366 * Retrieve the current Window for the activity. This can be used to 367 * directly access parts of the Window API that are not available 368 * through Activity/Screen. 369 * 370 * @return Window The current window, or null if the activity is not 371 * visual. 372 */ 373 public Window getWindow() { 374 return mWindow; 375 } 376 377 /** 378 * Call {@link android.view.Window#getCurrentFocus} on the 379 * Window if this Activity to return the currently focused view. 380 * 381 * @return View The current View with focus or null. 382 * 383 * @see #getWindow 384 * @see android.view.Window#getCurrentFocus 385 */ 386 public View getCurrentFocus() { 387 return mWindow != null ? mWindow.getCurrentFocus() : null; 388 } 389 390 /** 391 * Finds a view that was identified by the id attribute from the XML that 392 * was processed in {@link #onStart}. 393 * 394 * @param id the identifier of the view to find 395 * @return The view if found or null otherwise. 396 */ 397 public View findViewById(int id) { 398 return mWindow.findViewById(id); 399 } 400 401 /** 402 * Set the screen content from a layout resource. The resource will be 403 * inflated, adding all top-level views to the screen. 404 * 405 * @param layoutResID Resource ID to be inflated. 406 */ 407 public void setContentView(int layoutResID) { 408 mWindow.setContentView(layoutResID); 409 } 410 411 /** 412 * Set the screen content to an explicit view. This view is placed 413 * directly into the screen's view hierarchy. It can itself be a complex 414 * view hierarhcy. 415 * 416 * @param view The desired content to display. 417 */ 418 public void setContentView(View view) { 419 mWindow.setContentView(view); 420 } 421 422 /** 423 * Set the screen content to an explicit view. This view is placed 424 * directly into the screen's view hierarchy. It can itself be a complex 425 * view hierarhcy. 426 * 427 * @param view The desired content to display. 428 * @param params Layout parameters for the view. 429 */ 430 public void setContentView(View view, ViewGroup.LayoutParams params) { 431 mWindow.setContentView(view, params); 432 } 433 434 /** 435 * Add an additional content view to the screen. Added after any existing 436 * ones in the screen -- existing views are NOT removed. 437 * 438 * @param view The desired content to display. 439 * @param params Layout parameters for the view. 440 */ 441 public void addContentView(View view, ViewGroup.LayoutParams params) { 442 mWindow.addContentView(view, params); 443 } 444 445 /** 446 * Set the title text for this dialog's window. 447 * 448 * @param title The new text to display in the title. 449 */ 450 public void setTitle(CharSequence title) { 451 mWindow.setTitle(title); 452 mWindow.getAttributes().setTitle(title); 453 } 454 455 /** 456 * Set the title text for this dialog's window. The text is retrieved 457 * from the resources with the supplied identifier. 458 * 459 * @param titleId the title's text resource identifier 460 */ 461 public void setTitle(int titleId) { 462 setTitle(mContext.getText(titleId)); 463 } 464 465 /** 466 * A key was pressed down. 467 * 468 * <p>If the focused view didn't want this event, this method is called. 469 * 470 * <p>The default implementation handles KEYCODE_BACK to close the 471 * dialog. 472 * 473 * @see #onKeyUp 474 * @see android.view.KeyEvent 475 */ 476 public boolean onKeyDown(int keyCode, KeyEvent event) { 477 if (keyCode == KeyEvent.KEYCODE_BACK) { 478 if (mCancelable) { 479 cancel(); 480 } 481 return true; 482 } 483 484 return false; 485 } 486 487 /** 488 * A key was released. 489 * 490 * @see #onKeyDown 491 * @see KeyEvent 492 */ 493 public boolean onKeyUp(int keyCode, KeyEvent event) { 494 return false; 495 } 496 497 /** 498 * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) 499 * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle 500 * the event). 501 */ 502 public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { 503 return false; 504 } 505 506 /** 507 * Called when a touch screen event was not handled by any of the views 508 * under it. This is most useful to process touch events that happen outside 509 * of your window bounds, where there is no view to receive it. 510 * 511 * @param event The touch screen event being processed. 512 * @return Return true if you have consumed the event, false if you haven't. 513 * The default implementation will cancel the dialog when a touch 514 * happens outside of the window bounds. 515 */ 516 public boolean onTouchEvent(MotionEvent event) { 517 if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN 518 && isOutOfBounds(event)) { 519 cancel(); 520 return true; 521 } 522 523 return false; 524 } 525 526 private boolean isOutOfBounds(MotionEvent event) { 527 final int x = (int) event.getX(); 528 final int y = (int) event.getY(); 529 final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); 530 final View decorView = getWindow().getDecorView(); 531 return (x < -slop) || (y < -slop) 532 || (x > (decorView.getWidth()+slop)) 533 || (y > (decorView.getHeight()+slop)); 534 } 535 536 /** 537 * Called when the trackball was moved and not handled by any of the 538 * views inside of the activity. So, for example, if the trackball moves 539 * while focus is on a button, you will receive a call here because 540 * buttons do not normally do anything with trackball events. The call 541 * here happens <em>before</em> trackball movements are converted to 542 * DPAD key events, which then get sent back to the view hierarchy, and 543 * will be processed at the point for things like focus navigation. 544 * 545 * @param event The trackball event being processed. 546 * 547 * @return Return true if you have consumed the event, false if you haven't. 548 * The default implementation always returns false. 549 */ 550 public boolean onTrackballEvent(MotionEvent event) { 551 return false; 552 } 553 554 public void onWindowAttributesChanged(WindowManager.LayoutParams params) { 555 if (mDecor != null) { 556 mWindowManager.updateViewLayout(mDecor, params); 557 } 558 } 559 560 public void onContentChanged() { 561 } 562 563 public void onWindowFocusChanged(boolean hasFocus) { 564 } 565 566 /** 567 * Called to process key events. You can override this to intercept all 568 * key events before they are dispatched to the window. Be sure to call 569 * this implementation for key events that should be handled normally. 570 * 571 * @param event The key event. 572 * 573 * @return boolean Return true if this event was consumed. 574 */ 575 public boolean dispatchKeyEvent(KeyEvent event) { 576 if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) { 577 return true; 578 } 579 if (mWindow.superDispatchKeyEvent(event)) { 580 return true; 581 } 582 return event.dispatch(this); 583 } 584 585 /** 586 * Called to process touch screen events. You can override this to 587 * intercept all touch screen events before they are dispatched to the 588 * window. Be sure to call this implementation for touch screen events 589 * that should be handled normally. 590 * 591 * @param ev The touch screen event. 592 * 593 * @return boolean Return true if this event was consumed. 594 */ 595 public boolean dispatchTouchEvent(MotionEvent ev) { 596 if (mWindow.superDispatchTouchEvent(ev)) { 597 return true; 598 } 599 return onTouchEvent(ev); 600 } 601 602 /** 603 * Called to process trackball events. You can override this to 604 * intercept all trackball events before they are dispatched to the 605 * window. Be sure to call this implementation for trackball events 606 * that should be handled normally. 607 * 608 * @param ev The trackball event. 609 * 610 * @return boolean Return true if this event was consumed. 611 */ 612 public boolean dispatchTrackballEvent(MotionEvent ev) { 613 if (mWindow.superDispatchTrackballEvent(ev)) { 614 return true; 615 } 616 return onTrackballEvent(ev); 617 } 618 619 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 620 event.setClassName(getClass().getName()); 621 event.setPackageName(mContext.getPackageName()); 622 623 LayoutParams params = getWindow().getAttributes(); 624 boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && 625 (params.height == LayoutParams.FILL_PARENT); 626 event.setFullScreen(isFullScreen); 627 628 return false; 629 } 630 631 /** 632 * @see Activity#onCreatePanelView(int) 633 */ 634 public View onCreatePanelView(int featureId) { 635 return null; 636 } 637 638 /** 639 * @see Activity#onCreatePanelMenu(int, Menu) 640 */ 641 public boolean onCreatePanelMenu(int featureId, Menu menu) { 642 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 643 return onCreateOptionsMenu(menu); 644 } 645 646 return false; 647 } 648 649 /** 650 * @see Activity#onPreparePanel(int, View, Menu) 651 */ 652 public boolean onPreparePanel(int featureId, View view, Menu menu) { 653 if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) { 654 boolean goforit = onPrepareOptionsMenu(menu); 655 return goforit && menu.hasVisibleItems(); 656 } 657 return true; 658 } 659 660 /** 661 * @see Activity#onMenuOpened(int, Menu) 662 */ 663 public boolean onMenuOpened(int featureId, Menu menu) { 664 return true; 665 } 666 667 /** 668 * @see Activity#onMenuItemSelected(int, MenuItem) 669 */ 670 public boolean onMenuItemSelected(int featureId, MenuItem item) { 671 return false; 672 } 673 674 /** 675 * @see Activity#onPanelClosed(int, Menu) 676 */ 677 public void onPanelClosed(int featureId, Menu menu) { 678 } 679 680 /** 681 * It is usually safe to proxy this call to the owner activity's 682 * {@link Activity#onCreateOptionsMenu(Menu)} if the client desires the same 683 * menu for this Dialog. 684 * 685 * @see Activity#onCreateOptionsMenu(Menu) 686 * @see #getOwnerActivity() 687 */ 688 public boolean onCreateOptionsMenu(Menu menu) { 689 return true; 690 } 691 692 /** 693 * It is usually safe to proxy this call to the owner activity's 694 * {@link Activity#onPrepareOptionsMenu(Menu)} if the client desires the 695 * same menu for this Dialog. 696 * 697 * @see Activity#onPrepareOptionsMenu(Menu) 698 * @see #getOwnerActivity() 699 */ 700 public boolean onPrepareOptionsMenu(Menu menu) { 701 return true; 702 } 703 704 /** 705 * @see Activity#onOptionsItemSelected(MenuItem) 706 */ 707 public boolean onOptionsItemSelected(MenuItem item) { 708 return false; 709 } 710 711 /** 712 * @see Activity#onOptionsMenuClosed(Menu) 713 */ 714 public void onOptionsMenuClosed(Menu menu) { 715 } 716 717 /** 718 * @see Activity#openOptionsMenu() 719 */ 720 public void openOptionsMenu() { 721 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); 722 } 723 724 /** 725 * @see Activity#closeOptionsMenu() 726 */ 727 public void closeOptionsMenu() { 728 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); 729 } 730 731 /** 732 * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) 733 */ 734 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 735 } 736 737 /** 738 * @see Activity#registerForContextMenu(View) 739 */ 740 public void registerForContextMenu(View view) { 741 view.setOnCreateContextMenuListener(this); 742 } 743 744 /** 745 * @see Activity#unregisterForContextMenu(View) 746 */ 747 public void unregisterForContextMenu(View view) { 748 view.setOnCreateContextMenuListener(null); 749 } 750 751 /** 752 * @see Activity#openContextMenu(View) 753 */ 754 public void openContextMenu(View view) { 755 view.showContextMenu(); 756 } 757 758 /** 759 * @see Activity#onContextItemSelected(MenuItem) 760 */ 761 public boolean onContextItemSelected(MenuItem item) { 762 return false; 763 } 764 765 /** 766 * @see Activity#onContextMenuClosed(Menu) 767 */ 768 public void onContextMenuClosed(Menu menu) { 769 } 770 771 /** 772 * This hook is called when the user signals the desire to start a search. 773 */ 774 public boolean onSearchRequested() { 775 // not during dialogs, no. 776 return false; 777 } 778 779 780 /** 781 * Request that key events come to this dialog. Use this if your 782 * dialog has no views with focus, but the dialog still wants 783 * a chance to process key events. 784 * 785 * @param get true if the dialog should receive key events, false otherwise 786 * @see android.view.Window#takeKeyEvents 787 */ 788 public void takeKeyEvents(boolean get) { 789 mWindow.takeKeyEvents(get); 790 } 791 792 /** 793 * Enable extended window features. This is a convenience for calling 794 * {@link android.view.Window#requestFeature getWindow().requestFeature()}. 795 * 796 * @param featureId The desired feature as defined in 797 * {@link android.view.Window}. 798 * @return Returns true if the requested feature is supported and now 799 * enabled. 800 * 801 * @see android.view.Window#requestFeature 802 */ 803 public final boolean requestWindowFeature(int featureId) { 804 return getWindow().requestFeature(featureId); 805 } 806 807 /** 808 * Convenience for calling 809 * {@link android.view.Window#setFeatureDrawableResource}. 810 */ 811 public final void setFeatureDrawableResource(int featureId, int resId) { 812 getWindow().setFeatureDrawableResource(featureId, resId); 813 } 814 815 /** 816 * Convenience for calling 817 * {@link android.view.Window#setFeatureDrawableUri}. 818 */ 819 public final void setFeatureDrawableUri(int featureId, Uri uri) { 820 getWindow().setFeatureDrawableUri(featureId, uri); 821 } 822 823 /** 824 * Convenience for calling 825 * {@link android.view.Window#setFeatureDrawable(int, Drawable)}. 826 */ 827 public final void setFeatureDrawable(int featureId, Drawable drawable) { 828 getWindow().setFeatureDrawable(featureId, drawable); 829 } 830 831 /** 832 * Convenience for calling 833 * {@link android.view.Window#setFeatureDrawableAlpha}. 834 */ 835 public final void setFeatureDrawableAlpha(int featureId, int alpha) { 836 getWindow().setFeatureDrawableAlpha(featureId, alpha); 837 } 838 839 public LayoutInflater getLayoutInflater() { 840 return getWindow().getLayoutInflater(); 841 } 842 843 /** 844 * Sets whether this dialog is cancelable with the 845 * {@link KeyEvent#KEYCODE_BACK BACK} key. 846 */ 847 public void setCancelable(boolean flag) { 848 mCancelable = flag; 849 } 850 851 /** 852 * Sets whether this dialog is canceled when touched outside the window's 853 * bounds. If setting to true, the dialog is set to be cancelable if not 854 * already set. 855 * 856 * @param cancel Whether the dialog should be canceled when touched outside 857 * the window. 858 */ 859 public void setCanceledOnTouchOutside(boolean cancel) { 860 if (cancel && !mCancelable) { 861 mCancelable = true; 862 } 863 864 mCanceledOnTouchOutside = cancel; 865 } 866 867 /** 868 * Cancel the dialog. This is essentially the same as calling {@link #dismiss()}, but it will 869 * also call your {@link DialogInterface.OnCancelListener} (if registered). 870 */ 871 public void cancel() { 872 if (mCancelMessage != null) { 873 874 // Obtain a new message so this dialog can be re-used 875 Message.obtain(mCancelMessage).sendToTarget(); 876 } 877 dismiss(); 878 } 879 880 /** 881 * Set a listener to be invoked when the dialog is canceled. 882 * <p> 883 * This will only be invoked when the dialog is canceled, if the creator 884 * needs to know when it is dismissed in general, use 885 * {@link #setOnDismissListener}. 886 * 887 * @param listener The {@link DialogInterface.OnCancelListener} to use. 888 */ 889 public void setOnCancelListener(final OnCancelListener listener) { 890 if (listener != null) { 891 mCancelMessage = mDismissCancelHandler.obtainMessage(CANCEL, listener); 892 } else { 893 mCancelMessage = null; 894 } 895 } 896 897 /** 898 * Set a message to be sent when the dialog is canceled. 899 * @param msg The msg to send when the dialog is canceled. 900 * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener) 901 */ 902 public void setCancelMessage(final Message msg) { 903 mCancelMessage = msg; 904 } 905 906 /** 907 * Set a listener to be invoked when the dialog is dismissed. 908 * @param listener The {@link DialogInterface.OnDismissListener} to use. 909 */ 910 public void setOnDismissListener(final OnDismissListener listener) { 911 if (listener != null) { 912 mDismissMessage = mDismissCancelHandler.obtainMessage(DISMISS, listener); 913 } else { 914 mDismissMessage = null; 915 } 916 } 917 918 /** 919 * Set a message to be sent when the dialog is dismissed. 920 * @param msg The msg to send when the dialog is dismissed. 921 */ 922 public void setDismissMessage(final Message msg) { 923 mDismissMessage = msg; 924 } 925 926 /** 927 * By default, this will use the owner Activity's suggested stream type. 928 * 929 * @see Activity#setVolumeControlStream(int) 930 * @see #setOwnerActivity(Activity) 931 */ 932 public final void setVolumeControlStream(int streamType) { 933 getWindow().setVolumeControlStream(streamType); 934 } 935 936 /** 937 * @see Activity#getVolumeControlStream() 938 */ 939 public final int getVolumeControlStream() { 940 return getWindow().getVolumeControlStream(); 941 } 942 943 /** 944 * Sets the callback that will be called if a key is dispatched to the dialog. 945 */ 946 public void setOnKeyListener(final OnKeyListener onKeyListener) { 947 mOnKeyListener = onKeyListener; 948 } 949 950 private static final int DISMISS = 0x43; 951 private static final int CANCEL = 0x44; 952 953 private Handler mDismissCancelHandler; 954 955 private static final class DismissCancelHandler extends Handler { 956 private WeakReference<DialogInterface> mDialog; 957 958 public DismissCancelHandler(Dialog dialog) { 959 mDialog = new WeakReference<DialogInterface>(dialog); 960 } 961 962 @Override 963 public void handleMessage(Message msg) { 964 switch (msg.what) { 965 case DISMISS: 966 ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); 967 break; 968 case CANCEL: 969 ((OnCancelListener) msg.obj).onCancel(mDialog.get()); 970 break; 971 } 972 } 973 } 974} 975