Dialog.java revision c63806d852a550d82bbe6cadff8a2139d78ed559
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.Build; 29import android.os.Bundle; 30import android.os.Handler; 31import android.os.Message; 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.ViewConfiguration; 45import android.view.ViewGroup; 46import android.view.ViewGroup.LayoutParams; 47import android.view.Window; 48import android.view.WindowManager; 49import android.view.accessibility.AccessibilityEvent; 50 51import java.lang.ref.WeakReference; 52 53/** 54 * Base class for Dialogs. 55 * 56 * <p>Note: Activities provide a facility to manage the creation, saving and 57 * restoring of dialogs. See {@link Activity#onCreateDialog(int)}, 58 * {@link Activity#onPrepareDialog(int, Dialog)}, 59 * {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If 60 * these methods are used, {@link #getOwnerActivity()} will return the Activity 61 * that managed this dialog. 62 * 63 * <p>Often you will want to have a Dialog display on top of the current 64 * input method, because there is no reason for it to accept text. You can 65 * do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM 66 * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming 67 * your Dialog takes input focus, as it the default) with the following code: 68 * 69 * <pre> 70 * getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 71 * WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 72 * </pre> 73 */ 74public class Dialog implements DialogInterface, Window.Callback, 75 KeyEvent.Callback, OnCreateContextMenuListener { 76 private Activity mOwnerActivity; 77 78 final Context mContext; 79 final WindowManager mWindowManager; 80 Window mWindow; 81 View mDecor; 82 private ActionBarImpl mActionBar; 83 /** 84 * This field should be made private, so it is hidden from the SDK. 85 * {@hide} 86 */ 87 protected boolean mCancelable = true; 88 89 private Message mCancelMessage; 90 private Message mDismissMessage; 91 private Message mShowMessage; 92 93 /** 94 * Whether to cancel the dialog when a touch is received outside of the 95 * window's bounds. 96 */ 97 private boolean mCanceledOnTouchOutside = false; 98 99 private OnKeyListener mOnKeyListener; 100 101 private boolean mCreated = false; 102 private boolean mShowing = false; 103 104 private final Thread mUiThread; 105 private final Handler mHandler = new Handler(); 106 107 private static final int DISMISS = 0x43; 108 private static final int CANCEL = 0x44; 109 private static final int SHOW = 0x45; 110 111 private Handler mListenersHandler; 112 113 private final Runnable mDismissAction = new Runnable() { 114 public void run() { 115 dismissDialog(); 116 } 117 }; 118 119 /** 120 * Create a Dialog window that uses the default dialog frame style. 121 * 122 * @param context The Context the Dialog is to run it. In particular, it 123 * uses the window manager and theme in this context to 124 * present its UI. 125 */ 126 public Dialog(Context context) { 127 this(context, 0); 128 } 129 130 /** 131 * Create a Dialog window that uses a custom dialog style. 132 * 133 * @param context The Context in which the Dialog should run. In particular, it 134 * uses the window manager and theme from this context to 135 * present its UI. 136 * @param theme A style resource describing the theme to use for the 137 * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style 138 * and Theme Resources</a> for more information about defining and using 139 * styles. This theme is applied on top of the current theme in 140 * <var>context</var>. If 0, the default dialog theme will be used. 141 */ 142 public Dialog(Context context, int theme) { 143 mContext = new ContextThemeWrapper( 144 context, theme == 0 ? 145 (context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB 146 ? com.android.internal.R.style.Theme_Holo_Dialog 147 : com.android.internal.R.style.Theme_Dialog) : theme); 148 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); 149 Window w = PolicyManager.makeNewWindow(mContext); 150 mWindow = w; 151 w.setCallback(this); 152 w.setWindowManager(mWindowManager, null, null); 153 w.setGravity(Gravity.CENTER); 154 mUiThread = Thread.currentThread(); 155 mListenersHandler = new ListenersHandler(this); 156 } 157 158 /** 159 * @deprecated 160 * @hide 161 */ 162 @Deprecated 163 protected Dialog(Context context, boolean cancelable, 164 Message cancelCallback) { 165 this(context); 166 mCancelable = cancelable; 167 mCancelMessage = cancelCallback; 168 } 169 170 protected Dialog(Context context, boolean cancelable, 171 OnCancelListener cancelListener) { 172 this(context); 173 mCancelable = cancelable; 174 setOnCancelListener(cancelListener); 175 } 176 177 /** 178 * Retrieve the Context this Dialog is running in. 179 * 180 * @return Context The Context that was supplied to the constructor. 181 */ 182 public final Context getContext() { 183 return mContext; 184 } 185 186 /** 187 * Retrieve the {@link ActionBar} attached to this dialog, if present. 188 * 189 * @return The ActionBar attached to the dialog or null if no ActionBar is present. 190 */ 191 public ActionBar getActionBar() { 192 return mActionBar; 193 } 194 195 /** 196 * Sets the Activity that owns this dialog. An example use: This Dialog will 197 * use the suggested volume control stream of the Activity. 198 * 199 * @param activity The Activity that owns this dialog. 200 */ 201 public final void setOwnerActivity(Activity activity) { 202 mOwnerActivity = activity; 203 204 getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream()); 205 } 206 207 /** 208 * Returns the Activity that owns this Dialog. For example, if 209 * {@link Activity#showDialog(int)} is used to show this Dialog, that 210 * Activity will be the owner (by default). Depending on how this dialog was 211 * created, this may return null. 212 * 213 * @return The Activity that owns this Dialog. 214 */ 215 public final Activity getOwnerActivity() { 216 return mOwnerActivity; 217 } 218 219 /** 220 * @return Whether the dialog is currently showing. 221 */ 222 public boolean isShowing() { 223 return mShowing; 224 } 225 226 /** 227 * Start the dialog and display it on screen. The window is placed in the 228 * application layer and opaque. Note that you should not override this 229 * method to do initialization when the dialog is shown, instead implement 230 * that in {@link #onStart}. 231 */ 232 public void show() { 233 if (mShowing) { 234 if (mDecor != null) { 235 if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { 236 mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); 237 } 238 mDecor.setVisibility(View.VISIBLE); 239 } 240 return; 241 } 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 a touch screen event was not handled by any of the views 571 * under it. This is most useful to process touch events that happen outside 572 * of your window bounds, where there is no view to receive it. 573 * 574 * @param event The touch screen event being processed. 575 * @return Return true if you have consumed the event, false if you haven't. 576 * The default implementation will cancel the dialog when a touch 577 * happens outside of the window bounds. 578 */ 579 public boolean onTouchEvent(MotionEvent event) { 580 if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN 581 && isOutOfBounds(event)) { 582 cancel(); 583 return true; 584 } 585 586 return false; 587 } 588 589 private boolean isOutOfBounds(MotionEvent event) { 590 final int x = (int) event.getX(); 591 final int y = (int) event.getY(); 592 final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); 593 final View decorView = getWindow().getDecorView(); 594 return (x < -slop) || (y < -slop) 595 || (x > (decorView.getWidth()+slop)) 596 || (y > (decorView.getHeight()+slop)); 597 } 598 599 /** 600 * Called when the trackball was moved and not handled by any of the 601 * views inside of the activity. So, for example, if the trackball moves 602 * while focus is on a button, you will receive a call here because 603 * buttons do not normally do anything with trackball events. The call 604 * here happens <em>before</em> trackball movements are converted to 605 * DPAD key events, which then get sent back to the view hierarchy, and 606 * will be processed at the point for things like focus navigation. 607 * 608 * @param event The trackball event being processed. 609 * 610 * @return Return true if you have consumed the event, false if you haven't. 611 * The default implementation always returns false. 612 */ 613 public boolean onTrackballEvent(MotionEvent event) { 614 return false; 615 } 616 617 public void onWindowAttributesChanged(WindowManager.LayoutParams params) { 618 if (mDecor != null) { 619 mWindowManager.updateViewLayout(mDecor, params); 620 } 621 } 622 623 public void onContentChanged() { 624 } 625 626 public void onWindowFocusChanged(boolean hasFocus) { 627 } 628 629 public void onAttachedToWindow() { 630 } 631 632 public void onDetachedFromWindow() { 633 } 634 635 /** 636 * Called to process key events. You can override this to intercept all 637 * key events before they are dispatched to the window. Be sure to call 638 * this implementation for key events that should be handled normally. 639 * 640 * @param event The key event. 641 * 642 * @return boolean Return true if this event was consumed. 643 */ 644 public boolean dispatchKeyEvent(KeyEvent event) { 645 if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) { 646 return true; 647 } 648 if (mWindow.superDispatchKeyEvent(event)) { 649 return true; 650 } 651 return event.dispatch(this, mDecor != null 652 ? mDecor.getKeyDispatcherState() : null, this); 653 } 654 655 /** 656 * Called to process touch screen events. You can override this to 657 * intercept all touch screen events before they are dispatched to the 658 * window. Be sure to call this implementation for touch screen events 659 * that should be handled normally. 660 * 661 * @param ev The touch screen event. 662 * 663 * @return boolean Return true if this event was consumed. 664 */ 665 public boolean dispatchTouchEvent(MotionEvent ev) { 666 if (mWindow.superDispatchTouchEvent(ev)) { 667 return true; 668 } 669 return onTouchEvent(ev); 670 } 671 672 /** 673 * Called to process trackball events. You can override this to 674 * intercept all trackball events before they are dispatched to the 675 * window. Be sure to call this implementation for trackball events 676 * that should be handled normally. 677 * 678 * @param ev The trackball event. 679 * 680 * @return boolean Return true if this event was consumed. 681 */ 682 public boolean dispatchTrackballEvent(MotionEvent ev) { 683 if (mWindow.superDispatchTrackballEvent(ev)) { 684 return true; 685 } 686 return onTrackballEvent(ev); 687 } 688 689 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 690 event.setClassName(getClass().getName()); 691 event.setPackageName(mContext.getPackageName()); 692 693 LayoutParams params = getWindow().getAttributes(); 694 boolean isFullScreen = (params.width == LayoutParams.MATCH_PARENT) && 695 (params.height == LayoutParams.MATCH_PARENT); 696 event.setFullScreen(isFullScreen); 697 698 return false; 699 } 700 701 /** 702 * @see Activity#onCreatePanelView(int) 703 */ 704 public View onCreatePanelView(int featureId) { 705 return null; 706 } 707 708 /** 709 * @see Activity#onCreatePanelMenu(int, Menu) 710 */ 711 public boolean onCreatePanelMenu(int featureId, Menu menu) { 712 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 713 return onCreateOptionsMenu(menu); 714 } 715 716 return false; 717 } 718 719 /** 720 * @see Activity#onPreparePanel(int, View, Menu) 721 */ 722 public boolean onPreparePanel(int featureId, View view, Menu menu) { 723 if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) { 724 boolean goforit = onPrepareOptionsMenu(menu); 725 return goforit && menu.hasVisibleItems(); 726 } 727 return true; 728 } 729 730 /** 731 * @see Activity#onMenuOpened(int, Menu) 732 */ 733 public boolean onMenuOpened(int featureId, Menu menu) { 734 return true; 735 } 736 737 /** 738 * @see Activity#onMenuItemSelected(int, MenuItem) 739 */ 740 public boolean onMenuItemSelected(int featureId, MenuItem item) { 741 return false; 742 } 743 744 /** 745 * @see Activity#onPanelClosed(int, Menu) 746 */ 747 public void onPanelClosed(int featureId, Menu menu) { 748 } 749 750 /** 751 * It is usually safe to proxy this call to the owner activity's 752 * {@link Activity#onCreateOptionsMenu(Menu)} if the client desires the same 753 * menu for this Dialog. 754 * 755 * @see Activity#onCreateOptionsMenu(Menu) 756 * @see #getOwnerActivity() 757 */ 758 public boolean onCreateOptionsMenu(Menu menu) { 759 return true; 760 } 761 762 /** 763 * It is usually safe to proxy this call to the owner activity's 764 * {@link Activity#onPrepareOptionsMenu(Menu)} if the client desires the 765 * same menu for this Dialog. 766 * 767 * @see Activity#onPrepareOptionsMenu(Menu) 768 * @see #getOwnerActivity() 769 */ 770 public boolean onPrepareOptionsMenu(Menu menu) { 771 return true; 772 } 773 774 /** 775 * @see Activity#onOptionsItemSelected(MenuItem) 776 */ 777 public boolean onOptionsItemSelected(MenuItem item) { 778 return false; 779 } 780 781 /** 782 * @see Activity#onOptionsMenuClosed(Menu) 783 */ 784 public void onOptionsMenuClosed(Menu menu) { 785 } 786 787 /** 788 * @see Activity#openOptionsMenu() 789 */ 790 public void openOptionsMenu() { 791 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); 792 } 793 794 /** 795 * @see Activity#closeOptionsMenu() 796 */ 797 public void closeOptionsMenu() { 798 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); 799 } 800 801 /** 802 * @see Activity#invalidateOptionsMenu() 803 */ 804 public void invalidateOptionsMenu() { 805 mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); 806 } 807 808 /** 809 * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) 810 */ 811 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 812 } 813 814 /** 815 * @see Activity#registerForContextMenu(View) 816 */ 817 public void registerForContextMenu(View view) { 818 view.setOnCreateContextMenuListener(this); 819 } 820 821 /** 822 * @see Activity#unregisterForContextMenu(View) 823 */ 824 public void unregisterForContextMenu(View view) { 825 view.setOnCreateContextMenuListener(null); 826 } 827 828 /** 829 * @see Activity#openContextMenu(View) 830 */ 831 public void openContextMenu(View view) { 832 view.showContextMenu(); 833 } 834 835 /** 836 * @see Activity#onContextItemSelected(MenuItem) 837 */ 838 public boolean onContextItemSelected(MenuItem item) { 839 return false; 840 } 841 842 /** 843 * @see Activity#onContextMenuClosed(Menu) 844 */ 845 public void onContextMenuClosed(Menu menu) { 846 } 847 848 /** 849 * This hook is called when the user signals the desire to start a search. 850 */ 851 public boolean onSearchRequested() { 852 final SearchManager searchManager = (SearchManager) mContext 853 .getSystemService(Context.SEARCH_SERVICE); 854 855 // associate search with owner activity 856 final ComponentName appName = getAssociatedActivity(); 857 if (appName != null) { 858 searchManager.startSearch(null, false, appName, null, false); 859 dismiss(); 860 return true; 861 } else { 862 return false; 863 } 864 } 865 866 public ActionMode onStartActionMode(ActionMode.Callback callback) { 867 if (mActionBar != null) { 868 return mActionBar.startActionMode(callback); 869 } 870 return null; 871 } 872 873 /** 874 * @return The activity associated with this dialog, or null if there is no associated activity. 875 */ 876 private ComponentName getAssociatedActivity() { 877 Activity activity = mOwnerActivity; 878 Context context = getContext(); 879 while (activity == null && context != null) { 880 if (context instanceof Activity) { 881 activity = (Activity) context; // found it! 882 } else { 883 context = (context instanceof ContextWrapper) ? 884 ((ContextWrapper) context).getBaseContext() : // unwrap one level 885 null; // done 886 } 887 } 888 return activity == null ? null : activity.getComponentName(); 889 } 890 891 892 /** 893 * Request that key events come to this dialog. Use this if your 894 * dialog has no views with focus, but the dialog still wants 895 * a chance to process key events. 896 * 897 * @param get true if the dialog should receive key events, false otherwise 898 * @see android.view.Window#takeKeyEvents 899 */ 900 public void takeKeyEvents(boolean get) { 901 mWindow.takeKeyEvents(get); 902 } 903 904 /** 905 * Enable extended window features. This is a convenience for calling 906 * {@link android.view.Window#requestFeature getWindow().requestFeature()}. 907 * 908 * @param featureId The desired feature as defined in 909 * {@link android.view.Window}. 910 * @return Returns true if the requested feature is supported and now 911 * enabled. 912 * 913 * @see android.view.Window#requestFeature 914 */ 915 public final boolean requestWindowFeature(int featureId) { 916 return getWindow().requestFeature(featureId); 917 } 918 919 /** 920 * Convenience for calling 921 * {@link android.view.Window#setFeatureDrawableResource}. 922 */ 923 public final void setFeatureDrawableResource(int featureId, int resId) { 924 getWindow().setFeatureDrawableResource(featureId, resId); 925 } 926 927 /** 928 * Convenience for calling 929 * {@link android.view.Window#setFeatureDrawableUri}. 930 */ 931 public final void setFeatureDrawableUri(int featureId, Uri uri) { 932 getWindow().setFeatureDrawableUri(featureId, uri); 933 } 934 935 /** 936 * Convenience for calling 937 * {@link android.view.Window#setFeatureDrawable(int, Drawable)}. 938 */ 939 public final void setFeatureDrawable(int featureId, Drawable drawable) { 940 getWindow().setFeatureDrawable(featureId, drawable); 941 } 942 943 /** 944 * Convenience for calling 945 * {@link android.view.Window#setFeatureDrawableAlpha}. 946 */ 947 public final void setFeatureDrawableAlpha(int featureId, int alpha) { 948 getWindow().setFeatureDrawableAlpha(featureId, alpha); 949 } 950 951 public LayoutInflater getLayoutInflater() { 952 return getWindow().getLayoutInflater(); 953 } 954 955 /** 956 * Sets whether this dialog is cancelable with the 957 * {@link KeyEvent#KEYCODE_BACK BACK} key. 958 */ 959 public void setCancelable(boolean flag) { 960 mCancelable = flag; 961 } 962 963 /** 964 * Sets whether this dialog is canceled when touched outside the window's 965 * bounds. If setting to true, the dialog is set to be cancelable if not 966 * already set. 967 * 968 * @param cancel Whether the dialog should be canceled when touched outside 969 * the window. 970 */ 971 public void setCanceledOnTouchOutside(boolean cancel) { 972 if (cancel && !mCancelable) { 973 mCancelable = true; 974 } 975 976 mCanceledOnTouchOutside = cancel; 977 } 978 979 /** 980 * Cancel the dialog. This is essentially the same as calling {@link #dismiss()}, but it will 981 * also call your {@link DialogInterface.OnCancelListener} (if registered). 982 */ 983 public void cancel() { 984 if (mCancelMessage != null) { 985 986 // Obtain a new message so this dialog can be re-used 987 Message.obtain(mCancelMessage).sendToTarget(); 988 } 989 dismiss(); 990 } 991 992 /** 993 * Set a listener to be invoked when the dialog is canceled. 994 * <p> 995 * This will only be invoked when the dialog is canceled, if the creator 996 * needs to know when it is dismissed in general, use 997 * {@link #setOnDismissListener}. 998 * 999 * @param listener The {@link DialogInterface.OnCancelListener} to use. 1000 */ 1001 public void setOnCancelListener(final OnCancelListener listener) { 1002 if (listener != null) { 1003 mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener); 1004 } else { 1005 mCancelMessage = null; 1006 } 1007 } 1008 1009 /** 1010 * Set a message to be sent when the dialog is canceled. 1011 * @param msg The msg to send when the dialog is canceled. 1012 * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener) 1013 */ 1014 public void setCancelMessage(final Message msg) { 1015 mCancelMessage = msg; 1016 } 1017 1018 /** 1019 * Set a listener to be invoked when the dialog is dismissed. 1020 * @param listener The {@link DialogInterface.OnDismissListener} to use. 1021 */ 1022 public void setOnDismissListener(final OnDismissListener listener) { 1023 if (listener != null) { 1024 mDismissMessage = mListenersHandler.obtainMessage(DISMISS, listener); 1025 } else { 1026 mDismissMessage = null; 1027 } 1028 } 1029 1030 /** 1031 * Sets a listener to be invoked when the dialog is shown. 1032 * @param listener The {@link DialogInterface.OnShowListener} to use. 1033 */ 1034 public void setOnShowListener(OnShowListener listener) { 1035 if (listener != null) { 1036 mShowMessage = mListenersHandler.obtainMessage(SHOW, listener); 1037 } else { 1038 mShowMessage = null; 1039 } 1040 } 1041 1042 /** 1043 * Set a message to be sent when the dialog is dismissed. 1044 * @param msg The msg to send when the dialog is dismissed. 1045 */ 1046 public void setDismissMessage(final Message msg) { 1047 mDismissMessage = msg; 1048 } 1049 1050 /** 1051 * By default, this will use the owner Activity's suggested stream type. 1052 * 1053 * @see Activity#setVolumeControlStream(int) 1054 * @see #setOwnerActivity(Activity) 1055 */ 1056 public final void setVolumeControlStream(int streamType) { 1057 getWindow().setVolumeControlStream(streamType); 1058 } 1059 1060 /** 1061 * @see Activity#getVolumeControlStream() 1062 */ 1063 public final int getVolumeControlStream() { 1064 return getWindow().getVolumeControlStream(); 1065 } 1066 1067 /** 1068 * Sets the callback that will be called if a key is dispatched to the dialog. 1069 */ 1070 public void setOnKeyListener(final OnKeyListener onKeyListener) { 1071 mOnKeyListener = onKeyListener; 1072 } 1073 1074 private static final class ListenersHandler extends Handler { 1075 private WeakReference<DialogInterface> mDialog; 1076 1077 public ListenersHandler(Dialog dialog) { 1078 mDialog = new WeakReference<DialogInterface>(dialog); 1079 } 1080 1081 @Override 1082 public void handleMessage(Message msg) { 1083 switch (msg.what) { 1084 case DISMISS: 1085 ((OnDismissListener) msg.obj).onDismiss(mDialog.get()); 1086 break; 1087 case CANCEL: 1088 ((OnCancelListener) msg.obj).onCancel(mDialog.get()); 1089 break; 1090 case SHOW: 1091 ((OnShowListener) msg.obj).onShow(mDialog.get()); 1092 break; 1093 } 1094 } 1095 } 1096} 1097