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