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