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