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