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