1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.policy; 18 19 import static android.view.View.MeasureSpec.AT_MOST; 20 import static android.view.View.MeasureSpec.EXACTLY; 21 import static android.view.View.MeasureSpec.getMode; 22 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 23 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 24 import static android.view.WindowManager.LayoutParams.*; 25 26 import android.animation.Animator; 27 import android.animation.ObjectAnimator; 28 import android.app.ActivityManagerNative; 29 import android.app.SearchManager; 30 import android.os.Build; 31 import android.os.UserHandle; 32 33 import android.view.ActionMode; 34 import android.view.ContextThemeWrapper; 35 import android.view.Gravity; 36 import android.view.IRotationWatcher.Stub; 37 import android.view.IWindowManager; 38 import android.view.InputDevice; 39 import android.view.InputEvent; 40 import android.view.InputQueue; 41 import android.view.KeyCharacterMap; 42 import android.view.KeyEvent; 43 import android.view.LayoutInflater; 44 import android.view.Menu; 45 import android.view.MenuItem; 46 import android.view.MotionEvent; 47 import android.view.SearchEvent; 48 import android.view.SurfaceHolder.Callback2; 49 import android.view.View; 50 import android.view.ViewConfiguration; 51 import android.view.ViewGroup; 52 import android.view.ViewManager; 53 import android.view.ViewParent; 54 import android.view.ViewRootImpl; 55 import android.view.ViewStub; 56 import android.view.ViewTreeObserver.OnPreDrawListener; 57 import android.view.Window; 58 import android.view.WindowInsets; 59 import android.view.WindowManager; 60 import com.android.internal.R; 61 import com.android.internal.view.FloatingActionMode; 62 import com.android.internal.view.RootViewSurfaceTaker; 63 import com.android.internal.view.StandaloneActionMode; 64 import com.android.internal.view.menu.ContextMenuBuilder; 65 import com.android.internal.view.menu.IconMenuPresenter; 66 import com.android.internal.view.menu.ListMenuPresenter; 67 import com.android.internal.view.menu.MenuBuilder; 68 import com.android.internal.view.menu.MenuDialogHelper; 69 import com.android.internal.view.menu.MenuPresenter; 70 import com.android.internal.view.menu.MenuView; 71 import com.android.internal.widget.ActionBarContextView; 72 import com.android.internal.widget.BackgroundFallback; 73 import com.android.internal.widget.DecorContentParent; 74 import com.android.internal.widget.FloatingToolbar; 75 import com.android.internal.widget.SwipeDismissLayout; 76 77 import android.app.ActivityManager; 78 import android.app.KeyguardManager; 79 import android.content.Context; 80 import android.content.Intent; 81 import android.content.pm.PackageManager; 82 import android.content.res.Configuration; 83 import android.content.res.Resources.Theme; 84 import android.content.res.TypedArray; 85 import android.graphics.Canvas; 86 import android.graphics.Color; 87 import android.graphics.PixelFormat; 88 import android.graphics.Rect; 89 import android.graphics.drawable.Drawable; 90 import android.media.AudioManager; 91 import android.media.session.MediaController; 92 import android.media.session.MediaSessionLegacyHelper; 93 import android.net.Uri; 94 import android.os.Bundle; 95 import android.os.Handler; 96 import android.os.Parcel; 97 import android.os.Parcelable; 98 import android.os.RemoteException; 99 import android.os.ServiceManager; 100 import android.transition.Scene; 101 import android.transition.Transition; 102 import android.transition.TransitionInflater; 103 import android.transition.TransitionManager; 104 import android.transition.TransitionSet; 105 import android.util.AndroidRuntimeException; 106 import android.util.DisplayMetrics; 107 import android.util.EventLog; 108 import android.util.Log; 109 import android.util.SparseArray; 110 import android.util.TypedValue; 111 import android.view.accessibility.AccessibilityEvent; 112 import android.view.accessibility.AccessibilityManager; 113 import android.view.animation.Animation; 114 import android.view.animation.AnimationUtils; 115 import android.view.animation.Interpolator; 116 import android.widget.FrameLayout; 117 import android.widget.ImageView; 118 import android.widget.PopupWindow; 119 import android.widget.ProgressBar; 120 import android.widget.TextView; 121 122 import java.lang.ref.WeakReference; 123 import 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 */ 133 public 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 PhoneWindow(Context context)304 public PhoneWindow(Context context) { 305 super(context); 306 mLayoutInflater = LayoutInflater.from(context); 307 } 308 309 @Override setContainer(Window container)310 public final void setContainer(Window container) { 311 super.setContainer(container); 312 } 313 314 @Override requestFeature(int featureId)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 setUiOptions(int uiOptions)353 public void setUiOptions(int uiOptions) { 354 mUiOptions = uiOptions; 355 } 356 357 @Override setUiOptions(int uiOptions, int mask)358 public void setUiOptions(int uiOptions, int mask) { 359 mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask); 360 } 361 362 @Override getTransitionManager()363 public TransitionManager getTransitionManager() { 364 return mTransitionManager; 365 } 366 367 @Override setTransitionManager(TransitionManager tm)368 public void setTransitionManager(TransitionManager tm) { 369 mTransitionManager = tm; 370 } 371 372 @Override getContentScene()373 public Scene getContentScene() { 374 return mContentScene; 375 } 376 377 @Override setContentView(int layoutResID)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 setContentView(View view)403 public void setContentView(View view) { 404 setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 405 } 406 407 @Override setContentView(View view, ViewGroup.LayoutParams params)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 addContentView(View view, ViewGroup.LayoutParams params)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 transitionTo(Scene scene)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 getCurrentFocus()459 public View getCurrentFocus() { 460 return mDecor != null ? mDecor.findFocus() : null; 461 } 462 463 @Override takeSurface(Callback2 callback)464 public void takeSurface(Callback2 callback) { 465 mTakeSurfaceCallback = callback; 466 } 467 takeInputQueue(InputQueue.Callback callback)468 public void takeInputQueue(InputQueue.Callback callback) { 469 mTakeInputQueueCallback = callback; 470 } 471 472 @Override isFloating()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 getLayoutInflater()484 public LayoutInflater getLayoutInflater() { 485 return mLayoutInflater; 486 } 487 488 @Override setTitle(CharSequence title)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 setTitleColor(int textColor)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 */ preparePanel(PanelFeatureState st, KeyEvent event)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 onConfigurationChanged(Configuration newConfig)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 clearMenuViews(PanelFeatureState st)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 openPanel(int featureId, KeyEvent event)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 openPanel(final PanelFeatureState st, KeyEvent event)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 closePanel(int featureId)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 */ closePanel(PanelFeatureState st, boolean doCallback)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 checkCloseActionMenu(Menu menu)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 togglePanel(int featureId, KeyEvent event)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 invalidatePanelMenu(int featureId)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 doPendingInvalidatePanelMenu()907 void doPendingInvalidatePanelMenu() { 908 if (mInvalidatePanelMenuPosted) { 909 mDecor.removeCallbacks(mInvalidatePanelMenuRunnable); 910 mInvalidatePanelMenuRunnable.run(); 911 } 912 } 913 doInvalidatePanelMenu(int featureId)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 */ onKeyDownPanel(int featureId, KeyEvent event)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 */ onKeyUpPanel(int featureId, KeyEvent event)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 closeAllPanels()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 */ closeContextMenu()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 */ dismissContextMenu()1071 private synchronized void dismissContextMenu() { 1072 mContextMenu = null; 1073 1074 if (mContextMenuHelper != null) { 1075 mContextMenuHelper.dismiss(); 1076 mContextMenuHelper = null; 1077 } 1078 } 1079 1080 @Override performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags)1081 public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { 1082 return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags); 1083 } 1084 performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event, int flags)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 performPanelIdentifierAction(int featureId, int id, int flags)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 findMenuPanel(Menu menu)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 onMenuItemSelected(MenuBuilder menu, MenuItem item)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 onMenuModeChange(MenuBuilder menu)1157 public void onMenuModeChange(MenuBuilder menu) { 1158 reopenMenu(true); 1159 } 1160 reopenMenu(boolean toggleMenuMode)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 */ initializePanelMenu(final PanelFeatureState st)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 */ initializePanelDecor(PanelFeatureState st)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 */ getOptionsPanelGravity()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 onOptionsPanelRotationChanged()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 */ initializePanelContent(PanelFeatureState st)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 performContextMenuIdentifierAction(int id, int flags)1358 public boolean performContextMenuIdentifierAction(int id, int flags) { 1359 return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false; 1360 } 1361 1362 @Override setElevation(float elevation)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 setClipToOutline(boolean clipToOutline)1372 public final void setClipToOutline(boolean clipToOutline) { 1373 mClipToOutline = clipToOutline; 1374 if (mDecor != null) { 1375 mDecor.setClipToOutline(clipToOutline); 1376 } 1377 } 1378 1379 @Override setBackgroundDrawable(Drawable drawable)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 setFeatureDrawableResource(int featureId, int resId)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 setFeatureDrawableUri(int featureId, Uri uri)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 setFeatureDrawable(int featureId, Drawable drawable)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 setFeatureDrawableAlpha(int featureId, int alpha)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 setFeatureDefaultDrawable(int featureId, Drawable drawable)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 setFeatureInt(int featureId, int value)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 */ updateDrawable(int featureId, boolean fromActive)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 */ onDrawableChanged(int featureId, Drawable drawable, int alpha)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 */ onIntChanged(int featureId, int value)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 */ updateProgressBars(int value)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 showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar)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 hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar)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 setIcon(int resId)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 setDefaultIcon(int resId)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 setLogo(int resId)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 setDefaultLogo(int resId)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 setLocalFocus(boolean hasFocus, boolean inTouchMode)1695 public void setLocalFocus(boolean hasFocus, boolean inTouchMode) { 1696 getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode); 1697 1698 } 1699 1700 @Override injectInputEvent(InputEvent event)1701 public void injectInputEvent(InputEvent event) { 1702 getViewRootImpl().dispatchInputEvent(event); 1703 } 1704 getViewRootImpl()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 takeKeyEvents(boolean get)1721 public void takeKeyEvents(boolean get) { 1722 mDecor.setFocusable(get); 1723 } 1724 1725 @Override superDispatchKeyEvent(KeyEvent event)1726 public boolean superDispatchKeyEvent(KeyEvent event) { 1727 return mDecor.superDispatchKeyEvent(event); 1728 } 1729 1730 @Override superDispatchKeyShortcutEvent(KeyEvent event)1731 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 1732 return mDecor.superDispatchKeyShortcutEvent(event); 1733 } 1734 1735 @Override superDispatchTouchEvent(MotionEvent event)1736 public boolean superDispatchTouchEvent(MotionEvent event) { 1737 return mDecor.superDispatchTouchEvent(event); 1738 } 1739 1740 @Override superDispatchTrackballEvent(MotionEvent event)1741 public boolean superDispatchTrackballEvent(MotionEvent event) { 1742 return mDecor.superDispatchTrackballEvent(event); 1743 } 1744 1745 @Override superDispatchGenericMotionEvent(MotionEvent event)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 */ onKeyDown(int featureId, int keyCode, KeyEvent event)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 getKeyguardManager()1846 private KeyguardManager getKeyguardManager() { 1847 if (mKeyguardManager == null) { 1848 mKeyguardManager = (KeyguardManager) getContext().getSystemService( 1849 Context.KEYGUARD_SERVICE); 1850 } 1851 return mKeyguardManager; 1852 } 1853 getAudioManager()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 */ onKeyUp(int featureId, int keyCode, KeyEvent event)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 */ restorePanelState(SparseArray<Parcelable> icicles)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 */ openPanelsAfterRestore()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 onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing)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 onOpenSubMenu(MenuBuilder subMenu)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 onOpenSubMenu(MenuBuilder subMenu)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 onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing)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 DecorView(Context context, int featureId)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 setBackgroundFallback(int resId)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 onDraw(Canvas c)2278 public void onDraw(Canvas c) { 2279 super.onDraw(c); 2280 mBackgroundFallback.draw(mContentRoot, c, mContentParent); 2281 } 2282 2283 @Override dispatchKeyEvent(KeyEvent event)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 onLayout(boolean changed, int left, int top, int right, int bottom)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 draw(Canvas canvas)2689 public void draw(Canvas canvas) { 2690 super.draw(canvas); 2691 2692 if (mMenuBackground != null) { 2693 mMenuBackground.draw(canvas); 2694 } 2695 } 2696 2697 @Override showContextMenuForChild(View originalView)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 startActionModeForChild(View originalView, ActionMode.Callback callback)2721 public ActionMode startActionModeForChild(View originalView, 2722 ActionMode.Callback callback) { 2723 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY); 2724 } 2725 2726 @Override startActionModeForChild( View child, ActionMode.Callback callback, int type)2727 public ActionMode startActionModeForChild( 2728 View child, ActionMode.Callback callback, int type) { 2729 return startActionMode(child, callback, type); 2730 } 2731 2732 @Override startActionMode(ActionMode.Callback callback)2733 public ActionMode startActionMode(ActionMode.Callback callback) { 2734 return startActionMode(callback, ActionMode.TYPE_PRIMARY); 2735 } 2736 2737 @Override startActionMode(ActionMode.Callback callback, int type)2738 public ActionMode startActionMode(ActionMode.Callback callback, int type) { 2739 return startActionMode(this, callback, type); 2740 } 2741 startActionMode( View originatingView, ActionMode.Callback callback, int type)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 cleanupPrimaryActionMode()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 cleanupFloatingActionModeViews()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 startChanging()2813 public void startChanging() { 2814 mChanging = true; 2815 } 2816 finishChanging()2817 public void finishChanging() { 2818 mChanging = false; 2819 drawableChanged(); 2820 } 2821 setWindowBackground(Drawable drawable)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 setBackgroundDrawable(Drawable d)2835 public void setBackgroundDrawable(Drawable d) { 2836 super.setBackgroundDrawable(d); 2837 if (getWindowToken() != null) { 2838 updateWindowResizeState(); 2839 } 2840 } 2841 setWindowFrame(Drawable drawable)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 onWindowSystemUiVisibilityChanged(int visible)2855 public void onWindowSystemUiVisibilityChanged(int visible) { 2856 updateColorViews(null /* insets */, true /* animate */); 2857 } 2858 2859 @Override onApplyWindowInsets(WindowInsets insets)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 isTransitionGroup()2872 public boolean isTransitionGroup() { 2873 return false; 2874 } 2875 updateColorViews(WindowInsets insets, boolean animate)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 */ updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int size, boolean verticalBar, int rightMargin, boolean animate)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 if (show) { 3014 LayoutParams lp = (LayoutParams) view.getLayoutParams(); 3015 if (lp.height != resolvedHeight || lp.width != resolvedWidth 3016 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { 3017 lp.height = resolvedHeight; 3018 lp.width = resolvedWidth; 3019 lp.gravity = resolvedGravity; 3020 lp.rightMargin = rightMargin; 3021 view.setLayoutParams(lp); 3022 } 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 updateColorViewTranslations()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