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