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