ActionBarActivityDelegateBase.java revision 4b217eaec19887b37d6d124df5ba04c5bb8b8141
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.os.Bundle; 24import android.support.v4.app.NavUtils; 25import android.support.v4.view.ViewCompat; 26import android.support.v4.view.ViewConfigurationCompat; 27import android.support.v4.view.WindowCompat; 28import android.support.v7.appcompat.R; 29import android.support.v7.internal.app.ToolbarActionBar; 30import android.support.v7.internal.app.WindowDecorActionBar; 31import android.support.v7.internal.view.StandaloneActionMode; 32import android.support.v7.internal.view.menu.ListMenuPresenter; 33import android.support.v7.internal.view.menu.MenuBuilder; 34import android.support.v7.internal.view.menu.MenuPresenter; 35import android.support.v7.internal.view.menu.MenuView; 36import android.support.v7.internal.widget.ActionBarContextView; 37import android.support.v7.internal.widget.DecorContentParent; 38import android.support.v7.internal.widget.ProgressBarCompat; 39import android.support.v7.view.ActionMode; 40import android.support.v7.widget.Toolbar; 41import android.util.DisplayMetrics; 42import android.util.TypedValue; 43import android.view.ContextThemeWrapper; 44import android.view.LayoutInflater; 45import android.view.Gravity; 46import android.view.Menu; 47import android.view.MenuItem; 48import android.view.View; 49import android.view.ViewConfiguration; 50import android.view.ViewGroup; 51import android.view.ViewStub; 52import android.view.Window; 53import android.view.accessibility.AccessibilityEvent; 54import android.widget.FrameLayout; 55import android.widget.PopupWindow; 56 57class ActionBarActivityDelegateBase extends ActionBarActivityDelegate 58 implements MenuBuilder.Callback { 59 private static final String TAG = "ActionBarActivityDelegateBase"; 60 61 private DecorContentParent mDecorContentParent; 62 private ActionMenuPresenterCallback mActionMenuPresenterCallback; 63 private PanelMenuPresenterCallback mPanelMenuPresenterCallback; 64 65 private ListMenuPresenter mListMenuPresenter; 66 private MenuBuilder mMenu; 67 68 ActionMode mActionMode; 69 ActionBarContextView mActionModeView; 70 PopupWindow mActionModePopup; 71 Runnable mShowActionModePopup; 72 73 // true if we have installed a window sub-decor layout. 74 private boolean mSubDecorInstalled; 75 private ViewGroup mWindowDecor; 76 77 private CharSequence mTitleToSet; 78 79 // Used to keep track of Progress Bar Window features 80 private boolean mFeatureProgress, mFeatureIndeterminateProgress; 81 82 // Used for emulating PanelFeatureState 83 private boolean mClosingActionMenu; 84 private boolean mPanelIsPrepared; 85 private boolean mPanelRefreshMenuContent; 86 private Bundle mPanelFrozenActionViewState; 87 88 private boolean mInvalidatePanelMenuPosted; 89 private final Runnable mInvalidatePanelMenuRunnable = new Runnable() { 90 @Override 91 public void run() { 92 supportInvalidateOptionsMenu(); 93 } 94 }; 95 96 private boolean mEnableDefaultActionBarUp; 97 98 ActionBarActivityDelegateBase(ActionBarActivity activity) { 99 super(activity); 100 } 101 102 @Override 103 void onCreate(Bundle savedInstanceState) { 104 super.onCreate(savedInstanceState); 105 106 mWindowDecor = (ViewGroup) mActivity.getWindow().getDecorView(); 107 108 if (NavUtils.getParentActivityName(mActivity) != null) { 109 ActionBar ab = getSupportActionBar(); 110 if (ab == null) { 111 mEnableDefaultActionBarUp = true; 112 } else { 113 ab.setDefaultDisplayHomeAsUpEnabled(true); 114 } 115 } 116 } 117 118 @Override 119 public ActionBar createSupportActionBar() { 120 ensureSubDecor(); 121 ActionBar ab = new WindowDecorActionBar(mActivity, mOverlayActionBar); 122 ab.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); 123 return ab; 124 } 125 126 @Override 127 void setSupportActionBar(Toolbar toolbar) { 128 if (getSupportActionBar() instanceof WindowDecorActionBar) { 129 throw new IllegalStateException("This Activity already has an action bar supplied " + 130 "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + 131 "windowActionBar to false in your theme to use a Toolbar instead."); 132 } 133 ToolbarActionBar tbab = new ToolbarActionBar(toolbar, mActivity.getTitle(), 134 mWindowMenuCallback); 135 setSupportActionBar(tbab); 136 setWindowCallback(tbab.getWrappedWindowCallback()); 137 tbab.invalidateOptionsMenu(); 138 } 139 140 @Override 141 public void onConfigurationChanged(Configuration newConfig) { 142 // If this is called before sub-decor is installed, ActionBar will not 143 // be properly initialized. 144 if (mHasActionBar && mSubDecorInstalled) { 145 // Note: The action bar will need to access 146 // view changes from superclass. 147 ActionBar ab = getSupportActionBar(); 148 if (ab != null) { 149 ab.onConfigurationChanged(newConfig); 150 } 151 } 152 } 153 154 @Override 155 public void onStop() { 156 ActionBar ab = getSupportActionBar(); 157 if (ab != null) { 158 ab.setShowHideAnimationEnabled(false); 159 } 160 } 161 162 @Override 163 public void onPostResume() { 164 ActionBar ab = getSupportActionBar(); 165 if (ab != null) { 166 ab.setShowHideAnimationEnabled(true); 167 } 168 } 169 170 @Override 171 public void setContentView(View v) { 172 ensureSubDecor(); 173 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 174 contentParent.removeAllViews(); 175 contentParent.addView(v); 176 mActivity.onSupportContentChanged(); 177 } 178 179 @Override 180 public void setContentView(int resId) { 181 ensureSubDecor(); 182 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 183 contentParent.removeAllViews(); 184 mActivity.getLayoutInflater().inflate(resId, contentParent); 185 mActivity.onSupportContentChanged(); 186 } 187 188 @Override 189 public void setContentView(View v, ViewGroup.LayoutParams lp) { 190 ensureSubDecor(); 191 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 192 contentParent.removeAllViews(); 193 contentParent.addView(v, lp); 194 mActivity.onSupportContentChanged(); 195 } 196 197 @Override 198 public void addContentView(View v, ViewGroup.LayoutParams lp) { 199 ensureSubDecor(); 200 ViewGroup contentParent = (ViewGroup) mActivity.findViewById(android.R.id.content); 201 contentParent.addView(v, lp); 202 mActivity.onSupportContentChanged(); 203 } 204 205 @Override 206 public void onContentChanged() { 207 // Ignore all calls to this method as we call onSupportContentChanged manually above 208 } 209 210 final void ensureSubDecor() { 211 if (!mSubDecorInstalled) { 212 if (mHasActionBar) { 213 /** 214 * This needs some explanation. As we can not use the android:theme attribute 215 * pre-L, we emulate it by manually creating a LayoutInflater using a 216 * ContextThemeWrapper pointing to actionBarTheme. 217 */ 218 TypedValue outValue = new TypedValue(); 219 mActivity.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true); 220 221 Context themedContext; 222 if (outValue.resourceId != 0) { 223 themedContext = new ContextThemeWrapper(mActivity, outValue.resourceId); 224 } else { 225 themedContext = mActivity; 226 } 227 228 // Now inflate the view using the themed context and set it as the content view 229 View decor = LayoutInflater.from(themedContext) 230 .inflate(R.layout.abc_screen_toolbar, null); 231 mActivity.superSetContentView(decor); 232 233 mDecorContentParent = (DecorContentParent) mActivity 234 .findViewById(R.id.decor_content_parent); 235 mDecorContentParent.setWindowCallback(mWindowMenuCallback); 236 237 /** 238 * Propagate features to DecorContentParent 239 */ 240 if (mOverlayActionBar) { 241 mDecorContentParent.initFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); 242 } 243 if (mFeatureProgress) { 244 mDecorContentParent.initFeature(Window.FEATURE_PROGRESS); 245 } 246 if (mFeatureIndeterminateProgress) { 247 mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 248 } 249 } else if (mOverlayActionMode) { 250 mActivity.superSetContentView(R.layout.abc_screen_simple_overlay_action_mode); 251 } else { 252 mActivity.superSetContentView(R.layout.abc_screen_simple); 253 } 254 255 // Change our content FrameLayout to use the android.R.id.content id. 256 // Useful for fragments. 257 final View decorContent = mActivity.findViewById(android.R.id.content); 258 decorContent.setId(View.NO_ID); 259 View abcContent = mActivity.findViewById(R.id.action_bar_activity_content); 260 abcContent.setId(android.R.id.content); 261 262 // The decorContent may have a foreground drawable set (windowContentOverlay). 263 // Remove this as we handle it ourselves 264 if (decorContent instanceof FrameLayout) { 265 ((FrameLayout) decorContent).setForeground(null); 266 } 267 268 // A title was set before we've install the decor so set it now. 269 if (mTitleToSet != null && mDecorContentParent != null) { 270 mDecorContentParent.setWindowTitle(mTitleToSet); 271 mTitleToSet = null; 272 } 273 274 applyFixedSizeWindow(); 275 276 onSubDecorInstalled(); 277 278 mSubDecorInstalled = true; 279 280 invalidatePanelMenu(); 281 } 282 } 283 284 void onSubDecorInstalled() {} 285 286 private void applyFixedSizeWindow() { 287 TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme); 288 289 TypedValue mFixedWidthMajor = null; 290 TypedValue mFixedWidthMinor = null; 291 TypedValue mFixedHeightMajor = null; 292 TypedValue mFixedHeightMinor = null; 293 294 if (a.hasValue(R.styleable.Theme_windowFixedWidthMajor)) { 295 if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); 296 a.getValue(R.styleable.Theme_windowFixedWidthMajor, mFixedWidthMajor); 297 } 298 if (a.hasValue(R.styleable.Theme_windowFixedWidthMinor)) { 299 if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); 300 a.getValue(R.styleable.Theme_windowFixedWidthMinor, mFixedWidthMinor); 301 } 302 if (a.hasValue(R.styleable.Theme_windowFixedHeightMajor)) { 303 if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); 304 a.getValue(R.styleable.Theme_windowFixedHeightMajor, mFixedHeightMajor); 305 } 306 if (a.hasValue(R.styleable.Theme_windowFixedHeightMinor)) { 307 if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); 308 a.getValue(R.styleable.Theme_windowFixedHeightMinor, mFixedHeightMinor); 309 } 310 311 final DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics(); 312 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; 313 int w = ViewGroup.LayoutParams.MATCH_PARENT; 314 int h = ViewGroup.LayoutParams.MATCH_PARENT; 315 316 final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; 317 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { 318 if (tvw.type == TypedValue.TYPE_DIMENSION) { 319 w = (int) tvw.getDimension(metrics); 320 } else if (tvw.type == TypedValue.TYPE_FRACTION) { 321 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); 322 } 323 } 324 325 final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; 326 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { 327 if (tvh.type == TypedValue.TYPE_DIMENSION) { 328 h = (int) tvh.getDimension(metrics); 329 } else if (tvh.type == TypedValue.TYPE_FRACTION) { 330 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); 331 } 332 } 333 334 if (w != ViewGroup.LayoutParams.MATCH_PARENT || h != ViewGroup.LayoutParams.MATCH_PARENT) { 335 mActivity.getWindow().setLayout(w, h); 336 } 337 338 a.recycle(); 339 } 340 341 @Override 342 public boolean supportRequestWindowFeature(int featureId) { 343 switch (featureId) { 344 case WindowCompat.FEATURE_ACTION_BAR: 345 mHasActionBar = true; 346 return true; 347 case WindowCompat.FEATURE_ACTION_BAR_OVERLAY: 348 mOverlayActionBar = true; 349 return true; 350 case WindowCompat.FEATURE_ACTION_MODE_OVERLAY: 351 mOverlayActionMode = true; 352 return true; 353 case Window.FEATURE_PROGRESS: 354 mFeatureProgress = true; 355 return true; 356 case Window.FEATURE_INDETERMINATE_PROGRESS: 357 mFeatureIndeterminateProgress = true; 358 return true; 359 default: 360 return mActivity.requestWindowFeature(featureId); 361 } 362 } 363 364 @Override 365 public void onTitleChanged(CharSequence title) { 366 if (mDecorContentParent != null) { 367 mDecorContentParent.setWindowTitle(title); 368 } else if (getSupportActionBar() != null) { 369 getSupportActionBar().setWindowTitle(title); 370 } else { 371 mTitleToSet = title; 372 } 373 } 374 375 @Override 376 public View onCreatePanelView(int featureId) { 377 View createdPanelView = null; 378 379 if (featureId == Window.FEATURE_OPTIONS_PANEL && preparePanel()) { 380 createdPanelView = (View) getListMenuView(mActivity); 381 } 382 383 return createdPanelView; 384 } 385 386 @Override 387 public boolean onCreatePanelMenu(int featureId, Menu menu) { 388 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 389 return mWindowMenuCallback.onCreatePanelMenu(featureId, menu); 390 } 391 return false; 392 } 393 394 @Override 395 public boolean onPreparePanel(int featureId, View view, Menu menu) { 396 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 397 return mWindowMenuCallback.onPreparePanel(featureId, view, menu); 398 } 399 return false; 400 } 401 402 @Override 403 public void onPanelClosed(int featureId, Menu menu) { 404 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 405 mPanelIsPrepared = false; 406 } 407 } 408 409 @Override 410 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 411 return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); 412 } 413 414 @Override 415 public void onMenuModeChange(MenuBuilder menu) { 416 reopenMenu(menu, true); 417 } 418 419 @Override 420 public ActionMode startSupportActionMode(ActionMode.Callback callback) { 421 if (callback == null) { 422 throw new IllegalArgumentException("ActionMode callback can not be null."); 423 } 424 425 if (mActionMode != null) { 426 mActionMode.finish(); 427 } 428 429 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 430 431 ActionBar ab = getSupportActionBar(); 432 if (ab != null) { 433 mActionMode = ab.startActionMode(wrappedCallback); 434 if (mActionMode != null) { 435 mActivity.onSupportActionModeStarted(mActionMode); 436 } 437 } 438 439 if (mActionMode == null) { 440 // If the action bar didn't provide an action mode, start the emulated window one 441 mActionMode = startSupportActionModeFromWindow(wrappedCallback); 442 } 443 444 return mActionMode; 445 } 446 447 @Override 448 public void supportInvalidateOptionsMenu() { 449 final ActionBar ab = getSupportActionBar(); 450 if (ab != null && ab.invalidateOptionsMenu()) return; 451 452 if (mMenu != null) { 453 Bundle savedActionViewStates = new Bundle(); 454 mMenu.saveActionViewStates(savedActionViewStates); 455 if (savedActionViewStates.size() > 0) { 456 mPanelFrozenActionViewState = savedActionViewStates; 457 } 458 // This will be started again when the panel is prepared. 459 mMenu.stopDispatchingItemsChanged(); 460 mMenu.clear(); 461 } 462 mPanelRefreshMenuContent = true; 463 464 // Prepare the options panel if we have an action bar 465 if (mDecorContentParent != null) { 466 mPanelIsPrepared = false; 467 preparePanel(); 468 } 469 } 470 471 @Override 472 ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback) { 473 if (mActionMode != null) { 474 mActionMode.finish(); 475 } 476 477 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 478 ActionMode mode = null; 479 480 if (mActionModeView == null) { 481 if (mIsFloating) { 482 mActionModeView = new ActionBarContextView(mActivity); 483 mActionModePopup = new PopupWindow(mActivity, null, 484 R.attr.actionModePopupWindowStyle); 485 mActionModePopup.setContentView(mActionModeView); 486 mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT); 487 488 TypedValue heightValue = new TypedValue(); 489 mActivity.getTheme().resolveAttribute(R.attr.actionBarSize, heightValue, true); 490 final int height = TypedValue.complexToDimensionPixelSize(heightValue.data, 491 mActivity.getResources().getDisplayMetrics()); 492 mActionModeView.setContentHeight(height); 493 mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); 494 mShowActionModePopup = new Runnable() { 495 public void run() { 496 mActionModePopup.showAtLocation( 497 mActionModeView, 498 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0); 499 } 500 }; 501 } else { 502 ViewStub stub = (ViewStub) mActivity.findViewById(R.id.action_mode_bar_stub); 503 if (stub != null) { 504 mActionModeView = (ActionBarContextView) stub.inflate(); 505 } 506 } 507 } 508 509 if (mActionModeView != null) { 510 mActionModeView.killMode(); 511 mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback, 512 mActionModePopup == null); 513 if (callback.onCreateActionMode(mode, mode.getMenu())) { 514 mode.invalidate(); 515 mActionModeView.initForMode(mode); 516 mActionModeView.setVisibility(View.VISIBLE); 517 mActionMode = mode; 518 if (mActionModePopup != null) { 519 mActivity.getWindow().getDecorView().post(mShowActionModePopup); 520 } 521 mActionModeView.sendAccessibilityEvent( 522 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 523 } else { 524 mActionMode = null; 525 } 526 } 527 if (mActionMode != null && mActivity != null) { 528 mActivity.onSupportActionModeStarted(mActionMode); 529 } 530 return mActionMode; 531 } 532 533 private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) { 534 if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu() && 535 (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity)) || 536 mDecorContentParent.isOverflowMenuShowPending())) { 537 if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) { 538 539 // If we have a menu invalidation pending, do it now. 540 if (mInvalidatePanelMenuPosted) { 541 mWindowDecor.removeCallbacks(mInvalidatePanelMenuRunnable); 542 mInvalidatePanelMenuRunnable.run(); 543 } 544 545 // If we don't have a menu or we're waiting for a full content refresh, 546 // forget it. This is a lingering event that no longer matters. 547 if (mMenu != null && !mPanelRefreshMenuContent && preparePanel()) { 548 mDecorContentParent.showOverflowMenu(); 549 } 550 } else { 551 mDecorContentParent.hideOverflowMenu(); 552 } 553 return; 554 } 555 } 556 557 private MenuView getListMenuView(Context context) { 558 if (mMenu == null) { 559 return null; 560 } 561 562 if (mPanelMenuPresenterCallback == null) { 563 mPanelMenuPresenterCallback = new PanelMenuPresenterCallback(); 564 } 565 566 if (mListMenuPresenter == null) { 567 TypedArray a = context.obtainStyledAttributes(R.styleable.Theme); 568 final int listPresenterTheme = a.getResourceId( 569 R.styleable.Theme_panelMenuListTheme, 570 R.style.Theme_AppCompat_CompactMenu); 571 a.recycle(); 572 573 mListMenuPresenter = new ListMenuPresenter( 574 R.layout.abc_list_menu_item_layout, listPresenterTheme); 575 mListMenuPresenter.setCallback(mPanelMenuPresenterCallback); 576 mMenu.addMenuPresenter(mListMenuPresenter, mActivity); 577 } else { 578 // Make sure we update the ListView 579 mListMenuPresenter.updateMenuView(false); 580 } 581 582 if (mListMenuPresenter.getAdapter().isEmpty()) { 583 return null; 584 } 585 586 return mListMenuPresenter.getMenuView(mWindowDecor); 587 } 588 589 @Override 590 public boolean onBackPressed() { 591 // Back cancels action modes first. 592 if (mActionMode != null) { 593 mActionMode.finish(); 594 return true; 595 } 596 597 // Next collapse any expanded action views. 598 ActionBar ab = getSupportActionBar(); 599 if (ab != null && ab.collapseActionView()) { 600 return true; 601 } 602 603 return false; 604 } 605 606 @Override 607 void setSupportProgressBarVisibility(boolean visible) { 608 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 609 Window.PROGRESS_VISIBILITY_OFF); 610 } 611 612 @Override 613 void setSupportProgressBarIndeterminateVisibility(boolean visible) { 614 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 615 Window.PROGRESS_VISIBILITY_OFF); 616 } 617 618 @Override 619 void setSupportProgressBarIndeterminate(boolean indeterminate) { 620 updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON 621 : Window.PROGRESS_INDETERMINATE_OFF); 622 } 623 624 @Override 625 void setSupportProgress(int progress) { 626 updateProgressBars(Window.PROGRESS_START + progress); 627 } 628 629 @Override 630 int getHomeAsUpIndicatorAttrId() { 631 return R.attr.homeAsUpIndicator; 632 } 633 634 /** 635 * Progress Bar function. Mostly extracted from PhoneWindow.java 636 */ 637 private void updateProgressBars(int value) { 638 ProgressBarCompat circularProgressBar = getCircularProgressBar(); 639 ProgressBarCompat horizontalProgressBar = getHorizontalProgressBar(); 640 641 if (value == Window.PROGRESS_VISIBILITY_ON) { 642 if (mFeatureProgress) { 643 int level = horizontalProgressBar.getProgress(); 644 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? 645 View.VISIBLE : View.INVISIBLE; 646 horizontalProgressBar.setVisibility(visibility); 647 } 648 if (mFeatureIndeterminateProgress) { 649 circularProgressBar.setVisibility(View.VISIBLE); 650 } 651 } else if (value == Window.PROGRESS_VISIBILITY_OFF) { 652 if (mFeatureProgress) { 653 horizontalProgressBar.setVisibility(View.GONE); 654 } 655 if (mFeatureIndeterminateProgress) { 656 circularProgressBar.setVisibility(View.GONE); 657 } 658 } else if (value == Window.PROGRESS_INDETERMINATE_ON) { 659 horizontalProgressBar.setIndeterminate(true); 660 } else if (value == Window.PROGRESS_INDETERMINATE_OFF) { 661 horizontalProgressBar.setIndeterminate(false); 662 } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) { 663 // We want to set the progress value before testing for visibility 664 // so that when the progress bar becomes visible again, it has the 665 // correct level. 666 horizontalProgressBar.setProgress(value - Window.PROGRESS_START); 667 668 if (value < Window.PROGRESS_END) { 669 showProgressBars(horizontalProgressBar, circularProgressBar); 670 } else { 671 hideProgressBars(horizontalProgressBar, circularProgressBar); 672 } 673 } 674 } 675 676 private void showProgressBars(ProgressBarCompat horizontalProgressBar, 677 ProgressBarCompat spinnyProgressBar) { 678 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) { 679 spinnyProgressBar.setVisibility(View.VISIBLE); 680 } 681 // Only show the progress bars if the primary progress is not complete 682 if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) { 683 horizontalProgressBar.setVisibility(View.VISIBLE); 684 } 685 } 686 687 private void hideProgressBars(ProgressBarCompat horizontalProgressBar, 688 ProgressBarCompat spinnyProgressBar) { 689 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) { 690 spinnyProgressBar.setVisibility(View.INVISIBLE); 691 } 692 if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) { 693 horizontalProgressBar.setVisibility(View.INVISIBLE); 694 } 695 } 696 697 private ProgressBarCompat getCircularProgressBar() { 698 ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_circular); 699 if (pb != null) { 700 pb.setVisibility(View.INVISIBLE); 701 } 702 return pb; 703 } 704 705 private ProgressBarCompat getHorizontalProgressBar() { 706 ProgressBarCompat pb = (ProgressBarCompat) mActivity.findViewById(R.id.progress_horizontal); 707 if (pb != null) { 708 pb.setVisibility(View.INVISIBLE); 709 } 710 return pb; 711 } 712 713 private boolean initializePanelMenu() { 714 Context context = mActivity; 715 716 if (mDecorContentParent != null) { 717 final TypedValue outValue = new TypedValue(); 718 final Resources.Theme baseTheme = context.getTheme(); 719 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); 720 721 Resources.Theme widgetTheme = null; 722 if (outValue.resourceId != 0) { 723 widgetTheme = context.getResources().newTheme(); 724 widgetTheme.setTo(baseTheme); 725 widgetTheme.applyStyle(outValue.resourceId, true); 726 widgetTheme.resolveAttribute( 727 R.attr.actionBarWidgetTheme, outValue, true); 728 } else { 729 baseTheme.resolveAttribute( 730 R.attr.actionBarWidgetTheme, outValue, true); 731 } 732 733 if (outValue.resourceId != 0) { 734 if (widgetTheme == null) { 735 widgetTheme = context.getResources().newTheme(); 736 widgetTheme.setTo(baseTheme); 737 } 738 widgetTheme.applyStyle(outValue.resourceId, true); 739 } 740 741 if (widgetTheme != null) { 742 context = new ContextThemeWrapper(context, 0); 743 context.getTheme().setTo(widgetTheme); 744 } 745 } 746 747 mMenu = new MenuBuilder(context); 748 mMenu.setCallback(this); 749 750 return true; 751 } 752 753 private boolean preparePanel() { 754 // Already prepared (isPrepared will be reset to false later) 755 if (mPanelIsPrepared) { 756 return true; 757 } 758 759 if (mDecorContentParent != null) { 760 // Enforce ordering guarantees around events so that the action bar never 761 // dispatches menu-related events before the panel is prepared. 762 mDecorContentParent.setMenuPrepared(); 763 } 764 765 // Init the panel state's menu--return false if init failed 766 if (mMenu == null || mPanelRefreshMenuContent) { 767 if (mMenu == null) { 768 if (!initializePanelMenu() || (mMenu == null)) { 769 return false; 770 } 771 } 772 773 if (mDecorContentParent != null) { 774 if (mActionMenuPresenterCallback == null) { 775 mActionMenuPresenterCallback = new ActionMenuPresenterCallback(); 776 } 777 mDecorContentParent.setMenu(mMenu, mActionMenuPresenterCallback); 778 } 779 780 // Creating the panel menu will involve a lot of manipulation; 781 // don't dispatch change events to presenters until we're done. 782 mMenu.stopDispatchingItemsChanged(); 783 784 // Call callback, and return if it doesn't want to display menu. 785 if (!mWindowMenuCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, mMenu)) { 786 // Ditch the menu created above 787 mMenu = null; 788 789 if (mDecorContentParent != null) { 790 // Don't show it in the action bar either 791 mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); 792 } 793 794 return false; 795 } 796 797 mPanelRefreshMenuContent = false; 798 } 799 800 // Preparing the panel menu can involve a lot of manipulation; 801 // don't dispatch change events to presenters until we're done. 802 mMenu.stopDispatchingItemsChanged(); 803 804 // Restore action view state before we prepare. This gives apps 805 // an opportunity to override frozen/restored state in onPrepare. 806 if (mPanelFrozenActionViewState != null) { 807 mMenu.restoreActionViewStates(mPanelFrozenActionViewState); 808 mPanelFrozenActionViewState = null; 809 } 810 811 // Callback and return if the callback does not want to show the menu 812 if (!mWindowMenuCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, mMenu)) { 813 if (mDecorContentParent != null) { 814 // The app didn't want to show the menu for now but it still exists. 815 // Clear it out of the action bar. 816 mDecorContentParent.setMenu(null, mActionMenuPresenterCallback); 817 } 818 mMenu.startDispatchingItemsChanged(); 819 return false; 820 } 821 822 mMenu.startDispatchingItemsChanged(); 823 824 // Set other state 825 mPanelIsPrepared = true; 826 827 return true; 828 } 829 830 private void checkCloseActionMenu() { 831 if (mClosingActionMenu) { 832 return; 833 } 834 835 mClosingActionMenu = true; 836 mDecorContentParent.dismissPopups(); 837 mClosingActionMenu = false; 838 } 839 840 private void closePanel(int featureId) { 841 if (featureId == Window.FEATURE_OPTIONS_PANEL && mDecorContentParent != null && 842 mDecorContentParent.canShowOverflowMenu() && 843 !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mActivity))) { 844 mDecorContentParent.hideOverflowMenu(); 845 } else { 846 mActivity.closeOptionsMenu(); 847 mPanelIsPrepared = false; 848 } 849 } 850 851 private void invalidatePanelMenu() { 852 if (!mInvalidatePanelMenuPosted && mWindowDecor != null) { 853 ViewCompat.postOnAnimation(mWindowDecor, mInvalidatePanelMenuRunnable); 854 mInvalidatePanelMenuPosted = true; 855 } 856 } 857 858 /** 859 * Clears out internal reference when the action mode is destroyed. 860 */ 861 private class ActionModeCallbackWrapper implements ActionMode.Callback { 862 private ActionMode.Callback mWrapped; 863 864 public ActionModeCallbackWrapper(ActionMode.Callback wrapped) { 865 mWrapped = wrapped; 866 } 867 868 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 869 return mWrapped.onCreateActionMode(mode, menu); 870 } 871 872 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 873 return mWrapped.onPrepareActionMode(mode, menu); 874 } 875 876 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 877 return mWrapped.onActionItemClicked(mode, item); 878 } 879 880 public void onDestroyActionMode(ActionMode mode) { 881 mWrapped.onDestroyActionMode(mode); 882 if (mActionModePopup != null) { 883 mActivity.getWindow().getDecorView().removeCallbacks(mShowActionModePopup); 884 mActionModePopup.dismiss(); 885 } else if (mActionModeView != null) { 886 mActionModeView.setVisibility(View.GONE); 887 } 888 if (mActionModeView != null) { 889 mActionModeView.removeAllViews(); 890 } 891 if (mActivity != null) { 892 try { 893 mActivity.onSupportActionModeFinished(mActionMode); 894 } catch (AbstractMethodError ame) { 895 // Older apps might not implement this callback method. 896 } 897 } 898 mActionMode = null; 899 } 900 } 901 902 private final class PanelMenuPresenterCallback implements MenuPresenter.Callback { 903 @Override 904 public boolean onOpenSubMenu(MenuBuilder subMenu) { 905 return false; 906 } 907 908 @Override 909 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 910 closePanel(Window.FEATURE_OPTIONS_PANEL); 911 } 912 } 913 914 private final class ActionMenuPresenterCallback implements MenuPresenter.Callback { 915 @Override 916 public boolean onOpenSubMenu(MenuBuilder subMenu) { 917 return false; 918 } 919 920 @Override 921 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 922 checkCloseActionMenu(); 923 } 924 } 925 926} 927