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