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