PhoneWindow.java revision b07f943273a06383b4ab81793cbb6292f3d1586a
1/* 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package com.android.internal.policy.impl; 17 18import static android.view.View.MeasureSpec.AT_MOST; 19import static android.view.View.MeasureSpec.EXACTLY; 20import static android.view.View.MeasureSpec.getMode; 21import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 22import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 23import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 24import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 25import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 26import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 27import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; 28 29import com.android.internal.view.BaseSurfaceHolder; 30import com.android.internal.view.RootViewSurfaceTaker; 31import com.android.internal.view.StandaloneActionMode; 32import com.android.internal.view.menu.ActionMenuView; 33import com.android.internal.view.menu.ContextMenuBuilder; 34import com.android.internal.view.menu.MenuBuilder; 35import com.android.internal.view.menu.MenuDialogHelper; 36import com.android.internal.view.menu.MenuItemImpl; 37import com.android.internal.view.menu.MenuPopupHelper; 38import com.android.internal.view.menu.MenuView; 39import com.android.internal.view.menu.SubMenuBuilder; 40import com.android.internal.widget.ActionBarContextView; 41import com.android.internal.widget.ActionBarView; 42 43import android.app.KeyguardManager; 44import android.content.Context; 45import android.content.res.Configuration; 46import android.content.res.TypedArray; 47import android.graphics.Canvas; 48import android.graphics.PixelFormat; 49import android.graphics.Rect; 50import android.graphics.drawable.Drawable; 51import android.media.AudioManager; 52import android.net.Uri; 53import android.os.Bundle; 54import android.os.Parcel; 55import android.os.Parcelable; 56import android.util.AndroidRuntimeException; 57import android.util.Config; 58import android.util.DisplayMetrics; 59import android.util.EventLog; 60import android.util.Log; 61import android.util.SparseArray; 62import android.util.TypedValue; 63import android.view.ActionMode; 64import android.view.Gravity; 65import android.view.InputQueue; 66import android.view.KeyCharacterMap; 67import android.view.KeyEvent; 68import android.view.LayoutInflater; 69import android.view.Menu; 70import android.view.MenuItem; 71import android.view.MotionEvent; 72import android.view.SurfaceHolder; 73import android.view.View; 74import android.view.ViewGroup; 75import android.view.ViewManager; 76import android.view.ViewStub; 77import android.view.Window; 78import android.view.WindowManager; 79import android.view.View.MeasureSpec; 80import android.view.accessibility.AccessibilityEvent; 81import android.view.accessibility.AccessibilityManager; 82import android.view.animation.Animation; 83import android.view.animation.AnimationUtils; 84import android.widget.FrameLayout; 85import android.widget.ImageView; 86import android.widget.PopupWindow; 87import android.widget.ProgressBar; 88import android.widget.TextView; 89 90/** 91 * Android-specific Window. 92 * <p> 93 * todo: need to pull the generic functionality out into a base class 94 * in android.widget. 95 */ 96public class PhoneWindow extends Window implements MenuBuilder.Callback { 97 98 private final static String TAG = "PhoneWindow"; 99 100 private final static boolean SWEEP_OPEN_MENU = false; 101 102 /** 103 * Simple callback used by the context menu and its submenus. The options 104 * menu submenus do not use this (their behavior is more complex). 105 */ 106 final DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU); 107 108 final TypedValue mMinWidthMajor = new TypedValue(); 109 final TypedValue mMinWidthMinor = new TypedValue(); 110 111 // This is the top-level view of the window, containing the window decor. 112 private DecorView mDecor; 113 114 // This is the view in which the window contents are placed. It is either 115 // mDecor itself, or a child of mDecor where the contents go. 116 private ViewGroup mContentParent; 117 118 SurfaceHolder.Callback2 mTakeSurfaceCallback; 119 120 InputQueue.Callback mTakeInputQueueCallback; 121 122 private boolean mIsFloating; 123 124 private LayoutInflater mLayoutInflater; 125 126 private TextView mTitleView; 127 128 private ActionBarView mActionBar; 129 130 private DrawableFeatureState[] mDrawables; 131 132 private PanelFeatureState[] mPanels; 133 134 /** 135 * The panel that is prepared or opened (the most recent one if there are 136 * multiple panels). Shortcuts will go to this panel. It gets set in 137 * {@link #preparePanel} and cleared in {@link #closePanel}. 138 */ 139 private PanelFeatureState mPreparedPanel; 140 141 /** 142 * The keycode that is currently held down (as a modifier) for chording. If 143 * this is 0, there is no key held down. 144 */ 145 private int mPanelChordingKey; 146 147 private ImageView mLeftIconView; 148 149 private ImageView mRightIconView; 150 151 private ProgressBar mCircularProgressBar; 152 153 private ProgressBar mHorizontalProgressBar; 154 155 private int mBackgroundResource = 0; 156 157 private Drawable mBackgroundDrawable; 158 159 private int mFrameResource = 0; 160 161 private int mTextColor = 0; 162 163 private CharSequence mTitle = null; 164 165 private int mTitleColor = 0; 166 167 private boolean mAlwaysReadCloseOnTouchAttr = false; 168 169 private ContextMenuBuilder mContextMenu; 170 private MenuDialogHelper mContextMenuHelper; 171 private ActionButtonSubmenu mActionButtonPopup; 172 private boolean mClosingActionMenu; 173 174 private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; 175 176 private AudioManager mAudioManager; 177 private KeyguardManager mKeyguardManager; 178 179 public PhoneWindow(Context context) { 180 super(context); 181 mLayoutInflater = LayoutInflater.from(context); 182 } 183 184 @Override 185 public final void setContainer(Window container) { 186 super.setContainer(container); 187 } 188 189 @Override 190 public boolean requestFeature(int featureId) { 191 if (mContentParent != null) { 192 throw new AndroidRuntimeException("requestFeature() must be called before adding content"); 193 } 194 final int features = getFeatures(); 195 if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) { 196 197 /* Another feature is enabled and the user is trying to enable the custom title feature */ 198 throw new AndroidRuntimeException("You cannot combine custom titles with other title features"); 199 } 200 if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) && 201 (featureId != FEATURE_CUSTOM_TITLE) && (featureId != FEATURE_ACTION_MODE_OVERLAY)) { 202 203 /* Custom title feature is enabled and the user is trying to enable another feature */ 204 throw new AndroidRuntimeException("You cannot combine custom titles with other title features"); 205 } 206 if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) { 207 return false; // Ignore. No title dominates. 208 } 209 if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) { 210 // Remove the action bar feature if we have no title. No title dominates. 211 removeFeature(FEATURE_ACTION_BAR); 212 } 213 return super.requestFeature(featureId); 214 } 215 216 @Override 217 public void setContentView(int layoutResID) { 218 if (mContentParent == null) { 219 installDecor(); 220 } else { 221 mContentParent.removeAllViews(); 222 } 223 mLayoutInflater.inflate(layoutResID, mContentParent); 224 final Callback cb = getCallback(); 225 if (cb != null && !isDestroyed()) { 226 cb.onContentChanged(); 227 } 228 } 229 230 @Override 231 public void setContentView(View view) { 232 setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 233 } 234 235 @Override 236 public void setContentView(View view, ViewGroup.LayoutParams params) { 237 if (mContentParent == null) { 238 installDecor(); 239 } else { 240 mContentParent.removeAllViews(); 241 } 242 mContentParent.addView(view, params); 243 final Callback cb = getCallback(); 244 if (cb != null && !isDestroyed()) { 245 cb.onContentChanged(); 246 } 247 } 248 249 @Override 250 public void addContentView(View view, ViewGroup.LayoutParams params) { 251 if (mContentParent == null) { 252 installDecor(); 253 } 254 mContentParent.addView(view, params); 255 final Callback cb = getCallback(); 256 if (cb != null && !isDestroyed()) { 257 cb.onContentChanged(); 258 } 259 } 260 261 @Override 262 public View getCurrentFocus() { 263 return mDecor != null ? mDecor.findFocus() : null; 264 } 265 266 @Override 267 public void takeSurface(SurfaceHolder.Callback2 callback) { 268 mTakeSurfaceCallback = callback; 269 } 270 271 public void takeInputQueue(InputQueue.Callback callback) { 272 mTakeInputQueueCallback = callback; 273 } 274 275 @Override 276 public boolean isFloating() { 277 return mIsFloating; 278 } 279 280 /** 281 * Return a LayoutInflater instance that can be used to inflate XML view layout 282 * resources for use in this Window. 283 * 284 * @return LayoutInflater The shared LayoutInflater. 285 */ 286 @Override 287 public LayoutInflater getLayoutInflater() { 288 return mLayoutInflater; 289 } 290 291 @Override 292 public void setTitle(CharSequence title) { 293 if (mTitleView != null) { 294 mTitleView.setText(title); 295 } else if (mActionBar != null) { 296 mActionBar.setWindowTitle(title); 297 } 298 mTitle = title; 299 } 300 301 @Override 302 public void setTitleColor(int textColor) { 303 if (mTitleView != null) { 304 mTitleView.setTextColor(textColor); 305 } 306 mTitleColor = textColor; 307 } 308 309 /** 310 * Prepares the panel to either be opened or chorded. This creates the Menu 311 * instance for the panel and populates it via the Activity callbacks. 312 * 313 * @param st The panel state to prepare. 314 * @param event The event that triggered the preparing of the panel. 315 * @return Whether the panel was prepared. If the panel should not be shown, 316 * returns false. 317 */ 318 public final boolean preparePanel(PanelFeatureState st, KeyEvent event) { 319 if (isDestroyed()) { 320 return false; 321 } 322 323 // Already prepared (isPrepared will be reset to false later) 324 if (st.isPrepared) 325 return true; 326 327 if ((mPreparedPanel != null) && (mPreparedPanel != st)) { 328 // Another Panel is prepared and possibly open, so close it 329 closePanel(mPreparedPanel, false); 330 } 331 332 final Callback cb = getCallback(); 333 334 if (cb != null) { 335 st.createdPanelView = cb.onCreatePanelView(st.featureId); 336 } 337 338 if (st.createdPanelView == null) { 339 // Init the panel state's menu--return false if init failed 340 if (st.menu == null || st.refreshMenuContent) { 341 if (st.menu == null) { 342 if (!initializePanelMenu(st) || (st.menu == null)) { 343 return false; 344 } 345 } 346 // Call callback, and return if it doesn't want to display menu 347 if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) { 348 // Ditch the menu created above 349 st.menu = null; 350 351 return false; 352 } 353 354 st.refreshMenuContent = false; 355 356 if (mActionBar != null) { 357 mActionBar.setMenu(st.menu); 358 } 359 } 360 361 // Callback and return if the callback does not want to show the menu 362 if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) { 363 return false; 364 } 365 366 // Set the proper keymap 367 KeyCharacterMap kmap = KeyCharacterMap.load( 368 event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD); 369 st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC; 370 st.menu.setQwertyMode(st.qwertyMode); 371 } 372 373 // Set other state 374 st.isPrepared = true; 375 st.isHandled = false; 376 mPreparedPanel = st; 377 378 return true; 379 } 380 381 @Override 382 public void onConfigurationChanged(Configuration newConfig) { 383 // Action bars handle their own menu state 384 if (mActionBar == null) { 385 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 386 if ((st != null) && (st.menu != null)) { 387 final MenuBuilder menuBuilder = (MenuBuilder) st.menu; 388 389 if (st.isOpen) { 390 // Freeze state 391 final Bundle state = new Bundle(); 392 menuBuilder.saveHierarchyState(state); 393 394 // Remove the menu views since they need to be recreated 395 // according to the new configuration 396 clearMenuViews(st); 397 398 // Re-open the same menu 399 reopenMenu(false); 400 401 // Restore state 402 menuBuilder.restoreHierarchyState(state); 403 404 } else { 405 // Clear menu views so on next menu opening, it will use 406 // the proper layout 407 clearMenuViews(st); 408 } 409 } 410 } 411 } 412 413 private static void clearMenuViews(PanelFeatureState st) { 414 // This can be called on config changes, so we should make sure 415 // the views will be reconstructed based on the new orientation, etc. 416 417 // Allow the callback to create a new panel view 418 st.createdPanelView = null; 419 420 // Causes the decor view to be recreated 421 st.refreshDecorView = true; 422 423 ((MenuBuilder) st.menu).clearMenuViews(); 424 } 425 426 @Override 427 public final void openPanel(int featureId, KeyEvent event) { 428 if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && 429 mActionBar.isOverflowReserved()) { 430 if (mActionBar.getVisibility() == View.VISIBLE) { 431 // Invalidate the options menu, we want a prepare event that the app can respond to. 432 invalidatePanelMenu(FEATURE_OPTIONS_PANEL); 433 mActionBar.showOverflowMenu(); 434 } 435 } else { 436 openPanel(getPanelState(featureId, true), event); 437 } 438 } 439 440 private void openPanel(PanelFeatureState st, KeyEvent event) { 441 // System.out.println("Open panel: isOpen=" + st.isOpen); 442 443 // Already open, return 444 if (st.isOpen || isDestroyed()) { 445 return; 446 } 447 448 // Don't open an options panel for honeycomb apps on xlarge devices. 449 // (The app should be using an action bar for menu items.) 450 if (st.featureId == FEATURE_OPTIONS_PANEL) { 451 Context context = getContext(); 452 Configuration config = context.getResources().getConfiguration(); 453 boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >= 454 android.os.Build.VERSION_CODES.HONEYCOMB; 455 456 if (isHoneycombApp && 457 config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE)) { 458 return; 459 } 460 } 461 462 Callback cb = getCallback(); 463 if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) { 464 // Callback doesn't want the menu to open, reset any state 465 closePanel(st, true); 466 return; 467 } 468 469 final WindowManager wm = getWindowManager(); 470 if (wm == null) { 471 return; 472 } 473 474 // Prepare panel (should have been done before, but just in case) 475 if (!preparePanel(st, event)) { 476 return; 477 } 478 479 if (st.decorView == null || st.refreshDecorView) { 480 if (st.decorView == null) { 481 // Initialize the panel decor, this will populate st.decorView 482 if (!initializePanelDecor(st) || (st.decorView == null)) 483 return; 484 } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) { 485 // Decor needs refreshing, so remove its views 486 st.decorView.removeAllViews(); 487 } 488 489 // This will populate st.shownPanelView 490 if (!initializePanelContent(st) || (st.shownPanelView == null)) { 491 return; 492 } 493 494 ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams(); 495 if (lp == null) { 496 lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); 497 } 498 499 int backgroundResId; 500 if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) { 501 // If the contents is fill parent for the width, set the 502 // corresponding background 503 backgroundResId = st.fullBackground; 504 } else { 505 // Otherwise, set the normal panel background 506 backgroundResId = st.background; 507 } 508 st.decorView.setWindowBackground(getContext().getResources().getDrawable( 509 backgroundResId)); 510 511 512 st.decorView.addView(st.shownPanelView, lp); 513 514 /* 515 * Give focus to the view, if it or one of its children does not 516 * already have it. 517 */ 518 if (!st.shownPanelView.hasFocus()) { 519 st.shownPanelView.requestFocus(); 520 } 521 } 522 523 st.isOpen = true; 524 st.isHandled = false; 525 526 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 527 WRAP_CONTENT, WRAP_CONTENT, 528 st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, 529 WindowManager.LayoutParams.FLAG_DITHER 530 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 531 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 532 st.decorView.mDefaultOpacity); 533 534 lp.gravity = st.gravity; 535 lp.windowAnimations = st.windowAnimations; 536 537 wm.addView(st.decorView, lp); 538 // Log.v(TAG, "Adding main menu to window manager."); 539 } 540 541 @Override 542 public final void closePanel(int featureId) { 543 if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && 544 mActionBar.isOverflowReserved()) { 545 mActionBar.hideOverflowMenu(); 546 } else if (featureId == FEATURE_CONTEXT_MENU) { 547 closeContextMenu(); 548 } else { 549 closePanel(getPanelState(featureId, true), true); 550 } 551 } 552 553 /** 554 * Closes the given panel. 555 * 556 * @param st The panel to be closed. 557 * @param doCallback Whether to notify the callback that the panel was 558 * closed. If the panel is in the process of re-opening or 559 * opening another panel (e.g., menu opening a sub menu), the 560 * callback should not happen and this variable should be false. 561 * In addition, this method internally will only perform the 562 * callback if the panel is open. 563 */ 564 public final void closePanel(PanelFeatureState st, boolean doCallback) { 565 // System.out.println("Close panel: isOpen=" + st.isOpen); 566 final ViewManager wm = getWindowManager(); 567 if ((wm != null) && st.isOpen) { 568 if (st.decorView != null) { 569 wm.removeView(st.decorView); 570 // Log.v(TAG, "Removing main menu from window manager."); 571 } 572 573 if (doCallback) { 574 callOnPanelClosed(st.featureId, st, null); 575 } 576 } else if (st.featureId == FEATURE_OPTIONS_PANEL && doCallback && 577 mActionBar != null) { 578 checkCloseActionMenu(st.menu); 579 } 580 st.isPrepared = false; 581 st.isHandled = false; 582 st.isOpen = false; 583 584 // This view is no longer shown, so null it out 585 st.shownPanelView = null; 586 587 if (st.isInExpandedMode) { 588 // Next time the menu opens, it should not be in expanded mode, so 589 // force a refresh of the decor 590 st.refreshDecorView = true; 591 st.isInExpandedMode = false; 592 } 593 594 if (mPreparedPanel == st) { 595 mPreparedPanel = null; 596 mPanelChordingKey = 0; 597 } 598 } 599 600 private void checkCloseActionMenu(Menu menu) { 601 if (mClosingActionMenu) { 602 return; 603 } 604 605 boolean closed = false; 606 mClosingActionMenu = true; 607 if (mActionBar.isOverflowMenuOpen() && mActionBar.hideOverflowMenu()) { 608 closed = true; 609 } 610 if (mActionButtonPopup != null) { 611 mActionButtonPopup.dismiss(); 612 closed = true; 613 } 614 Callback cb = getCallback(); 615 if (cb != null && closed && !isDestroyed()) { 616 cb.onPanelClosed(FEATURE_ACTION_BAR, menu); 617 } 618 mClosingActionMenu = false; 619 } 620 621 @Override 622 public final void togglePanel(int featureId, KeyEvent event) { 623 PanelFeatureState st = getPanelState(featureId, true); 624 if (st.isOpen) { 625 closePanel(st, true); 626 } else { 627 openPanel(st, event); 628 } 629 } 630 631 @Override 632 public void invalidatePanelMenu(int featureId) { 633 PanelFeatureState st = getPanelState(featureId, true); 634 if (st.menu != null) { 635 st.menu.clear(); 636 } 637 st.refreshMenuContent = true; 638 st.refreshDecorView = true; 639 640 // Prepare the options panel if we have an action bar 641 if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL) 642 && mActionBar != null) { 643 st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false); 644 if (st != null) { 645 st.isPrepared = false; 646 preparePanel(st, null); 647 } 648 } 649 } 650 651 /** 652 * Called when the panel key is pushed down. 653 * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}. 654 * @param event The key event. 655 * @return Whether the key was handled. 656 */ 657 public final boolean onKeyDownPanel(int featureId, KeyEvent event) { 658 final int keyCode = event.getKeyCode(); 659 660 if (event.getRepeatCount() == 0) { 661 // The panel key was pushed, so set the chording key 662 mPanelChordingKey = keyCode; 663 664 PanelFeatureState st = getPanelState(featureId, true); 665 if (!st.isOpen) { 666 return preparePanel(st, event); 667 } 668 } 669 670 return false; 671 } 672 673 /** 674 * Called when the panel key is released. 675 * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}. 676 * @param event The key event. 677 */ 678 public final void onKeyUpPanel(int featureId, KeyEvent event) { 679 // The panel key was released, so clear the chording key 680 if (mPanelChordingKey != 0) { 681 mPanelChordingKey = 0; 682 683 if (event.isCanceled()) { 684 return; 685 } 686 687 boolean playSoundEffect = false; 688 final PanelFeatureState st = getPanelState(featureId, true); 689 if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null && 690 mActionBar.isOverflowReserved()) { 691 if (mActionBar.getVisibility() == View.VISIBLE) { 692 if (!mActionBar.isOverflowMenuShowing()) { 693 final Callback cb = getCallback(); 694 if (cb != null && !isDestroyed() && 695 cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) { 696 playSoundEffect = mActionBar.showOverflowMenu(); 697 } 698 } else { 699 playSoundEffect = mActionBar.hideOverflowMenu(); 700 } 701 } 702 } else { 703 if (st.isOpen || st.isHandled) { 704 705 // Play the sound effect if the user closed an open menu (and not if 706 // they just released a menu shortcut) 707 playSoundEffect = st.isOpen; 708 709 // Close menu 710 closePanel(st, true); 711 712 } else if (st.isPrepared) { 713 714 // Write 'menu opened' to event log 715 EventLog.writeEvent(50001, 0); 716 717 // Show menu 718 openPanel(st, event); 719 720 playSoundEffect = true; 721 } 722 } 723 724 if (playSoundEffect) { 725 AudioManager audioManager = (AudioManager) getContext().getSystemService( 726 Context.AUDIO_SERVICE); 727 if (audioManager != null) { 728 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 729 } else { 730 Log.w(TAG, "Couldn't get audio manager"); 731 } 732 } 733 } 734 } 735 736 @Override 737 public final void closeAllPanels() { 738 final ViewManager wm = getWindowManager(); 739 if (wm == null) { 740 return; 741 } 742 743 final PanelFeatureState[] panels = mPanels; 744 final int N = panels != null ? panels.length : 0; 745 for (int i = 0; i < N; i++) { 746 final PanelFeatureState panel = panels[i]; 747 if (panel != null) { 748 closePanel(panel, true); 749 } 750 } 751 752 closeContextMenu(); 753 } 754 755 /** 756 * Closes the context menu. This notifies the menu logic of the close, along 757 * with dismissing it from the UI. 758 */ 759 private synchronized void closeContextMenu() { 760 if (mContextMenu != null) { 761 mContextMenu.close(); 762 dismissContextMenu(); 763 } 764 } 765 766 /** 767 * Dismisses just the context menu UI. To close the context menu, use 768 * {@link #closeContextMenu()}. 769 */ 770 private synchronized void dismissContextMenu() { 771 mContextMenu = null; 772 773 if (mContextMenuHelper != null) { 774 mContextMenuHelper.dismiss(); 775 mContextMenuHelper = null; 776 } 777 } 778 779 @Override 780 public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { 781 return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags); 782 } 783 784 private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event, 785 int flags) { 786 if (event.isSystem() || (st == null)) { 787 return false; 788 } 789 790 boolean handled = false; 791 792 // Only try to perform menu shortcuts if preparePanel returned true (possible false 793 // return value from application not wanting to show the menu). 794 if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) { 795 // The menu is prepared now, perform the shortcut on it 796 handled = st.menu.performShortcut(keyCode, event, flags); 797 } 798 799 if (handled) { 800 // Mark as handled 801 st.isHandled = true; 802 803 if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0) { 804 closePanel(st, true); 805 } 806 } 807 808 return handled; 809 } 810 811 @Override 812 public boolean performPanelIdentifierAction(int featureId, int id, int flags) { 813 814 PanelFeatureState st = getPanelState(featureId, true); 815 if (!preparePanel(st, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU))) { 816 return false; 817 } 818 if (st.menu == null) { 819 return false; 820 } 821 822 boolean res = st.menu.performIdentifierAction(id, flags); 823 824 closePanel(st, true); 825 826 return res; 827 } 828 829 public PanelFeatureState findMenuPanel(Menu menu) { 830 final PanelFeatureState[] panels = mPanels; 831 final int N = panels != null ? panels.length : 0; 832 for (int i = 0; i < N; i++) { 833 final PanelFeatureState panel = panels[i]; 834 if (panel != null && panel.menu == menu) { 835 return panel; 836 } 837 } 838 return null; 839 } 840 841 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 842 final Callback cb = getCallback(); 843 if (cb != null && !isDestroyed()) { 844 final PanelFeatureState panel = findMenuPanel(menu.getRootMenu()); 845 if (panel != null) { 846 return cb.onMenuItemSelected(panel.featureId, item); 847 } 848 } 849 return false; 850 } 851 852 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 853 final PanelFeatureState panel = findMenuPanel(menu); 854 if (panel != null) { 855 // Close the panel and only do the callback if the menu is being 856 // closed 857 // completely, not if opening a sub menu 858 closePanel(panel, allMenusAreClosing); 859 } 860 } 861 862 public void onCloseSubMenu(SubMenuBuilder subMenu) { 863 final Menu parentMenu = subMenu.getRootMenu(); 864 final PanelFeatureState panel = findMenuPanel(parentMenu); 865 866 // Callback 867 if (panel != null) { 868 callOnPanelClosed(panel.featureId, panel, parentMenu); 869 closePanel(panel, true); 870 } 871 } 872 873 public boolean onSubMenuSelected(final SubMenuBuilder subMenu) { 874 if (!subMenu.hasVisibleItems()) { 875 return true; 876 } 877 878 final Menu parentMenu = subMenu.getRootMenu(); 879 final PanelFeatureState panel = findMenuPanel(parentMenu); 880 881 if (hasFeature(FEATURE_ACTION_BAR) && panel.featureId == FEATURE_OPTIONS_PANEL) { 882 mDecor.post(new Runnable() { 883 public void run() { 884 mActionButtonPopup = new ActionButtonSubmenu(getContext(), subMenu); 885 mActionButtonPopup.show(); 886 Callback cb = getCallback(); 887 if (cb != null && !isDestroyed()) { 888 cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu); 889 } 890 } 891 }); 892 } else { 893 // The window manager will give us a valid window token 894 new MenuDialogHelper(subMenu).show(null); 895 } 896 897 return true; 898 } 899 900 public void onMenuModeChange(MenuBuilder menu) { 901 reopenMenu(true); 902 } 903 904 private void reopenMenu(boolean toggleMenuMode) { 905 if (mActionBar != null) { 906 final Callback cb = getCallback(); 907 if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) { 908 if (cb != null && !isDestroyed() && mActionBar.getVisibility() == View.VISIBLE) { 909 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 910 if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) { 911 cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); 912 mActionBar.openOverflowMenu(); 913 } 914 } 915 } else { 916 mActionBar.hideOverflowMenu(); 917 if (cb != null && !isDestroyed()) { 918 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 919 cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu); 920 } 921 } 922 return; 923 } 924 925 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 926 927 // Save the future expanded mode state since closePanel will reset it 928 boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode; 929 930 st.refreshDecorView = true; 931 closePanel(st, false); 932 933 // Set the expanded mode state 934 st.isInExpandedMode = newExpandedMode; 935 936 openPanel(st, null); 937 } 938 939 /** 940 * Initializes the menu associated with the given panel feature state. You 941 * must at the very least set PanelFeatureState.menu to the Menu to be 942 * associated with the given panel state. The default implementation creates 943 * a new menu for the panel state. 944 * 945 * @param st The panel whose menu is being initialized. 946 * @return Whether the initialization was successful. 947 */ 948 protected boolean initializePanelMenu(final PanelFeatureState st) { 949 final MenuBuilder menu = new MenuBuilder(getContext()); 950 951 menu.setCallback(this); 952 st.setMenu(menu); 953 954 return true; 955 } 956 957 /** 958 * Perform initial setup of a panel. This should at the very least set the 959 * style information in the PanelFeatureState and must set 960 * PanelFeatureState.decor to the panel's window decor view. 961 * 962 * @param st The panel being initialized. 963 */ 964 protected boolean initializePanelDecor(PanelFeatureState st) { 965 st.decorView = new DecorView(getContext(), st.featureId); 966 st.gravity = Gravity.CENTER | Gravity.BOTTOM; 967 st.setStyle(getContext()); 968 969 return true; 970 } 971 972 /** 973 * Initializes the panel associated with the panel feature state. You must 974 * at the very least set PanelFeatureState.panel to the View implementing 975 * its contents. The default implementation gets the panel from the menu. 976 * 977 * @param st The panel state being initialized. 978 * @return Whether the initialization was successful. 979 */ 980 protected boolean initializePanelContent(PanelFeatureState st) { 981 982 if (st.createdPanelView != null) { 983 st.shownPanelView = st.createdPanelView; 984 return true; 985 } 986 987 final MenuBuilder menu = (MenuBuilder)st.menu; 988 if (menu == null) { 989 return false; 990 } 991 992 st.shownPanelView = menu.getMenuView((st.isInExpandedMode) ? MenuBuilder.TYPE_EXPANDED 993 : MenuBuilder.TYPE_ICON, st.decorView); 994 995 if (st.shownPanelView != null) { 996 // Use the menu View's default animations if it has any 997 final int defaultAnimations = ((MenuView) st.shownPanelView).getWindowAnimations(); 998 if (defaultAnimations != 0) { 999 st.windowAnimations = defaultAnimations; 1000 } 1001 return true; 1002 } else { 1003 return false; 1004 } 1005 } 1006 1007 @Override 1008 public boolean performContextMenuIdentifierAction(int id, int flags) { 1009 return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false; 1010 } 1011 1012 @Override 1013 public final void setBackgroundDrawable(Drawable drawable) { 1014 if (drawable != mBackgroundDrawable || mBackgroundResource != 0) { 1015 mBackgroundResource = 0; 1016 mBackgroundDrawable = drawable; 1017 if (mDecor != null) { 1018 mDecor.setWindowBackground(drawable); 1019 } 1020 } 1021 } 1022 1023 @Override 1024 public final void setFeatureDrawableResource(int featureId, int resId) { 1025 if (resId != 0) { 1026 DrawableFeatureState st = getDrawableState(featureId, true); 1027 if (st.resid != resId) { 1028 st.resid = resId; 1029 st.uri = null; 1030 st.local = getContext().getResources().getDrawable(resId); 1031 updateDrawable(featureId, st, false); 1032 } 1033 } else { 1034 setFeatureDrawable(featureId, null); 1035 } 1036 } 1037 1038 @Override 1039 public final void setFeatureDrawableUri(int featureId, Uri uri) { 1040 if (uri != null) { 1041 DrawableFeatureState st = getDrawableState(featureId, true); 1042 if (st.uri == null || !st.uri.equals(uri)) { 1043 st.resid = 0; 1044 st.uri = uri; 1045 st.local = loadImageURI(uri); 1046 updateDrawable(featureId, st, false); 1047 } 1048 } else { 1049 setFeatureDrawable(featureId, null); 1050 } 1051 } 1052 1053 @Override 1054 public final void setFeatureDrawable(int featureId, Drawable drawable) { 1055 DrawableFeatureState st = getDrawableState(featureId, true); 1056 st.resid = 0; 1057 st.uri = null; 1058 if (st.local != drawable) { 1059 st.local = drawable; 1060 updateDrawable(featureId, st, false); 1061 } 1062 } 1063 1064 @Override 1065 public void setFeatureDrawableAlpha(int featureId, int alpha) { 1066 DrawableFeatureState st = getDrawableState(featureId, true); 1067 if (st.alpha != alpha) { 1068 st.alpha = alpha; 1069 updateDrawable(featureId, st, false); 1070 } 1071 } 1072 1073 protected final void setFeatureDefaultDrawable(int featureId, Drawable drawable) { 1074 DrawableFeatureState st = getDrawableState(featureId, true); 1075 if (st.def != drawable) { 1076 st.def = drawable; 1077 updateDrawable(featureId, st, false); 1078 } 1079 } 1080 1081 @Override 1082 public final void setFeatureInt(int featureId, int value) { 1083 // XXX Should do more management (as with drawable features) to 1084 // deal with interactions between multiple window policies. 1085 updateInt(featureId, value, false); 1086 } 1087 1088 /** 1089 * Update the state of a drawable feature. This should be called, for every 1090 * drawable feature supported, as part of onActive(), to make sure that the 1091 * contents of a containing window is properly updated. 1092 * 1093 * @see #onActive 1094 * @param featureId The desired drawable feature to change. 1095 * @param fromActive Always true when called from onActive(). 1096 */ 1097 protected final void updateDrawable(int featureId, boolean fromActive) { 1098 final DrawableFeatureState st = getDrawableState(featureId, false); 1099 if (st != null) { 1100 updateDrawable(featureId, st, fromActive); 1101 } 1102 } 1103 1104 /** 1105 * Called when a Drawable feature changes, for the window to update its 1106 * graphics. 1107 * 1108 * @param featureId The feature being changed. 1109 * @param drawable The new Drawable to show, or null if none. 1110 * @param alpha The new alpha blending of the Drawable. 1111 */ 1112 protected void onDrawableChanged(int featureId, Drawable drawable, int alpha) { 1113 ImageView view; 1114 if (featureId == FEATURE_LEFT_ICON) { 1115 view = getLeftIconView(); 1116 } else if (featureId == FEATURE_RIGHT_ICON) { 1117 view = getRightIconView(); 1118 } else { 1119 return; 1120 } 1121 1122 if (drawable != null) { 1123 drawable.setAlpha(alpha); 1124 view.setImageDrawable(drawable); 1125 view.setVisibility(View.VISIBLE); 1126 } else { 1127 view.setVisibility(View.GONE); 1128 } 1129 } 1130 1131 /** 1132 * Called when an int feature changes, for the window to update its 1133 * graphics. 1134 * 1135 * @param featureId The feature being changed. 1136 * @param value The new integer value. 1137 */ 1138 protected void onIntChanged(int featureId, int value) { 1139 if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) { 1140 updateProgressBars(value); 1141 } else if (featureId == FEATURE_CUSTOM_TITLE) { 1142 FrameLayout titleContainer = (FrameLayout) findViewById(com.android.internal.R.id.title_container); 1143 if (titleContainer != null) { 1144 mLayoutInflater.inflate(value, titleContainer); 1145 } 1146 } 1147 } 1148 1149 /** 1150 * Updates the progress bars that are shown in the title bar. 1151 * 1152 * @param value Can be one of {@link Window#PROGRESS_VISIBILITY_ON}, 1153 * {@link Window#PROGRESS_VISIBILITY_OFF}, 1154 * {@link Window#PROGRESS_INDETERMINATE_ON}, 1155 * {@link Window#PROGRESS_INDETERMINATE_OFF}, or a value 1156 * starting at {@link Window#PROGRESS_START} through 1157 * {@link Window#PROGRESS_END} for setting the default 1158 * progress (if {@link Window#PROGRESS_END} is given, 1159 * the progress bar widgets in the title will be hidden after an 1160 * animation), a value between 1161 * {@link Window#PROGRESS_SECONDARY_START} - 1162 * {@link Window#PROGRESS_SECONDARY_END} for the 1163 * secondary progress (if 1164 * {@link Window#PROGRESS_SECONDARY_END} is given, the 1165 * progress bar widgets will still be shown with the secondary 1166 * progress bar will be completely filled in.) 1167 */ 1168 private void updateProgressBars(int value) { 1169 ProgressBar circularProgressBar = getCircularProgressBar(true); 1170 ProgressBar horizontalProgressBar = getHorizontalProgressBar(true); 1171 1172 final int features = getLocalFeatures(); 1173 if (value == PROGRESS_VISIBILITY_ON) { 1174 if ((features & (1 << FEATURE_PROGRESS)) != 0) { 1175 int level = horizontalProgressBar.getProgress(); 1176 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? 1177 View.VISIBLE : View.INVISIBLE; 1178 horizontalProgressBar.setVisibility(visibility); 1179 } 1180 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 1181 circularProgressBar.setVisibility(View.VISIBLE); 1182 } 1183 } else if (value == PROGRESS_VISIBILITY_OFF) { 1184 if ((features & (1 << FEATURE_PROGRESS)) != 0) { 1185 horizontalProgressBar.setVisibility(View.GONE); 1186 } 1187 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 1188 circularProgressBar.setVisibility(View.GONE); 1189 } 1190 } else if (value == PROGRESS_INDETERMINATE_ON) { 1191 horizontalProgressBar.setIndeterminate(true); 1192 } else if (value == PROGRESS_INDETERMINATE_OFF) { 1193 horizontalProgressBar.setIndeterminate(false); 1194 } else if (PROGRESS_START <= value && value <= PROGRESS_END) { 1195 // We want to set the progress value before testing for visibility 1196 // so that when the progress bar becomes visible again, it has the 1197 // correct level. 1198 horizontalProgressBar.setProgress(value - PROGRESS_START); 1199 1200 if (value < PROGRESS_END) { 1201 showProgressBars(horizontalProgressBar, circularProgressBar); 1202 } else { 1203 hideProgressBars(horizontalProgressBar, circularProgressBar); 1204 } 1205 } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) { 1206 horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START); 1207 1208 showProgressBars(horizontalProgressBar, circularProgressBar); 1209 } 1210 1211 } 1212 1213 private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) { 1214 final int features = getLocalFeatures(); 1215 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 && 1216 spinnyProgressBar.getVisibility() == View.INVISIBLE) { 1217 spinnyProgressBar.setVisibility(View.VISIBLE); 1218 } 1219 // Only show the progress bars if the primary progress is not complete 1220 if ((features & (1 << FEATURE_PROGRESS)) != 0 && 1221 horizontalProgressBar.getProgress() < 10000) { 1222 horizontalProgressBar.setVisibility(View.VISIBLE); 1223 } 1224 } 1225 1226 private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) { 1227 final int features = getLocalFeatures(); 1228 Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out); 1229 anim.setDuration(1000); 1230 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 && 1231 spinnyProgressBar.getVisibility() == View.VISIBLE) { 1232 spinnyProgressBar.startAnimation(anim); 1233 spinnyProgressBar.setVisibility(View.INVISIBLE); 1234 } 1235 if ((features & (1 << FEATURE_PROGRESS)) != 0 && 1236 horizontalProgressBar.getVisibility() == View.VISIBLE) { 1237 horizontalProgressBar.startAnimation(anim); 1238 horizontalProgressBar.setVisibility(View.INVISIBLE); 1239 } 1240 } 1241 1242 /** 1243 * Request that key events come to this activity. Use this if your activity 1244 * has no views with focus, but the activity still wants a chance to process 1245 * key events. 1246 */ 1247 @Override 1248 public void takeKeyEvents(boolean get) { 1249 mDecor.setFocusable(get); 1250 } 1251 1252 @Override 1253 public boolean superDispatchKeyEvent(KeyEvent event) { 1254 return mDecor.superDispatchKeyEvent(event); 1255 } 1256 1257 @Override 1258 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 1259 return mDecor.superDispatchKeyShortcutEvent(event); 1260 } 1261 1262 @Override 1263 public boolean superDispatchTouchEvent(MotionEvent event) { 1264 return mDecor.superDispatchTouchEvent(event); 1265 } 1266 1267 @Override 1268 public boolean superDispatchTrackballEvent(MotionEvent event) { 1269 return mDecor.superDispatchTrackballEvent(event); 1270 } 1271 1272 @Override 1273 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 1274 return mDecor.superDispatchGenericMotionEvent(event); 1275 } 1276 1277 /** 1278 * A key was pressed down and not handled by anything else in the window. 1279 * 1280 * @see #onKeyUp 1281 * @see android.view.KeyEvent 1282 */ 1283 protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) { 1284 /* **************************************************************************** 1285 * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. 1286 * 1287 * If your key handling must happen before the app gets a crack at the event, 1288 * it goes in PhoneWindowManager. 1289 * 1290 * If your key handling should happen in all windows, and does not depend on 1291 * the state of the current application, other than that the current 1292 * application can override the behavior by handling the event itself, it 1293 * should go in PhoneFallbackEventHandler. 1294 * 1295 * Only if your handling depends on the window, and the fact that it has 1296 * a DecorView, should it go here. 1297 * ****************************************************************************/ 1298 1299 final KeyEvent.DispatcherState dispatcher = 1300 mDecor != null ? mDecor.getKeyDispatcherState() : null; 1301 //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount() 1302 // + " flags=0x" + Integer.toHexString(event.getFlags())); 1303 1304 switch (keyCode) { 1305 case KeyEvent.KEYCODE_VOLUME_UP: 1306 case KeyEvent.KEYCODE_VOLUME_DOWN: 1307 case KeyEvent.KEYCODE_VOLUME_MUTE: { 1308 // Similar code is in PhoneFallbackEventHandler in case the window 1309 // doesn't have one of these. In this case, we execute it here and 1310 // eat the event instead, because we have mVolumeControlStreamType 1311 // and they don't. 1312 getAudioManager().handleKeyDown(keyCode, mVolumeControlStreamType); 1313 return true; 1314 } 1315 1316 case KeyEvent.KEYCODE_MENU: { 1317 onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event); 1318 return true; 1319 } 1320 1321 case KeyEvent.KEYCODE_BACK: { 1322 if (event.getRepeatCount() > 0) break; 1323 if (featureId < 0) break; 1324 // Currently don't do anything with long press. 1325 dispatcher.startTracking(event, this); 1326 return true; 1327 } 1328 1329 } 1330 1331 return false; 1332 } 1333 1334 private KeyguardManager getKeyguardManager() { 1335 if (mKeyguardManager == null) { 1336 mKeyguardManager = (KeyguardManager) getContext().getSystemService( 1337 Context.KEYGUARD_SERVICE); 1338 } 1339 return mKeyguardManager; 1340 } 1341 1342 AudioManager getAudioManager() { 1343 if (mAudioManager == null) { 1344 mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE); 1345 } 1346 return mAudioManager; 1347 } 1348 1349 /** 1350 * A key was released and not handled by anything else in the window. 1351 * 1352 * @see #onKeyDown 1353 * @see android.view.KeyEvent 1354 */ 1355 protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) { 1356 final KeyEvent.DispatcherState dispatcher = 1357 mDecor != null ? mDecor.getKeyDispatcherState() : null; 1358 if (dispatcher != null) { 1359 dispatcher.handleUpEvent(event); 1360 } 1361 //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount() 1362 // + " flags=0x" + Integer.toHexString(event.getFlags())); 1363 1364 switch (keyCode) { 1365 case KeyEvent.KEYCODE_VOLUME_UP: 1366 case KeyEvent.KEYCODE_VOLUME_DOWN: 1367 case KeyEvent.KEYCODE_VOLUME_MUTE: { 1368 // Similar code is in PhoneFallbackEventHandler in case the window 1369 // doesn't have one of these. In this case, we execute it here and 1370 // eat the event instead, because we have mVolumeControlStreamType 1371 // and they don't. 1372 getAudioManager().handleKeyUp(keyCode, mVolumeControlStreamType); 1373 return true; 1374 } 1375 1376 case KeyEvent.KEYCODE_MENU: { 1377 onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId, 1378 event); 1379 return true; 1380 } 1381 1382 case KeyEvent.KEYCODE_BACK: { 1383 if (featureId < 0) break; 1384 if (event.isTracking() && !event.isCanceled()) { 1385 if (featureId == FEATURE_OPTIONS_PANEL) { 1386 PanelFeatureState st = getPanelState(featureId, false); 1387 if (st != null && st.isInExpandedMode) { 1388 // If the user is in an expanded menu and hits back, it 1389 // should go back to the icon menu 1390 reopenMenu(true); 1391 return true; 1392 } 1393 } 1394 closePanel(featureId); 1395 return true; 1396 } 1397 break; 1398 } 1399 1400 case KeyEvent.KEYCODE_SEARCH: { 1401 /* 1402 * Do this in onKeyUp since the Search key is also used for 1403 * chording quick launch shortcuts. 1404 */ 1405 if (getKeyguardManager().inKeyguardRestrictedInputMode()) { 1406 break; 1407 } 1408 if (event.isTracking() && !event.isCanceled()) { 1409 launchDefaultSearch(); 1410 } 1411 return true; 1412 } 1413 } 1414 1415 return false; 1416 } 1417 1418 @Override 1419 protected void onActive() { 1420 } 1421 1422 @Override 1423 public final View getDecorView() { 1424 if (mDecor == null) { 1425 installDecor(); 1426 } 1427 return mDecor; 1428 } 1429 1430 @Override 1431 public final View peekDecorView() { 1432 return mDecor; 1433 } 1434 1435 static private final String FOCUSED_ID_TAG = "android:focusedViewId"; 1436 static private final String VIEWS_TAG = "android:views"; 1437 static private final String PANELS_TAG = "android:Panels"; 1438 static private final String ACTION_BAR_TAG = "android:ActionBar"; 1439 1440 /** {@inheritDoc} */ 1441 @Override 1442 public Bundle saveHierarchyState() { 1443 Bundle outState = new Bundle(); 1444 if (mContentParent == null) { 1445 return outState; 1446 } 1447 1448 SparseArray<Parcelable> states = new SparseArray<Parcelable>(); 1449 mContentParent.saveHierarchyState(states); 1450 outState.putSparseParcelableArray(VIEWS_TAG, states); 1451 1452 // save the focused view id 1453 View focusedView = mContentParent.findFocus(); 1454 if (focusedView != null) { 1455 if (focusedView.getId() != View.NO_ID) { 1456 outState.putInt(FOCUSED_ID_TAG, focusedView.getId()); 1457 } else { 1458 if (Config.LOGD) { 1459 Log.d(TAG, "couldn't save which view has focus because the focused view " 1460 + focusedView + " has no id."); 1461 } 1462 } 1463 } 1464 1465 // save the panels 1466 SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>(); 1467 savePanelState(panelStates); 1468 if (panelStates.size() > 0) { 1469 outState.putSparseParcelableArray(PANELS_TAG, panelStates); 1470 } 1471 1472 if (mActionBar != null) { 1473 outState.putBoolean(ACTION_BAR_TAG, mActionBar.isOverflowMenuShowing()); 1474 } 1475 1476 return outState; 1477 } 1478 1479 /** {@inheritDoc} */ 1480 @Override 1481 public void restoreHierarchyState(Bundle savedInstanceState) { 1482 if (mContentParent == null) { 1483 return; 1484 } 1485 1486 SparseArray<Parcelable> savedStates 1487 = savedInstanceState.getSparseParcelableArray(VIEWS_TAG); 1488 if (savedStates != null) { 1489 mContentParent.restoreHierarchyState(savedStates); 1490 } 1491 1492 // restore the focused view 1493 int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID); 1494 if (focusedViewId != View.NO_ID) { 1495 View needsFocus = mContentParent.findViewById(focusedViewId); 1496 if (needsFocus != null) { 1497 needsFocus.requestFocus(); 1498 } else { 1499 Log.w(TAG, 1500 "Previously focused view reported id " + focusedViewId 1501 + " during save, but can't be found during restore."); 1502 } 1503 } 1504 1505 // restore the panels 1506 SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG); 1507 if (panelStates != null) { 1508 restorePanelState(panelStates); 1509 } 1510 1511 if (mActionBar != null && savedInstanceState.getBoolean(ACTION_BAR_TAG)) { 1512 mActionBar.postShowOverflowMenu(); 1513 } 1514 } 1515 1516 /** 1517 * Invoked when the panels should freeze their state. 1518 * 1519 * @param icicles Save state into this. This is usually indexed by the 1520 * featureId. This will be given to {@link #restorePanelState} in the 1521 * future. 1522 */ 1523 private void savePanelState(SparseArray<Parcelable> icicles) { 1524 PanelFeatureState[] panels = mPanels; 1525 if (panels == null) { 1526 return; 1527 } 1528 1529 for (int curFeatureId = panels.length - 1; curFeatureId >= 0; curFeatureId--) { 1530 if (panels[curFeatureId] != null) { 1531 icicles.put(curFeatureId, panels[curFeatureId].onSaveInstanceState()); 1532 } 1533 } 1534 } 1535 1536 /** 1537 * Invoked when the panels should thaw their state from a previously frozen state. 1538 * 1539 * @param icicles The state saved by {@link #savePanelState} that needs to be thawed. 1540 */ 1541 private void restorePanelState(SparseArray<Parcelable> icicles) { 1542 PanelFeatureState st; 1543 for (int curFeatureId = icicles.size() - 1; curFeatureId >= 0; curFeatureId--) { 1544 st = getPanelState(curFeatureId, false /* required */); 1545 if (st == null) { 1546 // The panel must not have been required, and is currently not around, skip it 1547 continue; 1548 } 1549 1550 st.onRestoreInstanceState(icicles.get(curFeatureId)); 1551 } 1552 1553 /* 1554 * Implementation note: call openPanelsAfterRestore later to actually open the 1555 * restored panels. 1556 */ 1557 } 1558 1559 /** 1560 * Opens the panels that have had their state restored. This should be 1561 * called sometime after {@link #restorePanelState} when it is safe to add 1562 * to the window manager. 1563 */ 1564 private void openPanelsAfterRestore() { 1565 PanelFeatureState[] panels = mPanels; 1566 1567 if (panels == null) { 1568 return; 1569 } 1570 1571 PanelFeatureState st; 1572 for (int i = panels.length - 1; i >= 0; i--) { 1573 st = panels[i]; 1574 // We restore the panel if it was last open; we skip it if it 1575 // now is open, to avoid a race condition if the user immediately 1576 // opens it when we are resuming. 1577 if ((st != null) && !st.isOpen && st.wasLastOpen) { 1578 st.isInExpandedMode = st.wasLastExpanded; 1579 openPanel(st, null); 1580 } 1581 } 1582 } 1583 1584 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker { 1585 /* package */int mDefaultOpacity = PixelFormat.OPAQUE; 1586 1587 /** The feature ID of the panel, or -1 if this is the application's DecorView */ 1588 private final int mFeatureId; 1589 1590 private final Rect mDrawingBounds = new Rect(); 1591 1592 private final Rect mBackgroundPadding = new Rect(); 1593 1594 private final Rect mFramePadding = new Rect(); 1595 1596 private final Rect mFrameOffsets = new Rect(); 1597 1598 private boolean mChanging; 1599 1600 private Drawable mMenuBackground; 1601 private boolean mWatchingForMenu; 1602 private int mDownY; 1603 1604 private ActionMode mActionMode; 1605 private ActionBarContextView mActionModeView; 1606 private PopupWindow mActionModePopup; 1607 private Runnable mShowActionModePopup; 1608 1609 public DecorView(Context context, int featureId) { 1610 super(context); 1611 mFeatureId = featureId; 1612 } 1613 1614 @Override 1615 public boolean dispatchKeyEvent(KeyEvent event) { 1616 final int keyCode = event.getKeyCode(); 1617 final int action = event.getAction(); 1618 final boolean isDown = action == KeyEvent.ACTION_DOWN; 1619 1620 if (isDown && (event.getRepeatCount() == 0)) { 1621 // First handle chording of panel key: if a panel key is held 1622 // but not released, try to execute a shortcut in it. 1623 if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) { 1624 boolean handled = dispatchKeyShortcutEvent(event); 1625 if (handled) { 1626 return true; 1627 } 1628 } 1629 1630 // If a panel is open, perform a shortcut on it without the 1631 // chorded panel key 1632 if ((mPreparedPanel != null) && mPreparedPanel.isOpen) { 1633 if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) { 1634 return true; 1635 } 1636 } 1637 } 1638 1639 // Back cancels action modes first. 1640 if (mActionMode != null && keyCode == KeyEvent.KEYCODE_BACK) { 1641 if (action == KeyEvent.ACTION_UP) { 1642 mActionMode.finish(); 1643 } 1644 return true; 1645 } 1646 1647 if (!isDestroyed()) { 1648 final Callback cb = getCallback(); 1649 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) 1650 : super.dispatchKeyEvent(event); 1651 if (handled) { 1652 return true; 1653 } 1654 } 1655 return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event) 1656 : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event); 1657 } 1658 1659 @Override 1660 public boolean dispatchKeyShortcutEvent(KeyEvent ev) { 1661 // Perform the shortcut (mPreparedPanel can be null since 1662 // global shortcuts (such as search) don't rely on a 1663 // prepared panel or menu). 1664 boolean handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev, 1665 Menu.FLAG_PERFORM_NO_CLOSE); 1666 if (handled) { 1667 if (mPreparedPanel != null) { 1668 mPreparedPanel.isHandled = true; 1669 } 1670 return true; 1671 } 1672 1673 // Shortcut not handled by the panel. Dispatch to the view hierarchy. 1674 final Callback cb = getCallback(); 1675 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchKeyShortcutEvent(ev) 1676 : super.dispatchKeyShortcutEvent(ev); 1677 } 1678 1679 @Override 1680 public boolean dispatchTouchEvent(MotionEvent ev) { 1681 final Callback cb = getCallback(); 1682 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) 1683 : super.dispatchTouchEvent(ev); 1684 } 1685 1686 @Override 1687 public boolean dispatchTrackballEvent(MotionEvent ev) { 1688 final Callback cb = getCallback(); 1689 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev) 1690 : super.dispatchTrackballEvent(ev); 1691 } 1692 1693 @Override 1694 public boolean dispatchGenericMotionEvent(MotionEvent ev) { 1695 final Callback cb = getCallback(); 1696 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) 1697 : super.dispatchGenericMotionEvent(ev); 1698 } 1699 1700 public boolean superDispatchKeyEvent(KeyEvent event) { 1701 return super.dispatchKeyEvent(event); 1702 } 1703 1704 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 1705 return super.dispatchKeyShortcutEvent(event); 1706 } 1707 1708 public boolean superDispatchTouchEvent(MotionEvent event) { 1709 return super.dispatchTouchEvent(event); 1710 } 1711 1712 public boolean superDispatchTrackballEvent(MotionEvent event) { 1713 return super.dispatchTrackballEvent(event); 1714 } 1715 1716 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 1717 return super.dispatchGenericMotionEvent(event); 1718 } 1719 1720 @Override 1721 public boolean onTouchEvent(MotionEvent event) { 1722 return onInterceptTouchEvent(event); 1723 } 1724 1725 private boolean isOutOfBounds(int x, int y) { 1726 return x < -5 || y < -5 || x > (getWidth() + 5) 1727 || y > (getHeight() + 5); 1728 } 1729 1730 @Override 1731 public boolean onInterceptTouchEvent(MotionEvent event) { 1732 int action = event.getAction(); 1733 if (mFeatureId >= 0) { 1734 if (action == MotionEvent.ACTION_DOWN) { 1735 int x = (int)event.getX(); 1736 int y = (int)event.getY(); 1737 if (isOutOfBounds(x, y)) { 1738 closePanel(mFeatureId); 1739 return true; 1740 } 1741 } 1742 } 1743 1744 if (!SWEEP_OPEN_MENU) { 1745 return false; 1746 } 1747 1748 if (mFeatureId >= 0) { 1749 if (action == MotionEvent.ACTION_DOWN) { 1750 Log.i(TAG, "Watchiing!"); 1751 mWatchingForMenu = true; 1752 mDownY = (int) event.getY(); 1753 return false; 1754 } 1755 1756 if (!mWatchingForMenu) { 1757 return false; 1758 } 1759 1760 int y = (int)event.getY(); 1761 if (action == MotionEvent.ACTION_MOVE) { 1762 if (y > (mDownY+30)) { 1763 Log.i(TAG, "Closing!"); 1764 closePanel(mFeatureId); 1765 mWatchingForMenu = false; 1766 return true; 1767 } 1768 } else if (action == MotionEvent.ACTION_UP) { 1769 mWatchingForMenu = false; 1770 } 1771 1772 return false; 1773 } 1774 1775 //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY() 1776 // + " (in " + getHeight() + ")"); 1777 1778 if (action == MotionEvent.ACTION_DOWN) { 1779 int y = (int)event.getY(); 1780 if (y >= (getHeight()-5) && !hasChildren()) { 1781 Log.i(TAG, "Watchiing!"); 1782 mWatchingForMenu = true; 1783 } 1784 return false; 1785 } 1786 1787 if (!mWatchingForMenu) { 1788 return false; 1789 } 1790 1791 int y = (int)event.getY(); 1792 if (action == MotionEvent.ACTION_MOVE) { 1793 if (y < (getHeight()-30)) { 1794 Log.i(TAG, "Opening!"); 1795 openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent( 1796 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); 1797 mWatchingForMenu = false; 1798 return true; 1799 } 1800 } else if (action == MotionEvent.ACTION_UP) { 1801 mWatchingForMenu = false; 1802 } 1803 1804 return false; 1805 } 1806 1807 @Override 1808 public void sendAccessibilityEvent(int eventType) { 1809 if (!AccessibilityManager.getInstance(mContext).isEnabled()) { 1810 return; 1811 } 1812 1813 // if we are showing a feature that should be announced and one child 1814 // make this child the event source since this is the feature itself 1815 // otherwise the callback will take over and announce its client 1816 if ((mFeatureId == FEATURE_OPTIONS_PANEL || 1817 mFeatureId == FEATURE_CONTEXT_MENU || 1818 mFeatureId == FEATURE_PROGRESS || 1819 mFeatureId == FEATURE_INDETERMINATE_PROGRESS) 1820 && getChildCount() == 1) { 1821 getChildAt(0).sendAccessibilityEvent(eventType); 1822 } else { 1823 super.sendAccessibilityEvent(eventType); 1824 } 1825 } 1826 1827 @Override 1828 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1829 final Callback cb = getCallback(); 1830 if (cb != null && !isDestroyed()) { 1831 if (cb.dispatchPopulateAccessibilityEvent(event)) { 1832 return true; 1833 } 1834 } 1835 return super.dispatchPopulateAccessibilityEvent(event); 1836 } 1837 1838 @Override 1839 protected boolean setFrame(int l, int t, int r, int b) { 1840 boolean changed = super.setFrame(l, t, r, b); 1841 if (changed) { 1842 final Rect drawingBounds = mDrawingBounds; 1843 getDrawingRect(drawingBounds); 1844 1845 Drawable fg = getForeground(); 1846 if (fg != null) { 1847 final Rect frameOffsets = mFrameOffsets; 1848 drawingBounds.left += frameOffsets.left; 1849 drawingBounds.top += frameOffsets.top; 1850 drawingBounds.right -= frameOffsets.right; 1851 drawingBounds.bottom -= frameOffsets.bottom; 1852 fg.setBounds(drawingBounds); 1853 final Rect framePadding = mFramePadding; 1854 drawingBounds.left += framePadding.left - frameOffsets.left; 1855 drawingBounds.top += framePadding.top - frameOffsets.top; 1856 drawingBounds.right -= framePadding.right - frameOffsets.right; 1857 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom; 1858 } 1859 1860 Drawable bg = getBackground(); 1861 if (bg != null) { 1862 bg.setBounds(drawingBounds); 1863 } 1864 1865 if (SWEEP_OPEN_MENU) { 1866 if (mMenuBackground == null && mFeatureId < 0 1867 && getAttributes().height 1868 == WindowManager.LayoutParams.MATCH_PARENT) { 1869 mMenuBackground = getContext().getResources().getDrawable( 1870 com.android.internal.R.drawable.menu_background); 1871 } 1872 if (mMenuBackground != null) { 1873 mMenuBackground.setBounds(drawingBounds.left, 1874 drawingBounds.bottom-6, drawingBounds.right, 1875 drawingBounds.bottom+20); 1876 } 1877 } 1878 } 1879 return changed; 1880 } 1881 1882 @Override 1883 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1884 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); 1885 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; 1886 1887 final int widthMode = getMode(widthMeasureSpec); 1888 1889 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1890 1891 int width = getMeasuredWidth(); 1892 boolean measure = false; 1893 1894 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); 1895 1896 final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; 1897 1898 if (widthMode == AT_MOST && tv.type != TypedValue.TYPE_NULL) { 1899 final int min; 1900 if (tv.type == TypedValue.TYPE_DIMENSION) { 1901 min = (int)tv.getDimension(metrics); 1902 } else if (tv.type == TypedValue.TYPE_FRACTION) { 1903 min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); 1904 } else { 1905 min = 0; 1906 } 1907 1908 if (width < min) { 1909 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); 1910 measure = true; 1911 } 1912 } 1913 1914 // TODO: Support height? 1915 1916 if (measure) { 1917 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1918 } 1919 } 1920 1921 @Override 1922 public void draw(Canvas canvas) { 1923 super.draw(canvas); 1924 1925 if (mMenuBackground != null) { 1926 mMenuBackground.draw(canvas); 1927 } 1928 } 1929 1930 1931 @Override 1932 public boolean showContextMenuForChild(View originalView) { 1933 // Reuse the context menu builder 1934 if (mContextMenu == null) { 1935 mContextMenu = new ContextMenuBuilder(getContext()); 1936 mContextMenu.setCallback(mContextMenuCallback); 1937 } else { 1938 mContextMenu.clearAll(); 1939 } 1940 1941 mContextMenuHelper = mContextMenu.show(originalView, originalView.getWindowToken()); 1942 return mContextMenuHelper != null; 1943 } 1944 1945 @Override 1946 public ActionMode startActionModeForChild(View originalView, 1947 ActionMode.Callback callback) { 1948 // originalView can be used here to be sure that we don't obscure 1949 // relevant content with the context mode UI. 1950 return startActionMode(callback); 1951 } 1952 1953 @Override 1954 public ActionMode startActionMode(ActionMode.Callback callback) { 1955 if (mActionMode != null) { 1956 mActionMode.finish(); 1957 } 1958 1959 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 1960 ActionMode mode = null; 1961 if (getCallback() != null && !isDestroyed()) { 1962 try { 1963 mode = getCallback().onWindowStartingActionMode(wrappedCallback); 1964 } catch (AbstractMethodError ame) { 1965 // Older apps might not implement this callback method. 1966 } 1967 } 1968 if (mode != null) { 1969 mActionMode = mode; 1970 } else { 1971 if (mActionModeView == null) { 1972 if (hasFeature(FEATURE_ACTION_MODE_OVERLAY)) { 1973 mActionModeView = new ActionBarContextView(mContext); 1974 mActionModePopup = new PopupWindow(mContext, null, 1975 com.android.internal.R.attr.actionModePopupWindowStyle); 1976 mActionModePopup.setLayoutInScreenEnabled(true); 1977 mActionModePopup.setClippingEnabled(false); 1978 mActionModePopup.setContentView(mActionModeView); 1979 mActionModePopup.setWidth(MATCH_PARENT); 1980 1981 TypedValue heightValue = new TypedValue(); 1982 mContext.getTheme().resolveAttribute( 1983 com.android.internal.R.attr.actionBarSize, heightValue, false); 1984 final int height = TypedValue.complexToDimensionPixelSize(heightValue.data, 1985 mContext.getResources().getDisplayMetrics()); 1986 mActionModePopup.setHeight(height); 1987 mShowActionModePopup = new Runnable() { 1988 public void run() { 1989 mActionModePopup.showAtLocation(PhoneWindow.DecorView.this, 1990 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0); 1991 } 1992 }; 1993 } else { 1994 ViewStub stub = (ViewStub) findViewById( 1995 com.android.internal.R.id.action_mode_bar_stub); 1996 if (stub != null) { 1997 mActionModeView = (ActionBarContextView) stub.inflate(); 1998 } 1999 } 2000 } 2001 2002 if (mActionModeView != null) { 2003 mActionModeView.killMode(); 2004 mode = new StandaloneActionMode(getContext(), mActionModeView, wrappedCallback); 2005 if (callback.onCreateActionMode(mode, mode.getMenu())) { 2006 mode.invalidate(); 2007 mActionModeView.initForMode(mode); 2008 mActionModeView.setVisibility(View.VISIBLE); 2009 mActionMode = mode; 2010 if (mActionModePopup != null) { 2011 post(mShowActionModePopup); 2012 } 2013 } else { 2014 mActionMode = null; 2015 } 2016 } 2017 } 2018 if (mActionMode != null && getCallback() != null && !isDestroyed()) { 2019 try { 2020 getCallback().onActionModeStarted(mActionMode); 2021 } catch (AbstractMethodError ame) { 2022 // Older apps might not implement this callback method. 2023 } 2024 } 2025 return mActionMode; 2026 } 2027 2028 public void startChanging() { 2029 mChanging = true; 2030 } 2031 2032 public void finishChanging() { 2033 mChanging = false; 2034 drawableChanged(); 2035 } 2036 2037 public void setWindowBackground(Drawable drawable) { 2038 if (getBackground() != drawable) { 2039 setBackgroundDrawable(drawable); 2040 if (drawable != null) { 2041 drawable.getPadding(mBackgroundPadding); 2042 } else { 2043 mBackgroundPadding.setEmpty(); 2044 } 2045 drawableChanged(); 2046 } 2047 } 2048 2049 @Override 2050 public void setBackgroundDrawable(Drawable d) { 2051 super.setBackgroundDrawable(d); 2052 if (getWindowToken() != null) { 2053 updateWindowResizeState(); 2054 } 2055 } 2056 2057 public void setWindowFrame(Drawable drawable) { 2058 if (getForeground() != drawable) { 2059 setForeground(drawable); 2060 if (drawable != null) { 2061 drawable.getPadding(mFramePadding); 2062 } else { 2063 mFramePadding.setEmpty(); 2064 } 2065 drawableChanged(); 2066 } 2067 } 2068 2069 @Override 2070 protected boolean fitSystemWindows(Rect insets) { 2071 mFrameOffsets.set(insets); 2072 if (getForeground() != null) { 2073 drawableChanged(); 2074 } 2075 return super.fitSystemWindows(insets); 2076 } 2077 2078 private void drawableChanged() { 2079 if (mChanging) { 2080 return; 2081 } 2082 2083 setPadding(mFramePadding.left + mBackgroundPadding.left, mFramePadding.top 2084 + mBackgroundPadding.top, mFramePadding.right + mBackgroundPadding.right, 2085 mFramePadding.bottom + mBackgroundPadding.bottom); 2086 requestLayout(); 2087 invalidate(); 2088 2089 int opacity = PixelFormat.OPAQUE; 2090 2091 // Note: if there is no background, we will assume opaque. The 2092 // common case seems to be that an application sets there to be 2093 // no background so it can draw everything itself. For that, 2094 // we would like to assume OPAQUE and let the app force it to 2095 // the slower TRANSLUCENT mode if that is really what it wants. 2096 Drawable bg = getBackground(); 2097 Drawable fg = getForeground(); 2098 if (bg != null) { 2099 if (fg == null) { 2100 opacity = bg.getOpacity(); 2101 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0 2102 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) { 2103 // If the frame padding is zero, then we can be opaque 2104 // if either the frame -or- the background is opaque. 2105 int fop = fg.getOpacity(); 2106 int bop = bg.getOpacity(); 2107 if (Config.LOGV) 2108 Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop); 2109 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) { 2110 opacity = PixelFormat.OPAQUE; 2111 } else if (fop == PixelFormat.UNKNOWN) { 2112 opacity = bop; 2113 } else if (bop == PixelFormat.UNKNOWN) { 2114 opacity = fop; 2115 } else { 2116 opacity = Drawable.resolveOpacity(fop, bop); 2117 } 2118 } else { 2119 // For now we have to assume translucent if there is a 2120 // frame with padding... there is no way to tell if the 2121 // frame and background together will draw all pixels. 2122 if (Config.LOGV) 2123 Log.v(TAG, "Padding: " + mFramePadding); 2124 opacity = PixelFormat.TRANSLUCENT; 2125 } 2126 } 2127 2128 if (Config.LOGV) 2129 Log.v(TAG, "Background: " + bg + ", Frame: " + fg); 2130 if (Config.LOGV) 2131 Log.v(TAG, "Selected default opacity: " + opacity); 2132 2133 mDefaultOpacity = opacity; 2134 if (mFeatureId < 0) { 2135 setDefaultWindowFormat(opacity); 2136 } 2137 } 2138 2139 @Override 2140 public void onWindowFocusChanged(boolean hasWindowFocus) { 2141 super.onWindowFocusChanged(hasWindowFocus); 2142 2143 // If the user is chording a menu shortcut, release the chord since 2144 // this window lost focus 2145 if (!hasWindowFocus && mPanelChordingKey != 0) { 2146 closePanel(FEATURE_OPTIONS_PANEL); 2147 } 2148 2149 final Callback cb = getCallback(); 2150 if (cb != null && !isDestroyed() && mFeatureId < 0) { 2151 cb.onWindowFocusChanged(hasWindowFocus); 2152 } 2153 } 2154 2155 void updateWindowResizeState() { 2156 Drawable bg = getBackground(); 2157 hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity() 2158 != PixelFormat.OPAQUE); 2159 } 2160 2161 @Override 2162 protected void onAttachedToWindow() { 2163 super.onAttachedToWindow(); 2164 2165 updateWindowResizeState(); 2166 2167 final Callback cb = getCallback(); 2168 if (cb != null && !isDestroyed() && mFeatureId < 0) { 2169 cb.onAttachedToWindow(); 2170 } 2171 2172 if (mFeatureId == -1) { 2173 /* 2174 * The main window has been attached, try to restore any panels 2175 * that may have been open before. This is called in cases where 2176 * an activity is being killed for configuration change and the 2177 * menu was open. When the activity is recreated, the menu 2178 * should be shown again. 2179 */ 2180 openPanelsAfterRestore(); 2181 } 2182 } 2183 2184 @Override 2185 protected void onDetachedFromWindow() { 2186 super.onDetachedFromWindow(); 2187 2188 final Callback cb = getCallback(); 2189 if (cb != null && mFeatureId < 0) { 2190 cb.onDetachedFromWindow(); 2191 } 2192 2193 if (mActionButtonPopup != null) { 2194 if (mActionButtonPopup.isShowing()) { 2195 mActionButtonPopup.dismiss(); 2196 } 2197 mActionButtonPopup = null; 2198 } 2199 2200 if (mActionModePopup != null) { 2201 removeCallbacks(mShowActionModePopup); 2202 if (mActionModePopup.isShowing()) { 2203 mActionModePopup.dismiss(); 2204 } 2205 mActionModePopup = null; 2206 } 2207 } 2208 2209 @Override 2210 protected void onConfigurationChanged(Configuration newConfig) { 2211 if (mActionButtonPopup != null) { 2212 mActionButtonPopup.dismiss(); 2213 post(mActionButtonPopup); 2214 } 2215 } 2216 2217 @Override 2218 public void onCloseSystemDialogs(String reason) { 2219 if (mFeatureId >= 0) { 2220 closeAllPanels(); 2221 } 2222 } 2223 2224 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() { 2225 return mFeatureId < 0 ? mTakeSurfaceCallback : null; 2226 } 2227 2228 public InputQueue.Callback willYouTakeTheInputQueue() { 2229 return mFeatureId < 0 ? mTakeInputQueueCallback : null; 2230 } 2231 2232 public void setSurfaceType(int type) { 2233 PhoneWindow.this.setType(type); 2234 } 2235 2236 public void setSurfaceFormat(int format) { 2237 PhoneWindow.this.setFormat(format); 2238 } 2239 2240 public void setSurfaceKeepScreenOn(boolean keepOn) { 2241 if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2242 else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2243 } 2244 2245 /** 2246 * Clears out internal reference when the action mode is destroyed. 2247 */ 2248 private class ActionModeCallbackWrapper implements ActionMode.Callback { 2249 private ActionMode.Callback mWrapped; 2250 2251 public ActionModeCallbackWrapper(ActionMode.Callback wrapped) { 2252 mWrapped = wrapped; 2253 } 2254 2255 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 2256 return mWrapped.onCreateActionMode(mode, menu); 2257 } 2258 2259 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 2260 return mWrapped.onPrepareActionMode(mode, menu); 2261 } 2262 2263 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 2264 return mWrapped.onActionItemClicked(mode, item); 2265 } 2266 2267 public void onDestroyActionMode(ActionMode mode) { 2268 mWrapped.onDestroyActionMode(mode); 2269 if (mActionModePopup != null) { 2270 removeCallbacks(mShowActionModePopup); 2271 mActionModePopup.dismiss(); 2272 } else if (mActionModeView != null) { 2273 mActionModeView.setVisibility(GONE); 2274 } 2275 if (mActionModeView != null) { 2276 mActionModeView.removeAllViews(); 2277 } 2278 if (getCallback() != null && !isDestroyed()) { 2279 try { 2280 getCallback().onActionModeFinished(mActionMode); 2281 } catch (AbstractMethodError ame) { 2282 // Older apps might not implement this callback method. 2283 } 2284 } 2285 mActionMode = null; 2286 } 2287 } 2288 } 2289 2290 protected DecorView generateDecor() { 2291 return new DecorView(getContext(), -1); 2292 } 2293 2294 protected void setFeatureFromAttrs(int featureId, TypedArray attrs, 2295 int drawableAttr, int alphaAttr) { 2296 Drawable d = attrs.getDrawable(drawableAttr); 2297 if (d != null) { 2298 requestFeature(featureId); 2299 setFeatureDefaultDrawable(featureId, d); 2300 } 2301 if ((getFeatures() & (1 << featureId)) != 0) { 2302 int alpha = attrs.getInt(alphaAttr, -1); 2303 if (alpha >= 0) { 2304 setFeatureDrawableAlpha(featureId, alpha); 2305 } 2306 } 2307 } 2308 2309 protected ViewGroup generateLayout(DecorView decor) { 2310 // Apply data from current theme. 2311 2312 TypedArray a = getWindowStyle(); 2313 2314 if (false) { 2315 System.out.println("From style:"); 2316 String s = "Attrs:"; 2317 for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) { 2318 s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "=" 2319 + a.getString(i); 2320 } 2321 System.out.println(s); 2322 } 2323 2324 mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false); 2325 int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) 2326 & (~getForcedWindowFlags()); 2327 if (mIsFloating) { 2328 setLayout(WRAP_CONTENT, WRAP_CONTENT); 2329 setFlags(0, flagsToUpdate); 2330 } else { 2331 setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); 2332 } 2333 2334 if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { 2335 requestFeature(FEATURE_NO_TITLE); 2336 } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) { 2337 // Don't allow an action bar if there is no title. 2338 requestFeature(FEATURE_ACTION_BAR); 2339 } 2340 2341 if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) { 2342 requestFeature(FEATURE_ACTION_BAR_OVERLAY); 2343 } 2344 2345 if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) { 2346 requestFeature(FEATURE_ACTION_MODE_OVERLAY); 2347 } 2348 2349 if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) { 2350 setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags())); 2351 } 2352 2353 if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { 2354 setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); 2355 } 2356 2357 if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch, 2358 getContext().getApplicationInfo().targetSdkVersion 2359 >= android.os.Build.VERSION_CODES.HONEYCOMB)) { 2360 setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags())); 2361 } 2362 2363 a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor); 2364 a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor); 2365 2366 if (getContext().getApplicationInfo().targetSdkVersion 2367 < android.os.Build.VERSION_CODES.HONEYCOMB) { 2368 addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); 2369 } 2370 2371 if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion 2372 >= android.os.Build.VERSION_CODES.HONEYCOMB) { 2373 if (a.getBoolean( 2374 com.android.internal.R.styleable.Window_windowCloseOnTouchOutside, 2375 false)) { 2376 setCloseOnTouchOutsideIfNotSet(true); 2377 } 2378 } 2379 2380 WindowManager.LayoutParams params = getAttributes(); 2381 2382 if (!hasSoftInputMode()) { 2383 params.softInputMode = a.getInt( 2384 com.android.internal.R.styleable.Window_windowSoftInputMode, 2385 params.softInputMode); 2386 } 2387 2388 if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled, 2389 mIsFloating)) { 2390 /* All dialogs should have the window dimmed */ 2391 if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { 2392 params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; 2393 } 2394 params.dimAmount = a.getFloat( 2395 android.R.styleable.Window_backgroundDimAmount, 0.5f); 2396 } 2397 2398 if (params.windowAnimations == 0) { 2399 params.windowAnimations = a.getResourceId( 2400 com.android.internal.R.styleable.Window_windowAnimationStyle, 0); 2401 } 2402 2403 // The rest are only done if this window is not embedded; otherwise, 2404 // the values are inherited from our container. 2405 if (getContainer() == null) { 2406 if (mBackgroundDrawable == null) { 2407 if (mBackgroundResource == 0) { 2408 mBackgroundResource = a.getResourceId( 2409 com.android.internal.R.styleable.Window_windowBackground, 0); 2410 } 2411 if (mFrameResource == 0) { 2412 mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0); 2413 } 2414 if (false) { 2415 System.out.println("Background: " 2416 + Integer.toHexString(mBackgroundResource) + " Frame: " 2417 + Integer.toHexString(mFrameResource)); 2418 } 2419 } 2420 mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000); 2421 } 2422 2423 // Inflate the window decor. 2424 2425 int layoutResource; 2426 int features = getLocalFeatures(); 2427 // System.out.println("Features: 0x" + Integer.toHexString(features)); 2428 if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { 2429 if (mIsFloating) { 2430 TypedValue res = new TypedValue(); 2431 getContext().getTheme().resolveAttribute( 2432 com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true); 2433 layoutResource = res.resourceId; 2434 } else { 2435 layoutResource = com.android.internal.R.layout.screen_title_icons; 2436 } 2437 // XXX Remove this once action bar supports these features. 2438 removeFeature(FEATURE_ACTION_BAR); 2439 // System.out.println("Title Icons!"); 2440 } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 2441 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { 2442 // Special case for a window with only a progress bar (and title). 2443 // XXX Need to have a no-title version of embedded windows. 2444 layoutResource = com.android.internal.R.layout.screen_progress; 2445 // System.out.println("Progress!"); 2446 } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { 2447 // Special case for a window with a custom title. 2448 // If the window is floating, we need a dialog layout 2449 if (mIsFloating) { 2450 TypedValue res = new TypedValue(); 2451 getContext().getTheme().resolveAttribute( 2452 com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true); 2453 layoutResource = res.resourceId; 2454 } else { 2455 layoutResource = com.android.internal.R.layout.screen_custom_title; 2456 } 2457 // XXX Remove this once action bar supports these features. 2458 removeFeature(FEATURE_ACTION_BAR); 2459 } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { 2460 // If no other features and not embedded, only need a title. 2461 // If the window is floating, we need a dialog layout 2462 if (mIsFloating) { 2463 TypedValue res = new TypedValue(); 2464 getContext().getTheme().resolveAttribute( 2465 com.android.internal.R.attr.dialogTitleDecorLayout, res, true); 2466 layoutResource = res.resourceId; 2467 } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { 2468 if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) { 2469 layoutResource = com.android.internal.R.layout.screen_action_bar_overlay; 2470 } else { 2471 layoutResource = com.android.internal.R.layout.screen_action_bar; 2472 } 2473 } else { 2474 layoutResource = com.android.internal.R.layout.screen_title; 2475 } 2476 // System.out.println("Title!"); 2477 } else { 2478 // Embedded, so no decoration is needed. 2479 layoutResource = com.android.internal.R.layout.screen_simple; 2480 // System.out.println("Simple!"); 2481 } 2482 2483 mDecor.startChanging(); 2484 2485 View in = mLayoutInflater.inflate(layoutResource, null); 2486 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 2487 2488 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 2489 if (contentParent == null) { 2490 throw new RuntimeException("Window couldn't find content container view"); 2491 } 2492 2493 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 2494 ProgressBar progress = getCircularProgressBar(false); 2495 if (progress != null) { 2496 progress.setIndeterminate(true); 2497 } 2498 } 2499 2500 // Remaining setup -- of background and title -- that only applies 2501 // to top-level windows. 2502 if (getContainer() == null) { 2503 Drawable drawable = mBackgroundDrawable; 2504 if (mBackgroundResource != 0) { 2505 drawable = getContext().getResources().getDrawable(mBackgroundResource); 2506 } 2507 mDecor.setWindowBackground(drawable); 2508 drawable = null; 2509 if (mFrameResource != 0) { 2510 drawable = getContext().getResources().getDrawable(mFrameResource); 2511 } 2512 mDecor.setWindowFrame(drawable); 2513 2514 // System.out.println("Text=" + Integer.toHexString(mTextColor) + 2515 // " Sel=" + Integer.toHexString(mTextSelectedColor) + 2516 // " Title=" + Integer.toHexString(mTitleColor)); 2517 2518 if (mTitleColor == 0) { 2519 mTitleColor = mTextColor; 2520 } 2521 2522 if (mTitle != null) { 2523 setTitle(mTitle); 2524 } 2525 setTitleColor(mTitleColor); 2526 } 2527 2528 mDecor.finishChanging(); 2529 2530 return contentParent; 2531 } 2532 2533 /** @hide */ 2534 public void alwaysReadCloseOnTouchAttr() { 2535 mAlwaysReadCloseOnTouchAttr = true; 2536 } 2537 2538 private void installDecor() { 2539 if (mDecor == null) { 2540 mDecor = generateDecor(); 2541 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 2542 mDecor.setIsRootNamespace(true); 2543 } 2544 if (mContentParent == null) { 2545 mContentParent = generateLayout(mDecor); 2546 2547 mTitleView = (TextView)findViewById(com.android.internal.R.id.title); 2548 if (mTitleView != null) { 2549 if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { 2550 View titleContainer = findViewById(com.android.internal.R.id.title_container); 2551 if (titleContainer != null) { 2552 titleContainer.setVisibility(View.GONE); 2553 } else { 2554 mTitleView.setVisibility(View.GONE); 2555 } 2556 if (mContentParent instanceof FrameLayout) { 2557 ((FrameLayout)mContentParent).setForeground(null); 2558 } 2559 } else { 2560 mTitleView.setText(mTitle); 2561 } 2562 } else { 2563 mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); 2564 if (mActionBar != null) { 2565 if (mActionBar.getTitle() == null) { 2566 mActionBar.setWindowTitle(mTitle); 2567 } 2568 final int localFeatures = getLocalFeatures(); 2569 if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) { 2570 mActionBar.initProgress(); 2571 } 2572 if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 2573 mActionBar.initIndeterminateProgress(); 2574 } 2575 // Post the panel invalidate for later; avoid application onCreateOptionsMenu 2576 // being called in the middle of onCreate or similar. 2577 mDecor.post(new Runnable() { 2578 public void run() { 2579 if (!isDestroyed()) { 2580 invalidatePanelMenu(FEATURE_ACTION_BAR); 2581 } 2582 } 2583 }); 2584 } 2585 } 2586 } 2587 } 2588 2589 private Drawable loadImageURI(Uri uri) { 2590 try { 2591 return Drawable.createFromStream( 2592 getContext().getContentResolver().openInputStream(uri), null); 2593 } catch (Exception e) { 2594 Log.w(TAG, "Unable to open content: " + uri); 2595 } 2596 return null; 2597 } 2598 2599 private DrawableFeatureState getDrawableState(int featureId, boolean required) { 2600 if ((getFeatures() & (1 << featureId)) == 0) { 2601 if (!required) { 2602 return null; 2603 } 2604 throw new RuntimeException("The feature has not been requested"); 2605 } 2606 2607 DrawableFeatureState[] ar; 2608 if ((ar = mDrawables) == null || ar.length <= featureId) { 2609 DrawableFeatureState[] nar = new DrawableFeatureState[featureId + 1]; 2610 if (ar != null) { 2611 System.arraycopy(ar, 0, nar, 0, ar.length); 2612 } 2613 mDrawables = ar = nar; 2614 } 2615 2616 DrawableFeatureState st = ar[featureId]; 2617 if (st == null) { 2618 ar[featureId] = st = new DrawableFeatureState(featureId); 2619 } 2620 return st; 2621 } 2622 2623 /** 2624 * Gets a panel's state based on its feature ID. 2625 * 2626 * @param featureId The feature ID of the panel. 2627 * @param required Whether the panel is required (if it is required and it 2628 * isn't in our features, this throws an exception). 2629 * @return The panel state. 2630 */ 2631 private PanelFeatureState getPanelState(int featureId, boolean required) { 2632 return getPanelState(featureId, required, null); 2633 } 2634 2635 /** 2636 * Gets a panel's state based on its feature ID. 2637 * 2638 * @param featureId The feature ID of the panel. 2639 * @param required Whether the panel is required (if it is required and it 2640 * isn't in our features, this throws an exception). 2641 * @param convertPanelState Optional: If the panel state does not exist, use 2642 * this as the panel state. 2643 * @return The panel state. 2644 */ 2645 private PanelFeatureState getPanelState(int featureId, boolean required, 2646 PanelFeatureState convertPanelState) { 2647 if ((getFeatures() & (1 << featureId)) == 0) { 2648 if (!required) { 2649 return null; 2650 } 2651 throw new RuntimeException("The feature has not been requested"); 2652 } 2653 2654 PanelFeatureState[] ar; 2655 if ((ar = mPanels) == null || ar.length <= featureId) { 2656 PanelFeatureState[] nar = new PanelFeatureState[featureId + 1]; 2657 if (ar != null) { 2658 System.arraycopy(ar, 0, nar, 0, ar.length); 2659 } 2660 mPanels = ar = nar; 2661 } 2662 2663 PanelFeatureState st = ar[featureId]; 2664 if (st == null) { 2665 ar[featureId] = st = (convertPanelState != null) 2666 ? convertPanelState 2667 : new PanelFeatureState(featureId); 2668 } 2669 return st; 2670 } 2671 2672 @Override 2673 public final void setChildDrawable(int featureId, Drawable drawable) { 2674 DrawableFeatureState st = getDrawableState(featureId, true); 2675 st.child = drawable; 2676 updateDrawable(featureId, st, false); 2677 } 2678 2679 @Override 2680 public final void setChildInt(int featureId, int value) { 2681 updateInt(featureId, value, false); 2682 } 2683 2684 @Override 2685 public boolean isShortcutKey(int keyCode, KeyEvent event) { 2686 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 2687 return st.menu != null && st.menu.isShortcutKey(keyCode, event); 2688 } 2689 2690 private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) { 2691 // Do nothing if the decor is not yet installed... an update will 2692 // need to be forced when we eventually become active. 2693 if (mContentParent == null) { 2694 return; 2695 } 2696 2697 final int featureMask = 1 << featureId; 2698 2699 if ((getFeatures() & featureMask) == 0 && !fromResume) { 2700 return; 2701 } 2702 2703 Drawable drawable = null; 2704 if (st != null) { 2705 drawable = st.child; 2706 if (drawable == null) 2707 drawable = st.local; 2708 if (drawable == null) 2709 drawable = st.def; 2710 } 2711 if ((getLocalFeatures() & featureMask) == 0) { 2712 if (getContainer() != null) { 2713 if (isActive() || fromResume) { 2714 getContainer().setChildDrawable(featureId, drawable); 2715 } 2716 } 2717 } else if (st != null && (st.cur != drawable || st.curAlpha != st.alpha)) { 2718 // System.out.println("Drawable changed: old=" + st.cur 2719 // + ", new=" + drawable); 2720 st.cur = drawable; 2721 st.curAlpha = st.alpha; 2722 onDrawableChanged(featureId, drawable, st.alpha); 2723 } 2724 } 2725 2726 private void updateInt(int featureId, int value, boolean fromResume) { 2727 2728 // Do nothing if the decor is not yet installed... an update will 2729 // need to be forced when we eventually become active. 2730 if (mContentParent == null) { 2731 return; 2732 } 2733 2734 final int featureMask = 1 << featureId; 2735 2736 if ((getFeatures() & featureMask) == 0 && !fromResume) { 2737 return; 2738 } 2739 2740 if ((getLocalFeatures() & featureMask) == 0) { 2741 if (getContainer() != null) { 2742 getContainer().setChildInt(featureId, value); 2743 } 2744 } else { 2745 onIntChanged(featureId, value); 2746 } 2747 } 2748 2749 private ImageView getLeftIconView() { 2750 if (mLeftIconView != null) { 2751 return mLeftIconView; 2752 } 2753 if (mContentParent == null) { 2754 installDecor(); 2755 } 2756 return (mLeftIconView = (ImageView)findViewById(com.android.internal.R.id.left_icon)); 2757 } 2758 2759 private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) { 2760 if (mCircularProgressBar != null) { 2761 return mCircularProgressBar; 2762 } 2763 if (mContentParent == null && shouldInstallDecor) { 2764 installDecor(); 2765 } 2766 mCircularProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_circular); 2767 if (mCircularProgressBar != null) { 2768 mCircularProgressBar.setVisibility(View.INVISIBLE); 2769 } 2770 return mCircularProgressBar; 2771 } 2772 2773 private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) { 2774 if (mHorizontalProgressBar != null) { 2775 return mHorizontalProgressBar; 2776 } 2777 if (mContentParent == null && shouldInstallDecor) { 2778 installDecor(); 2779 } 2780 mHorizontalProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress_horizontal); 2781 if (mHorizontalProgressBar != null) { 2782 mHorizontalProgressBar.setVisibility(View.INVISIBLE); 2783 } 2784 return mHorizontalProgressBar; 2785 } 2786 2787 private ImageView getRightIconView() { 2788 if (mRightIconView != null) { 2789 return mRightIconView; 2790 } 2791 if (mContentParent == null) { 2792 installDecor(); 2793 } 2794 return (mRightIconView = (ImageView)findViewById(com.android.internal.R.id.right_icon)); 2795 } 2796 2797 /** 2798 * Helper method for calling the {@link Callback#onPanelClosed(int, Menu)} 2799 * callback. This method will grab whatever extra state is needed for the 2800 * callback that isn't given in the parameters. If the panel is not open, 2801 * this will not perform the callback. 2802 * 2803 * @param featureId Feature ID of the panel that was closed. Must be given. 2804 * @param panel Panel that was closed. Optional but useful if there is no 2805 * menu given. 2806 * @param menu The menu that was closed. Optional, but give if you have. 2807 */ 2808 private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) { 2809 final Callback cb = getCallback(); 2810 if (cb == null) 2811 return; 2812 2813 // Try to get a menu 2814 if (menu == null) { 2815 // Need a panel to grab the menu, so try to get that 2816 if (panel == null) { 2817 if ((featureId >= 0) && (featureId < mPanels.length)) { 2818 panel = mPanels[featureId]; 2819 } 2820 } 2821 2822 if (panel != null) { 2823 // menu still may be null, which is okay--we tried our best 2824 menu = panel.menu; 2825 } 2826 } 2827 2828 // If the panel is not open, do not callback 2829 if ((panel != null) && (!panel.isOpen)) 2830 return; 2831 2832 if (!isDestroyed()) { 2833 cb.onPanelClosed(featureId, menu); 2834 } 2835 } 2836 2837 /** 2838 * Helper method for adding launch-search to most applications. Opens the 2839 * search window using default settings. 2840 * 2841 * @return true if search window opened 2842 */ 2843 private boolean launchDefaultSearch() { 2844 final Callback cb = getCallback(); 2845 if (cb == null || isDestroyed()) { 2846 return false; 2847 } else { 2848 sendCloseSystemWindows("search"); 2849 return cb.onSearchRequested(); 2850 } 2851 } 2852 2853 @Override 2854 public void setVolumeControlStream(int streamType) { 2855 mVolumeControlStreamType = streamType; 2856 } 2857 2858 @Override 2859 public int getVolumeControlStream() { 2860 return mVolumeControlStreamType; 2861 } 2862 2863 private static final class DrawableFeatureState { 2864 DrawableFeatureState(int _featureId) { 2865 featureId = _featureId; 2866 } 2867 2868 final int featureId; 2869 2870 int resid; 2871 2872 Uri uri; 2873 2874 Drawable local; 2875 2876 Drawable child; 2877 2878 Drawable def; 2879 2880 Drawable cur; 2881 2882 int alpha = 255; 2883 2884 int curAlpha = 255; 2885 } 2886 2887 private static final class PanelFeatureState { 2888 2889 /** Feature ID for this panel. */ 2890 int featureId; 2891 2892 // Information pulled from the style for this panel. 2893 2894 int background; 2895 2896 /** The background when the panel spans the entire available width. */ 2897 int fullBackground; 2898 2899 int gravity; 2900 2901 int x; 2902 2903 int y; 2904 2905 int windowAnimations; 2906 2907 /** Dynamic state of the panel. */ 2908 DecorView decorView; 2909 2910 /** The panel that was returned by onCreatePanelView(). */ 2911 View createdPanelView; 2912 2913 /** The panel that we are actually showing. */ 2914 View shownPanelView; 2915 2916 /** Use {@link #setMenu} to set this. */ 2917 Menu menu; 2918 2919 /** 2920 * Whether the panel has been prepared (see 2921 * {@link PhoneWindow#preparePanel}). 2922 */ 2923 boolean isPrepared; 2924 2925 /** 2926 * Whether an item's action has been performed. This happens in obvious 2927 * scenarios (user clicks on menu item), but can also happen with 2928 * chording menu+(shortcut key). 2929 */ 2930 boolean isHandled; 2931 2932 boolean isOpen; 2933 2934 /** 2935 * True if the menu is in expanded mode, false if the menu is in icon 2936 * mode 2937 */ 2938 boolean isInExpandedMode; 2939 2940 public boolean qwertyMode; 2941 2942 boolean refreshDecorView; 2943 2944 boolean refreshMenuContent; 2945 2946 boolean wasLastOpen; 2947 2948 boolean wasLastExpanded; 2949 2950 /** 2951 * Contains the state of the menu when told to freeze. 2952 */ 2953 Bundle frozenMenuState; 2954 2955 PanelFeatureState(int featureId) { 2956 this.featureId = featureId; 2957 2958 refreshDecorView = false; 2959 } 2960 2961 void setStyle(Context context) { 2962 TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme); 2963 background = a.getResourceId( 2964 com.android.internal.R.styleable.Theme_panelBackground, 0); 2965 fullBackground = a.getResourceId( 2966 com.android.internal.R.styleable.Theme_panelFullBackground, 0); 2967 windowAnimations = a.getResourceId( 2968 com.android.internal.R.styleable.Theme_windowAnimationStyle, 0); 2969 a.recycle(); 2970 } 2971 2972 void setMenu(Menu menu) { 2973 this.menu = menu; 2974 2975 if (frozenMenuState != null) { 2976 ((MenuBuilder) menu).restoreHierarchyState(frozenMenuState); 2977 frozenMenuState = null; 2978 } 2979 } 2980 2981 Parcelable onSaveInstanceState() { 2982 SavedState savedState = new SavedState(); 2983 savedState.featureId = featureId; 2984 savedState.isOpen = isOpen; 2985 savedState.isInExpandedMode = isInExpandedMode; 2986 2987 if (menu != null) { 2988 savedState.menuState = new Bundle(); 2989 ((MenuBuilder) menu).saveHierarchyState(savedState.menuState); 2990 } 2991 2992 return savedState; 2993 } 2994 2995 void onRestoreInstanceState(Parcelable state) { 2996 SavedState savedState = (SavedState) state; 2997 featureId = savedState.featureId; 2998 wasLastOpen = savedState.isOpen; 2999 wasLastExpanded = savedState.isInExpandedMode; 3000 frozenMenuState = savedState.menuState; 3001 3002 /* 3003 * A LocalActivityManager keeps the same instance of this class around. 3004 * The first time the menu is being shown after restoring, the 3005 * Activity.onCreateOptionsMenu should be called. But, if it is the 3006 * same instance then menu != null and we won't call that method. 3007 * So, clear this. Also clear any cached views. 3008 */ 3009 menu = null; 3010 createdPanelView = null; 3011 shownPanelView = null; 3012 decorView = null; 3013 } 3014 3015 private static class SavedState implements Parcelable { 3016 int featureId; 3017 boolean isOpen; 3018 boolean isInExpandedMode; 3019 Bundle menuState; 3020 3021 public int describeContents() { 3022 return 0; 3023 } 3024 3025 public void writeToParcel(Parcel dest, int flags) { 3026 dest.writeInt(featureId); 3027 dest.writeInt(isOpen ? 1 : 0); 3028 dest.writeInt(isInExpandedMode ? 1 : 0); 3029 3030 if (isOpen) { 3031 dest.writeBundle(menuState); 3032 } 3033 } 3034 3035 private static SavedState readFromParcel(Parcel source) { 3036 SavedState savedState = new SavedState(); 3037 savedState.featureId = source.readInt(); 3038 savedState.isOpen = source.readInt() == 1; 3039 savedState.isInExpandedMode = source.readInt() == 1; 3040 3041 if (savedState.isOpen) { 3042 savedState.menuState = source.readBundle(); 3043 } 3044 3045 return savedState; 3046 } 3047 3048 public static final Parcelable.Creator<SavedState> CREATOR 3049 = new Parcelable.Creator<SavedState>() { 3050 public SavedState createFromParcel(Parcel in) { 3051 return readFromParcel(in); 3052 } 3053 3054 public SavedState[] newArray(int size) { 3055 return new SavedState[size]; 3056 } 3057 }; 3058 } 3059 3060 } 3061 3062 /** 3063 * Simple implementation of MenuBuilder.Callback that: 3064 * <li> Opens a submenu when selected. 3065 * <li> Calls back to the callback's onMenuItemSelected when an item is 3066 * selected. 3067 */ 3068 private final class DialogMenuCallback implements MenuBuilder.Callback { 3069 private int mFeatureId; 3070 private MenuDialogHelper mSubMenuHelper; 3071 3072 public DialogMenuCallback(int featureId) { 3073 mFeatureId = featureId; 3074 } 3075 3076 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 3077 if (allMenusAreClosing) { 3078 Callback callback = getCallback(); 3079 if (callback != null && !isDestroyed()) { 3080 callback.onPanelClosed(mFeatureId, menu); 3081 } 3082 3083 if (menu == mContextMenu) { 3084 dismissContextMenu(); 3085 } 3086 3087 // Dismiss the submenu, if it is showing 3088 if (mSubMenuHelper != null) { 3089 mSubMenuHelper.dismiss(); 3090 mSubMenuHelper = null; 3091 } 3092 } 3093 } 3094 3095 public void onCloseSubMenu(SubMenuBuilder menu) { 3096 Callback callback = getCallback(); 3097 if (callback != null && !isDestroyed()) { 3098 callback.onPanelClosed(mFeatureId, menu.getRootMenu()); 3099 } 3100 } 3101 3102 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 3103 Callback callback = getCallback(); 3104 return (callback != null && !isDestroyed()) 3105 && callback.onMenuItemSelected(mFeatureId, item); 3106 } 3107 3108 public void onMenuModeChange(MenuBuilder menu) { 3109 } 3110 3111 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 3112 // Set a simple callback for the submenu 3113 subMenu.setCallback(this); 3114 3115 // The window manager will give us a valid window token 3116 mSubMenuHelper = new MenuDialogHelper(subMenu); 3117 mSubMenuHelper.show(null); 3118 3119 return true; 3120 } 3121 } 3122 3123 void sendCloseSystemWindows() { 3124 PhoneWindowManager.sendCloseSystemWindows(getContext(), null); 3125 } 3126 3127 void sendCloseSystemWindows(String reason) { 3128 PhoneWindowManager.sendCloseSystemWindows(getContext(), reason); 3129 } 3130 3131 private class ActionButtonSubmenu extends MenuPopupHelper implements Runnable { 3132 private SubMenuBuilder mSubMenu; 3133 3134 public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) { 3135 super(context, subMenu); 3136 mSubMenu = subMenu; 3137 3138 MenuBuilder parentMenu = subMenu.getRootMenu(); 3139 MenuItemImpl item = (MenuItemImpl) subMenu.getItem(); 3140 if (!item.isActionButton()) { 3141 // Give a reasonable anchor to nested submenus. 3142 ActionMenuView amv = (ActionMenuView) parentMenu.getMenuView( 3143 MenuBuilder.TYPE_ACTION_BUTTON, null); 3144 3145 View anchor = amv.getOverflowButton(); 3146 if (anchor == null) { 3147 anchor = amv; 3148 } 3149 setAnchorView(anchor); 3150 } 3151 } 3152 3153 @Override 3154 public void onDismiss() { 3155 super.onDismiss(); 3156 mSubMenu.getCallback().onCloseSubMenu(mSubMenu); 3157 mActionButtonPopup = null; 3158 } 3159 3160 @Override 3161 public void run() { 3162 if (tryShow()) { 3163 Callback cb = getCallback(); 3164 if (cb != null && !isDestroyed()) { 3165 cb.onMenuOpened(FEATURE_ACTION_BAR, mSubMenu); 3166 } 3167 } 3168 } 3169 } 3170} 3171