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