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