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