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