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