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.view.View.MeasureSpec.AT_MOST; 20import static android.view.View.MeasureSpec.EXACTLY; 21import static android.view.View.MeasureSpec.getMode; 22import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 23import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 24import static android.view.WindowManager.LayoutParams.*; 25 26import android.animation.Animator; 27import android.animation.ObjectAnimator; 28import android.app.ActivityManagerNative; 29import android.app.SearchManager; 30import android.os.Build; 31import android.os.UserHandle; 32 33import android.view.ActionMode; 34import android.view.ContextThemeWrapper; 35import android.view.Gravity; 36import android.view.IRotationWatcher.Stub; 37import android.view.IWindowManager; 38import android.view.InputDevice; 39import android.view.InputEvent; 40import android.view.InputQueue; 41import android.view.KeyCharacterMap; 42import android.view.KeyEvent; 43import android.view.LayoutInflater; 44import android.view.Menu; 45import android.view.MenuItem; 46import android.view.MotionEvent; 47import android.view.SearchEvent; 48import android.view.SurfaceHolder.Callback2; 49import android.view.View; 50import android.view.ViewConfiguration; 51import android.view.ViewGroup; 52import android.view.ViewManager; 53import android.view.ViewParent; 54import android.view.ViewRootImpl; 55import android.view.ViewStub; 56import android.view.ViewTreeObserver.OnPreDrawListener; 57import android.view.Window; 58import android.view.WindowInsets; 59import android.view.WindowManager; 60import com.android.internal.R; 61import com.android.internal.view.FloatingActionMode; 62import com.android.internal.view.RootViewSurfaceTaker; 63import com.android.internal.view.StandaloneActionMode; 64import com.android.internal.view.menu.ContextMenuBuilder; 65import com.android.internal.view.menu.IconMenuPresenter; 66import com.android.internal.view.menu.ListMenuPresenter; 67import com.android.internal.view.menu.MenuBuilder; 68import com.android.internal.view.menu.MenuDialogHelper; 69import com.android.internal.view.menu.MenuPresenter; 70import com.android.internal.view.menu.MenuView; 71import com.android.internal.widget.ActionBarContextView; 72import com.android.internal.widget.BackgroundFallback; 73import com.android.internal.widget.DecorContentParent; 74import com.android.internal.widget.FloatingToolbar; 75import com.android.internal.widget.SwipeDismissLayout; 76 77import android.app.ActivityManager; 78import android.app.KeyguardManager; 79import android.content.Context; 80import android.content.Intent; 81import android.content.pm.PackageManager; 82import android.content.res.Configuration; 83import android.content.res.Resources.Theme; 84import android.content.res.TypedArray; 85import android.graphics.Canvas; 86import android.graphics.Color; 87import android.graphics.PixelFormat; 88import android.graphics.Rect; 89import android.graphics.drawable.Drawable; 90import android.media.AudioManager; 91import android.media.session.MediaController; 92import android.media.session.MediaSessionLegacyHelper; 93import android.net.Uri; 94import android.os.Bundle; 95import android.os.Handler; 96import android.os.Parcel; 97import android.os.Parcelable; 98import android.os.RemoteException; 99import android.os.ServiceManager; 100import android.transition.Scene; 101import android.transition.Transition; 102import android.transition.TransitionInflater; 103import android.transition.TransitionManager; 104import android.transition.TransitionSet; 105import android.util.AndroidRuntimeException; 106import android.util.DisplayMetrics; 107import android.util.EventLog; 108import android.util.Log; 109import android.util.SparseArray; 110import android.util.TypedValue; 111import android.view.accessibility.AccessibilityEvent; 112import android.view.accessibility.AccessibilityManager; 113import android.view.animation.Animation; 114import android.view.animation.AnimationUtils; 115import android.view.animation.Interpolator; 116import android.widget.FrameLayout; 117import android.widget.ImageView; 118import android.widget.PopupWindow; 119import android.widget.ProgressBar; 120import android.widget.TextView; 121 122import java.lang.ref.WeakReference; 123import java.util.ArrayList; 124 125/** 126 * Android-specific Window. 127 * <p> 128 * todo: need to pull the generic functionality out into a base class 129 * in android.widget. 130 * 131 * @hide 132 */ 133public class PhoneWindow extends Window implements MenuBuilder.Callback { 134 135 private final static String TAG = "PhoneWindow"; 136 137 private final static boolean SWEEP_OPEN_MENU = false; 138 139 private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300; 140 141 private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES | 142 (1 << FEATURE_CUSTOM_TITLE) | 143 (1 << FEATURE_CONTENT_TRANSITIONS) | 144 (1 << FEATURE_ACTIVITY_TRANSITIONS) | 145 (1 << FEATURE_ACTION_MODE_OVERLAY); 146 147 private static final Transition USE_DEFAULT_TRANSITION = new TransitionSet(); 148 149 /** 150 * Simple callback used by the context menu and its submenus. The options 151 * menu submenus do not use this (their behavior is more complex). 152 */ 153 final DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU); 154 155 final TypedValue mMinWidthMajor = new TypedValue(); 156 final TypedValue mMinWidthMinor = new TypedValue(); 157 TypedValue mFixedWidthMajor; 158 TypedValue mFixedWidthMinor; 159 TypedValue mFixedHeightMajor; 160 TypedValue mFixedHeightMinor; 161 162 // This is the top-level view of the window, containing the window decor. 163 private DecorView mDecor; 164 165 // This is the view in which the window contents are placed. It is either 166 // mDecor itself, or a child of mDecor where the contents go. 167 private ViewGroup mContentParent; 168 169 private ViewGroup mContentRoot; 170 171 Callback2 mTakeSurfaceCallback; 172 173 InputQueue.Callback mTakeInputQueueCallback; 174 175 private boolean mIsFloating; 176 177 private LayoutInflater mLayoutInflater; 178 179 private TextView mTitleView; 180 181 private DecorContentParent mDecorContentParent; 182 private ActionMenuPresenterCallback mActionMenuPresenterCallback; 183 private PanelMenuPresenterCallback mPanelMenuPresenterCallback; 184 185 private TransitionManager mTransitionManager; 186 private Scene mContentScene; 187 188 // The icon resource has been explicitly set elsewhere 189 // and should not be overwritten with a default. 190 static final int FLAG_RESOURCE_SET_ICON = 1 << 0; 191 192 // The logo resource has been explicitly set elsewhere 193 // and should not be overwritten with a default. 194 static final int FLAG_RESOURCE_SET_LOGO = 1 << 1; 195 196 // The icon resource is currently configured to use the system fallback 197 // as no default was previously specified. Anything can override this. 198 static final int FLAG_RESOURCE_SET_ICON_FALLBACK = 1 << 2; 199 200 int mResourcesSetFlags; 201 int mIconRes; 202 int mLogoRes; 203 204 private DrawableFeatureState[] mDrawables; 205 206 private PanelFeatureState[] mPanels; 207 208 /** 209 * The panel that is prepared or opened (the most recent one if there are 210 * multiple panels). Shortcuts will go to this panel. It gets set in 211 * {@link #preparePanel} and cleared in {@link #closePanel}. 212 */ 213 private PanelFeatureState mPreparedPanel; 214 215 /** 216 * The keycode that is currently held down (as a modifier) for chording. If 217 * this is 0, there is no key held down. 218 */ 219 private int mPanelChordingKey; 220 221 private ImageView mLeftIconView; 222 223 private ImageView mRightIconView; 224 225 private ProgressBar mCircularProgressBar; 226 227 private ProgressBar mHorizontalProgressBar; 228 229 private int mBackgroundResource = 0; 230 private int mBackgroundFallbackResource = 0; 231 232 private Drawable mBackgroundDrawable; 233 234 private float mElevation; 235 236 /** Whether window content should be clipped to the background outline. */ 237 private boolean mClipToOutline; 238 239 private int mFrameResource = 0; 240 241 private int mTextColor = 0; 242 private int mStatusBarColor = 0; 243 private int mNavigationBarColor = 0; 244 private boolean mForcedStatusBarColor = false; 245 private boolean mForcedNavigationBarColor = false; 246 247 private CharSequence mTitle = null; 248 249 private int mTitleColor = 0; 250 251 private boolean mAlwaysReadCloseOnTouchAttr = false; 252 253 private ContextMenuBuilder mContextMenu; 254 private MenuDialogHelper mContextMenuHelper; 255 private boolean mClosingActionMenu; 256 257 private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; 258 private MediaController mMediaController; 259 260 private AudioManager mAudioManager; 261 private KeyguardManager mKeyguardManager; 262 263 private int mUiOptions = 0; 264 265 private boolean mInvalidatePanelMenuPosted; 266 private int mInvalidatePanelMenuFeatures; 267 private final Runnable mInvalidatePanelMenuRunnable = new Runnable() { 268 @Override public void run() { 269 for (int i = 0; i <= FEATURE_MAX; i++) { 270 if ((mInvalidatePanelMenuFeatures & 1 << i) != 0) { 271 doInvalidatePanelMenu(i); 272 } 273 } 274 mInvalidatePanelMenuPosted = false; 275 mInvalidatePanelMenuFeatures = 0; 276 } 277 }; 278 279 private Transition mEnterTransition = null; 280 private Transition mReturnTransition = USE_DEFAULT_TRANSITION; 281 private Transition mExitTransition = null; 282 private Transition mReenterTransition = USE_DEFAULT_TRANSITION; 283 private Transition mSharedElementEnterTransition = null; 284 private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION; 285 private Transition mSharedElementExitTransition = null; 286 private Transition mSharedElementReenterTransition = USE_DEFAULT_TRANSITION; 287 private Boolean mAllowReturnTransitionOverlap; 288 private Boolean mAllowEnterTransitionOverlap; 289 private long mBackgroundFadeDurationMillis = -1; 290 private Boolean mSharedElementsUseOverlay; 291 292 private Rect mTempRect; 293 private Rect mOutsets = new Rect(); 294 295 private boolean mIsStartingWindow; 296 297 static class WindowManagerHolder { 298 static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( 299 ServiceManager.getService("window")); 300 } 301 302 static final RotationWatcher sRotationWatcher = new RotationWatcher(); 303 304 public PhoneWindow(Context context) { 305 super(context); 306 mLayoutInflater = LayoutInflater.from(context); 307 } 308 309 @Override 310 public final void setContainer(Window container) { 311 super.setContainer(container); 312 } 313 314 @Override 315 public boolean requestFeature(int featureId) { 316 if (mContentParent != null) { 317 throw new AndroidRuntimeException("requestFeature() must be called before adding content"); 318 } 319 final int features = getFeatures(); 320 final int newFeatures = features | (1 << featureId); 321 if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 && 322 (newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) { 323 // Another feature is enabled and the user is trying to enable the custom title feature 324 // or custom title feature is enabled and the user is trying to enable another feature 325 throw new AndroidRuntimeException( 326 "You cannot combine custom titles with other title features"); 327 } 328 if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) { 329 return false; // Ignore. No title dominates. 330 } 331 if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) { 332 // Remove the action bar feature if we have no title. No title dominates. 333 removeFeature(FEATURE_ACTION_BAR); 334 } 335 336 if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_SWIPE_TO_DISMISS) { 337 throw new AndroidRuntimeException( 338 "You cannot combine swipe dismissal and the action bar."); 339 } 340 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0 && featureId == FEATURE_ACTION_BAR) { 341 throw new AndroidRuntimeException( 342 "You cannot combine swipe dismissal and the action bar."); 343 } 344 345 if (featureId == FEATURE_INDETERMINATE_PROGRESS && 346 getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 347 throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch."); 348 } 349 return super.requestFeature(featureId); 350 } 351 352 @Override 353 public void setUiOptions(int uiOptions) { 354 mUiOptions = uiOptions; 355 } 356 357 @Override 358 public void setUiOptions(int uiOptions, int mask) { 359 mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask); 360 } 361 362 @Override 363 public TransitionManager getTransitionManager() { 364 return mTransitionManager; 365 } 366 367 @Override 368 public void setTransitionManager(TransitionManager tm) { 369 mTransitionManager = tm; 370 } 371 372 @Override 373 public Scene getContentScene() { 374 return mContentScene; 375 } 376 377 @Override 378 public void setContentView(int layoutResID) { 379 // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window 380 // decor, when theme attributes and the like are crystalized. Do not check the feature 381 // before this happens. 382 if (mContentParent == null) { 383 installDecor(); 384 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 385 mContentParent.removeAllViews(); 386 } 387 388 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 389 final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, 390 getContext()); 391 transitionTo(newScene); 392 } else { 393 mLayoutInflater.inflate(layoutResID, mContentParent); 394 } 395 mContentParent.requestApplyInsets(); 396 final Callback cb = getCallback(); 397 if (cb != null && !isDestroyed()) { 398 cb.onContentChanged(); 399 } 400 } 401 402 @Override 403 public void setContentView(View view) { 404 setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 405 } 406 407 @Override 408 public void setContentView(View view, ViewGroup.LayoutParams params) { 409 // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window 410 // decor, when theme attributes and the like are crystalized. Do not check the feature 411 // before this happens. 412 if (mContentParent == null) { 413 installDecor(); 414 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 415 mContentParent.removeAllViews(); 416 } 417 418 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 419 view.setLayoutParams(params); 420 final Scene newScene = new Scene(mContentParent, view); 421 transitionTo(newScene); 422 } else { 423 mContentParent.addView(view, params); 424 } 425 mContentParent.requestApplyInsets(); 426 final Callback cb = getCallback(); 427 if (cb != null && !isDestroyed()) { 428 cb.onContentChanged(); 429 } 430 } 431 432 @Override 433 public void addContentView(View view, ViewGroup.LayoutParams params) { 434 if (mContentParent == null) { 435 installDecor(); 436 } 437 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 438 // TODO Augment the scenes/transitions API to support this. 439 Log.v(TAG, "addContentView does not support content transitions"); 440 } 441 mContentParent.addView(view, params); 442 mContentParent.requestApplyInsets(); 443 final Callback cb = getCallback(); 444 if (cb != null && !isDestroyed()) { 445 cb.onContentChanged(); 446 } 447 } 448 449 private void transitionTo(Scene scene) { 450 if (mContentScene == null) { 451 scene.enter(); 452 } else { 453 mTransitionManager.transitionTo(scene); 454 } 455 mContentScene = scene; 456 } 457 458 @Override 459 public View getCurrentFocus() { 460 return mDecor != null ? mDecor.findFocus() : null; 461 } 462 463 @Override 464 public void takeSurface(Callback2 callback) { 465 mTakeSurfaceCallback = callback; 466 } 467 468 public void takeInputQueue(InputQueue.Callback callback) { 469 mTakeInputQueueCallback = callback; 470 } 471 472 @Override 473 public boolean isFloating() { 474 return mIsFloating; 475 } 476 477 /** 478 * Return a LayoutInflater instance that can be used to inflate XML view layout 479 * resources for use in this Window. 480 * 481 * @return LayoutInflater The shared LayoutInflater. 482 */ 483 @Override 484 public LayoutInflater getLayoutInflater() { 485 return mLayoutInflater; 486 } 487 488 @Override 489 public void setTitle(CharSequence title) { 490 if (mTitleView != null) { 491 mTitleView.setText(title); 492 } else if (mDecorContentParent != null) { 493 mDecorContentParent.setWindowTitle(title); 494 } 495 mTitle = title; 496 } 497 498 @Override 499 @Deprecated 500 public void setTitleColor(int textColor) { 501 if (mTitleView != null) { 502 mTitleView.setTextColor(textColor); 503 } 504 mTitleColor = textColor; 505 } 506 507 /** 508 * Prepares the panel to either be opened or chorded. This creates the Menu 509 * instance for the panel and populates it via the Activity callbacks. 510 * 511 * @param st The panel state to prepare. 512 * @param event The event that triggered the preparing of the panel. 513 * @return Whether the panel was prepared. If the panel should not be shown, 514 * returns false. 515 */ 516 public final boolean preparePanel(PanelFeatureState st, KeyEvent event) { 517 if (isDestroyed()) { 518 return false; 519 } 520 521 // Already prepared (isPrepared will be reset to false later) 522 if (st.isPrepared) { 523 return true; 524 } 525 526 if ((mPreparedPanel != null) && (mPreparedPanel != st)) { 527 // Another Panel is prepared and possibly open, so close it 528 closePanel(mPreparedPanel, false); 529 } 530 531 final Callback cb = getCallback(); 532 533 if (cb != null) { 534 st.createdPanelView = cb.onCreatePanelView(st.featureId); 535 } 536 537 final boolean isActionBarMenu = 538 (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR); 539 540 if (isActionBarMenu && mDecorContentParent != null) { 541 // Enforce ordering guarantees around events so that the action bar never 542 // dispatches menu-related events before the panel is prepared. 543 mDecorContentParent.setMenuPrepared(); 544 } 545 546 if (st.createdPanelView == null) { 547 // Init the panel state's menu--return false if init failed 548 if (st.menu == null || st.refreshMenuContent) { 549 if (st.menu == null) { 550 if (!initializePanelMenu(st) || (st.menu == null)) { 551 return false; 552 } 553 } 554 555 if (isActionBarMenu && mDecorContentParent != null) { 556 if (mActionMenuPresenterCallback == null) { 557 mActionMenuPresenterCallback = new ActionMenuPresenterCallback(); 558 } 559 mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback); 560 } 561 562 // Call callback, and return if it doesn't want to display menu. 563 564 // Creating the panel menu will involve a lot of manipulation; 565 // don't dispatch change events to presenters until we're done. 566 st.menu.stopDispatchingItemsChanged(); 567 if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) { 568 // Ditch the menu created above 569 st.setMenu(null); 570 571 if (isActionBarMenu && mDecorContentParent != null) { 572 // Don't show it in the action bar either 573 mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); 574 } 575 576 return false; 577 } 578 579 st.refreshMenuContent = false; 580 } 581 582 // Callback and return if the callback does not want to show the menu 583 584 // Preparing the panel menu can involve a lot of manipulation; 585 // don't dispatch change events to presenters until we're done. 586 st.menu.stopDispatchingItemsChanged(); 587 588 // Restore action view state before we prepare. This gives apps 589 // an opportunity to override frozen/restored state in onPrepare. 590 if (st.frozenActionViewState != null) { 591 st.menu.restoreActionViewStates(st.frozenActionViewState); 592 st.frozenActionViewState = null; 593 } 594 595 if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) { 596 if (isActionBarMenu && mDecorContentParent != null) { 597 // The app didn't want to show the menu for now but it still exists. 598 // Clear it out of the action bar. 599 mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); 600 } 601 st.menu.startDispatchingItemsChanged(); 602 return false; 603 } 604 605 // Set the proper keymap 606 KeyCharacterMap kmap = KeyCharacterMap.load( 607 event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD); 608 st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC; 609 st.menu.setQwertyMode(st.qwertyMode); 610 st.menu.startDispatchingItemsChanged(); 611 } 612 613 // Set other state 614 st.isPrepared = true; 615 st.isHandled = false; 616 mPreparedPanel = st; 617 618 return true; 619 } 620 621 @Override 622 public void onConfigurationChanged(Configuration newConfig) { 623 // Action bars handle their own menu state 624 if (mDecorContentParent == null) { 625 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 626 if ((st != null) && (st.menu != null)) { 627 if (st.isOpen) { 628 // Freeze state 629 final Bundle state = new Bundle(); 630 if (st.iconMenuPresenter != null) { 631 st.iconMenuPresenter.saveHierarchyState(state); 632 } 633 if (st.listMenuPresenter != null) { 634 st.listMenuPresenter.saveHierarchyState(state); 635 } 636 637 // Remove the menu views since they need to be recreated 638 // according to the new configuration 639 clearMenuViews(st); 640 641 // Re-open the same menu 642 reopenMenu(false); 643 644 // Restore state 645 if (st.iconMenuPresenter != null) { 646 st.iconMenuPresenter.restoreHierarchyState(state); 647 } 648 if (st.listMenuPresenter != null) { 649 st.listMenuPresenter.restoreHierarchyState(state); 650 } 651 652 } else { 653 // Clear menu views so on next menu opening, it will use 654 // the proper layout 655 clearMenuViews(st); 656 } 657 } 658 } 659 } 660 661 private static void clearMenuViews(PanelFeatureState st) { 662 // This can be called on config changes, so we should make sure 663 // the views will be reconstructed based on the new orientation, etc. 664 665 // Allow the callback to create a new panel view 666 st.createdPanelView = null; 667 668 // Causes the decor view to be recreated 669 st.refreshDecorView = true; 670 671 st.clearMenuPresenters(); 672 } 673 674 @Override 675 public final void openPanel(int featureId, KeyEvent event) { 676 if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && 677 mDecorContentParent.canShowOverflowMenu() && 678 !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { 679 mDecorContentParent.showOverflowMenu(); 680 } else { 681 openPanel(getPanelState(featureId, true), event); 682 } 683 } 684 685 private void openPanel(final PanelFeatureState st, KeyEvent event) { 686 // System.out.println("Open panel: isOpen=" + st.isOpen); 687 688 // Already open, return 689 if (st.isOpen || isDestroyed()) { 690 return; 691 } 692 693 // Don't open an options panel for honeycomb apps on xlarge devices. 694 // (The app should be using an action bar for menu items.) 695 if (st.featureId == FEATURE_OPTIONS_PANEL) { 696 Context context = getContext(); 697 Configuration config = context.getResources().getConfiguration(); 698 boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 699 Configuration.SCREENLAYOUT_SIZE_XLARGE; 700 boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >= 701 android.os.Build.VERSION_CODES.HONEYCOMB; 702 703 if (isXLarge && isHoneycombApp) { 704 return; 705 } 706 } 707 708 Callback cb = getCallback(); 709 if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) { 710 // Callback doesn't want the menu to open, reset any state 711 closePanel(st, true); 712 return; 713 } 714 715 final WindowManager wm = getWindowManager(); 716 if (wm == null) { 717 return; 718 } 719 720 // Prepare panel (should have been done before, but just in case) 721 if (!preparePanel(st, event)) { 722 return; 723 } 724 725 int width = WRAP_CONTENT; 726 if (st.decorView == null || st.refreshDecorView) { 727 if (st.decorView == null) { 728 // Initialize the panel decor, this will populate st.decorView 729 if (!initializePanelDecor(st) || (st.decorView == null)) 730 return; 731 } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) { 732 // Decor needs refreshing, so remove its views 733 st.decorView.removeAllViews(); 734 } 735 736 // This will populate st.shownPanelView 737 if (!initializePanelContent(st) || !st.hasPanelItems()) { 738 return; 739 } 740 741 ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams(); 742 if (lp == null) { 743 lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); 744 } 745 746 int backgroundResId; 747 if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) { 748 // If the contents is fill parent for the width, set the 749 // corresponding background 750 backgroundResId = st.fullBackground; 751 width = MATCH_PARENT; 752 } else { 753 // Otherwise, set the normal panel background 754 backgroundResId = st.background; 755 } 756 st.decorView.setWindowBackground(getContext().getDrawable( 757 backgroundResId)); 758 759 ViewParent shownPanelParent = st.shownPanelView.getParent(); 760 if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) { 761 ((ViewGroup) shownPanelParent).removeView(st.shownPanelView); 762 } 763 st.decorView.addView(st.shownPanelView, lp); 764 765 /* 766 * Give focus to the view, if it or one of its children does not 767 * already have it. 768 */ 769 if (!st.shownPanelView.hasFocus()) { 770 st.shownPanelView.requestFocus(); 771 } 772 } else if (!st.isInListMode()) { 773 width = MATCH_PARENT; 774 } else if (st.createdPanelView != null) { 775 // If we already had a panel view, carry width=MATCH_PARENT through 776 // as we did above when it was created. 777 ViewGroup.LayoutParams lp = st.createdPanelView.getLayoutParams(); 778 if (lp != null && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) { 779 width = MATCH_PARENT; 780 } 781 } 782 783 st.isHandled = false; 784 785 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 786 width, WRAP_CONTENT, 787 st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, 788 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 789 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 790 st.decorView.mDefaultOpacity); 791 792 if (st.isCompact) { 793 lp.gravity = getOptionsPanelGravity(); 794 sRotationWatcher.addWindow(this); 795 } else { 796 lp.gravity = st.gravity; 797 } 798 799 lp.windowAnimations = st.windowAnimations; 800 801 wm.addView(st.decorView, lp); 802 st.isOpen = true; 803 // Log.v(TAG, "Adding main menu to window manager."); 804 } 805 806 @Override 807 public final void closePanel(int featureId) { 808 if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && 809 mDecorContentParent.canShowOverflowMenu() && 810 !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { 811 mDecorContentParent.hideOverflowMenu(); 812 } else if (featureId == FEATURE_CONTEXT_MENU) { 813 closeContextMenu(); 814 } else { 815 closePanel(getPanelState(featureId, true), true); 816 } 817 } 818 819 /** 820 * Closes the given panel. 821 * 822 * @param st The panel to be closed. 823 * @param doCallback Whether to notify the callback that the panel was 824 * closed. If the panel is in the process of re-opening or 825 * opening another panel (e.g., menu opening a sub menu), the 826 * callback should not happen and this variable should be false. 827 * In addition, this method internally will only perform the 828 * callback if the panel is open. 829 */ 830 public final void closePanel(PanelFeatureState st, boolean doCallback) { 831 // System.out.println("Close panel: isOpen=" + st.isOpen); 832 if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL && 833 mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) { 834 checkCloseActionMenu(st.menu); 835 return; 836 } 837 838 final ViewManager wm = getWindowManager(); 839 if ((wm != null) && st.isOpen) { 840 if (st.decorView != null) { 841 wm.removeView(st.decorView); 842 // Log.v(TAG, "Removing main menu from window manager."); 843 if (st.isCompact) { 844 sRotationWatcher.removeWindow(this); 845 } 846 } 847 848 if (doCallback) { 849 callOnPanelClosed(st.featureId, st, null); 850 } 851 } 852 853 st.isPrepared = false; 854 st.isHandled = false; 855 st.isOpen = false; 856 857 // This view is no longer shown, so null it out 858 st.shownPanelView = null; 859 860 if (st.isInExpandedMode) { 861 // Next time the menu opens, it should not be in expanded mode, so 862 // force a refresh of the decor 863 st.refreshDecorView = true; 864 st.isInExpandedMode = false; 865 } 866 867 if (mPreparedPanel == st) { 868 mPreparedPanel = null; 869 mPanelChordingKey = 0; 870 } 871 } 872 873 void checkCloseActionMenu(Menu menu) { 874 if (mClosingActionMenu) { 875 return; 876 } 877 878 mClosingActionMenu = true; 879 mDecorContentParent.dismissPopups(); 880 Callback cb = getCallback(); 881 if (cb != null && !isDestroyed()) { 882 cb.onPanelClosed(FEATURE_ACTION_BAR, menu); 883 } 884 mClosingActionMenu = false; 885 } 886 887 @Override 888 public final void togglePanel(int featureId, KeyEvent event) { 889 PanelFeatureState st = getPanelState(featureId, true); 890 if (st.isOpen) { 891 closePanel(st, true); 892 } else { 893 openPanel(st, event); 894 } 895 } 896 897 @Override 898 public void invalidatePanelMenu(int featureId) { 899 mInvalidatePanelMenuFeatures |= 1 << featureId; 900 901 if (!mInvalidatePanelMenuPosted && mDecor != null) { 902 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); 903 mInvalidatePanelMenuPosted = true; 904 } 905 } 906 907 void doPendingInvalidatePanelMenu() { 908 if (mInvalidatePanelMenuPosted) { 909 mDecor.removeCallbacks(mInvalidatePanelMenuRunnable); 910 mInvalidatePanelMenuRunnable.run(); 911 } 912 } 913 914 void doInvalidatePanelMenu(int featureId) { 915 PanelFeatureState st = getPanelState(featureId, false); 916 if (st == null) { 917 return; 918 } 919 Bundle savedActionViewStates = null; 920 if (st.menu != null) { 921 savedActionViewStates = new Bundle(); 922 st.menu.saveActionViewStates(savedActionViewStates); 923 if (savedActionViewStates.size() > 0) { 924 st.frozenActionViewState = savedActionViewStates; 925 } 926 // This will be started again when the panel is prepared. 927 st.menu.stopDispatchingItemsChanged(); 928 st.menu.clear(); 929 } 930 st.refreshMenuContent = true; 931 st.refreshDecorView = true; 932 933 // Prepare the options panel if we have an action bar 934 if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL) 935 && mDecorContentParent != null) { 936 st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false); 937 if (st != null) { 938 st.isPrepared = false; 939 preparePanel(st, null); 940 } 941 } 942 } 943 944 /** 945 * Called when the panel key is pushed down. 946 * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}. 947 * @param event The key event. 948 * @return Whether the key was handled. 949 */ 950 public final boolean onKeyDownPanel(int featureId, KeyEvent event) { 951 final int keyCode = event.getKeyCode(); 952 953 if (event.getRepeatCount() == 0) { 954 // The panel key was pushed, so set the chording key 955 mPanelChordingKey = keyCode; 956 957 PanelFeatureState st = getPanelState(featureId, false); 958 if (st != null && !st.isOpen) { 959 return preparePanel(st, event); 960 } 961 } 962 963 return false; 964 } 965 966 /** 967 * Called when the panel key is released. 968 * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}. 969 * @param event The key event. 970 */ 971 public final void onKeyUpPanel(int featureId, KeyEvent event) { 972 // The panel key was released, so clear the chording key 973 if (mPanelChordingKey != 0) { 974 mPanelChordingKey = 0; 975 976 final PanelFeatureState st = getPanelState(featureId, false); 977 978 if (event.isCanceled() || (mDecor != null && mDecor.mPrimaryActionMode != null) || 979 (st == null)) { 980 return; 981 } 982 983 boolean playSoundEffect = false; 984 if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && 985 mDecorContentParent.canShowOverflowMenu() && 986 !ViewConfiguration.get(getContext()).hasPermanentMenuKey()) { 987 if (!mDecorContentParent.isOverflowMenuShowing()) { 988 if (!isDestroyed() && preparePanel(st, event)) { 989 playSoundEffect = mDecorContentParent.showOverflowMenu(); 990 } 991 } else { 992 playSoundEffect = mDecorContentParent.hideOverflowMenu(); 993 } 994 } else { 995 if (st.isOpen || st.isHandled) { 996 997 // Play the sound effect if the user closed an open menu (and not if 998 // they just released a menu shortcut) 999 playSoundEffect = st.isOpen; 1000 1001 // Close menu 1002 closePanel(st, true); 1003 1004 } else if (st.isPrepared) { 1005 boolean show = true; 1006 if (st.refreshMenuContent) { 1007 // Something may have invalidated the menu since we prepared it. 1008 // Re-prepare it to refresh. 1009 st.isPrepared = false; 1010 show = preparePanel(st, event); 1011 } 1012 1013 if (show) { 1014 // Write 'menu opened' to event log 1015 EventLog.writeEvent(50001, 0); 1016 1017 // Show menu 1018 openPanel(st, event); 1019 1020 playSoundEffect = true; 1021 } 1022 } 1023 } 1024 1025 if (playSoundEffect) { 1026 AudioManager audioManager = (AudioManager) getContext().getSystemService( 1027 Context.AUDIO_SERVICE); 1028 if (audioManager != null) { 1029 audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 1030 } else { 1031 Log.w(TAG, "Couldn't get audio manager"); 1032 } 1033 } 1034 } 1035 } 1036 1037 @Override 1038 public final void closeAllPanels() { 1039 final ViewManager wm = getWindowManager(); 1040 if (wm == null) { 1041 return; 1042 } 1043 1044 final PanelFeatureState[] panels = mPanels; 1045 final int N = panels != null ? panels.length : 0; 1046 for (int i = 0; i < N; i++) { 1047 final PanelFeatureState panel = panels[i]; 1048 if (panel != null) { 1049 closePanel(panel, true); 1050 } 1051 } 1052 1053 closeContextMenu(); 1054 } 1055 1056 /** 1057 * Closes the context menu. This notifies the menu logic of the close, along 1058 * with dismissing it from the UI. 1059 */ 1060 private synchronized void closeContextMenu() { 1061 if (mContextMenu != null) { 1062 mContextMenu.close(); 1063 dismissContextMenu(); 1064 } 1065 } 1066 1067 /** 1068 * Dismisses just the context menu UI. To close the context menu, use 1069 * {@link #closeContextMenu()}. 1070 */ 1071 private synchronized void dismissContextMenu() { 1072 mContextMenu = null; 1073 1074 if (mContextMenuHelper != null) { 1075 mContextMenuHelper.dismiss(); 1076 mContextMenuHelper = null; 1077 } 1078 } 1079 1080 @Override 1081 public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { 1082 return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags); 1083 } 1084 1085 private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event, 1086 int flags) { 1087 if (event.isSystem() || (st == null)) { 1088 return false; 1089 } 1090 1091 boolean handled = false; 1092 1093 // Only try to perform menu shortcuts if preparePanel returned true (possible false 1094 // return value from application not wanting to show the menu). 1095 if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) { 1096 // The menu is prepared now, perform the shortcut on it 1097 handled = st.menu.performShortcut(keyCode, event, flags); 1098 } 1099 1100 if (handled) { 1101 // Mark as handled 1102 st.isHandled = true; 1103 1104 // Only close down the menu if we don't have an action bar keeping it open. 1105 if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) { 1106 closePanel(st, true); 1107 } 1108 } 1109 1110 return handled; 1111 } 1112 1113 @Override 1114 public boolean performPanelIdentifierAction(int featureId, int id, int flags) { 1115 1116 PanelFeatureState st = getPanelState(featureId, true); 1117 if (!preparePanel(st, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU))) { 1118 return false; 1119 } 1120 if (st.menu == null) { 1121 return false; 1122 } 1123 1124 boolean res = st.menu.performIdentifierAction(id, flags); 1125 1126 // Only close down the menu if we don't have an action bar keeping it open. 1127 if (mDecorContentParent == null) { 1128 closePanel(st, true); 1129 } 1130 1131 return res; 1132 } 1133 1134 public PanelFeatureState findMenuPanel(Menu menu) { 1135 final PanelFeatureState[] panels = mPanels; 1136 final int N = panels != null ? panels.length : 0; 1137 for (int i = 0; i < N; i++) { 1138 final PanelFeatureState panel = panels[i]; 1139 if (panel != null && panel.menu == menu) { 1140 return panel; 1141 } 1142 } 1143 return null; 1144 } 1145 1146 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 1147 final Callback cb = getCallback(); 1148 if (cb != null && !isDestroyed()) { 1149 final PanelFeatureState panel = findMenuPanel(menu.getRootMenu()); 1150 if (panel != null) { 1151 return cb.onMenuItemSelected(panel.featureId, item); 1152 } 1153 } 1154 return false; 1155 } 1156 1157 public void onMenuModeChange(MenuBuilder menu) { 1158 reopenMenu(true); 1159 } 1160 1161 private void reopenMenu(boolean toggleMenuMode) { 1162 if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() && 1163 (!ViewConfiguration.get(getContext()).hasPermanentMenuKey() || 1164 mDecorContentParent.isOverflowMenuShowPending())) { 1165 final Callback cb = getCallback(); 1166 if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) { 1167 if (cb != null && !isDestroyed()) { 1168 // If we have a menu invalidation pending, do it now. 1169 if (mInvalidatePanelMenuPosted && 1170 (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) { 1171 mDecor.removeCallbacks(mInvalidatePanelMenuRunnable); 1172 mInvalidatePanelMenuRunnable.run(); 1173 } 1174 1175 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 1176 1177 // If we don't have a menu or we're waiting for a full content refresh, 1178 // forget it. This is a lingering event that no longer matters. 1179 if (st != null && st.menu != null && !st.refreshMenuContent && 1180 cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) { 1181 cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); 1182 mDecorContentParent.showOverflowMenu(); 1183 } 1184 } 1185 } else { 1186 mDecorContentParent.hideOverflowMenu(); 1187 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 1188 if (st != null && cb != null && !isDestroyed()) { 1189 cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu); 1190 } 1191 } 1192 return; 1193 } 1194 1195 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 1196 1197 if (st == null) { 1198 return; 1199 } 1200 1201 // Save the future expanded mode state since closePanel will reset it 1202 boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode; 1203 1204 st.refreshDecorView = true; 1205 closePanel(st, false); 1206 1207 // Set the expanded mode state 1208 st.isInExpandedMode = newExpandedMode; 1209 1210 openPanel(st, null); 1211 } 1212 1213 /** 1214 * Initializes the menu associated with the given panel feature state. You 1215 * must at the very least set PanelFeatureState.menu to the Menu to be 1216 * associated with the given panel state. The default implementation creates 1217 * a new menu for the panel state. 1218 * 1219 * @param st The panel whose menu is being initialized. 1220 * @return Whether the initialization was successful. 1221 */ 1222 protected boolean initializePanelMenu(final PanelFeatureState st) { 1223 Context context = getContext(); 1224 1225 // If we have an action bar, initialize the menu with the right theme. 1226 if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) && 1227 mDecorContentParent != null) { 1228 final TypedValue outValue = new TypedValue(); 1229 final Theme baseTheme = context.getTheme(); 1230 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); 1231 1232 Theme widgetTheme = null; 1233 if (outValue.resourceId != 0) { 1234 widgetTheme = context.getResources().newTheme(); 1235 widgetTheme.setTo(baseTheme); 1236 widgetTheme.applyStyle(outValue.resourceId, true); 1237 widgetTheme.resolveAttribute( 1238 R.attr.actionBarWidgetTheme, outValue, true); 1239 } else { 1240 baseTheme.resolveAttribute( 1241 R.attr.actionBarWidgetTheme, outValue, true); 1242 } 1243 1244 if (outValue.resourceId != 0) { 1245 if (widgetTheme == null) { 1246 widgetTheme = context.getResources().newTheme(); 1247 widgetTheme.setTo(baseTheme); 1248 } 1249 widgetTheme.applyStyle(outValue.resourceId, true); 1250 } 1251 1252 if (widgetTheme != null) { 1253 context = new ContextThemeWrapper(context, 0); 1254 context.getTheme().setTo(widgetTheme); 1255 } 1256 } 1257 1258 final MenuBuilder menu = new MenuBuilder(context); 1259 menu.setCallback(this); 1260 st.setMenu(menu); 1261 1262 return true; 1263 } 1264 1265 /** 1266 * Perform initial setup of a panel. This should at the very least set the 1267 * style information in the PanelFeatureState and must set 1268 * PanelFeatureState.decor to the panel's window decor view. 1269 * 1270 * @param st The panel being initialized. 1271 */ 1272 protected boolean initializePanelDecor(PanelFeatureState st) { 1273 st.decorView = new DecorView(getContext(), st.featureId); 1274 st.gravity = Gravity.CENTER | Gravity.BOTTOM; 1275 st.setStyle(getContext()); 1276 TypedArray a = getContext().obtainStyledAttributes(null, 1277 R.styleable.Window, 0, st.listPresenterTheme); 1278 final float elevation = a.getDimension(R.styleable.Window_windowElevation, 0); 1279 if (elevation != 0) { 1280 st.decorView.setElevation(elevation); 1281 } 1282 a.recycle(); 1283 1284 return true; 1285 } 1286 1287 /** 1288 * Determine the gravity value for the options panel. This can 1289 * differ in compact mode. 1290 * 1291 * @return gravity value to use for the panel window 1292 */ 1293 private int getOptionsPanelGravity() { 1294 try { 1295 return WindowManagerHolder.sWindowManager.getPreferredOptionsPanelGravity(); 1296 } catch (RemoteException ex) { 1297 Log.e(TAG, "Couldn't getOptionsPanelGravity; using default", ex); 1298 return Gravity.CENTER | Gravity.BOTTOM; 1299 } 1300 } 1301 1302 void onOptionsPanelRotationChanged() { 1303 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 1304 if (st == null) return; 1305 1306 final WindowManager.LayoutParams lp = st.decorView != null ? 1307 (WindowManager.LayoutParams) st.decorView.getLayoutParams() : null; 1308 if (lp != null) { 1309 lp.gravity = getOptionsPanelGravity(); 1310 final ViewManager wm = getWindowManager(); 1311 if (wm != null) { 1312 wm.updateViewLayout(st.decorView, lp); 1313 } 1314 } 1315 } 1316 1317 /** 1318 * Initializes the panel associated with the panel feature state. You must 1319 * at the very least set PanelFeatureState.panel to the View implementing 1320 * its contents. The default implementation gets the panel from the menu. 1321 * 1322 * @param st The panel state being initialized. 1323 * @return Whether the initialization was successful. 1324 */ 1325 protected boolean initializePanelContent(PanelFeatureState st) { 1326 if (st.createdPanelView != null) { 1327 st.shownPanelView = st.createdPanelView; 1328 return true; 1329 } 1330 1331 if (st.menu == null) { 1332 return false; 1333 } 1334 1335 if (mPanelMenuPresenterCallback == null) { 1336 mPanelMenuPresenterCallback = new PanelMenuPresenterCallback(); 1337 } 1338 1339 MenuView menuView = st.isInListMode() 1340 ? st.getListMenuView(getContext(), mPanelMenuPresenterCallback) 1341 : st.getIconMenuView(getContext(), mPanelMenuPresenterCallback); 1342 1343 st.shownPanelView = (View) menuView; 1344 1345 if (st.shownPanelView != null) { 1346 // Use the menu View's default animations if it has any 1347 final int defaultAnimations = menuView.getWindowAnimations(); 1348 if (defaultAnimations != 0) { 1349 st.windowAnimations = defaultAnimations; 1350 } 1351 return true; 1352 } else { 1353 return false; 1354 } 1355 } 1356 1357 @Override 1358 public boolean performContextMenuIdentifierAction(int id, int flags) { 1359 return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false; 1360 } 1361 1362 @Override 1363 public final void setElevation(float elevation) { 1364 mElevation = elevation; 1365 if (mDecor != null) { 1366 mDecor.setElevation(elevation); 1367 } 1368 dispatchWindowAttributesChanged(getAttributes()); 1369 } 1370 1371 @Override 1372 public final void setClipToOutline(boolean clipToOutline) { 1373 mClipToOutline = clipToOutline; 1374 if (mDecor != null) { 1375 mDecor.setClipToOutline(clipToOutline); 1376 } 1377 } 1378 1379 @Override 1380 public final void setBackgroundDrawable(Drawable drawable) { 1381 if (drawable != mBackgroundDrawable || mBackgroundResource != 0) { 1382 mBackgroundResource = 0; 1383 mBackgroundDrawable = drawable; 1384 if (mDecor != null) { 1385 mDecor.setWindowBackground(drawable); 1386 } 1387 if (mBackgroundFallbackResource != 0) { 1388 mDecor.setBackgroundFallback(drawable != null ? 0 : mBackgroundFallbackResource); 1389 } 1390 } 1391 } 1392 1393 @Override 1394 public final void setFeatureDrawableResource(int featureId, int resId) { 1395 if (resId != 0) { 1396 DrawableFeatureState st = getDrawableState(featureId, true); 1397 if (st.resid != resId) { 1398 st.resid = resId; 1399 st.uri = null; 1400 st.local = getContext().getDrawable(resId); 1401 updateDrawable(featureId, st, false); 1402 } 1403 } else { 1404 setFeatureDrawable(featureId, null); 1405 } 1406 } 1407 1408 @Override 1409 public final void setFeatureDrawableUri(int featureId, Uri uri) { 1410 if (uri != null) { 1411 DrawableFeatureState st = getDrawableState(featureId, true); 1412 if (st.uri == null || !st.uri.equals(uri)) { 1413 st.resid = 0; 1414 st.uri = uri; 1415 st.local = loadImageURI(uri); 1416 updateDrawable(featureId, st, false); 1417 } 1418 } else { 1419 setFeatureDrawable(featureId, null); 1420 } 1421 } 1422 1423 @Override 1424 public final void setFeatureDrawable(int featureId, Drawable drawable) { 1425 DrawableFeatureState st = getDrawableState(featureId, true); 1426 st.resid = 0; 1427 st.uri = null; 1428 if (st.local != drawable) { 1429 st.local = drawable; 1430 updateDrawable(featureId, st, false); 1431 } 1432 } 1433 1434 @Override 1435 public void setFeatureDrawableAlpha(int featureId, int alpha) { 1436 DrawableFeatureState st = getDrawableState(featureId, true); 1437 if (st.alpha != alpha) { 1438 st.alpha = alpha; 1439 updateDrawable(featureId, st, false); 1440 } 1441 } 1442 1443 protected final void setFeatureDefaultDrawable(int featureId, Drawable drawable) { 1444 DrawableFeatureState st = getDrawableState(featureId, true); 1445 if (st.def != drawable) { 1446 st.def = drawable; 1447 updateDrawable(featureId, st, false); 1448 } 1449 } 1450 1451 @Override 1452 public final void setFeatureInt(int featureId, int value) { 1453 // XXX Should do more management (as with drawable features) to 1454 // deal with interactions between multiple window policies. 1455 updateInt(featureId, value, false); 1456 } 1457 1458 /** 1459 * Update the state of a drawable feature. This should be called, for every 1460 * drawable feature supported, as part of onActive(), to make sure that the 1461 * contents of a containing window is properly updated. 1462 * 1463 * @see #onActive 1464 * @param featureId The desired drawable feature to change. 1465 * @param fromActive Always true when called from onActive(). 1466 */ 1467 protected final void updateDrawable(int featureId, boolean fromActive) { 1468 final DrawableFeatureState st = getDrawableState(featureId, false); 1469 if (st != null) { 1470 updateDrawable(featureId, st, fromActive); 1471 } 1472 } 1473 1474 /** 1475 * Called when a Drawable feature changes, for the window to update its 1476 * graphics. 1477 * 1478 * @param featureId The feature being changed. 1479 * @param drawable The new Drawable to show, or null if none. 1480 * @param alpha The new alpha blending of the Drawable. 1481 */ 1482 protected void onDrawableChanged(int featureId, Drawable drawable, int alpha) { 1483 ImageView view; 1484 if (featureId == FEATURE_LEFT_ICON) { 1485 view = getLeftIconView(); 1486 } else if (featureId == FEATURE_RIGHT_ICON) { 1487 view = getRightIconView(); 1488 } else { 1489 return; 1490 } 1491 1492 if (drawable != null) { 1493 drawable.setAlpha(alpha); 1494 view.setImageDrawable(drawable); 1495 view.setVisibility(View.VISIBLE); 1496 } else { 1497 view.setVisibility(View.GONE); 1498 } 1499 } 1500 1501 /** 1502 * Called when an int feature changes, for the window to update its 1503 * graphics. 1504 * 1505 * @param featureId The feature being changed. 1506 * @param value The new integer value. 1507 */ 1508 protected void onIntChanged(int featureId, int value) { 1509 if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) { 1510 updateProgressBars(value); 1511 } else if (featureId == FEATURE_CUSTOM_TITLE) { 1512 FrameLayout titleContainer = (FrameLayout) findViewById(R.id.title_container); 1513 if (titleContainer != null) { 1514 mLayoutInflater.inflate(value, titleContainer); 1515 } 1516 } 1517 } 1518 1519 /** 1520 * Updates the progress bars that are shown in the title bar. 1521 * 1522 * @param value Can be one of {@link Window#PROGRESS_VISIBILITY_ON}, 1523 * {@link Window#PROGRESS_VISIBILITY_OFF}, 1524 * {@link Window#PROGRESS_INDETERMINATE_ON}, 1525 * {@link Window#PROGRESS_INDETERMINATE_OFF}, or a value 1526 * starting at {@link Window#PROGRESS_START} through 1527 * {@link Window#PROGRESS_END} for setting the default 1528 * progress (if {@link Window#PROGRESS_END} is given, 1529 * the progress bar widgets in the title will be hidden after an 1530 * animation), a value between 1531 * {@link Window#PROGRESS_SECONDARY_START} - 1532 * {@link Window#PROGRESS_SECONDARY_END} for the 1533 * secondary progress (if 1534 * {@link Window#PROGRESS_SECONDARY_END} is given, the 1535 * progress bar widgets will still be shown with the secondary 1536 * progress bar will be completely filled in.) 1537 */ 1538 private void updateProgressBars(int value) { 1539 ProgressBar circularProgressBar = getCircularProgressBar(true); 1540 ProgressBar horizontalProgressBar = getHorizontalProgressBar(true); 1541 1542 final int features = getLocalFeatures(); 1543 if (value == PROGRESS_VISIBILITY_ON) { 1544 if ((features & (1 << FEATURE_PROGRESS)) != 0) { 1545 if (horizontalProgressBar != null) { 1546 int level = horizontalProgressBar.getProgress(); 1547 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? 1548 View.VISIBLE : View.INVISIBLE; 1549 horizontalProgressBar.setVisibility(visibility); 1550 } else { 1551 Log.e(TAG, "Horizontal progress bar not located in current window decor"); 1552 } 1553 } 1554 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 1555 if (circularProgressBar != null) { 1556 circularProgressBar.setVisibility(View.VISIBLE); 1557 } else { 1558 Log.e(TAG, "Circular progress bar not located in current window decor"); 1559 } 1560 } 1561 } else if (value == PROGRESS_VISIBILITY_OFF) { 1562 if ((features & (1 << FEATURE_PROGRESS)) != 0) { 1563 if (horizontalProgressBar != null) { 1564 horizontalProgressBar.setVisibility(View.GONE); 1565 } else { 1566 Log.e(TAG, "Horizontal progress bar not located in current window decor"); 1567 } 1568 } 1569 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 1570 if (circularProgressBar != null) { 1571 circularProgressBar.setVisibility(View.GONE); 1572 } else { 1573 Log.e(TAG, "Circular progress bar not located in current window decor"); 1574 } 1575 } 1576 } else if (value == PROGRESS_INDETERMINATE_ON) { 1577 if (horizontalProgressBar != null) { 1578 horizontalProgressBar.setIndeterminate(true); 1579 } else { 1580 Log.e(TAG, "Horizontal progress bar not located in current window decor"); 1581 } 1582 } else if (value == PROGRESS_INDETERMINATE_OFF) { 1583 if (horizontalProgressBar != null) { 1584 horizontalProgressBar.setIndeterminate(false); 1585 } else { 1586 Log.e(TAG, "Horizontal progress bar not located in current window decor"); 1587 } 1588 } else if (PROGRESS_START <= value && value <= PROGRESS_END) { 1589 // We want to set the progress value before testing for visibility 1590 // so that when the progress bar becomes visible again, it has the 1591 // correct level. 1592 if (horizontalProgressBar != null) { 1593 horizontalProgressBar.setProgress(value - PROGRESS_START); 1594 } else { 1595 Log.e(TAG, "Horizontal progress bar not located in current window decor"); 1596 } 1597 1598 if (value < PROGRESS_END) { 1599 showProgressBars(horizontalProgressBar, circularProgressBar); 1600 } else { 1601 hideProgressBars(horizontalProgressBar, circularProgressBar); 1602 } 1603 } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) { 1604 if (horizontalProgressBar != null) { 1605 horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START); 1606 } else { 1607 Log.e(TAG, "Horizontal progress bar not located in current window decor"); 1608 } 1609 1610 showProgressBars(horizontalProgressBar, circularProgressBar); 1611 } 1612 1613 } 1614 1615 private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) { 1616 final int features = getLocalFeatures(); 1617 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 && 1618 spinnyProgressBar != null && spinnyProgressBar.getVisibility() == View.INVISIBLE) { 1619 spinnyProgressBar.setVisibility(View.VISIBLE); 1620 } 1621 // Only show the progress bars if the primary progress is not complete 1622 if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null && 1623 horizontalProgressBar.getProgress() < 10000) { 1624 horizontalProgressBar.setVisibility(View.VISIBLE); 1625 } 1626 } 1627 1628 private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) { 1629 final int features = getLocalFeatures(); 1630 Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out); 1631 anim.setDuration(1000); 1632 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 && 1633 spinnyProgressBar != null && 1634 spinnyProgressBar.getVisibility() == View.VISIBLE) { 1635 spinnyProgressBar.startAnimation(anim); 1636 spinnyProgressBar.setVisibility(View.INVISIBLE); 1637 } 1638 if ((features & (1 << FEATURE_PROGRESS)) != 0 && horizontalProgressBar != null && 1639 horizontalProgressBar.getVisibility() == View.VISIBLE) { 1640 horizontalProgressBar.startAnimation(anim); 1641 horizontalProgressBar.setVisibility(View.INVISIBLE); 1642 } 1643 } 1644 1645 @Override 1646 public void setIcon(int resId) { 1647 mIconRes = resId; 1648 mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON; 1649 mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK; 1650 if (mDecorContentParent != null) { 1651 mDecorContentParent.setIcon(resId); 1652 } 1653 } 1654 1655 @Override 1656 public void setDefaultIcon(int resId) { 1657 if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0) { 1658 return; 1659 } 1660 mIconRes = resId; 1661 if (mDecorContentParent != null && (!mDecorContentParent.hasIcon() || 1662 (mResourcesSetFlags & FLAG_RESOURCE_SET_ICON_FALLBACK) != 0)) { 1663 if (resId != 0) { 1664 mDecorContentParent.setIcon(resId); 1665 mResourcesSetFlags &= ~FLAG_RESOURCE_SET_ICON_FALLBACK; 1666 } else { 1667 mDecorContentParent.setIcon( 1668 getContext().getPackageManager().getDefaultActivityIcon()); 1669 mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; 1670 } 1671 } 1672 } 1673 1674 @Override 1675 public void setLogo(int resId) { 1676 mLogoRes = resId; 1677 mResourcesSetFlags |= FLAG_RESOURCE_SET_LOGO; 1678 if (mDecorContentParent != null) { 1679 mDecorContentParent.setLogo(resId); 1680 } 1681 } 1682 1683 @Override 1684 public void setDefaultLogo(int resId) { 1685 if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0) { 1686 return; 1687 } 1688 mLogoRes = resId; 1689 if (mDecorContentParent != null && !mDecorContentParent.hasLogo()) { 1690 mDecorContentParent.setLogo(resId); 1691 } 1692 } 1693 1694 @Override 1695 public void setLocalFocus(boolean hasFocus, boolean inTouchMode) { 1696 getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode); 1697 1698 } 1699 1700 @Override 1701 public void injectInputEvent(InputEvent event) { 1702 getViewRootImpl().dispatchInputEvent(event); 1703 } 1704 1705 private ViewRootImpl getViewRootImpl() { 1706 if (mDecor != null) { 1707 ViewRootImpl viewRootImpl = mDecor.getViewRootImpl(); 1708 if (viewRootImpl != null) { 1709 return viewRootImpl; 1710 } 1711 } 1712 throw new IllegalStateException("view not added"); 1713 } 1714 1715 /** 1716 * Request that key events come to this activity. Use this if your activity 1717 * has no views with focus, but the activity still wants a chance to process 1718 * key events. 1719 */ 1720 @Override 1721 public void takeKeyEvents(boolean get) { 1722 mDecor.setFocusable(get); 1723 } 1724 1725 @Override 1726 public boolean superDispatchKeyEvent(KeyEvent event) { 1727 return mDecor.superDispatchKeyEvent(event); 1728 } 1729 1730 @Override 1731 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 1732 return mDecor.superDispatchKeyShortcutEvent(event); 1733 } 1734 1735 @Override 1736 public boolean superDispatchTouchEvent(MotionEvent event) { 1737 return mDecor.superDispatchTouchEvent(event); 1738 } 1739 1740 @Override 1741 public boolean superDispatchTrackballEvent(MotionEvent event) { 1742 return mDecor.superDispatchTrackballEvent(event); 1743 } 1744 1745 @Override 1746 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 1747 return mDecor.superDispatchGenericMotionEvent(event); 1748 } 1749 1750 /** 1751 * A key was pressed down and not handled by anything else in the window. 1752 * 1753 * @see #onKeyUp 1754 * @see android.view.KeyEvent 1755 */ 1756 protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) { 1757 /* **************************************************************************** 1758 * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. 1759 * 1760 * If your key handling must happen before the app gets a crack at the event, 1761 * it goes in PhoneWindowManager. 1762 * 1763 * If your key handling should happen in all windows, and does not depend on 1764 * the state of the current application, other than that the current 1765 * application can override the behavior by handling the event itself, it 1766 * should go in PhoneFallbackEventHandler. 1767 * 1768 * Only if your handling depends on the window, and the fact that it has 1769 * a DecorView, should it go here. 1770 * ****************************************************************************/ 1771 1772 final KeyEvent.DispatcherState dispatcher = 1773 mDecor != null ? mDecor.getKeyDispatcherState() : null; 1774 //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount() 1775 // + " flags=0x" + Integer.toHexString(event.getFlags())); 1776 1777 switch (keyCode) { 1778 case KeyEvent.KEYCODE_VOLUME_UP: 1779 case KeyEvent.KEYCODE_VOLUME_DOWN: 1780 case KeyEvent.KEYCODE_VOLUME_MUTE: { 1781 int direction = 0; 1782 switch (keyCode) { 1783 case KeyEvent.KEYCODE_VOLUME_UP: 1784 direction = AudioManager.ADJUST_RAISE; 1785 break; 1786 case KeyEvent.KEYCODE_VOLUME_DOWN: 1787 direction = AudioManager.ADJUST_LOWER; 1788 break; 1789 case KeyEvent.KEYCODE_VOLUME_MUTE: 1790 direction = AudioManager.ADJUST_TOGGLE_MUTE; 1791 break; 1792 } 1793 // If we have a session send it the volume command, otherwise 1794 // use the suggested stream. 1795 if (mMediaController != null) { 1796 mMediaController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI); 1797 } else { 1798 MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy( 1799 mVolumeControlStreamType, direction, 1800 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE 1801 | AudioManager.FLAG_FROM_KEY); 1802 } 1803 return true; 1804 } 1805 // These are all the recognized media key codes in 1806 // KeyEvent.isMediaKey() 1807 case KeyEvent.KEYCODE_MEDIA_PLAY: 1808 case KeyEvent.KEYCODE_MEDIA_PAUSE: 1809 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 1810 case KeyEvent.KEYCODE_MUTE: 1811 case KeyEvent.KEYCODE_HEADSETHOOK: 1812 case KeyEvent.KEYCODE_MEDIA_STOP: 1813 case KeyEvent.KEYCODE_MEDIA_NEXT: 1814 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 1815 case KeyEvent.KEYCODE_MEDIA_REWIND: 1816 case KeyEvent.KEYCODE_MEDIA_RECORD: 1817 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { 1818 if (mMediaController != null) { 1819 if (mMediaController.dispatchMediaButtonEvent(event)) { 1820 return true; 1821 } 1822 } 1823 return false; 1824 } 1825 1826 case KeyEvent.KEYCODE_MENU: { 1827 onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event); 1828 return true; 1829 } 1830 1831 case KeyEvent.KEYCODE_BACK: { 1832 if (event.getRepeatCount() > 0) break; 1833 if (featureId < 0) break; 1834 // Currently don't do anything with long press. 1835 if (dispatcher != null) { 1836 dispatcher.startTracking(event, this); 1837 } 1838 return true; 1839 } 1840 1841 } 1842 1843 return false; 1844 } 1845 1846 private KeyguardManager getKeyguardManager() { 1847 if (mKeyguardManager == null) { 1848 mKeyguardManager = (KeyguardManager) getContext().getSystemService( 1849 Context.KEYGUARD_SERVICE); 1850 } 1851 return mKeyguardManager; 1852 } 1853 1854 AudioManager getAudioManager() { 1855 if (mAudioManager == null) { 1856 mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE); 1857 } 1858 return mAudioManager; 1859 } 1860 1861 /** 1862 * A key was released and not handled by anything else in the window. 1863 * 1864 * @see #onKeyDown 1865 * @see android.view.KeyEvent 1866 */ 1867 protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) { 1868 final KeyEvent.DispatcherState dispatcher = 1869 mDecor != null ? mDecor.getKeyDispatcherState() : null; 1870 if (dispatcher != null) { 1871 dispatcher.handleUpEvent(event); 1872 } 1873 //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount() 1874 // + " flags=0x" + Integer.toHexString(event.getFlags())); 1875 1876 switch (keyCode) { 1877 case KeyEvent.KEYCODE_VOLUME_UP: 1878 case KeyEvent.KEYCODE_VOLUME_DOWN: { 1879 final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE 1880 | AudioManager.FLAG_FROM_KEY; 1881 // If we have a session send it the volume command, otherwise 1882 // use the suggested stream. 1883 if (mMediaController != null) { 1884 mMediaController.adjustVolume(0, flags); 1885 } else { 1886 MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy( 1887 mVolumeControlStreamType, 0, flags); 1888 } 1889 return true; 1890 } 1891 case KeyEvent.KEYCODE_VOLUME_MUTE: { 1892 // Similar code is in PhoneFallbackEventHandler in case the window 1893 // doesn't have one of these. In this case, we execute it here and 1894 // eat the event instead, because we have mVolumeControlStreamType 1895 // and they don't. 1896 getAudioManager().handleKeyUp(event, mVolumeControlStreamType); 1897 return true; 1898 } 1899 // These are all the recognized media key codes in 1900 // KeyEvent.isMediaKey() 1901 case KeyEvent.KEYCODE_MEDIA_PLAY: 1902 case KeyEvent.KEYCODE_MEDIA_PAUSE: 1903 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 1904 case KeyEvent.KEYCODE_MUTE: 1905 case KeyEvent.KEYCODE_HEADSETHOOK: 1906 case KeyEvent.KEYCODE_MEDIA_STOP: 1907 case KeyEvent.KEYCODE_MEDIA_NEXT: 1908 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 1909 case KeyEvent.KEYCODE_MEDIA_REWIND: 1910 case KeyEvent.KEYCODE_MEDIA_RECORD: 1911 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { 1912 if (mMediaController != null) { 1913 if (mMediaController.dispatchMediaButtonEvent(event)) { 1914 return true; 1915 } 1916 } 1917 return false; 1918 } 1919 1920 case KeyEvent.KEYCODE_MENU: { 1921 onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId, 1922 event); 1923 return true; 1924 } 1925 1926 case KeyEvent.KEYCODE_BACK: { 1927 if (featureId < 0) break; 1928 if (event.isTracking() && !event.isCanceled()) { 1929 if (featureId == FEATURE_OPTIONS_PANEL) { 1930 PanelFeatureState st = getPanelState(featureId, false); 1931 if (st != null && st.isInExpandedMode) { 1932 // If the user is in an expanded menu and hits back, it 1933 // should go back to the icon menu 1934 reopenMenu(true); 1935 return true; 1936 } 1937 } 1938 closePanel(featureId); 1939 return true; 1940 } 1941 break; 1942 } 1943 1944 case KeyEvent.KEYCODE_SEARCH: { 1945 /* 1946 * Do this in onKeyUp since the Search key is also used for 1947 * chording quick launch shortcuts. 1948 */ 1949 if (getKeyguardManager().inKeyguardRestrictedInputMode()) { 1950 break; 1951 } 1952 if (event.isTracking() && !event.isCanceled()) { 1953 launchDefaultSearch(event); 1954 } 1955 return true; 1956 } 1957 } 1958 1959 return false; 1960 } 1961 1962 @Override 1963 protected void onActive() { 1964 } 1965 1966 @Override 1967 public final View getDecorView() { 1968 if (mDecor == null) { 1969 installDecor(); 1970 } 1971 return mDecor; 1972 } 1973 1974 @Override 1975 public final View peekDecorView() { 1976 return mDecor; 1977 } 1978 1979 static private final String FOCUSED_ID_TAG = "android:focusedViewId"; 1980 static private final String VIEWS_TAG = "android:views"; 1981 static private final String PANELS_TAG = "android:Panels"; 1982 static private final String ACTION_BAR_TAG = "android:ActionBar"; 1983 1984 /** {@inheritDoc} */ 1985 @Override 1986 public Bundle saveHierarchyState() { 1987 Bundle outState = new Bundle(); 1988 if (mContentParent == null) { 1989 return outState; 1990 } 1991 1992 SparseArray<Parcelable> states = new SparseArray<Parcelable>(); 1993 mContentParent.saveHierarchyState(states); 1994 outState.putSparseParcelableArray(VIEWS_TAG, states); 1995 1996 // save the focused view id 1997 View focusedView = mContentParent.findFocus(); 1998 if (focusedView != null) { 1999 if (focusedView.getId() != View.NO_ID) { 2000 outState.putInt(FOCUSED_ID_TAG, focusedView.getId()); 2001 } else { 2002 if (false) { 2003 Log.d(TAG, "couldn't save which view has focus because the focused view " 2004 + focusedView + " has no id."); 2005 } 2006 } 2007 } 2008 2009 // save the panels 2010 SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>(); 2011 savePanelState(panelStates); 2012 if (panelStates.size() > 0) { 2013 outState.putSparseParcelableArray(PANELS_TAG, panelStates); 2014 } 2015 2016 if (mDecorContentParent != null) { 2017 SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>(); 2018 mDecorContentParent.saveToolbarHierarchyState(actionBarStates); 2019 outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates); 2020 } 2021 2022 return outState; 2023 } 2024 2025 /** {@inheritDoc} */ 2026 @Override 2027 public void restoreHierarchyState(Bundle savedInstanceState) { 2028 if (mContentParent == null) { 2029 return; 2030 } 2031 2032 SparseArray<Parcelable> savedStates 2033 = savedInstanceState.getSparseParcelableArray(VIEWS_TAG); 2034 if (savedStates != null) { 2035 mContentParent.restoreHierarchyState(savedStates); 2036 } 2037 2038 // restore the focused view 2039 int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID); 2040 if (focusedViewId != View.NO_ID) { 2041 View needsFocus = mContentParent.findViewById(focusedViewId); 2042 if (needsFocus != null) { 2043 needsFocus.requestFocus(); 2044 } else { 2045 Log.w(TAG, 2046 "Previously focused view reported id " + focusedViewId 2047 + " during save, but can't be found during restore."); 2048 } 2049 } 2050 2051 // restore the panels 2052 SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG); 2053 if (panelStates != null) { 2054 restorePanelState(panelStates); 2055 } 2056 2057 if (mDecorContentParent != null) { 2058 SparseArray<Parcelable> actionBarStates = 2059 savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG); 2060 if (actionBarStates != null) { 2061 doPendingInvalidatePanelMenu(); 2062 mDecorContentParent.restoreToolbarHierarchyState(actionBarStates); 2063 } else { 2064 Log.w(TAG, "Missing saved instance states for action bar views! " + 2065 "State will not be restored."); 2066 } 2067 } 2068 } 2069 2070 /** 2071 * Invoked when the panels should freeze their state. 2072 * 2073 * @param icicles Save state into this. This is usually indexed by the 2074 * featureId. This will be given to {@link #restorePanelState} in the 2075 * future. 2076 */ 2077 private void savePanelState(SparseArray<Parcelable> icicles) { 2078 PanelFeatureState[] panels = mPanels; 2079 if (panels == null) { 2080 return; 2081 } 2082 2083 for (int curFeatureId = panels.length - 1; curFeatureId >= 0; curFeatureId--) { 2084 if (panels[curFeatureId] != null) { 2085 icicles.put(curFeatureId, panels[curFeatureId].onSaveInstanceState()); 2086 } 2087 } 2088 } 2089 2090 /** 2091 * Invoked when the panels should thaw their state from a previously frozen state. 2092 * 2093 * @param icicles The state saved by {@link #savePanelState} that needs to be thawed. 2094 */ 2095 private void restorePanelState(SparseArray<Parcelable> icicles) { 2096 PanelFeatureState st; 2097 int curFeatureId; 2098 for (int i = icicles.size() - 1; i >= 0; i--) { 2099 curFeatureId = icicles.keyAt(i); 2100 st = getPanelState(curFeatureId, false /* required */); 2101 if (st == null) { 2102 // The panel must not have been required, and is currently not around, skip it 2103 continue; 2104 } 2105 2106 st.onRestoreInstanceState(icicles.get(curFeatureId)); 2107 invalidatePanelMenu(curFeatureId); 2108 } 2109 2110 /* 2111 * Implementation note: call openPanelsAfterRestore later to actually open the 2112 * restored panels. 2113 */ 2114 } 2115 2116 /** 2117 * Opens the panels that have had their state restored. This should be 2118 * called sometime after {@link #restorePanelState} when it is safe to add 2119 * to the window manager. 2120 */ 2121 private void openPanelsAfterRestore() { 2122 PanelFeatureState[] panels = mPanels; 2123 2124 if (panels == null) { 2125 return; 2126 } 2127 2128 PanelFeatureState st; 2129 for (int i = panels.length - 1; i >= 0; i--) { 2130 st = panels[i]; 2131 // We restore the panel if it was last open; we skip it if it 2132 // now is open, to avoid a race condition if the user immediately 2133 // opens it when we are resuming. 2134 if (st != null) { 2135 st.applyFrozenState(); 2136 if (!st.isOpen && st.wasLastOpen) { 2137 st.isInExpandedMode = st.wasLastExpanded; 2138 openPanel(st, null); 2139 } 2140 } 2141 } 2142 } 2143 2144 private class PanelMenuPresenterCallback implements MenuPresenter.Callback { 2145 @Override 2146 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 2147 final Menu parentMenu = menu.getRootMenu(); 2148 final boolean isSubMenu = parentMenu != menu; 2149 final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu); 2150 if (panel != null) { 2151 if (isSubMenu) { 2152 callOnPanelClosed(panel.featureId, panel, parentMenu); 2153 closePanel(panel, true); 2154 } else { 2155 // Close the panel and only do the callback if the menu is being 2156 // closed completely, not if opening a sub menu 2157 closePanel(panel, allMenusAreClosing); 2158 } 2159 } 2160 } 2161 2162 @Override 2163 public boolean onOpenSubMenu(MenuBuilder subMenu) { 2164 if (subMenu == null && hasFeature(FEATURE_ACTION_BAR)) { 2165 Callback cb = getCallback(); 2166 if (cb != null && !isDestroyed()) { 2167 cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu); 2168 } 2169 } 2170 2171 return true; 2172 } 2173 } 2174 2175 private final class ActionMenuPresenterCallback implements MenuPresenter.Callback { 2176 @Override 2177 public boolean onOpenSubMenu(MenuBuilder subMenu) { 2178 Callback cb = getCallback(); 2179 if (cb != null) { 2180 cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu); 2181 return true; 2182 } 2183 return false; 2184 } 2185 2186 @Override 2187 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 2188 checkCloseActionMenu(menu); 2189 } 2190 } 2191 2192 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker { 2193 2194 /* package */int mDefaultOpacity = PixelFormat.OPAQUE; 2195 2196 /** The feature ID of the panel, or -1 if this is the application's DecorView */ 2197 private final int mFeatureId; 2198 2199 private final Rect mDrawingBounds = new Rect(); 2200 2201 private final Rect mBackgroundPadding = new Rect(); 2202 2203 private final Rect mFramePadding = new Rect(); 2204 2205 private final Rect mFrameOffsets = new Rect(); 2206 2207 private boolean mChanging; 2208 2209 private Drawable mMenuBackground; 2210 private boolean mWatchingForMenu; 2211 private int mDownY; 2212 2213 private ActionMode mPrimaryActionMode; 2214 private ActionMode mFloatingActionMode; 2215 private ActionBarContextView mPrimaryActionModeView; 2216 private PopupWindow mPrimaryActionModePopup; 2217 private Runnable mShowPrimaryActionModePopup; 2218 private OnPreDrawListener mFloatingToolbarPreDrawListener; 2219 private View mFloatingActionModeOriginatingView; 2220 private FloatingToolbar mFloatingToolbar; 2221 private ObjectAnimator mFadeAnim; 2222 2223 // View added at runtime to draw under the status bar area 2224 private View mStatusGuard; 2225 // View added at runtime to draw under the navigation bar area 2226 private View mNavigationGuard; 2227 2228 private final ColorViewState mStatusColorViewState = new ColorViewState( 2229 SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, 2230 Gravity.TOP, 2231 Gravity.LEFT, 2232 STATUS_BAR_BACKGROUND_TRANSITION_NAME, 2233 com.android.internal.R.id.statusBarBackground, 2234 FLAG_FULLSCREEN); 2235 private final ColorViewState mNavigationColorViewState = new ColorViewState( 2236 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, 2237 Gravity.BOTTOM, 2238 Gravity.RIGHT, 2239 NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, 2240 com.android.internal.R.id.navigationBarBackground, 2241 0 /* hideWindowFlag */); 2242 2243 private final Interpolator mShowInterpolator; 2244 private final Interpolator mHideInterpolator; 2245 private final int mBarEnterExitDuration; 2246 2247 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback(); 2248 2249 private int mLastTopInset = 0; 2250 private int mLastBottomInset = 0; 2251 private int mLastRightInset = 0; 2252 private boolean mLastHasTopStableInset = false; 2253 private boolean mLastHasBottomStableInset = false; 2254 private boolean mLastHasRightStableInset = false; 2255 private int mLastWindowFlags = 0; 2256 2257 private int mRootScrollY = 0; 2258 2259 public DecorView(Context context, int featureId) { 2260 super(context); 2261 mFeatureId = featureId; 2262 2263 mShowInterpolator = AnimationUtils.loadInterpolator(context, 2264 android.R.interpolator.linear_out_slow_in); 2265 mHideInterpolator = AnimationUtils.loadInterpolator(context, 2266 android.R.interpolator.fast_out_linear_in); 2267 2268 mBarEnterExitDuration = context.getResources().getInteger( 2269 R.integer.dock_enter_exit_duration); 2270 } 2271 2272 public void setBackgroundFallback(int resId) { 2273 mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null); 2274 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback()); 2275 } 2276 2277 @Override 2278 public void onDraw(Canvas c) { 2279 super.onDraw(c); 2280 mBackgroundFallback.draw(mContentRoot, c, mContentParent); 2281 } 2282 2283 @Override 2284 public boolean dispatchKeyEvent(KeyEvent event) { 2285 final int keyCode = event.getKeyCode(); 2286 final int action = event.getAction(); 2287 final boolean isDown = action == KeyEvent.ACTION_DOWN; 2288 2289 if (isDown && (event.getRepeatCount() == 0)) { 2290 // First handle chording of panel key: if a panel key is held 2291 // but not released, try to execute a shortcut in it. 2292 if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) { 2293 boolean handled = dispatchKeyShortcutEvent(event); 2294 if (handled) { 2295 return true; 2296 } 2297 } 2298 2299 // If a panel is open, perform a shortcut on it without the 2300 // chorded panel key 2301 if ((mPreparedPanel != null) && mPreparedPanel.isOpen) { 2302 if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) { 2303 return true; 2304 } 2305 } 2306 } 2307 2308 if (!isDestroyed()) { 2309 final Callback cb = getCallback(); 2310 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) 2311 : super.dispatchKeyEvent(event); 2312 if (handled) { 2313 return true; 2314 } 2315 } 2316 2317 return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event) 2318 : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event); 2319 } 2320 2321 @Override 2322 public boolean dispatchKeyShortcutEvent(KeyEvent ev) { 2323 // If the panel is already prepared, then perform the shortcut using it. 2324 boolean handled; 2325 if (mPreparedPanel != null) { 2326 handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev, 2327 Menu.FLAG_PERFORM_NO_CLOSE); 2328 if (handled) { 2329 if (mPreparedPanel != null) { 2330 mPreparedPanel.isHandled = true; 2331 } 2332 return true; 2333 } 2334 } 2335 2336 // Shortcut not handled by the panel. Dispatch to the view hierarchy. 2337 final Callback cb = getCallback(); 2338 handled = cb != null && !isDestroyed() && mFeatureId < 0 2339 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev); 2340 if (handled) { 2341 return true; 2342 } 2343 2344 // If the panel is not prepared, then we may be trying to handle a shortcut key 2345 // combination such as Control+C. Temporarily prepare the panel then mark it 2346 // unprepared again when finished to ensure that the panel will again be prepared 2347 // the next time it is shown for real. 2348 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 2349 if (st != null && mPreparedPanel == null) { 2350 preparePanel(st, ev); 2351 handled = performPanelShortcut(st, ev.getKeyCode(), ev, 2352 Menu.FLAG_PERFORM_NO_CLOSE); 2353 st.isPrepared = false; 2354 if (handled) { 2355 return true; 2356 } 2357 } 2358 return false; 2359 } 2360 2361 @Override 2362 public boolean dispatchTouchEvent(MotionEvent ev) { 2363 final Callback cb = getCallback(); 2364 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) 2365 : super.dispatchTouchEvent(ev); 2366 } 2367 2368 @Override 2369 public boolean dispatchTrackballEvent(MotionEvent ev) { 2370 final Callback cb = getCallback(); 2371 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev) 2372 : super.dispatchTrackballEvent(ev); 2373 } 2374 2375 @Override 2376 public boolean dispatchGenericMotionEvent(MotionEvent ev) { 2377 final Callback cb = getCallback(); 2378 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) 2379 : super.dispatchGenericMotionEvent(ev); 2380 } 2381 2382 public boolean superDispatchKeyEvent(KeyEvent event) { 2383 // Give priority to closing action modes if applicable. 2384 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 2385 final int action = event.getAction(); 2386 // Back cancels action modes first. 2387 if (mPrimaryActionMode != null) { 2388 if (action == KeyEvent.ACTION_UP) { 2389 mPrimaryActionMode.finish(); 2390 } 2391 return true; 2392 } 2393 } 2394 2395 return super.dispatchKeyEvent(event); 2396 } 2397 2398 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 2399 return super.dispatchKeyShortcutEvent(event); 2400 } 2401 2402 public boolean superDispatchTouchEvent(MotionEvent event) { 2403 return super.dispatchTouchEvent(event); 2404 } 2405 2406 public boolean superDispatchTrackballEvent(MotionEvent event) { 2407 return super.dispatchTrackballEvent(event); 2408 } 2409 2410 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 2411 return super.dispatchGenericMotionEvent(event); 2412 } 2413 2414 @Override 2415 public boolean onTouchEvent(MotionEvent event) { 2416 return onInterceptTouchEvent(event); 2417 } 2418 2419 private boolean isOutOfBounds(int x, int y) { 2420 return x < -5 || y < -5 || x > (getWidth() + 5) 2421 || y > (getHeight() + 5); 2422 } 2423 2424 @Override 2425 public boolean onInterceptTouchEvent(MotionEvent event) { 2426 int action = event.getAction(); 2427 if (mFeatureId >= 0) { 2428 if (action == MotionEvent.ACTION_DOWN) { 2429 int x = (int)event.getX(); 2430 int y = (int)event.getY(); 2431 if (isOutOfBounds(x, y)) { 2432 closePanel(mFeatureId); 2433 return true; 2434 } 2435 } 2436 } 2437 2438 if (!SWEEP_OPEN_MENU) { 2439 return false; 2440 } 2441 2442 if (mFeatureId >= 0) { 2443 if (action == MotionEvent.ACTION_DOWN) { 2444 Log.i(TAG, "Watchiing!"); 2445 mWatchingForMenu = true; 2446 mDownY = (int) event.getY(); 2447 return false; 2448 } 2449 2450 if (!mWatchingForMenu) { 2451 return false; 2452 } 2453 2454 int y = (int)event.getY(); 2455 if (action == MotionEvent.ACTION_MOVE) { 2456 if (y > (mDownY+30)) { 2457 Log.i(TAG, "Closing!"); 2458 closePanel(mFeatureId); 2459 mWatchingForMenu = false; 2460 return true; 2461 } 2462 } else if (action == MotionEvent.ACTION_UP) { 2463 mWatchingForMenu = false; 2464 } 2465 2466 return false; 2467 } 2468 2469 //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY() 2470 // + " (in " + getHeight() + ")"); 2471 2472 if (action == MotionEvent.ACTION_DOWN) { 2473 int y = (int)event.getY(); 2474 if (y >= (getHeight()-5) && !hasChildren()) { 2475 Log.i(TAG, "Watchiing!"); 2476 mWatchingForMenu = true; 2477 } 2478 return false; 2479 } 2480 2481 if (!mWatchingForMenu) { 2482 return false; 2483 } 2484 2485 int y = (int)event.getY(); 2486 if (action == MotionEvent.ACTION_MOVE) { 2487 if (y < (getHeight()-30)) { 2488 Log.i(TAG, "Opening!"); 2489 openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent( 2490 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); 2491 mWatchingForMenu = false; 2492 return true; 2493 } 2494 } else if (action == MotionEvent.ACTION_UP) { 2495 mWatchingForMenu = false; 2496 } 2497 2498 return false; 2499 } 2500 2501 @Override 2502 public void sendAccessibilityEvent(int eventType) { 2503 if (!AccessibilityManager.getInstance(mContext).isEnabled()) { 2504 return; 2505 } 2506 2507 // if we are showing a feature that should be announced and one child 2508 // make this child the event source since this is the feature itself 2509 // otherwise the callback will take over and announce its client 2510 if ((mFeatureId == FEATURE_OPTIONS_PANEL || 2511 mFeatureId == FEATURE_CONTEXT_MENU || 2512 mFeatureId == FEATURE_PROGRESS || 2513 mFeatureId == FEATURE_INDETERMINATE_PROGRESS) 2514 && getChildCount() == 1) { 2515 getChildAt(0).sendAccessibilityEvent(eventType); 2516 } else { 2517 super.sendAccessibilityEvent(eventType); 2518 } 2519 } 2520 2521 @Override 2522 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 2523 final Callback cb = getCallback(); 2524 if (cb != null && !isDestroyed()) { 2525 if (cb.dispatchPopulateAccessibilityEvent(event)) { 2526 return true; 2527 } 2528 } 2529 return super.dispatchPopulateAccessibilityEventInternal(event); 2530 } 2531 2532 @Override 2533 protected boolean setFrame(int l, int t, int r, int b) { 2534 boolean changed = super.setFrame(l, t, r, b); 2535 if (changed) { 2536 final Rect drawingBounds = mDrawingBounds; 2537 getDrawingRect(drawingBounds); 2538 2539 Drawable fg = getForeground(); 2540 if (fg != null) { 2541 final Rect frameOffsets = mFrameOffsets; 2542 drawingBounds.left += frameOffsets.left; 2543 drawingBounds.top += frameOffsets.top; 2544 drawingBounds.right -= frameOffsets.right; 2545 drawingBounds.bottom -= frameOffsets.bottom; 2546 fg.setBounds(drawingBounds); 2547 final Rect framePadding = mFramePadding; 2548 drawingBounds.left += framePadding.left - frameOffsets.left; 2549 drawingBounds.top += framePadding.top - frameOffsets.top; 2550 drawingBounds.right -= framePadding.right - frameOffsets.right; 2551 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom; 2552 } 2553 2554 Drawable bg = getBackground(); 2555 if (bg != null) { 2556 bg.setBounds(drawingBounds); 2557 } 2558 2559 if (SWEEP_OPEN_MENU) { 2560 if (mMenuBackground == null && mFeatureId < 0 2561 && getAttributes().height 2562 == WindowManager.LayoutParams.MATCH_PARENT) { 2563 mMenuBackground = getContext().getDrawable( 2564 R.drawable.menu_background); 2565 } 2566 if (mMenuBackground != null) { 2567 mMenuBackground.setBounds(drawingBounds.left, 2568 drawingBounds.bottom-6, drawingBounds.right, 2569 drawingBounds.bottom+20); 2570 } 2571 } 2572 } 2573 return changed; 2574 } 2575 2576 @Override 2577 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 2578 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); 2579 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; 2580 2581 final int widthMode = getMode(widthMeasureSpec); 2582 final int heightMode = getMode(heightMeasureSpec); 2583 2584 boolean fixedWidth = false; 2585 if (widthMode == AT_MOST) { 2586 final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; 2587 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { 2588 final int w; 2589 if (tvw.type == TypedValue.TYPE_DIMENSION) { 2590 w = (int) tvw.getDimension(metrics); 2591 } else if (tvw.type == TypedValue.TYPE_FRACTION) { 2592 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); 2593 } else { 2594 w = 0; 2595 } 2596 2597 if (w > 0) { 2598 final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 2599 widthMeasureSpec = MeasureSpec.makeMeasureSpec( 2600 Math.min(w, widthSize), EXACTLY); 2601 fixedWidth = true; 2602 } 2603 } 2604 } 2605 2606 if (heightMode == AT_MOST) { 2607 final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; 2608 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { 2609 final int h; 2610 if (tvh.type == TypedValue.TYPE_DIMENSION) { 2611 h = (int) tvh.getDimension(metrics); 2612 } else if (tvh.type == TypedValue.TYPE_FRACTION) { 2613 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); 2614 } else { 2615 h = 0; 2616 } 2617 if (h > 0) { 2618 final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 2619 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 2620 Math.min(h, heightSize), EXACTLY); 2621 } 2622 } 2623 } 2624 2625 getOutsets(mOutsets); 2626 if (mOutsets.top > 0 || mOutsets.bottom > 0) { 2627 int mode = MeasureSpec.getMode(heightMeasureSpec); 2628 if (mode != MeasureSpec.UNSPECIFIED) { 2629 int height = MeasureSpec.getSize(heightMeasureSpec); 2630 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 2631 height + mOutsets.top + mOutsets.bottom, mode); 2632 } 2633 } 2634 if (mOutsets.left > 0 || mOutsets.right > 0) { 2635 int mode = MeasureSpec.getMode(widthMeasureSpec); 2636 if (mode != MeasureSpec.UNSPECIFIED) { 2637 int width = MeasureSpec.getSize(widthMeasureSpec); 2638 widthMeasureSpec = MeasureSpec.makeMeasureSpec( 2639 width + mOutsets.left + mOutsets.right, mode); 2640 } 2641 } 2642 2643 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 2644 2645 int width = getMeasuredWidth(); 2646 boolean measure = false; 2647 2648 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); 2649 2650 if (!fixedWidth && widthMode == AT_MOST) { 2651 final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; 2652 if (tv.type != TypedValue.TYPE_NULL) { 2653 final int min; 2654 if (tv.type == TypedValue.TYPE_DIMENSION) { 2655 min = (int)tv.getDimension(metrics); 2656 } else if (tv.type == TypedValue.TYPE_FRACTION) { 2657 min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); 2658 } else { 2659 min = 0; 2660 } 2661 2662 if (width < min) { 2663 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); 2664 measure = true; 2665 } 2666 } 2667 } 2668 2669 // TODO: Support height? 2670 2671 if (measure) { 2672 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 2673 } 2674 } 2675 2676 @Override 2677 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 2678 super.onLayout(changed, left, top, right, bottom); 2679 getOutsets(mOutsets); 2680 if (mOutsets.left > 0) { 2681 offsetLeftAndRight(-mOutsets.left); 2682 } 2683 if (mOutsets.top > 0) { 2684 offsetTopAndBottom(-mOutsets.top); 2685 } 2686 } 2687 2688 @Override 2689 public void draw(Canvas canvas) { 2690 super.draw(canvas); 2691 2692 if (mMenuBackground != null) { 2693 mMenuBackground.draw(canvas); 2694 } 2695 } 2696 2697 @Override 2698 public boolean showContextMenuForChild(View originalView) { 2699 // Reuse the context menu builder 2700 if (mContextMenu == null) { 2701 mContextMenu = new ContextMenuBuilder(getContext()); 2702 mContextMenu.setCallback(mContextMenuCallback); 2703 } else { 2704 mContextMenu.clearAll(); 2705 } 2706 2707 final MenuDialogHelper helper = mContextMenu.show(originalView, 2708 originalView.getWindowToken()); 2709 if (helper != null) { 2710 helper.setPresenterCallback(mContextMenuCallback); 2711 } else if (mContextMenuHelper != null) { 2712 // No menu to show, but if we have a menu currently showing it just became blank. 2713 // Close it. 2714 mContextMenuHelper.dismiss(); 2715 } 2716 mContextMenuHelper = helper; 2717 return helper != null; 2718 } 2719 2720 @Override 2721 public ActionMode startActionModeForChild(View originalView, 2722 ActionMode.Callback callback) { 2723 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY); 2724 } 2725 2726 @Override 2727 public ActionMode startActionModeForChild( 2728 View child, ActionMode.Callback callback, int type) { 2729 return startActionMode(child, callback, type); 2730 } 2731 2732 @Override 2733 public ActionMode startActionMode(ActionMode.Callback callback) { 2734 return startActionMode(callback, ActionMode.TYPE_PRIMARY); 2735 } 2736 2737 @Override 2738 public ActionMode startActionMode(ActionMode.Callback callback, int type) { 2739 return startActionMode(this, callback, type); 2740 } 2741 2742 private ActionMode startActionMode( 2743 View originatingView, ActionMode.Callback callback, int type) { 2744 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback); 2745 ActionMode mode = null; 2746 if (getCallback() != null && !isDestroyed()) { 2747 try { 2748 mode = getCallback().onWindowStartingActionMode(wrappedCallback, type); 2749 } catch (AbstractMethodError ame) { 2750 // Older apps might not implement the typed version of this method. 2751 if (type == ActionMode.TYPE_PRIMARY) { 2752 try { 2753 mode = getCallback().onWindowStartingActionMode(wrappedCallback); 2754 } catch (AbstractMethodError ame2) { 2755 // Older apps might not implement this callback method at all. 2756 } 2757 } 2758 } 2759 } 2760 if (mode != null) { 2761 if (mode.getType() == ActionMode.TYPE_PRIMARY) { 2762 cleanupPrimaryActionMode(); 2763 mPrimaryActionMode = mode; 2764 } else if (mode.getType() == ActionMode.TYPE_FLOATING) { 2765 if (mFloatingActionMode != null) { 2766 mFloatingActionMode.finish(); 2767 } 2768 mFloatingActionMode = mode; 2769 } 2770 } else { 2771 mode = createActionMode(type, wrappedCallback, originatingView); 2772 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) { 2773 setHandledActionMode(mode); 2774 } else { 2775 mode = null; 2776 } 2777 } 2778 if (mode != null && getCallback() != null && !isDestroyed()) { 2779 try { 2780 getCallback().onActionModeStarted(mode); 2781 } catch (AbstractMethodError ame) { 2782 // Older apps might not implement this callback method. 2783 } 2784 } 2785 return mode; 2786 } 2787 2788 private void cleanupPrimaryActionMode() { 2789 if (mPrimaryActionMode != null) { 2790 mPrimaryActionMode.finish(); 2791 mPrimaryActionMode = null; 2792 } 2793 if (mPrimaryActionModeView != null) { 2794 mPrimaryActionModeView.killMode(); 2795 } 2796 } 2797 2798 private void cleanupFloatingActionModeViews() { 2799 if (mFloatingToolbar != null) { 2800 mFloatingToolbar.dismiss(); 2801 mFloatingToolbar = null; 2802 } 2803 if (mFloatingActionModeOriginatingView != null) { 2804 if (mFloatingToolbarPreDrawListener != null) { 2805 mFloatingActionModeOriginatingView.getViewTreeObserver() 2806 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener); 2807 mFloatingToolbarPreDrawListener = null; 2808 } 2809 mFloatingActionModeOriginatingView = null; 2810 } 2811 } 2812 2813 public void startChanging() { 2814 mChanging = true; 2815 } 2816 2817 public void finishChanging() { 2818 mChanging = false; 2819 drawableChanged(); 2820 } 2821 2822 public void setWindowBackground(Drawable drawable) { 2823 if (getBackground() != drawable) { 2824 setBackgroundDrawable(drawable); 2825 if (drawable != null) { 2826 drawable.getPadding(mBackgroundPadding); 2827 } else { 2828 mBackgroundPadding.setEmpty(); 2829 } 2830 drawableChanged(); 2831 } 2832 } 2833 2834 @Override 2835 public void setBackgroundDrawable(Drawable d) { 2836 super.setBackgroundDrawable(d); 2837 if (getWindowToken() != null) { 2838 updateWindowResizeState(); 2839 } 2840 } 2841 2842 public void setWindowFrame(Drawable drawable) { 2843 if (getForeground() != drawable) { 2844 setForeground(drawable); 2845 if (drawable != null) { 2846 drawable.getPadding(mFramePadding); 2847 } else { 2848 mFramePadding.setEmpty(); 2849 } 2850 drawableChanged(); 2851 } 2852 } 2853 2854 @Override 2855 public void onWindowSystemUiVisibilityChanged(int visible) { 2856 updateColorViews(null /* insets */, true /* animate */); 2857 } 2858 2859 @Override 2860 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 2861 mFrameOffsets.set(insets.getSystemWindowInsets()); 2862 insets = updateColorViews(insets, true /* animate */); 2863 insets = updateStatusGuard(insets); 2864 updateNavigationGuard(insets); 2865 if (getForeground() != null) { 2866 drawableChanged(); 2867 } 2868 return insets; 2869 } 2870 2871 @Override 2872 public boolean isTransitionGroup() { 2873 return false; 2874 } 2875 2876 private WindowInsets updateColorViews(WindowInsets insets, boolean animate) { 2877 WindowManager.LayoutParams attrs = getAttributes(); 2878 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); 2879 2880 if (!mIsFloating && ActivityManager.isHighEndGfx()) { 2881 boolean disallowAnimate = !isLaidOut(); 2882 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags) 2883 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; 2884 mLastWindowFlags = attrs.flags; 2885 2886 if (insets != null) { 2887 mLastTopInset = Math.min(insets.getStableInsetTop(), 2888 insets.getSystemWindowInsetTop()); 2889 mLastBottomInset = Math.min(insets.getStableInsetBottom(), 2890 insets.getSystemWindowInsetBottom()); 2891 mLastRightInset = Math.min(insets.getStableInsetRight(), 2892 insets.getSystemWindowInsetRight()); 2893 2894 // Don't animate if the presence of stable insets has changed, because that 2895 // indicates that the window was either just added and received them for the 2896 // first time, or the window size or position has changed. 2897 boolean hasTopStableInset = insets.getStableInsetTop() != 0; 2898 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset); 2899 mLastHasTopStableInset = hasTopStableInset; 2900 2901 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0; 2902 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset); 2903 mLastHasBottomStableInset = hasBottomStableInset; 2904 2905 boolean hasRightStableInset = insets.getStableInsetRight() != 0; 2906 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset); 2907 mLastHasRightStableInset = hasRightStableInset; 2908 } 2909 2910 boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0; 2911 int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset; 2912 updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor, 2913 navBarSize, navBarToRightEdge, 0 /* rightInset */, 2914 animate && !disallowAnimate); 2915 2916 boolean statusBarNeedsRightInset = navBarToRightEdge 2917 && mNavigationColorViewState.present; 2918 int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0; 2919 updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor, 2920 mLastTopInset, false /* matchVertical */, statusBarRightInset, 2921 animate && !disallowAnimate); 2922 } 2923 2924 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need 2925 // to ensure that the rest of the view hierarchy doesn't notice it, unless they've 2926 // explicitly asked for it. 2927 2928 boolean consumingNavBar = 2929 (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 2930 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 2931 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; 2932 2933 int consumedRight = consumingNavBar ? mLastRightInset : 0; 2934 int consumedBottom = consumingNavBar ? mLastBottomInset : 0; 2935 2936 if (mContentRoot != null 2937 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) { 2938 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams(); 2939 if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) { 2940 lp.rightMargin = consumedRight; 2941 lp.bottomMargin = consumedBottom; 2942 mContentRoot.setLayoutParams(lp); 2943 2944 if (insets == null) { 2945 // The insets have changed, but we're not currently in the process 2946 // of dispatching them. 2947 requestApplyInsets(); 2948 } 2949 } 2950 if (insets != null) { 2951 insets = insets.replaceSystemWindowInsets( 2952 insets.getSystemWindowInsetLeft(), 2953 insets.getSystemWindowInsetTop(), 2954 insets.getSystemWindowInsetRight() - consumedRight, 2955 insets.getSystemWindowInsetBottom() - consumedBottom); 2956 } 2957 } 2958 2959 if (insets != null) { 2960 insets = insets.consumeStableInsets(); 2961 } 2962 return insets; 2963 } 2964 2965 /** 2966 * Update a color view 2967 * 2968 * @param state the color view to update. 2969 * @param sysUiVis the current systemUiVisibility to apply. 2970 * @param color the current color to apply. 2971 * @param size the current size in the non-parent-matching dimension. 2972 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a 2973 * horizontal edge, 2974 * @param rightMargin rightMargin for the color view. 2975 * @param animate if true, the change will be animated. 2976 */ 2977 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, 2978 int size, boolean verticalBar, int rightMargin, boolean animate) { 2979 state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0 2980 && (getAttributes().flags & state.hideWindowFlag) == 0 2981 && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; 2982 boolean show = state.present 2983 && (color & Color.BLACK) != 0 2984 && (getAttributes().flags & state.translucentFlag) == 0; 2985 2986 boolean visibilityChanged = false; 2987 View view = state.view; 2988 2989 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size; 2990 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT; 2991 int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity; 2992 2993 if (view == null) { 2994 if (show) { 2995 state.view = view = new View(mContext); 2996 view.setBackgroundColor(color); 2997 view.setTransitionName(state.transitionName); 2998 view.setId(state.id); 2999 visibilityChanged = true; 3000 view.setVisibility(INVISIBLE); 3001 state.targetVisibility = VISIBLE; 3002 3003 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight, 3004 resolvedGravity); 3005 lp.rightMargin = rightMargin; 3006 addView(view, lp); 3007 updateColorViewTranslations(); 3008 } 3009 } else { 3010 int vis = show ? VISIBLE : INVISIBLE; 3011 visibilityChanged = state.targetVisibility != vis; 3012 state.targetVisibility = vis; 3013 LayoutParams lp = (LayoutParams) view.getLayoutParams(); 3014 if (lp.height != resolvedHeight || lp.width != resolvedWidth 3015 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { 3016 lp.height = resolvedHeight; 3017 lp.width = resolvedWidth; 3018 lp.gravity = resolvedGravity; 3019 lp.rightMargin = rightMargin; 3020 view.setLayoutParams(lp); 3021 } 3022 if (show) { 3023 view.setBackgroundColor(color); 3024 } 3025 } 3026 if (visibilityChanged) { 3027 view.animate().cancel(); 3028 if (animate) { 3029 if (show) { 3030 if (view.getVisibility() != VISIBLE) { 3031 view.setVisibility(VISIBLE); 3032 view.setAlpha(0.0f); 3033 } 3034 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator). 3035 setDuration(mBarEnterExitDuration); 3036 } else { 3037 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator) 3038 .setDuration(mBarEnterExitDuration) 3039 .withEndAction(new Runnable() { 3040 @Override 3041 public void run() { 3042 state.view.setAlpha(1.0f); 3043 state.view.setVisibility(INVISIBLE); 3044 } 3045 }); 3046 } 3047 } else { 3048 view.setAlpha(1.0f); 3049 view.setVisibility(show ? VISIBLE : INVISIBLE); 3050 } 3051 } 3052 } 3053 3054 private void updateColorViewTranslations() { 3055 // Put the color views back in place when they get moved off the screen 3056 // due to the the ViewRootImpl panning. 3057 int rootScrollY = mRootScrollY; 3058 if (mStatusColorViewState.view != null) { 3059 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0); 3060 } 3061 if (mNavigationColorViewState.view != null) { 3062 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0); 3063 } 3064 } 3065 3066 private WindowInsets updateStatusGuard(WindowInsets insets) { 3067 boolean showStatusGuard = false; 3068 // Show the status guard when the non-overlay contextual action bar is showing 3069 if (mPrimaryActionModeView != null) { 3070 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) { 3071 // Insets are magic! 3072 final MarginLayoutParams mlp = (MarginLayoutParams) 3073 mPrimaryActionModeView.getLayoutParams(); 3074 boolean mlpChanged = false; 3075 if (mPrimaryActionModeView.isShown()) { 3076 if (mTempRect == null) { 3077 mTempRect = new Rect(); 3078 } 3079 final Rect rect = mTempRect; 3080 3081 // If the parent doesn't consume the insets, manually 3082 // apply the default system window insets. 3083 mContentParent.computeSystemWindowInsets(insets, rect); 3084 final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0; 3085 if (mlp.topMargin != newMargin) { 3086 mlpChanged = true; 3087 mlp.topMargin = insets.getSystemWindowInsetTop(); 3088 3089 if (mStatusGuard == null) { 3090 mStatusGuard = new View(mContext); 3091 mStatusGuard.setBackgroundColor(mContext.getColor( 3092 R.color.input_method_navigation_guard)); 3093 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), 3094 new LayoutParams(LayoutParams.MATCH_PARENT, 3095 mlp.topMargin, Gravity.START | Gravity.TOP)); 3096 } else { 3097 final LayoutParams lp = (LayoutParams) 3098 mStatusGuard.getLayoutParams(); 3099 if (lp.height != mlp.topMargin) { 3100 lp.height = mlp.topMargin; 3101 mStatusGuard.setLayoutParams(lp); 3102 } 3103 } 3104 } 3105 3106 // The action mode's theme may differ from the app, so 3107 // always show the status guard above it if we have one. 3108 showStatusGuard = mStatusGuard != null; 3109 3110 // We only need to consume the insets if the action 3111 // mode is overlaid on the app content (e.g. it's 3112 // sitting in a FrameLayout, see 3113 // screen_simple_overlay_action_mode.xml). 3114 final boolean nonOverlay = (getLocalFeatures() 3115 & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0; 3116 insets = insets.consumeSystemWindowInsets( 3117 false, nonOverlay && showStatusGuard /* top */, false, false); 3118 } else { 3119 // reset top margin 3120 if (mlp.topMargin != 0) { 3121 mlpChanged = true; 3122 mlp.topMargin = 0; 3123 } 3124 } 3125 if (mlpChanged) { 3126 mPrimaryActionModeView.setLayoutParams(mlp); 3127 } 3128 } 3129 } 3130 if (mStatusGuard != null) { 3131 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE); 3132 } 3133 return insets; 3134 } 3135 3136 private void updateNavigationGuard(WindowInsets insets) { 3137 // IMEs lay out below the nav bar, but the content view must not (for back compat) 3138 if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { 3139 // prevent the content view from including the nav bar height 3140 if (mContentParent != null) { 3141 if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) { 3142 MarginLayoutParams mlp = 3143 (MarginLayoutParams) mContentParent.getLayoutParams(); 3144 mlp.bottomMargin = insets.getSystemWindowInsetBottom(); 3145 mContentParent.setLayoutParams(mlp); 3146 } 3147 } 3148 // position the navigation guard view, creating it if necessary 3149 if (mNavigationGuard == null) { 3150 mNavigationGuard = new View(mContext); 3151 mNavigationGuard.setBackgroundColor(mContext.getColor( 3152 R.color.input_method_navigation_guard)); 3153 addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view), 3154 new LayoutParams(LayoutParams.MATCH_PARENT, 3155 insets.getSystemWindowInsetBottom(), 3156 Gravity.START | Gravity.BOTTOM)); 3157 } else { 3158 LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams(); 3159 lp.height = insets.getSystemWindowInsetBottom(); 3160 mNavigationGuard.setLayoutParams(lp); 3161 } 3162 } 3163 } 3164 3165 private void drawableChanged() { 3166 if (mChanging) { 3167 return; 3168 } 3169 3170 setPadding(mFramePadding.left + mBackgroundPadding.left, mFramePadding.top 3171 + mBackgroundPadding.top, mFramePadding.right + mBackgroundPadding.right, 3172 mFramePadding.bottom + mBackgroundPadding.bottom); 3173 requestLayout(); 3174 invalidate(); 3175 3176 int opacity = PixelFormat.OPAQUE; 3177 // Note: if there is no background, we will assume opaque. The 3178 // common case seems to be that an application sets there to be 3179 // no background so it can draw everything itself. For that, 3180 // we would like to assume OPAQUE and let the app force it to 3181 // the slower TRANSLUCENT mode if that is really what it wants. 3182 Drawable bg = getBackground(); 3183 Drawable fg = getForeground(); 3184 if (bg != null) { 3185 if (fg == null) { 3186 opacity = bg.getOpacity(); 3187 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0 3188 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) { 3189 // If the frame padding is zero, then we can be opaque 3190 // if either the frame -or- the background is opaque. 3191 int fop = fg.getOpacity(); 3192 int bop = bg.getOpacity(); 3193 if (false) 3194 Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop); 3195 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) { 3196 opacity = PixelFormat.OPAQUE; 3197 } else if (fop == PixelFormat.UNKNOWN) { 3198 opacity = bop; 3199 } else if (bop == PixelFormat.UNKNOWN) { 3200 opacity = fop; 3201 } else { 3202 opacity = Drawable.resolveOpacity(fop, bop); 3203 } 3204 } else { 3205 // For now we have to assume translucent if there is a 3206 // frame with padding... there is no way to tell if the 3207 // frame and background together will draw all pixels. 3208 if (false) 3209 Log.v(TAG, "Padding: " + mFramePadding); 3210 opacity = PixelFormat.TRANSLUCENT; 3211 } 3212 } 3213 3214 if (false) 3215 Log.v(TAG, "Background: " + bg + ", Frame: " + fg); 3216 if (false) 3217 Log.v(TAG, "Selected default opacity: " + opacity); 3218 3219 mDefaultOpacity = opacity; 3220 if (mFeatureId < 0) { 3221 setDefaultWindowFormat(opacity); 3222 } 3223 } 3224 3225 @Override 3226 public void onWindowFocusChanged(boolean hasWindowFocus) { 3227 super.onWindowFocusChanged(hasWindowFocus); 3228 3229 // If the user is chording a menu shortcut, release the chord since 3230 // this window lost focus 3231 if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) { 3232 closePanel(FEATURE_OPTIONS_PANEL); 3233 } 3234 3235 final Callback cb = getCallback(); 3236 if (cb != null && !isDestroyed() && mFeatureId < 0) { 3237 cb.onWindowFocusChanged(hasWindowFocus); 3238 } 3239 3240 if (mPrimaryActionMode != null) { 3241 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus); 3242 } 3243 if (mFloatingActionMode != null) { 3244 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus); 3245 } 3246 } 3247 3248 void updateWindowResizeState() { 3249 Drawable bg = getBackground(); 3250 hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity() 3251 != PixelFormat.OPAQUE); 3252 } 3253 3254 @Override 3255 protected void onAttachedToWindow() { 3256 super.onAttachedToWindow(); 3257 3258 updateWindowResizeState(); 3259 3260 final Callback cb = getCallback(); 3261 if (cb != null && !isDestroyed() && mFeatureId < 0) { 3262 cb.onAttachedToWindow(); 3263 } 3264 3265 if (mFeatureId == -1) { 3266 /* 3267 * The main window has been attached, try to restore any panels 3268 * that may have been open before. This is called in cases where 3269 * an activity is being killed for configuration change and the 3270 * menu was open. When the activity is recreated, the menu 3271 * should be shown again. 3272 */ 3273 openPanelsAfterRestore(); 3274 } 3275 } 3276 3277 @Override 3278 protected void onDetachedFromWindow() { 3279 super.onDetachedFromWindow(); 3280 3281 final Callback cb = getCallback(); 3282 if (cb != null && mFeatureId < 0) { 3283 cb.onDetachedFromWindow(); 3284 } 3285 3286 if (mDecorContentParent != null) { 3287 mDecorContentParent.dismissPopups(); 3288 } 3289 3290 if (mPrimaryActionModePopup != null) { 3291 removeCallbacks(mShowPrimaryActionModePopup); 3292 if (mPrimaryActionModePopup.isShowing()) { 3293 mPrimaryActionModePopup.dismiss(); 3294 } 3295 mPrimaryActionModePopup = null; 3296 } 3297 if (mFloatingToolbar != null) { 3298 mFloatingToolbar.dismiss(); 3299 mFloatingToolbar = null; 3300 } 3301 3302 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 3303 if (st != null && st.menu != null && mFeatureId < 0) { 3304 st.menu.close(); 3305 } 3306 } 3307 3308 @Override 3309 public void onCloseSystemDialogs(String reason) { 3310 if (mFeatureId >= 0) { 3311 closeAllPanels(); 3312 } 3313 } 3314 3315 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() { 3316 return mFeatureId < 0 ? mTakeSurfaceCallback : null; 3317 } 3318 3319 public InputQueue.Callback willYouTakeTheInputQueue() { 3320 return mFeatureId < 0 ? mTakeInputQueueCallback : null; 3321 } 3322 3323 public void setSurfaceType(int type) { 3324 PhoneWindow.this.setType(type); 3325 } 3326 3327 public void setSurfaceFormat(int format) { 3328 PhoneWindow.this.setFormat(format); 3329 } 3330 3331 public void setSurfaceKeepScreenOn(boolean keepOn) { 3332 if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 3333 else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 3334 } 3335 3336 @Override 3337 public void onRootViewScrollYChanged(int rootScrollY) { 3338 mRootScrollY = rootScrollY; 3339 updateColorViewTranslations(); 3340 } 3341 3342 private ActionMode createActionMode( 3343 int type, ActionMode.Callback2 callback, View originatingView) { 3344 switch (type) { 3345 case ActionMode.TYPE_PRIMARY: 3346 default: 3347 return createStandaloneActionMode(callback); 3348 case ActionMode.TYPE_FLOATING: 3349 return createFloatingActionMode(originatingView, callback); 3350 } 3351 } 3352 3353 private void setHandledActionMode(ActionMode mode) { 3354 if (mode.getType() == ActionMode.TYPE_PRIMARY) { 3355 setHandledPrimaryActionMode(mode); 3356 } else if (mode.getType() == ActionMode.TYPE_FLOATING) { 3357 setHandledFloatingActionMode(mode); 3358 } 3359 } 3360 3361 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) { 3362 endOnGoingFadeAnimation(); 3363 cleanupPrimaryActionMode(); 3364 if (mPrimaryActionModeView == null) { 3365 if (isFloating()) { 3366 // Use the action bar theme. 3367 final TypedValue outValue = new TypedValue(); 3368 final Theme baseTheme = mContext.getTheme(); 3369 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); 3370 3371 final Context actionBarContext; 3372 if (outValue.resourceId != 0) { 3373 final Theme actionBarTheme = mContext.getResources().newTheme(); 3374 actionBarTheme.setTo(baseTheme); 3375 actionBarTheme.applyStyle(outValue.resourceId, true); 3376 3377 actionBarContext = new ContextThemeWrapper(mContext, 0); 3378 actionBarContext.getTheme().setTo(actionBarTheme); 3379 } else { 3380 actionBarContext = mContext; 3381 } 3382 3383 mPrimaryActionModeView = new ActionBarContextView(actionBarContext); 3384 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null, 3385 R.attr.actionModePopupWindowStyle); 3386 mPrimaryActionModePopup.setWindowLayoutType( 3387 WindowManager.LayoutParams.TYPE_APPLICATION); 3388 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView); 3389 mPrimaryActionModePopup.setWidth(MATCH_PARENT); 3390 3391 actionBarContext.getTheme().resolveAttribute( 3392 R.attr.actionBarSize, outValue, true); 3393 final int height = TypedValue.complexToDimensionPixelSize(outValue.data, 3394 actionBarContext.getResources().getDisplayMetrics()); 3395 mPrimaryActionModeView.setContentHeight(height); 3396 mPrimaryActionModePopup.setHeight(WRAP_CONTENT); 3397 mShowPrimaryActionModePopup = new Runnable() { 3398 public void run() { 3399 mPrimaryActionModePopup.showAtLocation( 3400 mPrimaryActionModeView.getApplicationWindowToken(), 3401 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0); 3402 endOnGoingFadeAnimation(); 3403 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 3404 0f, 1f); 3405 mFadeAnim.addListener(new Animator.AnimatorListener() { 3406 @Override 3407 public void onAnimationStart(Animator animation) { 3408 mPrimaryActionModeView.setVisibility(VISIBLE); 3409 } 3410 3411 @Override 3412 public void onAnimationEnd(Animator animation) { 3413 mPrimaryActionModeView.setAlpha(1f); 3414 mFadeAnim = null; 3415 } 3416 3417 @Override 3418 public void onAnimationCancel(Animator animation) { 3419 3420 } 3421 3422 @Override 3423 public void onAnimationRepeat(Animator animation) { 3424 3425 } 3426 }); 3427 mFadeAnim.start(); 3428 } 3429 }; 3430 } else { 3431 ViewStub stub = (ViewStub) findViewById( 3432 R.id.action_mode_bar_stub); 3433 if (stub != null) { 3434 mPrimaryActionModeView = (ActionBarContextView) stub.inflate(); 3435 } 3436 } 3437 } 3438 if (mPrimaryActionModeView != null) { 3439 mPrimaryActionModeView.killMode(); 3440 ActionMode mode = new StandaloneActionMode( 3441 mPrimaryActionModeView.getContext(), mPrimaryActionModeView, 3442 callback, mPrimaryActionModePopup == null); 3443 return mode; 3444 } 3445 return null; 3446 } 3447 3448 private void endOnGoingFadeAnimation() { 3449 if (mFadeAnim != null) { 3450 mFadeAnim.end(); 3451 } 3452 } 3453 3454 private void setHandledPrimaryActionMode(ActionMode mode) { 3455 endOnGoingFadeAnimation(); 3456 mPrimaryActionMode = mode; 3457 mPrimaryActionMode.invalidate(); 3458 mPrimaryActionModeView.initForMode(mPrimaryActionMode); 3459 if (mPrimaryActionModePopup != null) { 3460 post(mShowPrimaryActionModePopup); 3461 } else { 3462 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f); 3463 mFadeAnim.addListener(new Animator.AnimatorListener() { 3464 @Override 3465 public void onAnimationStart(Animator animation) { 3466 mPrimaryActionModeView.setVisibility(View.VISIBLE); 3467 } 3468 3469 @Override 3470 public void onAnimationEnd(Animator animation) { 3471 mPrimaryActionModeView.setAlpha(1f); 3472 mFadeAnim = null; 3473 } 3474 3475 @Override 3476 public void onAnimationCancel(Animator animation) { 3477 3478 } 3479 3480 @Override 3481 public void onAnimationRepeat(Animator animation) { 3482 3483 } 3484 }); 3485 mFadeAnim.start(); 3486 } 3487 mPrimaryActionModeView.sendAccessibilityEvent( 3488 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 3489 } 3490 3491 private ActionMode createFloatingActionMode( 3492 View originatingView, ActionMode.Callback2 callback) { 3493 if (mFloatingActionMode != null) { 3494 mFloatingActionMode.finish(); 3495 } 3496 cleanupFloatingActionModeViews(); 3497 final FloatingActionMode mode = 3498 new FloatingActionMode(mContext, callback, originatingView); 3499 mFloatingActionModeOriginatingView = originatingView; 3500 mFloatingToolbarPreDrawListener = 3501 new OnPreDrawListener() { 3502 @Override 3503 public boolean onPreDraw() { 3504 mode.updateViewLocationInWindow(); 3505 return true; 3506 } 3507 }; 3508 return mode; 3509 } 3510 3511 private void setHandledFloatingActionMode(ActionMode mode) { 3512 mFloatingActionMode = mode; 3513 mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this); 3514 ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar); 3515 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary. 3516 mFloatingActionModeOriginatingView.getViewTreeObserver() 3517 .addOnPreDrawListener(mFloatingToolbarPreDrawListener); 3518 } 3519 3520 /** 3521 * Clears out internal references when the action mode is destroyed. 3522 */ 3523 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 { 3524 private final ActionMode.Callback mWrapped; 3525 3526 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) { 3527 mWrapped = wrapped; 3528 } 3529 3530 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 3531 return mWrapped.onCreateActionMode(mode, menu); 3532 } 3533 3534 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 3535 requestFitSystemWindows(); 3536 return mWrapped.onPrepareActionMode(mode, menu); 3537 } 3538 3539 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 3540 return mWrapped.onActionItemClicked(mode, item); 3541 } 3542 3543 public void onDestroyActionMode(ActionMode mode) { 3544 mWrapped.onDestroyActionMode(mode); 3545 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion 3546 >= Build.VERSION_CODES.M; 3547 final boolean isPrimary; 3548 final boolean isFloating; 3549 if (isMncApp) { 3550 isPrimary = mode == mPrimaryActionMode; 3551 isFloating = mode == mFloatingActionMode; 3552 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) { 3553 Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; " 3554 + mode + " was not the current primary action mode! Expected " 3555 + mPrimaryActionMode); 3556 } 3557 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) { 3558 Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_FLOATING; " 3559 + mode + " was not the current floating action mode! Expected " 3560 + mFloatingActionMode); 3561 } 3562 } else { 3563 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY; 3564 isFloating = mode.getType() == ActionMode.TYPE_FLOATING; 3565 } 3566 if (isPrimary) { 3567 if (mPrimaryActionModePopup != null) { 3568 removeCallbacks(mShowPrimaryActionModePopup); 3569 } 3570 if (mPrimaryActionModeView != null) { 3571 endOnGoingFadeAnimation(); 3572 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 3573 1f, 0f); 3574 mFadeAnim.addListener(new Animator.AnimatorListener() { 3575 @Override 3576 public void onAnimationStart(Animator animation) { 3577 3578 } 3579 3580 @Override 3581 public void onAnimationEnd(Animator animation) { 3582 mPrimaryActionModeView.setVisibility(GONE); 3583 if (mPrimaryActionModePopup != null) { 3584 mPrimaryActionModePopup.dismiss(); 3585 } 3586 mPrimaryActionModeView.removeAllViews(); 3587 mFadeAnim = null; 3588 } 3589 3590 @Override 3591 public void onAnimationCancel(Animator animation) { 3592 3593 } 3594 3595 @Override 3596 public void onAnimationRepeat(Animator animation) { 3597 3598 } 3599 }); 3600 mFadeAnim.start(); 3601 } 3602 3603 mPrimaryActionMode = null; 3604 } else if (isFloating) { 3605 cleanupFloatingActionModeViews(); 3606 mFloatingActionMode = null; 3607 } 3608 if (getCallback() != null && !isDestroyed()) { 3609 try { 3610 getCallback().onActionModeFinished(mode); 3611 } catch (AbstractMethodError ame) { 3612 // Older apps might not implement this callback method. 3613 } 3614 } 3615 requestFitSystemWindows(); 3616 } 3617 3618 @Override 3619 public void onGetContentRect(ActionMode mode, View view, Rect outRect) { 3620 if (mWrapped instanceof ActionMode.Callback2) { 3621 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect); 3622 } else { 3623 super.onGetContentRect(mode, view, outRect); 3624 } 3625 } 3626 } 3627 } 3628 3629 protected DecorView generateDecor() { 3630 return new DecorView(getContext(), -1); 3631 } 3632 3633 protected void setFeatureFromAttrs(int featureId, TypedArray attrs, 3634 int drawableAttr, int alphaAttr) { 3635 Drawable d = attrs.getDrawable(drawableAttr); 3636 if (d != null) { 3637 requestFeature(featureId); 3638 setFeatureDefaultDrawable(featureId, d); 3639 } 3640 if ((getFeatures() & (1 << featureId)) != 0) { 3641 int alpha = attrs.getInt(alphaAttr, -1); 3642 if (alpha >= 0) { 3643 setFeatureDrawableAlpha(featureId, alpha); 3644 } 3645 } 3646 } 3647 3648 protected ViewGroup generateLayout(DecorView decor) { 3649 // Apply data from current theme. 3650 3651 TypedArray a = getWindowStyle(); 3652 3653 if (false) { 3654 System.out.println("From style:"); 3655 String s = "Attrs:"; 3656 for (int i = 0; i < R.styleable.Window.length; i++) { 3657 s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "=" 3658 + a.getString(i); 3659 } 3660 System.out.println(s); 3661 } 3662 3663 mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false); 3664 int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) 3665 & (~getForcedWindowFlags()); 3666 if (mIsFloating) { 3667 setLayout(WRAP_CONTENT, WRAP_CONTENT); 3668 setFlags(0, flagsToUpdate); 3669 } else { 3670 setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); 3671 } 3672 3673 if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) { 3674 requestFeature(FEATURE_NO_TITLE); 3675 } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { 3676 // Don't allow an action bar if there is no title. 3677 requestFeature(FEATURE_ACTION_BAR); 3678 } 3679 3680 if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { 3681 requestFeature(FEATURE_ACTION_BAR_OVERLAY); 3682 } 3683 3684 if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) { 3685 requestFeature(FEATURE_ACTION_MODE_OVERLAY); 3686 } 3687 3688 if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { 3689 requestFeature(FEATURE_SWIPE_TO_DISMISS); 3690 } 3691 3692 if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { 3693 setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); 3694 } 3695 3696 if (a.getBoolean(R.styleable.Window_windowTranslucentStatus, 3697 false)) { 3698 setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS 3699 & (~getForcedWindowFlags())); 3700 } 3701 3702 if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation, 3703 false)) { 3704 setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION 3705 & (~getForcedWindowFlags())); 3706 } 3707 3708 if (a.getBoolean(R.styleable.Window_windowOverscan, false)) { 3709 setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags())); 3710 } 3711 3712 if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { 3713 setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); 3714 } 3715 3716 if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch, 3717 getContext().getApplicationInfo().targetSdkVersion 3718 >= android.os.Build.VERSION_CODES.HONEYCOMB)) { 3719 setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags())); 3720 } 3721 3722 a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor); 3723 a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor); 3724 if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) { 3725 if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); 3726 a.getValue(R.styleable.Window_windowFixedWidthMajor, 3727 mFixedWidthMajor); 3728 } 3729 if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) { 3730 if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); 3731 a.getValue(R.styleable.Window_windowFixedWidthMinor, 3732 mFixedWidthMinor); 3733 } 3734 if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) { 3735 if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); 3736 a.getValue(R.styleable.Window_windowFixedHeightMajor, 3737 mFixedHeightMajor); 3738 } 3739 if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) { 3740 if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); 3741 a.getValue(R.styleable.Window_windowFixedHeightMinor, 3742 mFixedHeightMinor); 3743 } 3744 if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) { 3745 requestFeature(FEATURE_CONTENT_TRANSITIONS); 3746 } 3747 if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) { 3748 requestFeature(FEATURE_ACTIVITY_TRANSITIONS); 3749 } 3750 3751 final Context context = getContext(); 3752 final int targetSdk = context.getApplicationInfo().targetSdkVersion; 3753 final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; 3754 final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; 3755 final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP; 3756 final boolean targetHcNeedsOptions = context.getResources().getBoolean( 3757 R.bool.target_honeycomb_needs_options_menu); 3758 final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); 3759 3760 if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) { 3761 setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE); 3762 } else { 3763 setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE); 3764 } 3765 3766 // Non-floating windows on high end devices must put up decor beneath the system bars and 3767 // therefore must know about visibility changes of those. 3768 if (!mIsFloating && ActivityManager.isHighEndGfx()) { 3769 if (!targetPreL && a.getBoolean( 3770 R.styleable.Window_windowDrawsSystemBarBackgrounds, 3771 false)) { 3772 setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, 3773 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags()); 3774 } 3775 } 3776 if (!mForcedStatusBarColor) { 3777 mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); 3778 } 3779 if (!mForcedNavigationBarColor) { 3780 mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); 3781 } 3782 if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { 3783 decor.setSystemUiVisibility( 3784 decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 3785 } 3786 3787 if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion 3788 >= android.os.Build.VERSION_CODES.HONEYCOMB) { 3789 if (a.getBoolean( 3790 R.styleable.Window_windowCloseOnTouchOutside, 3791 false)) { 3792 setCloseOnTouchOutsideIfNotSet(true); 3793 } 3794 } 3795 3796 WindowManager.LayoutParams params = getAttributes(); 3797 3798 if (!hasSoftInputMode()) { 3799 params.softInputMode = a.getInt( 3800 R.styleable.Window_windowSoftInputMode, 3801 params.softInputMode); 3802 } 3803 3804 if (a.getBoolean(R.styleable.Window_backgroundDimEnabled, 3805 mIsFloating)) { 3806 /* All dialogs should have the window dimmed */ 3807 if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { 3808 params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; 3809 } 3810 if (!haveDimAmount()) { 3811 params.dimAmount = a.getFloat( 3812 android.R.styleable.Window_backgroundDimAmount, 0.5f); 3813 } 3814 } 3815 3816 if (params.windowAnimations == 0) { 3817 params.windowAnimations = a.getResourceId( 3818 R.styleable.Window_windowAnimationStyle, 0); 3819 } 3820 3821 // The rest are only done if this window is not embedded; otherwise, 3822 // the values are inherited from our container. 3823 if (getContainer() == null) { 3824 if (mBackgroundDrawable == null) { 3825 if (mBackgroundResource == 0) { 3826 mBackgroundResource = a.getResourceId( 3827 R.styleable.Window_windowBackground, 0); 3828 } 3829 if (mFrameResource == 0) { 3830 mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0); 3831 } 3832 mBackgroundFallbackResource = a.getResourceId( 3833 R.styleable.Window_windowBackgroundFallback, 0); 3834 if (false) { 3835 System.out.println("Background: " 3836 + Integer.toHexString(mBackgroundResource) + " Frame: " 3837 + Integer.toHexString(mFrameResource)); 3838 } 3839 } 3840 mElevation = a.getDimension(R.styleable.Window_windowElevation, 0); 3841 mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false); 3842 mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT); 3843 } 3844 3845 // Inflate the window decor. 3846 3847 int layoutResource; 3848 int features = getLocalFeatures(); 3849 // System.out.println("Features: 0x" + Integer.toHexString(features)); 3850 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { 3851 layoutResource = R.layout.screen_swipe_dismiss; 3852 } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { 3853 if (mIsFloating) { 3854 TypedValue res = new TypedValue(); 3855 getContext().getTheme().resolveAttribute( 3856 R.attr.dialogTitleIconsDecorLayout, res, true); 3857 layoutResource = res.resourceId; 3858 } else { 3859 layoutResource = R.layout.screen_title_icons; 3860 } 3861 // XXX Remove this once action bar supports these features. 3862 removeFeature(FEATURE_ACTION_BAR); 3863 // System.out.println("Title Icons!"); 3864 } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 3865 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { 3866 // Special case for a window with only a progress bar (and title). 3867 // XXX Need to have a no-title version of embedded windows. 3868 layoutResource = R.layout.screen_progress; 3869 // System.out.println("Progress!"); 3870 } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { 3871 // Special case for a window with a custom title. 3872 // If the window is floating, we need a dialog layout 3873 if (mIsFloating) { 3874 TypedValue res = new TypedValue(); 3875 getContext().getTheme().resolveAttribute( 3876 R.attr.dialogCustomTitleDecorLayout, res, true); 3877 layoutResource = res.resourceId; 3878 } else { 3879 layoutResource = R.layout.screen_custom_title; 3880 } 3881 // XXX Remove this once action bar supports these features. 3882 removeFeature(FEATURE_ACTION_BAR); 3883 } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { 3884 // If no other features and not embedded, only need a title. 3885 // If the window is floating, we need a dialog layout 3886 if (mIsFloating) { 3887 TypedValue res = new TypedValue(); 3888 getContext().getTheme().resolveAttribute( 3889 R.attr.dialogTitleDecorLayout, res, true); 3890 layoutResource = res.resourceId; 3891 } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { 3892 layoutResource = a.getResourceId( 3893 R.styleable.Window_windowActionBarFullscreenDecorLayout, 3894 R.layout.screen_action_bar); 3895 } else { 3896 layoutResource = R.layout.screen_title; 3897 } 3898 // System.out.println("Title!"); 3899 } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { 3900 layoutResource = R.layout.screen_simple_overlay_action_mode; 3901 } else { 3902 // Embedded, so no decoration is needed. 3903 layoutResource = R.layout.screen_simple; 3904 // System.out.println("Simple!"); 3905 } 3906 3907 mDecor.startChanging(); 3908 3909 View in = mLayoutInflater.inflate(layoutResource, null); 3910 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 3911 mContentRoot = (ViewGroup) in; 3912 3913 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 3914 if (contentParent == null) { 3915 throw new RuntimeException("Window couldn't find content container view"); 3916 } 3917 3918 if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 3919 ProgressBar progress = getCircularProgressBar(false); 3920 if (progress != null) { 3921 progress.setIndeterminate(true); 3922 } 3923 } 3924 3925 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { 3926 registerSwipeCallbacks(); 3927 } 3928 3929 // Remaining setup -- of background and title -- that only applies 3930 // to top-level windows. 3931 if (getContainer() == null) { 3932 final Drawable background; 3933 if (mBackgroundResource != 0) { 3934 background = getContext().getDrawable(mBackgroundResource); 3935 } else { 3936 background = mBackgroundDrawable; 3937 } 3938 mDecor.setWindowBackground(background); 3939 3940 final Drawable frame; 3941 if (mFrameResource != 0) { 3942 frame = getContext().getDrawable(mFrameResource); 3943 } else { 3944 frame = null; 3945 } 3946 mDecor.setWindowFrame(frame); 3947 3948 mDecor.setElevation(mElevation); 3949 mDecor.setClipToOutline(mClipToOutline); 3950 3951 if (mTitle != null) { 3952 setTitle(mTitle); 3953 } 3954 3955 if (mTitleColor == 0) { 3956 mTitleColor = mTextColor; 3957 } 3958 setTitleColor(mTitleColor); 3959 } 3960 3961 mDecor.finishChanging(); 3962 3963 return contentParent; 3964 } 3965 3966 /** @hide */ 3967 public void alwaysReadCloseOnTouchAttr() { 3968 mAlwaysReadCloseOnTouchAttr = true; 3969 } 3970 3971 private void installDecor() { 3972 if (mDecor == null) { 3973 mDecor = generateDecor(); 3974 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 3975 mDecor.setIsRootNamespace(true); 3976 if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { 3977 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); 3978 } 3979 } 3980 if (mContentParent == null) { 3981 mContentParent = generateLayout(mDecor); 3982 3983 // Set up decor part of UI to ignore fitsSystemWindows if appropriate. 3984 mDecor.makeOptionalFitsSystemWindows(); 3985 3986 final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( 3987 R.id.decor_content_parent); 3988 3989 if (decorContentParent != null) { 3990 mDecorContentParent = decorContentParent; 3991 mDecorContentParent.setWindowCallback(getCallback()); 3992 if (mDecorContentParent.getTitle() == null) { 3993 mDecorContentParent.setWindowTitle(mTitle); 3994 } 3995 3996 final int localFeatures = getLocalFeatures(); 3997 for (int i = 0; i < FEATURE_MAX; i++) { 3998 if ((localFeatures & (1 << i)) != 0) { 3999 mDecorContentParent.initFeature(i); 4000 } 4001 } 4002 4003 mDecorContentParent.setUiOptions(mUiOptions); 4004 4005 if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 || 4006 (mIconRes != 0 && !mDecorContentParent.hasIcon())) { 4007 mDecorContentParent.setIcon(mIconRes); 4008 } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 && 4009 mIconRes == 0 && !mDecorContentParent.hasIcon()) { 4010 mDecorContentParent.setIcon( 4011 getContext().getPackageManager().getDefaultActivityIcon()); 4012 mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; 4013 } 4014 if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 || 4015 (mLogoRes != 0 && !mDecorContentParent.hasLogo())) { 4016 mDecorContentParent.setLogo(mLogoRes); 4017 } 4018 4019 // Invalidate if the panel menu hasn't been created before this. 4020 // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu 4021 // being called in the middle of onCreate or similar. 4022 // A pending invalidation will typically be resolved before the posted message 4023 // would run normally in order to satisfy instance state restoration. 4024 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 4025 if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) { 4026 invalidatePanelMenu(FEATURE_ACTION_BAR); 4027 } 4028 } else { 4029 mTitleView = (TextView)findViewById(R.id.title); 4030 if (mTitleView != null) { 4031 mTitleView.setLayoutDirection(mDecor.getLayoutDirection()); 4032 if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { 4033 View titleContainer = findViewById( 4034 R.id.title_container); 4035 if (titleContainer != null) { 4036 titleContainer.setVisibility(View.GONE); 4037 } else { 4038 mTitleView.setVisibility(View.GONE); 4039 } 4040 if (mContentParent instanceof FrameLayout) { 4041 ((FrameLayout)mContentParent).setForeground(null); 4042 } 4043 } else { 4044 mTitleView.setText(mTitle); 4045 } 4046 } 4047 } 4048 4049 if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) { 4050 mDecor.setBackgroundFallback(mBackgroundFallbackResource); 4051 } 4052 4053 // Only inflate or create a new TransitionManager if the caller hasn't 4054 // already set a custom one. 4055 if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) { 4056 if (mTransitionManager == null) { 4057 final int transitionRes = getWindowStyle().getResourceId( 4058 R.styleable.Window_windowContentTransitionManager, 4059 0); 4060 if (transitionRes != 0) { 4061 final TransitionInflater inflater = TransitionInflater.from(getContext()); 4062 mTransitionManager = inflater.inflateTransitionManager(transitionRes, 4063 mContentParent); 4064 } else { 4065 mTransitionManager = new TransitionManager(); 4066 } 4067 } 4068 4069 mEnterTransition = getTransition(mEnterTransition, null, 4070 R.styleable.Window_windowEnterTransition); 4071 mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION, 4072 R.styleable.Window_windowReturnTransition); 4073 mExitTransition = getTransition(mExitTransition, null, 4074 R.styleable.Window_windowExitTransition); 4075 mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION, 4076 R.styleable.Window_windowReenterTransition); 4077 mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null, 4078 R.styleable.Window_windowSharedElementEnterTransition); 4079 mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition, 4080 USE_DEFAULT_TRANSITION, 4081 R.styleable.Window_windowSharedElementReturnTransition); 4082 mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null, 4083 R.styleable.Window_windowSharedElementExitTransition); 4084 mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition, 4085 USE_DEFAULT_TRANSITION, 4086 R.styleable.Window_windowSharedElementReenterTransition); 4087 if (mAllowEnterTransitionOverlap == null) { 4088 mAllowEnterTransitionOverlap = getWindowStyle().getBoolean( 4089 R.styleable.Window_windowAllowEnterTransitionOverlap, true); 4090 } 4091 if (mAllowReturnTransitionOverlap == null) { 4092 mAllowReturnTransitionOverlap = getWindowStyle().getBoolean( 4093 R.styleable.Window_windowAllowReturnTransitionOverlap, true); 4094 } 4095 if (mBackgroundFadeDurationMillis < 0) { 4096 mBackgroundFadeDurationMillis = getWindowStyle().getInteger( 4097 R.styleable.Window_windowTransitionBackgroundFadeDuration, 4098 DEFAULT_BACKGROUND_FADE_DURATION_MS); 4099 } 4100 if (mSharedElementsUseOverlay == null) { 4101 mSharedElementsUseOverlay = getWindowStyle().getBoolean( 4102 R.styleable.Window_windowSharedElementsUseOverlay, true); 4103 } 4104 } 4105 } 4106 } 4107 4108 private Transition getTransition(Transition currentValue, Transition defaultValue, int id) { 4109 if (currentValue != defaultValue) { 4110 return currentValue; 4111 } 4112 int transitionId = getWindowStyle().getResourceId(id, -1); 4113 Transition transition = defaultValue; 4114 if (transitionId != -1 && transitionId != R.transition.no_transition) { 4115 TransitionInflater inflater = TransitionInflater.from(getContext()); 4116 transition = inflater.inflateTransition(transitionId); 4117 if (transition instanceof TransitionSet && 4118 ((TransitionSet)transition).getTransitionCount() == 0) { 4119 transition = null; 4120 } 4121 } 4122 return transition; 4123 } 4124 4125 private Drawable loadImageURI(Uri uri) { 4126 try { 4127 return Drawable.createFromStream( 4128 getContext().getContentResolver().openInputStream(uri), null); 4129 } catch (Exception e) { 4130 Log.w(TAG, "Unable to open content: " + uri); 4131 } 4132 return null; 4133 } 4134 4135 private DrawableFeatureState getDrawableState(int featureId, boolean required) { 4136 if ((getFeatures() & (1 << featureId)) == 0) { 4137 if (!required) { 4138 return null; 4139 } 4140 throw new RuntimeException("The feature has not been requested"); 4141 } 4142 4143 DrawableFeatureState[] ar; 4144 if ((ar = mDrawables) == null || ar.length <= featureId) { 4145 DrawableFeatureState[] nar = new DrawableFeatureState[featureId + 1]; 4146 if (ar != null) { 4147 System.arraycopy(ar, 0, nar, 0, ar.length); 4148 } 4149 mDrawables = ar = nar; 4150 } 4151 4152 DrawableFeatureState st = ar[featureId]; 4153 if (st == null) { 4154 ar[featureId] = st = new DrawableFeatureState(featureId); 4155 } 4156 return st; 4157 } 4158 4159 /** 4160 * Gets a panel's state based on its feature ID. 4161 * 4162 * @param featureId The feature ID of the panel. 4163 * @param required Whether the panel is required (if it is required and it 4164 * isn't in our features, this throws an exception). 4165 * @return The panel state. 4166 */ 4167 private PanelFeatureState getPanelState(int featureId, boolean required) { 4168 return getPanelState(featureId, required, null); 4169 } 4170 4171 /** 4172 * Gets a panel's state based on its feature ID. 4173 * 4174 * @param featureId The feature ID of the panel. 4175 * @param required Whether the panel is required (if it is required and it 4176 * isn't in our features, this throws an exception). 4177 * @param convertPanelState Optional: If the panel state does not exist, use 4178 * this as the panel state. 4179 * @return The panel state. 4180 */ 4181 private PanelFeatureState getPanelState(int featureId, boolean required, 4182 PanelFeatureState convertPanelState) { 4183 if ((getFeatures() & (1 << featureId)) == 0) { 4184 if (!required) { 4185 return null; 4186 } 4187 throw new RuntimeException("The feature has not been requested"); 4188 } 4189 4190 PanelFeatureState[] ar; 4191 if ((ar = mPanels) == null || ar.length <= featureId) { 4192 PanelFeatureState[] nar = new PanelFeatureState[featureId + 1]; 4193 if (ar != null) { 4194 System.arraycopy(ar, 0, nar, 0, ar.length); 4195 } 4196 mPanels = ar = nar; 4197 } 4198 4199 PanelFeatureState st = ar[featureId]; 4200 if (st == null) { 4201 ar[featureId] = st = (convertPanelState != null) 4202 ? convertPanelState 4203 : new PanelFeatureState(featureId); 4204 } 4205 return st; 4206 } 4207 4208 @Override 4209 public final void setChildDrawable(int featureId, Drawable drawable) { 4210 DrawableFeatureState st = getDrawableState(featureId, true); 4211 st.child = drawable; 4212 updateDrawable(featureId, st, false); 4213 } 4214 4215 @Override 4216 public final void setChildInt(int featureId, int value) { 4217 updateInt(featureId, value, false); 4218 } 4219 4220 @Override 4221 public boolean isShortcutKey(int keyCode, KeyEvent event) { 4222 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 4223 return st != null && st.menu != null && st.menu.isShortcutKey(keyCode, event); 4224 } 4225 4226 private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) { 4227 // Do nothing if the decor is not yet installed... an update will 4228 // need to be forced when we eventually become active. 4229 if (mContentParent == null) { 4230 return; 4231 } 4232 4233 final int featureMask = 1 << featureId; 4234 4235 if ((getFeatures() & featureMask) == 0 && !fromResume) { 4236 return; 4237 } 4238 4239 Drawable drawable = null; 4240 if (st != null) { 4241 drawable = st.child; 4242 if (drawable == null) 4243 drawable = st.local; 4244 if (drawable == null) 4245 drawable = st.def; 4246 } 4247 if ((getLocalFeatures() & featureMask) == 0) { 4248 if (getContainer() != null) { 4249 if (isActive() || fromResume) { 4250 getContainer().setChildDrawable(featureId, drawable); 4251 } 4252 } 4253 } else if (st != null && (st.cur != drawable || st.curAlpha != st.alpha)) { 4254 // System.out.println("Drawable changed: old=" + st.cur 4255 // + ", new=" + drawable); 4256 st.cur = drawable; 4257 st.curAlpha = st.alpha; 4258 onDrawableChanged(featureId, drawable, st.alpha); 4259 } 4260 } 4261 4262 private void updateInt(int featureId, int value, boolean fromResume) { 4263 4264 // Do nothing if the decor is not yet installed... an update will 4265 // need to be forced when we eventually become active. 4266 if (mContentParent == null) { 4267 return; 4268 } 4269 4270 final int featureMask = 1 << featureId; 4271 4272 if ((getFeatures() & featureMask) == 0 && !fromResume) { 4273 return; 4274 } 4275 4276 if ((getLocalFeatures() & featureMask) == 0) { 4277 if (getContainer() != null) { 4278 getContainer().setChildInt(featureId, value); 4279 } 4280 } else { 4281 onIntChanged(featureId, value); 4282 } 4283 } 4284 4285 private ImageView getLeftIconView() { 4286 if (mLeftIconView != null) { 4287 return mLeftIconView; 4288 } 4289 if (mContentParent == null) { 4290 installDecor(); 4291 } 4292 return (mLeftIconView = (ImageView)findViewById(R.id.left_icon)); 4293 } 4294 4295 @Override 4296 protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) { 4297 super.dispatchWindowAttributesChanged(attrs); 4298 if (mDecor != null) { 4299 mDecor.updateColorViews(null /* insets */, true /* animate */); 4300 } 4301 } 4302 4303 private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) { 4304 if (mCircularProgressBar != null) { 4305 return mCircularProgressBar; 4306 } 4307 if (mContentParent == null && shouldInstallDecor) { 4308 installDecor(); 4309 } 4310 mCircularProgressBar = (ProgressBar) findViewById(R.id.progress_circular); 4311 if (mCircularProgressBar != null) { 4312 mCircularProgressBar.setVisibility(View.INVISIBLE); 4313 } 4314 return mCircularProgressBar; 4315 } 4316 4317 private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) { 4318 if (mHorizontalProgressBar != null) { 4319 return mHorizontalProgressBar; 4320 } 4321 if (mContentParent == null && shouldInstallDecor) { 4322 installDecor(); 4323 } 4324 mHorizontalProgressBar = (ProgressBar) findViewById(R.id.progress_horizontal); 4325 if (mHorizontalProgressBar != null) { 4326 mHorizontalProgressBar.setVisibility(View.INVISIBLE); 4327 } 4328 return mHorizontalProgressBar; 4329 } 4330 4331 private ImageView getRightIconView() { 4332 if (mRightIconView != null) { 4333 return mRightIconView; 4334 } 4335 if (mContentParent == null) { 4336 installDecor(); 4337 } 4338 return (mRightIconView = (ImageView)findViewById(R.id.right_icon)); 4339 } 4340 4341 private void registerSwipeCallbacks() { 4342 SwipeDismissLayout swipeDismiss = 4343 (SwipeDismissLayout) findViewById(R.id.content); 4344 swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() { 4345 @Override 4346 public void onDismissed(SwipeDismissLayout layout) { 4347 dispatchOnWindowDismissed(); 4348 } 4349 }); 4350 swipeDismiss.setOnSwipeProgressChangedListener( 4351 new SwipeDismissLayout.OnSwipeProgressChangedListener() { 4352 private static final float ALPHA_DECREASE = 0.5f; 4353 private boolean mIsTranslucent = false; 4354 @Override 4355 public void onSwipeProgressChanged( 4356 SwipeDismissLayout layout, float progress, float translate) { 4357 WindowManager.LayoutParams newParams = getAttributes(); 4358 newParams.x = (int) translate; 4359 newParams.alpha = 1 - (progress * ALPHA_DECREASE); 4360 setAttributes(newParams); 4361 4362 int flags = 0; 4363 if (newParams.x == 0) { 4364 flags = FLAG_FULLSCREEN; 4365 } else { 4366 flags = FLAG_LAYOUT_NO_LIMITS; 4367 } 4368 setFlags(flags, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS); 4369 } 4370 4371 @Override 4372 public void onSwipeCancelled(SwipeDismissLayout layout) { 4373 WindowManager.LayoutParams newParams = getAttributes(); 4374 newParams.x = 0; 4375 newParams.alpha = 1; 4376 setAttributes(newParams); 4377 setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS); 4378 } 4379 }); 4380 } 4381 4382 /** 4383 * Helper method for calling the {@link Callback#onPanelClosed(int, Menu)} 4384 * callback. This method will grab whatever extra state is needed for the 4385 * callback that isn't given in the parameters. If the panel is not open, 4386 * this will not perform the callback. 4387 * 4388 * @param featureId Feature ID of the panel that was closed. Must be given. 4389 * @param panel Panel that was closed. Optional but useful if there is no 4390 * menu given. 4391 * @param menu The menu that was closed. Optional, but give if you have. 4392 */ 4393 private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) { 4394 final Callback cb = getCallback(); 4395 if (cb == null) 4396 return; 4397 4398 // Try to get a menu 4399 if (menu == null) { 4400 // Need a panel to grab the menu, so try to get that 4401 if (panel == null) { 4402 if ((featureId >= 0) && (featureId < mPanels.length)) { 4403 panel = mPanels[featureId]; 4404 } 4405 } 4406 4407 if (panel != null) { 4408 // menu still may be null, which is okay--we tried our best 4409 menu = panel.menu; 4410 } 4411 } 4412 4413 // If the panel is not open, do not callback 4414 if ((panel != null) && (!panel.isOpen)) 4415 return; 4416 4417 if (!isDestroyed()) { 4418 cb.onPanelClosed(featureId, menu); 4419 } 4420 } 4421 4422 /** 4423 * Helper method for adding launch-search to most applications. Opens the 4424 * search window using default settings. 4425 * 4426 * @return true if search window opened 4427 */ 4428 private boolean launchDefaultSearch(KeyEvent event) { 4429 boolean result; 4430 final Callback cb = getCallback(); 4431 if (cb == null || isDestroyed()) { 4432 result = false; 4433 } else { 4434 sendCloseSystemWindows("search"); 4435 int deviceId = event.getDeviceId(); 4436 SearchEvent searchEvent = null; 4437 if (deviceId != 0) { 4438 searchEvent = new SearchEvent(InputDevice.getDevice(deviceId)); 4439 } 4440 try { 4441 result = cb.onSearchRequested(searchEvent); 4442 } catch (AbstractMethodError e) { 4443 Log.e(TAG, "WindowCallback " + cb.getClass().getName() + " does not implement" 4444 + " method onSearchRequested(SearchEvent); fa", e); 4445 result = cb.onSearchRequested(); 4446 } 4447 } 4448 if (!result && (getContext().getResources().getConfiguration().uiMode 4449 & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { 4450 // On TVs, if the app doesn't implement search, we want to launch assist. 4451 Bundle args = new Bundle(); 4452 args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId()); 4453 return ((SearchManager)getContext().getSystemService(Context.SEARCH_SERVICE)) 4454 .launchLegacyAssist(null, UserHandle.myUserId(), args); 4455 } 4456 return result; 4457 } 4458 4459 @Override 4460 public void setVolumeControlStream(int streamType) { 4461 mVolumeControlStreamType = streamType; 4462 } 4463 4464 @Override 4465 public int getVolumeControlStream() { 4466 return mVolumeControlStreamType; 4467 } 4468 4469 @Override 4470 public void setMediaController(MediaController controller) { 4471 mMediaController = controller; 4472 } 4473 4474 @Override 4475 public MediaController getMediaController() { 4476 return mMediaController; 4477 } 4478 4479 @Override 4480 public void setEnterTransition(Transition enterTransition) { 4481 mEnterTransition = enterTransition; 4482 } 4483 4484 @Override 4485 public void setReturnTransition(Transition transition) { 4486 mReturnTransition = transition; 4487 } 4488 4489 @Override 4490 public void setExitTransition(Transition exitTransition) { 4491 mExitTransition = exitTransition; 4492 } 4493 4494 @Override 4495 public void setReenterTransition(Transition transition) { 4496 mReenterTransition = transition; 4497 } 4498 4499 @Override 4500 public void setSharedElementEnterTransition(Transition sharedElementEnterTransition) { 4501 mSharedElementEnterTransition = sharedElementEnterTransition; 4502 } 4503 4504 @Override 4505 public void setSharedElementReturnTransition(Transition transition) { 4506 mSharedElementReturnTransition = transition; 4507 } 4508 4509 @Override 4510 public void setSharedElementExitTransition(Transition sharedElementExitTransition) { 4511 mSharedElementExitTransition = sharedElementExitTransition; 4512 } 4513 4514 @Override 4515 public void setSharedElementReenterTransition(Transition transition) { 4516 mSharedElementReenterTransition = transition; 4517 } 4518 4519 @Override 4520 public Transition getEnterTransition() { 4521 return mEnterTransition; 4522 } 4523 4524 @Override 4525 public Transition getReturnTransition() { 4526 return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition() 4527 : mReturnTransition; 4528 } 4529 4530 @Override 4531 public Transition getExitTransition() { 4532 return mExitTransition; 4533 } 4534 4535 @Override 4536 public Transition getReenterTransition() { 4537 return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition() 4538 : mReenterTransition; 4539 } 4540 4541 @Override 4542 public Transition getSharedElementEnterTransition() { 4543 return mSharedElementEnterTransition; 4544 } 4545 4546 @Override 4547 public Transition getSharedElementReturnTransition() { 4548 return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION 4549 ? getSharedElementEnterTransition() : mSharedElementReturnTransition; 4550 } 4551 4552 @Override 4553 public Transition getSharedElementExitTransition() { 4554 return mSharedElementExitTransition; 4555 } 4556 4557 @Override 4558 public Transition getSharedElementReenterTransition() { 4559 return mSharedElementReenterTransition == USE_DEFAULT_TRANSITION 4560 ? getSharedElementExitTransition() : mSharedElementReenterTransition; 4561 } 4562 4563 @Override 4564 public void setAllowEnterTransitionOverlap(boolean allow) { 4565 mAllowEnterTransitionOverlap = allow; 4566 } 4567 4568 @Override 4569 public boolean getAllowEnterTransitionOverlap() { 4570 return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap; 4571 } 4572 4573 @Override 4574 public void setAllowReturnTransitionOverlap(boolean allowExitTransitionOverlap) { 4575 mAllowReturnTransitionOverlap = allowExitTransitionOverlap; 4576 } 4577 4578 @Override 4579 public boolean getAllowReturnTransitionOverlap() { 4580 return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap; 4581 } 4582 4583 @Override 4584 public long getTransitionBackgroundFadeDuration() { 4585 return (mBackgroundFadeDurationMillis < 0) ? DEFAULT_BACKGROUND_FADE_DURATION_MS 4586 : mBackgroundFadeDurationMillis; 4587 } 4588 4589 @Override 4590 public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { 4591 if (fadeDurationMillis < 0) { 4592 throw new IllegalArgumentException("negative durations are not allowed"); 4593 } 4594 mBackgroundFadeDurationMillis = fadeDurationMillis; 4595 } 4596 4597 @Override 4598 public void setSharedElementsUseOverlay(boolean sharedElementsUseOverlay) { 4599 mSharedElementsUseOverlay = sharedElementsUseOverlay; 4600 } 4601 4602 @Override 4603 public boolean getSharedElementsUseOverlay() { 4604 return (mSharedElementsUseOverlay == null) ? true : mSharedElementsUseOverlay; 4605 } 4606 4607 private static final class DrawableFeatureState { 4608 DrawableFeatureState(int _featureId) { 4609 featureId = _featureId; 4610 } 4611 4612 final int featureId; 4613 4614 int resid; 4615 4616 Uri uri; 4617 4618 Drawable local; 4619 4620 Drawable child; 4621 4622 Drawable def; 4623 4624 Drawable cur; 4625 4626 int alpha = 255; 4627 4628 int curAlpha = 255; 4629 } 4630 4631 private static final class PanelFeatureState { 4632 4633 /** Feature ID for this panel. */ 4634 int featureId; 4635 4636 // Information pulled from the style for this panel. 4637 4638 int background; 4639 4640 /** The background when the panel spans the entire available width. */ 4641 int fullBackground; 4642 4643 int gravity; 4644 4645 int x; 4646 4647 int y; 4648 4649 int windowAnimations; 4650 4651 /** Dynamic state of the panel. */ 4652 DecorView decorView; 4653 4654 /** The panel that was returned by onCreatePanelView(). */ 4655 View createdPanelView; 4656 4657 /** The panel that we are actually showing. */ 4658 View shownPanelView; 4659 4660 /** Use {@link #setMenu} to set this. */ 4661 MenuBuilder menu; 4662 4663 IconMenuPresenter iconMenuPresenter; 4664 ListMenuPresenter listMenuPresenter; 4665 4666 /** true if this menu will show in single-list compact mode */ 4667 boolean isCompact; 4668 4669 /** Theme resource ID for list elements of the panel menu */ 4670 int listPresenterTheme; 4671 4672 /** 4673 * Whether the panel has been prepared (see 4674 * {@link PhoneWindow#preparePanel}). 4675 */ 4676 boolean isPrepared; 4677 4678 /** 4679 * Whether an item's action has been performed. This happens in obvious 4680 * scenarios (user clicks on menu item), but can also happen with 4681 * chording menu+(shortcut key). 4682 */ 4683 boolean isHandled; 4684 4685 boolean isOpen; 4686 4687 /** 4688 * True if the menu is in expanded mode, false if the menu is in icon 4689 * mode 4690 */ 4691 boolean isInExpandedMode; 4692 4693 public boolean qwertyMode; 4694 4695 boolean refreshDecorView; 4696 4697 boolean refreshMenuContent; 4698 4699 boolean wasLastOpen; 4700 4701 boolean wasLastExpanded; 4702 4703 /** 4704 * Contains the state of the menu when told to freeze. 4705 */ 4706 Bundle frozenMenuState; 4707 4708 /** 4709 * Contains the state of associated action views when told to freeze. 4710 * These are saved across invalidations. 4711 */ 4712 Bundle frozenActionViewState; 4713 4714 PanelFeatureState(int featureId) { 4715 this.featureId = featureId; 4716 4717 refreshDecorView = false; 4718 } 4719 4720 public boolean isInListMode() { 4721 return isInExpandedMode || isCompact; 4722 } 4723 4724 public boolean hasPanelItems() { 4725 if (shownPanelView == null) return false; 4726 if (createdPanelView != null) return true; 4727 4728 if (isCompact || isInExpandedMode) { 4729 return listMenuPresenter.getAdapter().getCount() > 0; 4730 } else { 4731 return ((ViewGroup) shownPanelView).getChildCount() > 0; 4732 } 4733 } 4734 4735 /** 4736 * Unregister and free attached MenuPresenters. They will be recreated as needed. 4737 */ 4738 public void clearMenuPresenters() { 4739 if (menu != null) { 4740 menu.removeMenuPresenter(iconMenuPresenter); 4741 menu.removeMenuPresenter(listMenuPresenter); 4742 } 4743 iconMenuPresenter = null; 4744 listMenuPresenter = null; 4745 } 4746 4747 void setStyle(Context context) { 4748 TypedArray a = context.obtainStyledAttributes(R.styleable.Theme); 4749 background = a.getResourceId( 4750 R.styleable.Theme_panelBackground, 0); 4751 fullBackground = a.getResourceId( 4752 R.styleable.Theme_panelFullBackground, 0); 4753 windowAnimations = a.getResourceId( 4754 R.styleable.Theme_windowAnimationStyle, 0); 4755 isCompact = a.getBoolean( 4756 R.styleable.Theme_panelMenuIsCompact, false); 4757 listPresenterTheme = a.getResourceId( 4758 R.styleable.Theme_panelMenuListTheme, 4759 R.style.Theme_ExpandedMenu); 4760 a.recycle(); 4761 } 4762 4763 void setMenu(MenuBuilder menu) { 4764 if (menu == this.menu) return; 4765 4766 if (this.menu != null) { 4767 this.menu.removeMenuPresenter(iconMenuPresenter); 4768 this.menu.removeMenuPresenter(listMenuPresenter); 4769 } 4770 this.menu = menu; 4771 if (menu != null) { 4772 if (iconMenuPresenter != null) menu.addMenuPresenter(iconMenuPresenter); 4773 if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter); 4774 } 4775 } 4776 4777 MenuView getListMenuView(Context context, MenuPresenter.Callback cb) { 4778 if (menu == null) return null; 4779 4780 if (!isCompact) { 4781 getIconMenuView(context, cb); // Need this initialized to know where our offset goes 4782 } 4783 4784 if (listMenuPresenter == null) { 4785 listMenuPresenter = new ListMenuPresenter( 4786 R.layout.list_menu_item_layout, listPresenterTheme); 4787 listMenuPresenter.setCallback(cb); 4788 listMenuPresenter.setId(R.id.list_menu_presenter); 4789 menu.addMenuPresenter(listMenuPresenter); 4790 } 4791 4792 if (iconMenuPresenter != null) { 4793 listMenuPresenter.setItemIndexOffset( 4794 iconMenuPresenter.getNumActualItemsShown()); 4795 } 4796 MenuView result = listMenuPresenter.getMenuView(decorView); 4797 4798 return result; 4799 } 4800 4801 MenuView getIconMenuView(Context context, MenuPresenter.Callback cb) { 4802 if (menu == null) return null; 4803 4804 if (iconMenuPresenter == null) { 4805 iconMenuPresenter = new IconMenuPresenter(context); 4806 iconMenuPresenter.setCallback(cb); 4807 iconMenuPresenter.setId(R.id.icon_menu_presenter); 4808 menu.addMenuPresenter(iconMenuPresenter); 4809 } 4810 4811 MenuView result = iconMenuPresenter.getMenuView(decorView); 4812 4813 return result; 4814 } 4815 4816 Parcelable onSaveInstanceState() { 4817 SavedState savedState = new SavedState(); 4818 savedState.featureId = featureId; 4819 savedState.isOpen = isOpen; 4820 savedState.isInExpandedMode = isInExpandedMode; 4821 4822 if (menu != null) { 4823 savedState.menuState = new Bundle(); 4824 menu.savePresenterStates(savedState.menuState); 4825 } 4826 4827 return savedState; 4828 } 4829 4830 void onRestoreInstanceState(Parcelable state) { 4831 SavedState savedState = (SavedState) state; 4832 featureId = savedState.featureId; 4833 wasLastOpen = savedState.isOpen; 4834 wasLastExpanded = savedState.isInExpandedMode; 4835 frozenMenuState = savedState.menuState; 4836 4837 /* 4838 * A LocalActivityManager keeps the same instance of this class around. 4839 * The first time the menu is being shown after restoring, the 4840 * Activity.onCreateOptionsMenu should be called. But, if it is the 4841 * same instance then menu != null and we won't call that method. 4842 * We clear any cached views here. The caller should invalidatePanelMenu. 4843 */ 4844 createdPanelView = null; 4845 shownPanelView = null; 4846 decorView = null; 4847 } 4848 4849 void applyFrozenState() { 4850 if (menu != null && frozenMenuState != null) { 4851 menu.restorePresenterStates(frozenMenuState); 4852 frozenMenuState = null; 4853 } 4854 } 4855 4856 private static class SavedState implements Parcelable { 4857 int featureId; 4858 boolean isOpen; 4859 boolean isInExpandedMode; 4860 Bundle menuState; 4861 4862 public int describeContents() { 4863 return 0; 4864 } 4865 4866 public void writeToParcel(Parcel dest, int flags) { 4867 dest.writeInt(featureId); 4868 dest.writeInt(isOpen ? 1 : 0); 4869 dest.writeInt(isInExpandedMode ? 1 : 0); 4870 4871 if (isOpen) { 4872 dest.writeBundle(menuState); 4873 } 4874 } 4875 4876 private static SavedState readFromParcel(Parcel source) { 4877 SavedState savedState = new SavedState(); 4878 savedState.featureId = source.readInt(); 4879 savedState.isOpen = source.readInt() == 1; 4880 savedState.isInExpandedMode = source.readInt() == 1; 4881 4882 if (savedState.isOpen) { 4883 savedState.menuState = source.readBundle(); 4884 } 4885 4886 return savedState; 4887 } 4888 4889 public static final Parcelable.Creator<SavedState> CREATOR 4890 = new Parcelable.Creator<SavedState>() { 4891 public SavedState createFromParcel(Parcel in) { 4892 return readFromParcel(in); 4893 } 4894 4895 public SavedState[] newArray(int size) { 4896 return new SavedState[size]; 4897 } 4898 }; 4899 } 4900 4901 } 4902 4903 static class RotationWatcher extends Stub { 4904 private Handler mHandler; 4905 private final Runnable mRotationChanged = new Runnable() { 4906 public void run() { 4907 dispatchRotationChanged(); 4908 } 4909 }; 4910 private final ArrayList<WeakReference<PhoneWindow>> mWindows = 4911 new ArrayList<WeakReference<PhoneWindow>>(); 4912 private boolean mIsWatching; 4913 4914 @Override 4915 public void onRotationChanged(int rotation) throws RemoteException { 4916 mHandler.post(mRotationChanged); 4917 } 4918 4919 public void addWindow(PhoneWindow phoneWindow) { 4920 synchronized (mWindows) { 4921 if (!mIsWatching) { 4922 try { 4923 WindowManagerHolder.sWindowManager.watchRotation(this); 4924 mHandler = new Handler(); 4925 mIsWatching = true; 4926 } catch (RemoteException ex) { 4927 Log.e(TAG, "Couldn't start watching for device rotation", ex); 4928 } 4929 } 4930 mWindows.add(new WeakReference<PhoneWindow>(phoneWindow)); 4931 } 4932 } 4933 4934 public void removeWindow(PhoneWindow phoneWindow) { 4935 synchronized (mWindows) { 4936 int i = 0; 4937 while (i < mWindows.size()) { 4938 final WeakReference<PhoneWindow> ref = mWindows.get(i); 4939 final PhoneWindow win = ref.get(); 4940 if (win == null || win == phoneWindow) { 4941 mWindows.remove(i); 4942 } else { 4943 i++; 4944 } 4945 } 4946 } 4947 } 4948 4949 void dispatchRotationChanged() { 4950 synchronized (mWindows) { 4951 int i = 0; 4952 while (i < mWindows.size()) { 4953 final WeakReference<PhoneWindow> ref = mWindows.get(i); 4954 final PhoneWindow win = ref.get(); 4955 if (win != null) { 4956 win.onOptionsPanelRotationChanged(); 4957 i++; 4958 } else { 4959 mWindows.remove(i); 4960 } 4961 } 4962 } 4963 } 4964 } 4965 4966 /** 4967 * Simple implementation of MenuBuilder.Callback that: 4968 * <li> Opens a submenu when selected. 4969 * <li> Calls back to the callback's onMenuItemSelected when an item is 4970 * selected. 4971 */ 4972 private final class DialogMenuCallback implements MenuBuilder.Callback, MenuPresenter.Callback { 4973 private int mFeatureId; 4974 private MenuDialogHelper mSubMenuHelper; 4975 4976 public DialogMenuCallback(int featureId) { 4977 mFeatureId = featureId; 4978 } 4979 4980 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 4981 if (menu.getRootMenu() != menu) { 4982 onCloseSubMenu(menu); 4983 } 4984 4985 if (allMenusAreClosing) { 4986 Callback callback = getCallback(); 4987 if (callback != null && !isDestroyed()) { 4988 callback.onPanelClosed(mFeatureId, menu); 4989 } 4990 4991 if (menu == mContextMenu) { 4992 dismissContextMenu(); 4993 } 4994 4995 // Dismiss the submenu, if it is showing 4996 if (mSubMenuHelper != null) { 4997 mSubMenuHelper.dismiss(); 4998 mSubMenuHelper = null; 4999 } 5000 } 5001 } 5002 5003 public void onCloseSubMenu(MenuBuilder menu) { 5004 Callback callback = getCallback(); 5005 if (callback != null && !isDestroyed()) { 5006 callback.onPanelClosed(mFeatureId, menu.getRootMenu()); 5007 } 5008 } 5009 5010 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 5011 Callback callback = getCallback(); 5012 return (callback != null && !isDestroyed()) 5013 && callback.onMenuItemSelected(mFeatureId, item); 5014 } 5015 5016 public void onMenuModeChange(MenuBuilder menu) { 5017 } 5018 5019 public boolean onOpenSubMenu(MenuBuilder subMenu) { 5020 if (subMenu == null) return false; 5021 5022 // Set a simple callback for the submenu 5023 subMenu.setCallback(this); 5024 5025 // The window manager will give us a valid window token 5026 mSubMenuHelper = new MenuDialogHelper(subMenu); 5027 mSubMenuHelper.show(null); 5028 5029 return true; 5030 } 5031 } 5032 5033 private static class ColorViewState { 5034 View view = null; 5035 int targetVisibility = View.INVISIBLE; 5036 boolean present = false; 5037 5038 final int id; 5039 final int systemUiHideFlag; 5040 final int translucentFlag; 5041 final int verticalGravity; 5042 final int horizontalGravity; 5043 final String transitionName; 5044 final int hideWindowFlag; 5045 5046 ColorViewState(int systemUiHideFlag, 5047 int translucentFlag, int verticalGravity, int horizontalGravity, 5048 String transitionName, int id, int hideWindowFlag) { 5049 this.id = id; 5050 this.systemUiHideFlag = systemUiHideFlag; 5051 this.translucentFlag = translucentFlag; 5052 this.verticalGravity = verticalGravity; 5053 this.horizontalGravity = horizontalGravity; 5054 this.transitionName = transitionName; 5055 this.hideWindowFlag = hideWindowFlag; 5056 } 5057 } 5058 5059 void sendCloseSystemWindows() { 5060 sendCloseSystemWindows(getContext(), null); 5061 } 5062 5063 void sendCloseSystemWindows(String reason) { 5064 sendCloseSystemWindows(getContext(), reason); 5065 } 5066 5067 public static void sendCloseSystemWindows(Context context, String reason) { 5068 if (ActivityManagerNative.isSystemReady()) { 5069 try { 5070 ActivityManagerNative.getDefault().closeSystemDialogs(reason); 5071 } catch (RemoteException e) { 5072 } 5073 } 5074 } 5075 5076 @Override 5077 public int getStatusBarColor() { 5078 return mStatusBarColor; 5079 } 5080 5081 @Override 5082 public void setStatusBarColor(int color) { 5083 mStatusBarColor = color; 5084 mForcedStatusBarColor = true; 5085 if (mDecor != null) { 5086 mDecor.updateColorViews(null, false /* animate */); 5087 } 5088 } 5089 5090 @Override 5091 public int getNavigationBarColor() { 5092 return mNavigationBarColor; 5093 } 5094 5095 @Override 5096 public void setNavigationBarColor(int color) { 5097 mNavigationBarColor = color; 5098 mForcedNavigationBarColor = true; 5099 if (mDecor != null) { 5100 mDecor.updateColorViews(null, false /* animate */); 5101 } 5102 } 5103 5104 public void setIsStartingWindow(boolean isStartingWindow) { 5105 mIsStartingWindow = isStartingWindow; 5106 } 5107} 5108