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