AppCompatDelegateImplV7.java revision ea4f700ac38424954c56df5138ff794def50b019
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v7.app; 18 19import android.content.Context; 20import android.content.res.Configuration; 21import android.content.res.Resources; 22import android.content.res.TypedArray; 23import android.graphics.Rect; 24import android.os.Build; 25import android.os.Bundle; 26import android.os.Parcel; 27import android.os.Parcelable; 28import android.support.annotation.NonNull; 29import android.support.v4.app.NavUtils; 30import android.support.v4.view.OnApplyWindowInsetsListener; 31import android.support.v4.view.ViewCompat; 32import android.support.v4.view.ViewConfigurationCompat; 33import android.support.v4.view.WindowInsetsCompat; 34import android.support.v7.appcompat.R; 35import android.support.v7.internal.VersionUtils; 36import android.support.v7.internal.app.ToolbarActionBar; 37import android.support.v7.internal.app.WindowCallback; 38import android.support.v7.internal.app.WindowDecorActionBar; 39import android.support.v7.internal.view.StandaloneActionMode; 40import android.support.v7.internal.view.menu.ListMenuPresenter; 41import android.support.v7.internal.view.menu.MenuBuilder; 42import android.support.v7.internal.view.menu.MenuPresenter; 43import android.support.v7.internal.view.menu.MenuView; 44import android.support.v7.internal.widget.ActionBarContextView; 45import android.support.v7.internal.widget.DecorContentParent; 46import android.support.v7.internal.widget.FitWindowsViewGroup; 47import android.support.v7.internal.widget.ProgressBarCompat; 48import android.support.v7.internal.widget.TintCheckBox; 49import android.support.v7.internal.widget.TintCheckedTextView; 50import android.support.v7.internal.widget.TintEditText; 51import android.support.v7.internal.widget.TintRadioButton; 52import android.support.v7.internal.widget.TintSpinner; 53import android.support.v7.internal.widget.ViewStubCompat; 54import android.support.v7.internal.widget.ViewUtils; 55import android.support.v7.view.ActionMode; 56import android.support.v7.widget.Toolbar; 57import android.util.AttributeSet; 58import android.util.DisplayMetrics; 59import android.util.TypedValue; 60import android.view.ContextThemeWrapper; 61import android.view.Gravity; 62import android.view.KeyCharacterMap; 63import android.view.KeyEvent; 64import android.view.LayoutInflater; 65import android.view.Menu; 66import android.view.MenuItem; 67import android.view.View; 68import android.view.ViewConfiguration; 69import android.view.ViewGroup; 70import android.view.Window; 71import android.view.accessibility.AccessibilityEvent; 72import android.widget.FrameLayout; 73import android.widget.PopupWindow; 74 75import static android.support.v4.view.WindowCompat.FEATURE_ACTION_BAR; 76import static android.support.v4.view.WindowCompat.FEATURE_ACTION_BAR_OVERLAY; 77import static android.support.v4.view.WindowCompat.FEATURE_ACTION_MODE_OVERLAY; 78import static android.view.Window.FEATURE_OPTIONS_PANEL; 79 80class ActionBarActivityDelegateBase extends ActionBarActivityDelegate 81 implements MenuBuilder.Callback { 82 private static final String TAG = "ActionBarActivityDelegateBase"; 83 84 private DecorContentParent mDecorContentParent; 85 private ActionMenuPresenterCallback mActionMenuPresenterCallback; 86 private PanelMenuPresenterCallback mPanelMenuPresenterCallback; 87 88 ActionMode mActionMode; 89 ActionBarContextView mActionModeView; 90 PopupWindow mActionModePopup; 91 Runnable mShowActionModePopup; 92 93 // true if we have installed a window sub-decor layout. 94 private boolean mSubDecorInstalled; 95 private ViewGroup mWindowDecor; 96 private ViewGroup mSubDecor; 97 98 private View mStatusGuard; 99 100 private CharSequence mTitleToSet; 101 102 // Used to keep track of Progress Bar Window features 103 private boolean mFeatureProgress, mFeatureIndeterminateProgress; 104 105 // Used for emulating PanelFeatureState 106 private boolean mClosingActionMenu; 107 private PanelFeatureState[] mPanels; 108 private PanelFeatureState mPreparedPanel; 109 110 private boolean mInvalidatePanelMenuPosted; 111 private int mInvalidatePanelMenuFeatures; 112 private final Runnable mInvalidatePanelMenuRunnable = new Runnable() { 113 @Override 114 public void run() { 115 if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_OPTIONS_PANEL) != 0) { 116 doInvalidatePanelMenu(FEATURE_OPTIONS_PANEL); 117 } 118 if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_ACTION_BAR) != 0) { 119 doInvalidatePanelMenu(FEATURE_ACTION_BAR); 120 } 121 mInvalidatePanelMenuPosted = false; 122 mInvalidatePanelMenuFeatures = 0; 123 } 124 }; 125 126 private boolean mEnableDefaultActionBarUp; 127 128 ActionBarActivityDelegateBase(ActionBarActivity activity) { 129 super(activity); 130 } 131 132 @Override 133 void onCreate(Bundle savedInstanceState) { 134 super.onCreate(savedInstanceState); 135 136 mWindowDecor = (ViewGroup) mActivity.getWindow().getDecorView(); 137 138 if (NavUtils.getParentActivityName(mActivity) != null) { 139 ActionBar ab = getSupportActionBar(); 140 if (ab == null) { 141 mEnableDefaultActionBarUp = true; 142 } else { 143 ab.setDefaultDisplayHomeAsUpEnabled(true); 144 } 145 } 146 } 147 148 @Override 149 public ActionBar createSupportActionBar() { 150 ensureSubDecor(); 151 ActionBar ab = new WindowDecorActionBar(mActivity, mOverlayActionBar); 152 ab.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); 153 return ab; 154 } 155 156 @Override 157 void setSupportActionBar(Toolbar toolbar) { 158 if (getSupportActionBar() instanceof WindowDecorActionBar) { 159 throw new IllegalStateException("This Activity already has an action bar supplied " + 160 "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + 161 "windowActionBar to false in your theme to use a Toolbar instead."); 162 } 163 // Need to make sure we give the action bar the default window callback. Otherwise multiple 164 // setSupportActionBar() calls lead to memory leaks 165 ToolbarActionBar tbab = new ToolbarActionBar(toolbar, mActivity.getTitle(), 166 mDefaultWindowCallback); 167 setSupportActionBar(tbab); 168 setWindowCallback(tbab.getWrappedWindowCallback()); 169 tbab.invalidateOptionsMenu(); 170 } 171 172 @Override 173 public void onConfigurationChanged(Configuration newConfig) { 174 // If this is called before sub-decor is installed, ActionBar will not 175 // be properly initialized. 176 if (mHasActionBar && mSubDecorInstalled) { 177 // Note: The action bar will need to access 178 // view changes from superclass. 179 ActionBar ab = getSupportActionBar(); 180 if (ab != null) { 181 ab.onConfigurationChanged(newConfig); 182 } 183 } 184 } 185 186 @Override 187 public void onStop() { 188 ActionBar ab = getSupportActionBar(); 189 if (ab != null) { 190 ab.setShowHideAnimationEnabled(false); 191 } 192 } 193 194 @Override 195 public void onPostResume() { 196 ActionBar ab = getSupportActionBar(); 197 if (ab != null) { 198 ab.setShowHideAnimationEnabled(true); 199 } 200 } 201 202 @Override 203 public void setContentView(View v) { 204 ensureSubDecor(); 205 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 206 contentParent.removeAllViews(); 207 contentParent.addView(v); 208 mActivity.onSupportContentChanged(); 209 } 210 211 @Override 212 public void setContentView(int resId) { 213 ensureSubDecor(); 214 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 215 contentParent.removeAllViews(); 216 mActivity.getLayoutInflater().inflate(resId, contentParent); 217 mActivity.onSupportContentChanged(); 218 } 219 220 @Override 221 public void setContentView(View v, ViewGroup.LayoutParams lp) { 222 ensureSubDecor(); 223 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 224 contentParent.removeAllViews(); 225 contentParent.addView(v, lp); 226 mActivity.onSupportContentChanged(); 227 } 228 229 @Override 230 public void addContentView(View v, ViewGroup.LayoutParams lp) { 231 ensureSubDecor(); 232 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 233 contentParent.addView(v, lp); 234 mActivity.onSupportContentChanged(); 235 } 236 237 @Override 238 public void onContentChanged() { 239 // Ignore all calls to this method as we call onSupportContentChanged manually above 240 } 241 242 final void ensureSubDecor() { 243 if (!mSubDecorInstalled) { 244 if (mHasActionBar) { 245 /** 246 * This needs some explanation. As we can not use the android:theme attribute 247 * pre-L, we emulate it by manually creating a LayoutInflater using a 248 * ContextThemeWrapper pointing to actionBarTheme. 249 */ 250 TypedValue outValue = new TypedValue(); 251 mActivity.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true); 252 253 Context themedContext; 254 if (outValue.resourceId != 0) { 255 themedContext = new ContextThemeWrapper(mActivity, outValue.resourceId); 256 } else { 257 themedContext = mActivity; 258 } 259 260 // Now inflate the view using the themed context and set it as the content view 261 mSubDecor = (ViewGroup) LayoutInflater.from(themedContext) 262 .inflate(R.layout.abc_screen_toolbar, null); 263 264 mDecorContentParent = (DecorContentParent) mSubDecor 265 .findViewById(R.id.decor_content_parent); 266 mDecorContentParent.setWindowCallback(getWindowCallback()); 267 268 /** 269 * Propagate features to DecorContentParent 270 */ 271 if (mOverlayActionBar) { 272 mDecorContentParent.initFeature(FEATURE_ACTION_BAR_OVERLAY); 273 } 274 if (mFeatureProgress) { 275 mDecorContentParent.initFeature(Window.FEATURE_PROGRESS); 276 } 277 if (mFeatureIndeterminateProgress) { 278 mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 279 } 280 } else { 281 if (mOverlayActionMode) { 282 mSubDecor = (ViewGroup) LayoutInflater.from(mActivity) 283 .inflate(R.layout.abc_screen_simple_overlay_action_mode, null); 284 } else { 285 mSubDecor = (ViewGroup) LayoutInflater.from(mActivity) 286 .inflate(R.layout.abc_screen_simple, null); 287 } 288 289 if (Build.VERSION.SDK_INT >= 21) { 290 // If we're running on L or above, we can rely on ViewCompat's 291 // setOnApplyWindowInsetsListener 292 ViewCompat.setOnApplyWindowInsetsListener(mSubDecor, 293 new OnApplyWindowInsetsListener() { 294 @Override 295 public WindowInsetsCompat onApplyWindowInsets(View v, 296 WindowInsetsCompat insets) { 297 final int top = insets.getSystemWindowInsetTop(); 298 final int newTop = updateStatusGuard(top); 299 300 if (top != newTop) { 301 return insets.replaceSystemWindowInsets( 302 insets.getSystemWindowInsetLeft(), 303 newTop, 304 insets.getSystemWindowInsetRight(), 305 insets.getSystemWindowInsetBottom()); 306 } else { 307 return insets; 308 } 309 } 310 }); 311 } else { 312 // Else, we need to use our own FitWindowsViewGroup handling 313 ((FitWindowsViewGroup) mSubDecor).setOnFitSystemWindowsListener( 314 new FitWindowsViewGroup.OnFitSystemWindowsListener() { 315 @Override 316 public void onFitSystemWindows(Rect insets) { 317 insets.top = updateStatusGuard(insets.top); 318 } 319 }); 320 } 321 } 322 323 // Make the decor optionally fit system windows, like the window's decor 324 ViewUtils.makeOptionalFitsSystemWindows(mSubDecor); 325 326 // Now set the Activity's content view with the decor 327 mActivity.superSetContentView(mSubDecor); 328 329 // Change our content FrameLayout to use the android.R.id.content id. 330 // Useful for fragments. 331 final View decorContent = mActivity.findViewById(android.R.id.content); 332 decorContent.setId(View.NO_ID); 333 View abcContent = mActivity.findViewById(R.id.action_bar_activity_content); 334 abcContent.setId(android.R.id.content); 335 336 // The decorContent may have a foreground drawable set (windowContentOverlay). 337 // Remove this as we handle it ourselves 338 if (decorContent instanceof FrameLayout) { 339 ((FrameLayout) decorContent).setForeground(null); 340 } 341 342 // A title was set before we've install the decor so set it now. 343 if (mTitleToSet != null && mDecorContentParent != null) { 344 mDecorContentParent.setWindowTitle(mTitleToSet); 345 mTitleToSet = null; 346 } 347 348 applyFixedSizeWindow(); 349 350 onSubDecorInstalled(); 351 352 mSubDecorInstalled = true; 353 354 // Invalidate if the panel menu hasn't been created before this. 355 // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu 356 // being called in the middle of onCreate or similar. 357 // A pending invalidation will typically be resolved before the posted message 358 // would run normally in order to satisfy instance state restoration. 359 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); 360 if (!isDestroyed() && (st == null || st.menu == null)) { 361 invalidatePanelMenu(FEATURE_ACTION_BAR); 362 } 363 } 364 } 365 366 void onSubDecorInstalled() {} 367 368 private void applyFixedSizeWindow() { 369 TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme); 370 371 TypedValue mFixedWidthMajor = null; 372 TypedValue mFixedWidthMinor = null; 373 TypedValue mFixedHeightMajor = null; 374 TypedValue mFixedHeightMinor = null; 375 376 if (a.hasValue(R.styleable.Theme_windowFixedWidthMajor)) { 377 if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); 378 a.getValue(R.styleable.Theme_windowFixedWidthMajor, mFixedWidthMajor); 379 } 380 if (a.hasValue(R.styleable.Theme_windowFixedWidthMinor)) { 381 if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); 382 a.getValue(R.styleable.Theme_windowFixedWidthMinor, mFixedWidthMinor); 383 } 384 if (a.hasValue(R.styleable.Theme_windowFixedHeightMajor)) { 385 if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); 386 a.getValue(R.styleable.Theme_windowFixedHeightMajor, mFixedHeightMajor); 387 } 388 if (a.hasValue(R.styleable.Theme_windowFixedHeightMinor)) { 389 if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); 390 a.getValue(R.styleable.Theme_windowFixedHeightMinor, mFixedHeightMinor); 391 } 392 393 final DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics(); 394 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; 395 int w = ViewGroup.LayoutParams.MATCH_PARENT; 396 int h = ViewGroup.LayoutParams.MATCH_PARENT; 397 398 final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; 399 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { 400 if (tvw.type == TypedValue.TYPE_DIMENSION) { 401 w = (int) tvw.getDimension(metrics); 402 } else if (tvw.type == TypedValue.TYPE_FRACTION) { 403 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); 404 } 405 } 406 407 final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; 408 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { 409 if (tvh.type == TypedValue.TYPE_DIMENSION) { 410 h = (int) tvh.getDimension(metrics); 411 } else if (tvh.type == TypedValue.TYPE_FRACTION) { 412 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); 413 } 414 } 415 416 if (w != ViewGroup.LayoutParams.MATCH_PARENT || h != ViewGroup.LayoutParams.MATCH_PARENT) { 417 mActivity.getWindow().setLayout(w, h); 418 } 419 420 a.recycle(); 421 } 422 423 @Override 424 public boolean supportRequestWindowFeature(int featureId) { 425 switch (featureId) { 426 case FEATURE_ACTION_BAR: 427 mHasActionBar = true; 428 return true; 429 case FEATURE_ACTION_BAR_OVERLAY: 430 mOverlayActionBar = true; 431 return true; 432 case FEATURE_ACTION_MODE_OVERLAY: 433 mOverlayActionMode = true; 434 return true; 435 case Window.FEATURE_PROGRESS: 436 mFeatureProgress = true; 437 return true; 438 case Window.FEATURE_INDETERMINATE_PROGRESS: 439 mFeatureIndeterminateProgress = true; 440 return true; 441 default: 442 return mActivity.requestWindowFeature(featureId); 443 } 444 } 445 446 @Override 447 public void onTitleChanged(CharSequence title) { 448 if (mDecorContentParent != null) { 449 mDecorContentParent.setWindowTitle(title); 450 } else if (getSupportActionBar() != null) { 451 getSupportActionBar().setWindowTitle(title); 452 } else { 453 mTitleToSet = title; 454 } 455 } 456 457 @Override 458 public View onCreatePanelView(int featureId) { 459 View panelView = null; 460 461 // If there isn't an action mode currently being displayed 462 if (mActionMode == null) { 463 // Let our window callback try first 464 WindowCallback callback = getWindowCallback(); 465 if (callback != null) { 466 panelView = callback.onCreatePanelView(featureId); 467 } 468 469 if (panelView == null) { 470 // If the callback didn't return a view, check our panels 471 PanelFeatureState st = getPanelState(featureId, true); 472 openPanel(st, null); 473 if (st.isOpen) { 474 panelView = st.shownPanelView; 475 } 476 } 477 } 478 return panelView; 479 } 480 481 @Override 482 public boolean onCreatePanelMenu(int featureId, Menu menu) { 483 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 484 return getWindowCallback().onCreatePanelMenu(featureId, menu); 485 } 486 return false; 487 } 488 489 @Override 490 public boolean onPreparePanel(int featureId, View view, Menu menu) { 491 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 492 return getWindowCallback().onPreparePanel(featureId, view, menu); 493 } 494 return false; 495 } 496 497 @Override 498 public void onPanelClosed(final int featureId, Menu menu) { 499 PanelFeatureState st = getPanelState(featureId, false); 500 if (st != null) { 501 // If we know about the feature id, update it's state 502 closePanel(st, false); 503 } 504 505 if (featureId == FEATURE_ACTION_BAR) { 506 ActionBar ab = getSupportActionBar(); 507 if (ab != null) { 508 ab.dispatchMenuVisibilityChanged(false); 509 } 510 } else if (!isDestroyed()) { 511 // Only pass it through to the Activity's super impl if it's not ACTION_BAR. This is 512 // because ICS+ will try and create a framework action bar due to this call 513 mActivity.superOnPanelClosed(featureId, menu); 514 } 515 } 516 517 @Override 518 boolean onMenuOpened(final int featureId, Menu menu) { 519 if (featureId == FEATURE_ACTION_BAR) { 520 ActionBar ab = getSupportActionBar(); 521 if (ab != null) { 522 ab.dispatchMenuVisibilityChanged(true); 523 } 524 return true; 525 } else { 526 return mActivity.superOnMenuOpened(featureId, menu); 527 } 528 } 529 530 @Override 531 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 532 final WindowCallback cb = getWindowCallback(); 533 if (cb != null && !isDestroyed()) { 534 final PanelFeatureState panel = findMenuPanel(menu.getRootMenu()); 535 if (panel != null) { 536 return cb.onMenuItemSelected(panel.featureId, item); 537 } 538 } 539 return false; 540 } 541 542 @Override 543 public void onMenuModeChange(MenuBuilder menu) { 544 reopenMenu(menu, true); 545 } 546 547 @Override 548 public ActionMode startSupportActionMode(ActionMode.Callback callback) { 549 if (callback == null) { 550 throw new IllegalArgumentException("ActionMode callback can not be null."); 551 } 552 553 if (mActionMode != null) { 554 mActionMode.finish(); 555 } 556 557 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 558 559 ActionBar ab = getSupportActionBar(); 560 if (ab != null) { 561 mActionMode = ab.startActionMode(wrappedCallback); 562 if (mActionMode != null) { 563 mActivity.onSupportActionModeStarted(mActionMode); 564 } 565 } 566 567 if (mActionMode == null) { 568 // If the action bar didn't provide an action mode, start the emulated window one 569 mActionMode = startSupportActionModeFromWindow(wrappedCallback); 570 } 571 572 return mActionMode; 573 } 574 575 @Override 576 public void supportInvalidateOptionsMenu() { 577 final ActionBar ab = getSupportActionBar(); 578 if (ab != null && ab.invalidateOptionsMenu()) return; 579 580 invalidatePanelMenu(FEATURE_OPTIONS_PANEL); 581 } 582 583 @Override 584 ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback) { 585 if (mActionMode != null) { 586 mActionMode.finish(); 587 } 588 589 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 590 final Context context = getActionBarThemedContext(); 591 592 if (mActionModeView == null) { 593 if (mIsFloating) { 594 mActionModeView = new ActionBarContextView(context); 595 mActionModePopup = new PopupWindow(context, null, 596 R.attr.actionModePopupWindowStyle); 597 mActionModePopup.setContentView(mActionModeView); 598 mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT); 599 600 TypedValue heightValue = new TypedValue(); 601 mActivity.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true); 602 final int height = TypedValue.complexToDimensionPixelSize(heightValue.data, 603 mActivity.getResources().getDisplayMetrics()); 604 mActionModeView.setContentHeight(height); 605 mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); 606 mShowActionModePopup = new Runnable() { 607 public void run() { 608 mActionModePopup.showAtLocation( 609 mActionModeView, 610 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0); 611 } 612 }; 613 } else { 614 ViewStubCompat stub = (ViewStubCompat) mActivity 615 .findViewById(R.id.action_mode_bar_stub); 616 if (stub != null) { 617 // Set the layout inflater so that it is inflated with the action bar's context 618 stub.setLayoutInflater(LayoutInflater.from(context)); 619 mActionModeView = (ActionBarContextView) stub.inflate(); 620 } 621 } 622 } 623 624 if (mActionModeView != null) { 625 mActionModeView.killMode(); 626 ActionMode mode = new StandaloneActionMode(context, mActionModeView, wrappedCallback, 627 mActionModePopup == null); 628 if (callback.onCreateActionMode(mode, mode.getMenu())) { 629 mode.invalidate(); 630 mActionModeView.initForMode(mode); 631 mActionModeView.setVisibility(View.VISIBLE); 632 mActionMode = mode; 633 if (mActionModePopup != null) { 634 mActivity.getWindow().getDecorView().post(mShowActionModePopup); 635 } 636 mActionModeView.sendAccessibilityEvent( 637 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 638 639 if (mActionModeView.getParent() != null) { 640 ViewCompat.requestApplyInsets((View) mActionModeView.getParent()); 641 } 642 } else { 643 mActionMode = null; 644 } 645 } 646 if (mActionMode != null && mActivity != null) { 647 mActivity.onSupportActionModeStarted(mActionMode); 648 } 649 return mActionMode; 650 } 651 652 @Override 653 public boolean onBackPressed() { 654 // Back cancels action modes first. 655 if (mActionMode != null) { 656 mActionMode.finish(); 657 return true; 658 } 659 660 // Next collapse any expanded action views. 661 ActionBar ab = getSupportActionBar(); 662 if (ab != null && ab.collapseActionView()) { 663 return true; 664 } 665 666 return false; 667 } 668 669 @Override 670 void setSupportProgressBarVisibility(boolean visible) { 671 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 672 Window.PROGRESS_VISIBILITY_OFF); 673 } 674 675 @Override 676 void setSupportProgressBarIndeterminateVisibility(boolean visible) { 677 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 678 Window.PROGRESS_VISIBILITY_OFF); 679 } 680 681 @Override 682 void setSupportProgressBarIndeterminate(boolean indeterminate) { 683 updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON 684 : Window.PROGRESS_INDETERMINATE_OFF); 685 } 686 687 @Override 688 void setSupportProgress(int progress) { 689 updateProgressBars(Window.PROGRESS_START + progress); 690 } 691 692 @Override 693 int getHomeAsUpIndicatorAttrId() { 694 return R.attr.homeAsUpIndicator; 695 } 696 697 @Override 698 boolean onKeyShortcut(int keyCode, KeyEvent ev) { 699 // If the panel is already prepared, then perform the shortcut using it. 700 boolean handled; 701 if (mPreparedPanel != null) { 702 handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev, 703 Menu.FLAG_PERFORM_NO_CLOSE); 704 if (handled) { 705 if (mPreparedPanel != null) { 706 mPreparedPanel.isHandled = true; 707 } 708 return true; 709 } 710 } 711 712 // If the panel is not prepared, then we may be trying to handle a shortcut key 713 // combination such as Control+C. Temporarily prepare the panel then mark it 714 // unprepared again when finished to ensure that the panel will again be prepared 715 // the next time it is shown for real. 716 if (mPreparedPanel == null) { 717 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 718 preparePanel(st, ev); 719 handled = performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE); 720 st.isPrepared = false; 721 if (handled) { 722 return true; 723 } 724 } 725 return false; 726 } 727 728 @Override 729 boolean onKeyDown(int keyCode, KeyEvent event) { 730 // On API v7-10 we need to manually call onKeyShortcut() as this is not called 731 // from the Activity 732 return onKeyShortcut(keyCode, event); 733 } 734 735 @Override 736 View createView(final String name, @NonNull AttributeSet attrs) { 737 if (Build.VERSION.SDK_INT < 21) { 738 // If we're running pre-L, we need to 'inject' our tint aware Views in place of the 739 // standard framework versions 740 switch (name) { 741 case "EditText": 742 return new TintEditText(mActivity, attrs); 743 case "Spinner": 744 return new TintSpinner(mActivity, attrs); 745 case "CheckBox": 746 return new TintCheckBox(mActivity, attrs); 747 case "RadioButton": 748 return new TintRadioButton(mActivity, attrs); 749 case "CheckedTextView": 750 return new TintCheckedTextView(mActivity, attrs); 751 } 752 } 753 return null; 754 } 755 756 /** 757 * Progress Bar function. Mostly extracted from PhoneWindow.java 758 */ 759 private void updateProgressBars(int value) { 760 ProgressBarCompat circularProgressBar = getCircularProgressBar(); 761 ProgressBarCompat horizontalProgressBar = getHorizontalProgressBar(); 762 763 if (value == Window.PROGRESS_VISIBILITY_ON) { 764 if (mFeatureProgress) { 765 int level = horizontalProgressBar.getProgress(); 766 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? 767 View.VISIBLE : View.INVISIBLE; 768 horizontalProgressBar.setVisibility(visibility); 769 } 770 if (mFeatureIndeterminateProgress) { 771 circularProgressBar.setVisibility(View.VISIBLE); 772 } 773 } else if (value == Window.PROGRESS_VISIBILITY_OFF) { 774 if (mFeatureProgress) { 775 horizontalProgressBar.setVisibility(View.GONE); 776 } 777 if (mFeatureIndeterminateProgress) { 778 circularProgressBar.setVisibility(View.GONE); 779 } 780 } else if (value == Window.PROGRESS_INDETERMINATE_ON) { 781 horizontalProgressBar.setIndeterminate(true); 782 } else if (value == Window.PROGRESS_INDETERMINATE_OFF) { 783 horizontalProgressBar.setIndeterminate(false); 784 } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) { 785 // We want to set the progress value before testing for visibility 786 // so that when the progress bar becomes visible again, it has the 787 // correct level. 788 horizontalProgressBar.setProgress(value - Window.PROGRESS_START); 789 790 if (value < Window.PROGRESS_END) { 791 showProgressBars(horizontalProgressBar, circularProgressBar); 792 } else { 793 hideProgressBars(horizontalProgressBar, circularProgressBar); 794 } 795 } 796 } 797 798 private void openPanel(int featureId, KeyEvent event) { 799 if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null && 800 mDecorContentParent.canShowOverflowMenu() && 801 !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity))) { 802 mDecorContentParent.showOverflowMenu(); 803 } else { 804 openPanel(getPanelState(featureId, true), event); 805 } 806 } 807 808 private void openPanel(final PanelFeatureState st, KeyEvent event) { 809 // Already open, return 810 if (st.isOpen || isDestroyed()) { 811 return; 812 } 813 814 // Don't open an options panel for honeycomb apps on xlarge devices. 815 // (The app should be using an action bar for menu items.) 816 if (st.featureId == FEATURE_OPTIONS_PANEL) { 817 Context context = mActivity; 818 Configuration config = context.getResources().getConfiguration(); 819 boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 820 Configuration.SCREENLAYOUT_SIZE_XLARGE; 821 boolean isHoneycombApp = context.getApplicationInfo().targetSdkVersion >= 822 android.os.Build.VERSION_CODES.HONEYCOMB; 823 824 if (isXLarge && isHoneycombApp) { 825 return; 826 } 827 } 828 829 WindowCallback cb = getWindowCallback(); 830 if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) { 831 // Callback doesn't want the menu to open, reset any state 832 closePanel(st, true); 833 return; 834 } 835 836 // Prepare panel (should have been done before, but just in case) 837 if (!preparePanel(st, event)) { 838 return; 839 } 840 841 if (st.decorView == null || st.refreshDecorView) { 842 initializePanelDecor(st); 843 } 844 845 // This will populate st.shownPanelView 846 if (!initializePanelContent(st) || !st.hasPanelItems()) { 847 return; 848 } 849 850 st.isHandled = false; 851 st.isOpen = true; 852 } 853 854 private void initializePanelDecor(PanelFeatureState st) { 855 st.decorView = mWindowDecor; 856 st.setStyle(getActionBarThemedContext()); 857 } 858 859 private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) { 860 if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() && 861 (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity)) || 862 mDecorContentParent.isOverflowMenuShowPending())) { 863 864 WindowCallback cb = getWindowCallback(); 865 866 if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) { 867 if (cb != null && !isDestroyed()) { 868 // If we have a menu invalidation pending, do it now. 869 if (mInvalidatePanelMenuPosted && 870 (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) { 871 mWindowDecor.removeCallbacks(mInvalidatePanelMenuRunnable); 872 mInvalidatePanelMenuRunnable.run(); 873 } 874 875 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 876 877 // If we don't have a menu or we're waiting for a full content refresh, 878 // forget it. This is a lingering event that no longer matters. 879 if (st.menu != null && !st.refreshMenuContent && 880 cb.onPreparePanel(FEATURE_OPTIONS_PANEL, null, st.menu)) { 881 cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu); 882 mDecorContentParent.showOverflowMenu(); 883 } 884 } 885 } else { 886 mDecorContentParent.hideOverflowMenu(); 887 if (!isDestroyed()) { 888 final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 889 mActivity.onPanelClosed(FEATURE_ACTION_BAR, st.menu); 890 } 891 } 892 return; 893 } 894 895 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); 896 897 st.refreshDecorView = true; 898 closePanel(st, false); 899 900 openPanel(st, null); 901 } 902 903 private void showProgressBars(ProgressBarCompat horizontalProgressBar, 904 ProgressBarCompat spinnyProgressBar) { 905 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) { 906 spinnyProgressBar.setVisibility(View.VISIBLE); 907 } 908 // Only show the progress bars if the primary progress is not complete 909 if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) { 910 horizontalProgressBar.setVisibility(View.VISIBLE); 911 } 912 } 913 914 private void hideProgressBars(ProgressBarCompat horizontalProgressBar, 915 ProgressBarCompat spinnyProgressBar) { 916 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) { 917 spinnyProgressBar.setVisibility(View.INVISIBLE); 918 } 919 if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) { 920 horizontalProgressBar.setVisibility(View.INVISIBLE); 921 } 922 } 923 924 private ProgressBarCompat getCircularProgressBar() { 925 ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_circular); 926 if (pb != null) { 927 pb.setVisibility(View.INVISIBLE); 928 } 929 return pb; 930 } 931 932 private ProgressBarCompat getHorizontalProgressBar() { 933 ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_horizontal); 934 if (pb != null) { 935 pb.setVisibility(View.INVISIBLE); 936 } 937 return pb; 938 } 939 940 private boolean initializePanelMenu(final PanelFeatureState st) { 941 Context context = mActivity; 942 943 // If we have an action bar, initialize the menu with the right theme. 944 if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) && 945 mDecorContentParent != null) { 946 final TypedValue outValue = new TypedValue(); 947 final Resources.Theme baseTheme = context.getTheme(); 948 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); 949 950 Resources.Theme widgetTheme = null; 951 if (outValue.resourceId != 0) { 952 widgetTheme = context.getResources().newTheme(); 953 widgetTheme.setTo(baseTheme); 954 widgetTheme.applyStyle(outValue.resourceId, true); 955 widgetTheme.resolveAttribute( 956 R.attr.actionBarWidgetTheme, outValue, true); 957 } else { 958 baseTheme.resolveAttribute( 959 R.attr.actionBarWidgetTheme, outValue, true); 960 } 961 962 if (outValue.resourceId != 0) { 963 if (widgetTheme == null) { 964 widgetTheme = context.getResources().newTheme(); 965 widgetTheme.setTo(baseTheme); 966 } 967 widgetTheme.applyStyle(outValue.resourceId, true); 968 } 969 970 if (widgetTheme != null) { 971 context = new ContextThemeWrapper(context, 0); 972 context.getTheme().setTo(widgetTheme); 973 } 974 } 975 976 final MenuBuilder menu = new MenuBuilder(context); 977 menu.setCallback(this); 978 st.setMenu(menu); 979 980 return true; 981 } 982 983 private boolean initializePanelContent(PanelFeatureState st) { 984 if (st.menu == null) { 985 return false; 986 } 987 988 if (mPanelMenuPresenterCallback == null) { 989 mPanelMenuPresenterCallback = new PanelMenuPresenterCallback(); 990 } 991 992 MenuView menuView = st.getListMenuView(mPanelMenuPresenterCallback); 993 994 st.shownPanelView = (View) menuView; 995 996 return st.shownPanelView != null; 997 } 998 999 private boolean preparePanel(PanelFeatureState st, KeyEvent event) { 1000 if (isDestroyed()) { 1001 return false; 1002 } 1003 1004 // Already prepared (isPrepared will be reset to false later) 1005 if (st.isPrepared) { 1006 return true; 1007 } 1008 1009 if ((mPreparedPanel != null) && (mPreparedPanel != st)) { 1010 // Another Panel is prepared and possibly open, so close it 1011 closePanel(mPreparedPanel, false); 1012 } 1013 1014 final boolean isActionBarMenu = 1015 (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR); 1016 1017 if (isActionBarMenu && mDecorContentParent != null) { 1018 // Enforce ordering guarantees around events so that the action bar never 1019 // dispatches menu-related events before the panel is prepared. 1020 mDecorContentParent.setMenuPrepared(); 1021 } 1022 1023 // Init the panel state's menu--return false if init failed 1024 if (st.menu == null || st.refreshMenuContent) { 1025 if (st.menu == null) { 1026 if (!initializePanelMenu(st) || (st.menu == null)) { 1027 return false; 1028 } 1029 } 1030 1031 if (isActionBarMenu && mDecorContentParent != null) { 1032 if (mActionMenuPresenterCallback == null) { 1033 mActionMenuPresenterCallback = new ActionMenuPresenterCallback(); 1034 } 1035 mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback); 1036 } 1037 1038 // Creating the panel menu will involve a lot of manipulation; 1039 // don't dispatch change events to presenters until we're done. 1040 st.menu.stopDispatchingItemsChanged(); 1041 if (!getWindowCallback().onCreatePanelMenu(st.featureId, st.menu)) { 1042 // Ditch the menu created above 1043 st.setMenu(null); 1044 1045 if (isActionBarMenu && mDecorContentParent != null) { 1046 // Don't show it in the action bar either 1047 mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); 1048 } 1049 1050 return false; 1051 } 1052 1053 st.refreshMenuContent = false; 1054 } 1055 1056 // Preparing the panel menu can involve a lot of manipulation; 1057 // don't dispatch change events to presenters until we're done. 1058 st.menu.stopDispatchingItemsChanged(); 1059 1060 // Restore action view state before we prepare. This gives apps 1061 // an opportunity to override frozen/restored state in onPrepare. 1062 if (st.frozenActionViewState != null) { 1063 st.menu.restoreActionViewStates(st.frozenActionViewState); 1064 st.frozenActionViewState = null; 1065 } 1066 1067 // Callback and return if the callback does not want to show the menu 1068 if (!getWindowCallback().onPreparePanel(FEATURE_OPTIONS_PANEL, null, st.menu)) { 1069 if (isActionBarMenu && mDecorContentParent != null) { 1070 // The app didn't want to show the menu for now but it still exists. 1071 // Clear it out of the action bar. 1072 mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); 1073 } 1074 st.menu.startDispatchingItemsChanged(); 1075 return false; 1076 } 1077 1078 // Set the proper keymap 1079 KeyCharacterMap kmap = KeyCharacterMap.load( 1080 event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD); 1081 st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC; 1082 st.menu.setQwertyMode(st.qwertyMode); 1083 st.menu.startDispatchingItemsChanged(); 1084 1085 // Set other state 1086 st.isPrepared = true; 1087 st.isHandled = false; 1088 mPreparedPanel = st; 1089 1090 return true; 1091 } 1092 1093 private void checkCloseActionMenu(MenuBuilder menu) { 1094 if (mClosingActionMenu) { 1095 return; 1096 } 1097 1098 mClosingActionMenu = true; 1099 mDecorContentParent.dismissPopups(); 1100 WindowCallback cb = getWindowCallback(); 1101 if (cb != null && !isDestroyed()) { 1102 cb.onPanelClosed(FEATURE_ACTION_BAR, menu); 1103 } 1104 mClosingActionMenu = false; 1105 } 1106 1107 private void closePanel(PanelFeatureState st, boolean doCallback) { 1108 if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL && 1109 mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) { 1110 checkCloseActionMenu(st.menu); 1111 return; 1112 } 1113 1114 if (st.isOpen) { 1115 if (doCallback) { 1116 callOnPanelClosed(st.featureId, st, null); 1117 } 1118 } 1119 1120 st.isPrepared = false; 1121 st.isHandled = false; 1122 st.isOpen = false; 1123 1124 // This view is no longer shown, so null it out 1125 st.shownPanelView = null; 1126 1127 // Next time the menu opens, it should not be in expanded mode, so 1128 // force a refresh of the decor 1129 st.refreshDecorView = true; 1130 1131 if (mPreparedPanel == st) { 1132 mPreparedPanel = null; 1133 } 1134 } 1135 1136 private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) { 1137 // Try to get a menu 1138 if (menu == null) { 1139 // Need a panel to grab the menu, so try to get that 1140 if (panel == null) { 1141 if ((featureId >= 0) && (featureId < mPanels.length)) { 1142 panel = mPanels[featureId]; 1143 } 1144 } 1145 1146 if (panel != null) { 1147 // menu still may be null, which is okay--we tried our best 1148 menu = panel.menu; 1149 } 1150 } 1151 1152 // If the panel is not open, do not callback 1153 if ((panel != null) && (!panel.isOpen)) 1154 return; 1155 1156 getWindowCallback().onPanelClosed(featureId, menu); 1157 } 1158 1159 private PanelFeatureState findMenuPanel(Menu menu) { 1160 final PanelFeatureState[] panels = mPanels; 1161 final int N = panels != null ? panels.length : 0; 1162 for (int i = 0; i < N; i++) { 1163 final PanelFeatureState panel = panels[i]; 1164 if (panel != null && panel.menu == menu) { 1165 return panel; 1166 } 1167 } 1168 return null; 1169 } 1170 1171 private PanelFeatureState getPanelState(int featureId, boolean required) { 1172 PanelFeatureState[] ar; 1173 if ((ar = mPanels) == null || ar.length <= featureId) { 1174 PanelFeatureState[] nar = new PanelFeatureState[featureId + 1]; 1175 if (ar != null) { 1176 System.arraycopy(ar, 0, nar, 0, ar.length); 1177 } 1178 mPanels = ar = nar; 1179 } 1180 1181 PanelFeatureState st = ar[featureId]; 1182 if (st == null) { 1183 ar[featureId] = st = new PanelFeatureState(featureId); 1184 } 1185 return st; 1186 } 1187 1188 final boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event, 1189 int flags) { 1190 if (event.isSystem()) { 1191 return false; 1192 } 1193 1194 boolean handled = false; 1195 1196 // Only try to perform menu shortcuts if preparePanel returned true (possible false 1197 // return value from application not wanting to show the menu). 1198 if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) { 1199 // The menu is prepared now, perform the shortcut on it 1200 handled = st.menu.performShortcut(keyCode, event, flags); 1201 } 1202 1203 if (handled) { 1204 // Only close down the menu if we don't have an action bar keeping it open. 1205 if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) { 1206 closePanel(st, true); 1207 } 1208 } 1209 1210 return handled; 1211 } 1212 1213 private void invalidatePanelMenu(int featureId) { 1214 mInvalidatePanelMenuFeatures |= 1 << featureId; 1215 1216 if (!mInvalidatePanelMenuPosted && mWindowDecor != null) { 1217 ViewCompat.postOnAnimation(mWindowDecor, mInvalidatePanelMenuRunnable); 1218 mInvalidatePanelMenuPosted = true; 1219 } 1220 } 1221 1222 private void doInvalidatePanelMenu(int featureId) { 1223 PanelFeatureState st = getPanelState(featureId, true); 1224 Bundle savedActionViewStates = null; 1225 if (st.menu != null) { 1226 savedActionViewStates = new Bundle(); 1227 st.menu.saveActionViewStates(savedActionViewStates); 1228 if (savedActionViewStates.size() > 0) { 1229 st.frozenActionViewState = savedActionViewStates; 1230 } 1231 // This will be started again when the panel is prepared. 1232 st.menu.stopDispatchingItemsChanged(); 1233 st.menu.clear(); 1234 } 1235 st.refreshMenuContent = true; 1236 st.refreshDecorView = true; 1237 1238 // Prepare the options panel if we have an action bar 1239 if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL) 1240 && mDecorContentParent != null) { 1241 st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false); 1242 if (st != null) { 1243 st.isPrepared = false; 1244 preparePanel(st, null); 1245 } 1246 } 1247 } 1248 1249 /** 1250 * Updates the status bar guard 1251 * 1252 * @param insetTop the current top system window inset 1253 * @return the new top system window inset 1254 */ 1255 private int updateStatusGuard(int insetTop) { 1256 boolean showStatusGuard = false; 1257 // Show the status guard when the non-overlay contextual action bar is showing 1258 if (mActionModeView != null) { 1259 if (mActionModeView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { 1260 ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) 1261 mActionModeView.getLayoutParams(); 1262 boolean mlpChanged = false; 1263 1264 if (mActionModeView.isShown()) { 1265 if (mOverlayActionMode) { 1266 // If we have an overlay action mode, update it's margin so that it is 1267 // displayed below the status bar 1268 if (mlp.topMargin != insetTop) { 1269 mlpChanged = true; 1270 mlp.topMargin = insetTop; 1271 } 1272 } else if (insetTop > 0) { 1273 // If we have an inline action mode and we should inset, add a status 1274 // guard view which protects the status bar and pushes the action mode down 1275 if (mStatusGuard == null) { 1276 mStatusGuard = new View(mActivity); 1277 mStatusGuard.setBackgroundColor(mActivity.getResources() 1278 .getColor(R.color.abc_input_method_navigation_guard)); 1279 mSubDecor.addView(mStatusGuard, 0, 1280 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1281 insetTop)); 1282 } else { 1283 ViewGroup.LayoutParams lp = mStatusGuard.getLayoutParams(); 1284 if (lp.height != insetTop) { 1285 lp.height = insetTop; 1286 mStatusGuard.setLayoutParams(lp); 1287 } 1288 } 1289 showStatusGuard = true; 1290 } 1291 1292 // Now consume the inset 1293 insetTop = 0; 1294 } else { 1295 // reset top margin 1296 if (mlp.topMargin != 0) { 1297 mlpChanged = true; 1298 mlp.topMargin = 0; 1299 } 1300 } 1301 if (mlpChanged) { 1302 mActionModeView.setLayoutParams(mlp); 1303 } 1304 } 1305 } 1306 if (mStatusGuard != null) { 1307 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE); 1308 } 1309 1310 return insetTop; 1311 } 1312 1313 /** 1314 * Clears out internal reference when the action mode is destroyed. 1315 */ 1316 private class ActionModeCallbackWrapper implements ActionMode.Callback { 1317 private ActionMode.Callback mWrapped; 1318 1319 public ActionModeCallbackWrapper(ActionMode.Callback wrapped) { 1320 mWrapped = wrapped; 1321 } 1322 1323 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 1324 return mWrapped.onCreateActionMode(mode, menu); 1325 } 1326 1327 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 1328 return mWrapped.onPrepareActionMode(mode, menu); 1329 } 1330 1331 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 1332 return mWrapped.onActionItemClicked(mode, item); 1333 } 1334 1335 public void onDestroyActionMode(ActionMode mode) { 1336 mWrapped.onDestroyActionMode(mode); 1337 if (mActionModePopup != null) { 1338 mActivity.getWindow().getDecorView().removeCallbacks(mShowActionModePopup); 1339 mActionModePopup.dismiss(); 1340 } else if (mActionModeView != null) { 1341 mActionModeView.setVisibility(View.GONE); 1342 if (mActionModeView.getParent() != null) { 1343 ViewCompat.requestApplyInsets((View) mActionModeView.getParent()); 1344 } 1345 } 1346 if (mActionModeView != null) { 1347 mActionModeView.removeAllViews(); 1348 } 1349 if (mActivity != null) { 1350 try { 1351 mActivity.onSupportActionModeFinished(mActionMode); 1352 } catch (AbstractMethodError ame) { 1353 // Older apps might not implement this callback method. 1354 } 1355 } 1356 mActionMode = null; 1357 } 1358 } 1359 1360 private final class PanelMenuPresenterCallback implements MenuPresenter.Callback { 1361 @Override 1362 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1363 final Menu parentMenu = menu.getRootMenu(); 1364 final boolean isSubMenu = parentMenu != menu; 1365 final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu); 1366 if (panel != null) { 1367 if (isSubMenu) { 1368 callOnPanelClosed(panel.featureId, panel, parentMenu); 1369 closePanel(panel, true); 1370 } else { 1371 // Close the panel and only do the callback if the menu is being 1372 // closed completely, not if opening a sub menu 1373 mActivity.closeOptionsMenu(); 1374 closePanel(panel, allMenusAreClosing); 1375 } 1376 } 1377 } 1378 1379 @Override 1380 public boolean onOpenSubMenu(MenuBuilder subMenu) { 1381 if (subMenu == null && mHasActionBar) { 1382 WindowCallback cb = getWindowCallback(); 1383 if (cb != null && !isDestroyed()) { 1384 cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu); 1385 } 1386 } 1387 return true; 1388 } 1389 } 1390 1391 private final class ActionMenuPresenterCallback implements MenuPresenter.Callback { 1392 @Override 1393 public boolean onOpenSubMenu(MenuBuilder subMenu) { 1394 WindowCallback cb = getWindowCallback(); 1395 if (cb != null) { 1396 cb.onMenuOpened(FEATURE_ACTION_BAR, subMenu); 1397 } 1398 return true; 1399 } 1400 1401 @Override 1402 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1403 checkCloseActionMenu(menu); 1404 } 1405 } 1406 1407 private static final class PanelFeatureState { 1408 1409 /** Feature ID for this panel. */ 1410 int featureId; 1411 1412 /** Dynamic state of the panel. */ 1413 ViewGroup decorView; 1414 1415 /** The panel that we are actually showing. */ 1416 View shownPanelView; 1417 1418 /** Use {@link #setMenu} to set this. */ 1419 MenuBuilder menu; 1420 1421 ListMenuPresenter listMenuPresenter; 1422 1423 Context listPresenterContext; 1424 1425 /** 1426 * Whether the panel has been prepared (see 1427 * {@link #preparePanel}). 1428 */ 1429 boolean isPrepared; 1430 1431 /** 1432 * Whether an item's action has been performed. This happens in obvious 1433 * scenarios (user clicks on menu item), but can also happen with 1434 * chording menu+(shortcut key). 1435 */ 1436 boolean isHandled; 1437 1438 boolean isOpen; 1439 1440 public boolean qwertyMode; 1441 1442 boolean refreshDecorView; 1443 1444 boolean refreshMenuContent; 1445 1446 boolean wasLastOpen; 1447 1448 /** 1449 * Contains the state of the menu when told to freeze. 1450 */ 1451 Bundle frozenMenuState; 1452 1453 /** 1454 * Contains the state of associated action views when told to freeze. 1455 * These are saved across invalidations. 1456 */ 1457 Bundle frozenActionViewState; 1458 1459 PanelFeatureState(int featureId) { 1460 this.featureId = featureId; 1461 1462 refreshDecorView = false; 1463 } 1464 1465 public boolean hasPanelItems() { 1466 if (shownPanelView == null) return false; 1467 1468 return listMenuPresenter.getAdapter().getCount() > 0; 1469 } 1470 1471 /** 1472 * Unregister and free attached MenuPresenters. They will be recreated as needed. 1473 */ 1474 public void clearMenuPresenters() { 1475 if (menu != null) { 1476 menu.removeMenuPresenter(listMenuPresenter); 1477 } 1478 listMenuPresenter = null; 1479 } 1480 1481 void setStyle(Context context) { 1482 final TypedValue outValue = new TypedValue(); 1483 final Resources.Theme widgetTheme = context.getResources().newTheme(); 1484 widgetTheme.setTo(context.getTheme()); 1485 1486 // First apply the actionBarPopupTheme 1487 widgetTheme.resolveAttribute(R.attr.actionBarPopupTheme, outValue, true); 1488 if (outValue.resourceId != 0) { 1489 widgetTheme.applyStyle(outValue.resourceId, true); 1490 } 1491 1492 // Now apply the panelMenuListTheme 1493 widgetTheme.resolveAttribute(R.attr.panelMenuListTheme, outValue, true); 1494 if (outValue.resourceId != 0) { 1495 widgetTheme.applyStyle(outValue.resourceId, true); 1496 } else { 1497 widgetTheme.applyStyle(R.style.Theme_AppCompat_CompactMenu, true); 1498 } 1499 1500 context = new ContextThemeWrapper(context, 0); 1501 context.getTheme().setTo(widgetTheme); 1502 1503 listPresenterContext = context; 1504 } 1505 1506 void setMenu(MenuBuilder menu) { 1507 if (menu == this.menu) return; 1508 1509 if (this.menu != null) { 1510 this.menu.removeMenuPresenter(listMenuPresenter); 1511 } 1512 this.menu = menu; 1513 if (menu != null) { 1514 if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter); 1515 } 1516 } 1517 1518 MenuView getListMenuView(MenuPresenter.Callback cb) { 1519 if (menu == null) return null; 1520 1521 if (listMenuPresenter == null) { 1522 listMenuPresenter = new ListMenuPresenter(listPresenterContext, 1523 R.layout.abc_list_menu_item_layout); 1524 listMenuPresenter.setCallback(cb); 1525 menu.addMenuPresenter(listMenuPresenter); 1526 } 1527 1528 MenuView result = listMenuPresenter.getMenuView(decorView); 1529 1530 return result; 1531 } 1532 1533 Parcelable onSaveInstanceState() { 1534 SavedState savedState = new SavedState(); 1535 savedState.featureId = featureId; 1536 savedState.isOpen = isOpen; 1537 1538 if (menu != null) { 1539 savedState.menuState = new Bundle(); 1540 menu.savePresenterStates(savedState.menuState); 1541 } 1542 1543 return savedState; 1544 } 1545 1546 void onRestoreInstanceState(Parcelable state) { 1547 SavedState savedState = (SavedState) state; 1548 featureId = savedState.featureId; 1549 wasLastOpen = savedState.isOpen; 1550 frozenMenuState = savedState.menuState; 1551 1552 shownPanelView = null; 1553 decorView = null; 1554 } 1555 1556 void applyFrozenState() { 1557 if (menu != null && frozenMenuState != null) { 1558 menu.restorePresenterStates(frozenMenuState); 1559 frozenMenuState = null; 1560 } 1561 } 1562 1563 private static class SavedState implements Parcelable { 1564 int featureId; 1565 boolean isOpen; 1566 Bundle menuState; 1567 1568 public int describeContents() { 1569 return 0; 1570 } 1571 1572 public void writeToParcel(Parcel dest, int flags) { 1573 dest.writeInt(featureId); 1574 dest.writeInt(isOpen ? 1 : 0); 1575 1576 if (isOpen) { 1577 dest.writeBundle(menuState); 1578 } 1579 } 1580 1581 private static SavedState readFromParcel(Parcel source) { 1582 SavedState savedState = new SavedState(); 1583 savedState.featureId = source.readInt(); 1584 savedState.isOpen = source.readInt() == 1; 1585 1586 if (savedState.isOpen) { 1587 savedState.menuState = source.readBundle(); 1588 } 1589 1590 return savedState; 1591 } 1592 1593 public static final Parcelable.Creator<SavedState> CREATOR 1594 = new Parcelable.Creator<SavedState>() { 1595 public SavedState createFromParcel(Parcel in) { 1596 return readFromParcel(in); 1597 } 1598 1599 public SavedState[] newArray(int size) { 1600 return new SavedState[size]; 1601 } 1602 }; 1603 } 1604 } 1605 1606} 1607