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