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