ActionBarActivityDelegateBase.java revision f53c7bf3146bf9bcba46324f9f06449df475be36
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.TypedArray; 22import android.graphics.drawable.Drawable; 23import android.support.v4.app.ActionBarDrawerToggle; 24import android.support.v4.view.WindowCompat; 25import android.support.v7.appcompat.R; 26import android.support.v7.internal.view.menu.ListMenuPresenter; 27import android.support.v7.internal.view.menu.MenuBuilder; 28import android.support.v7.internal.view.menu.MenuPresenter; 29import android.support.v7.internal.view.menu.MenuView; 30import android.support.v7.internal.view.menu.MenuWrapperFactory; 31import android.support.v7.internal.widget.ActionBarContainer; 32import android.support.v7.internal.widget.ActionBarContextView; 33import android.support.v7.internal.widget.ActionBarView; 34import android.support.v7.internal.widget.ProgressBarICS; 35import android.support.v7.view.ActionMode; 36import android.view.LayoutInflater; 37import android.view.Menu; 38import android.view.MenuItem; 39import android.view.View; 40import android.view.ViewGroup; 41import android.view.Window; 42import android.widget.FrameLayout; 43 44class ActionBarActivityDelegateBase extends ActionBarActivityDelegate implements 45 MenuPresenter.Callback, MenuBuilder.Callback { 46 private static final String TAG = "ActionBarActivityDelegateBase"; 47 48 private static final int[] ACTION_BAR_DRAWABLE_TOGGLE_ATTRS = new int[] { 49 R.attr.homeAsUpIndicator 50 }; 51 52 private ActionBarView mActionBarView; 53 private ListMenuPresenter mListMenuPresenter; 54 private MenuBuilder mMenu; 55 56 private ActionMode mActionMode; 57 58 // true if we have installed a window sub-decor layout. 59 private boolean mSubDecorInstalled; 60 61 private CharSequence mTitleToSet; 62 63 // Used to keep track of Progress Bar Window features 64 private boolean mFeatureProgress, mFeatureIndeterminateProgress; 65 66 private boolean mInvalidateMenuPosted; 67 private final Runnable mInvalidateMenuRunnable = new Runnable() { 68 @Override 69 public void run() { 70 final MenuBuilder menu = createMenu(); 71 if (mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) && 72 mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) { 73 setMenu(menu); 74 } else { 75 setMenu(null); 76 } 77 78 mInvalidateMenuPosted = false; 79 } 80 }; 81 82 ActionBarActivityDelegateBase(ActionBarActivity activity) { 83 super(activity); 84 } 85 86 @Override 87 public ActionBar createSupportActionBar() { 88 ensureSubDecor(); 89 return new ActionBarImplBase(mActivity, mActivity); 90 } 91 92 @Override 93 public void onConfigurationChanged(Configuration newConfig) { 94 // If this is called before sub-decor is installed, ActionBar will not 95 // be properly initialized. 96 if (mHasActionBar && mSubDecorInstalled) { 97 // Note: The action bar will need to access 98 // view changes from superclass. 99 ActionBarImplBase actionBar = (ActionBarImplBase) getSupportActionBar(); 100 actionBar.onConfigurationChanged(newConfig); 101 } 102 } 103 104 @Override 105 public void onStop() { 106 ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar(); 107 if (ab != null) { 108 ab.setShowHideAnimationEnabled(false); 109 } 110 } 111 112 @Override 113 public void onPostResume() { 114 ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar(); 115 if (ab != null) { 116 ab.setShowHideAnimationEnabled(true); 117 } 118 } 119 120 @Override 121 public void setContentView(View v) { 122 ensureSubDecor(); 123 if (mHasActionBar) { 124 final ViewGroup contentParent = 125 (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content); 126 contentParent.removeAllViews(); 127 contentParent.addView(v); 128 } else { 129 mActivity.superSetContentView(v); 130 } 131 } 132 133 @Override 134 public void setContentView(int resId) { 135 ensureSubDecor(); 136 if (mHasActionBar) { 137 final ViewGroup contentParent = 138 (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content); 139 contentParent.removeAllViews(); 140 final LayoutInflater inflater = mActivity.getLayoutInflater(); 141 inflater.inflate(resId, contentParent); 142 } else { 143 mActivity.superSetContentView(resId); 144 } 145 } 146 147 @Override 148 public void setContentView(View v, ViewGroup.LayoutParams lp) { 149 ensureSubDecor(); 150 if (mHasActionBar) { 151 final ViewGroup contentParent = 152 (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content); 153 contentParent.removeAllViews(); 154 contentParent.addView(v, lp); 155 } else { 156 mActivity.superSetContentView(v, lp); 157 } 158 } 159 160 @Override 161 public void addContentView(View v, ViewGroup.LayoutParams lp) { 162 ensureSubDecor(); 163 if (mHasActionBar) { 164 final ViewGroup contentParent = 165 (ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content); 166 contentParent.addView(v, lp); 167 } else { 168 mActivity.superSetContentView(v, lp); 169 } 170 } 171 172 final void ensureSubDecor() { 173 if (mHasActionBar && !mSubDecorInstalled) { 174 if (mOverlayActionBar) { 175 mActivity.superSetContentView(R.layout.abc_action_bar_decor_overlay); 176 } else { 177 mActivity.superSetContentView(R.layout.abc_action_bar_decor); 178 } 179 mActionBarView = (ActionBarView) mActivity.findViewById(R.id.action_bar); 180 mActionBarView.setWindowCallback(mActivity); 181 182 /** 183 * Progress Bars 184 */ 185 if (mFeatureProgress) { 186 mActionBarView.initProgress(); 187 } 188 if (mFeatureIndeterminateProgress) { 189 mActionBarView.initIndeterminateProgress(); 190 } 191 192 /** 193 * Split Action Bar 194 */ 195 boolean splitWhenNarrow = UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW 196 .equals(getUiOptionsFromMetadata()); 197 boolean splitActionBar; 198 199 if (splitWhenNarrow) { 200 splitActionBar = mActivity.getResources() 201 .getBoolean(R.bool.abc_split_action_bar_is_narrow); 202 } else { 203 TypedArray a = mActivity.obtainStyledAttributes(R.styleable.ActionBarWindow); 204 splitActionBar = a 205 .getBoolean(R.styleable.ActionBarWindow_windowSplitActionBar, false); 206 a.recycle(); 207 } 208 209 final ActionBarContainer splitView = (ActionBarContainer) mActivity.findViewById( 210 R.id.split_action_bar); 211 if (splitView != null) { 212 mActionBarView.setSplitView(splitView); 213 mActionBarView.setSplitActionBar(splitActionBar); 214 mActionBarView.setSplitWhenNarrow(splitWhenNarrow); 215 216 final ActionBarContextView cab = (ActionBarContextView) mActivity.findViewById( 217 R.id.action_context_bar); 218 cab.setSplitView(splitView); 219 cab.setSplitActionBar(splitActionBar); 220 cab.setSplitWhenNarrow(splitWhenNarrow); 221 } 222 223 // Change our content FrameLayout to use the android.R.id.content id. 224 // Useful for fragments. 225 View content = mActivity.findViewById(android.R.id.content); 226 content.setId(View.NO_ID); 227 View abcContent = mActivity.findViewById(R.id.action_bar_activity_content); 228 abcContent.setId(android.R.id.content); 229 230 // A title was set before we've install the decor so set it now. 231 if (mTitleToSet != null) { 232 mActionBarView.setWindowTitle(mTitleToSet); 233 mTitleToSet = null; 234 } 235 236 mSubDecorInstalled = true; 237 supportInvalidateOptionsMenu(); 238 } 239 } 240 241 @Override 242 public boolean supportRequestWindowFeature(int featureId) { 243 switch (featureId) { 244 case WindowCompat.FEATURE_ACTION_BAR: 245 mHasActionBar = true; 246 return true; 247 case WindowCompat.FEATURE_ACTION_BAR_OVERLAY: 248 mOverlayActionBar = true; 249 return true; 250 case Window.FEATURE_PROGRESS: 251 mFeatureProgress = true; 252 return true; 253 case Window.FEATURE_INDETERMINATE_PROGRESS: 254 mFeatureIndeterminateProgress = true; 255 return true; 256 default: 257 return mActivity.requestWindowFeature(featureId); 258 } 259 } 260 261 @Override 262 public void onTitleChanged(CharSequence title) { 263 if (mActionBarView != null) { 264 mActionBarView.setWindowTitle(title); 265 } else { 266 mTitleToSet = title; 267 } 268 } 269 270 @Override 271 public View onCreatePanelView(int featureId) { 272 View createdPanelView = null; 273 274 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 275 boolean show = true; 276 MenuBuilder menu = mMenu; 277 278 if (mActionMode == null) { 279 // We only want to dispatch Activity/Fragment menu calls if there isn't 280 // currently an action mode 281 282 if (menu == null) { 283 // We don't have a menu created, so create one 284 menu = createMenu(); 285 setMenu(menu); 286 287 // Make sure we're not dispatching item changes to presenters 288 menu.stopDispatchingItemsChanged(); 289 // Dispatch onCreateOptionsMenu 290 show = mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu); 291 } 292 293 if (show) { 294 // Make sure we're not dispatching item changes to presenters 295 menu.stopDispatchingItemsChanged(); 296 // Dispatch onPrepareOptionsMenu 297 show = mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu); 298 } 299 } 300 301 if (show) { 302 createdPanelView = (View) getListMenuView(mActivity, this); 303 304 // Allow menu to start dispatching changes to presenters 305 menu.startDispatchingItemsChanged(); 306 } else { 307 // If the menu isn't being shown, we no longer need it 308 setMenu(null); 309 } 310 } 311 312 return createdPanelView; 313 } 314 315 @Override 316 public boolean onCreatePanelMenu(int featureId, Menu menu) { 317 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 318 return mActivity.superOnCreatePanelMenu(featureId, menu); 319 } 320 return false; 321 } 322 323 @Override 324 public boolean onPreparePanel(int featureId, View view, Menu menu) { 325 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 326 return mActivity.superOnPreparePanel(featureId, view, menu); 327 } 328 return false; 329 } 330 331 @Override 332 public boolean onMenuItemSelected(int featureId, MenuItem item) { 333 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 334 item = MenuWrapperFactory.createMenuItemWrapper(item); 335 } 336 return mActivity.superOnMenuItemSelected(featureId, item); 337 } 338 339 @Override 340 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 341 return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); 342 } 343 344 @Override 345 public void onMenuModeChange(MenuBuilder menu) { 346 reopenMenu(menu, true); 347 } 348 349 @Override 350 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 351 mActivity.closeOptionsMenu(); 352 } 353 354 @Override 355 public boolean onOpenSubMenu(MenuBuilder subMenu) { 356 return false; 357 } 358 359 @Override 360 public ActionMode startSupportActionMode(ActionMode.Callback callback) { 361 if (callback == null) { 362 throw new IllegalArgumentException("ActionMode callback can not be null."); 363 } 364 365 if (mActionMode != null) { 366 mActionMode.finish(); 367 } 368 369 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 370 371 ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar(); 372 if (ab != null) { 373 mActionMode = ab.startActionMode(wrappedCallback); 374 } 375 376 if (mActionMode != null) { 377 mActivity.onSupportActionModeStarted(mActionMode); 378 } 379 return mActionMode; 380 } 381 382 @Override 383 public void supportInvalidateOptionsMenu() { 384 if (!mInvalidateMenuPosted) { 385 mInvalidateMenuPosted = true; 386 mActivity.getWindow().getDecorView().post(mInvalidateMenuRunnable); 387 } 388 } 389 390 private MenuBuilder createMenu() { 391 MenuBuilder menu = new MenuBuilder(getActionBarThemedContext()); 392 menu.setCallback(this); 393 return menu; 394 } 395 396 private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) { 397 if (mActionBarView != null && mActionBarView.isOverflowReserved()) { 398 if (!mActionBarView.isOverflowMenuShowing() || !toggleMenuMode) { 399 if (mActionBarView.getVisibility() == View.VISIBLE) { 400 mActionBarView.showOverflowMenu(); 401 } 402 } else { 403 mActionBarView.hideOverflowMenu(); 404 } 405 return; 406 } 407 408 menu.close(); 409 } 410 411 private MenuView getListMenuView(Context context, MenuPresenter.Callback cb) { 412 if (mMenu == null) { 413 return null; 414 } 415 416 if (mListMenuPresenter == null) { 417 TypedArray a = context.obtainStyledAttributes(R.styleable.Theme); 418 final int listPresenterTheme = a.getResourceId( 419 R.styleable.Theme_panelMenuListTheme, 420 R.style.Theme_AppCompat_CompactMenu); 421 a.recycle(); 422 423 mListMenuPresenter = new ListMenuPresenter( 424 R.layout.abc_list_menu_item_layout, listPresenterTheme); 425 mListMenuPresenter.setCallback(cb); 426 mMenu.addMenuPresenter(mListMenuPresenter); 427 } else { 428 // Make sure we update the ListView 429 mListMenuPresenter.updateMenuView(false); 430 } 431 432 return mListMenuPresenter.getMenuView(new FrameLayout(context)); 433 } 434 435 private void setMenu(MenuBuilder menu) { 436 if (menu == mMenu) { 437 return; 438 } 439 440 if (mMenu != null) { 441 mMenu.removeMenuPresenter(mListMenuPresenter); 442 } 443 mMenu = menu; 444 445 if (menu != null && mListMenuPresenter != null) { 446 // Only update list menu if there isn't an action mode menu 447 menu.addMenuPresenter(mListMenuPresenter); 448 } 449 if (mActionBarView != null) { 450 mActionBarView.setMenu(menu, this); 451 } 452 } 453 454 @Override 455 public boolean onBackPressed() { 456 // Back cancels action modes first. 457 if (mActionMode != null) { 458 mActionMode.finish(); 459 return true; 460 } 461 462 // Next collapse any expanded action views. 463 if (mActionBarView != null && mActionBarView.hasExpandedActionView()) { 464 mActionBarView.collapseActionView(); 465 return true; 466 } 467 468 return false; 469 } 470 471 @Override 472 void setSupportProgressBarVisibility(boolean visible) { 473 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 474 Window.PROGRESS_VISIBILITY_OFF); 475 } 476 477 @Override 478 void setSupportProgressBarIndeterminateVisibility(boolean visible) { 479 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 480 Window.PROGRESS_VISIBILITY_OFF); 481 } 482 483 @Override 484 void setSupportProgressBarIndeterminate(boolean indeterminate) { 485 updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON 486 : Window.PROGRESS_INDETERMINATE_OFF); 487 } 488 489 @Override 490 void setSupportProgress(int progress) { 491 updateProgressBars(Window.PROGRESS_START + progress); 492 } 493 494 @Override 495 ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() { 496 return new ActionBarDrawableToggleImpl(); 497 } 498 499 /** 500 * Progress Bar function. Mostly extracted from PhoneWindow.java 501 */ 502 private void updateProgressBars(int value) { 503 ProgressBarICS circularProgressBar = getCircularProgressBar(); 504 ProgressBarICS horizontalProgressBar = getHorizontalProgressBar(); 505 506 if (value == Window.PROGRESS_VISIBILITY_ON) { 507 if (mFeatureProgress) { 508 int level = horizontalProgressBar.getProgress(); 509 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? 510 View.VISIBLE : View.INVISIBLE; 511 horizontalProgressBar.setVisibility(visibility); 512 } 513 if (mFeatureIndeterminateProgress) { 514 circularProgressBar.setVisibility(View.VISIBLE); 515 } 516 } else if (value == Window.PROGRESS_VISIBILITY_OFF) { 517 if (mFeatureProgress) { 518 horizontalProgressBar.setVisibility(View.GONE); 519 } 520 if (mFeatureIndeterminateProgress) { 521 circularProgressBar.setVisibility(View.GONE); 522 } 523 } else if (value == Window.PROGRESS_INDETERMINATE_ON) { 524 horizontalProgressBar.setIndeterminate(true); 525 } else if (value == Window.PROGRESS_INDETERMINATE_OFF) { 526 horizontalProgressBar.setIndeterminate(false); 527 } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) { 528 // We want to set the progress value before testing for visibility 529 // so that when the progress bar becomes visible again, it has the 530 // correct level. 531 horizontalProgressBar.setProgress(value - Window.PROGRESS_START); 532 533 if (value < Window.PROGRESS_END) { 534 showProgressBars(horizontalProgressBar, circularProgressBar); 535 } else { 536 hideProgressBars(horizontalProgressBar, circularProgressBar); 537 } 538 } 539 } 540 541 private void showProgressBars(ProgressBarICS horizontalProgressBar, 542 ProgressBarICS spinnyProgressBar) { 543 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) { 544 spinnyProgressBar.setVisibility(View.VISIBLE); 545 } 546 // Only show the progress bars if the primary progress is not complete 547 if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) { 548 horizontalProgressBar.setVisibility(View.VISIBLE); 549 } 550 } 551 552 private void hideProgressBars(ProgressBarICS horizontalProgressBar, 553 ProgressBarICS spinnyProgressBar) { 554 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) { 555 spinnyProgressBar.setVisibility(View.INVISIBLE); 556 } 557 if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) { 558 horizontalProgressBar.setVisibility(View.INVISIBLE); 559 } 560 } 561 562 private ProgressBarICS getCircularProgressBar() { 563 ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_circular); 564 if (pb != null) { 565 pb.setVisibility(View.INVISIBLE); 566 } 567 return pb; 568 } 569 570 private ProgressBarICS getHorizontalProgressBar() { 571 ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_horizontal); 572 if (pb != null) { 573 pb.setVisibility(View.INVISIBLE); 574 } 575 return pb; 576 } 577 578 /** 579 * Clears out internal reference when the action mode is destroyed. 580 */ 581 private class ActionModeCallbackWrapper implements ActionMode.Callback { 582 private ActionMode.Callback mWrapped; 583 584 public ActionModeCallbackWrapper(ActionMode.Callback wrapped) { 585 mWrapped = wrapped; 586 } 587 588 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 589 return mWrapped.onCreateActionMode(mode, menu); 590 } 591 592 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 593 return mWrapped.onPrepareActionMode(mode, menu); 594 } 595 596 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 597 return mWrapped.onActionItemClicked(mode, item); 598 } 599 600 public void onDestroyActionMode(ActionMode mode) { 601 mWrapped.onDestroyActionMode(mode); 602 mActivity.onSupportActionModeFinished(mode); 603 mActionMode = null; 604 } 605 } 606 607 private class ActionBarDrawableToggleImpl 608 implements ActionBarDrawerToggle.Delegate { 609 610 @Override 611 public Drawable getThemeUpIndicator() { 612 final TypedArray a = mActivity.obtainStyledAttributes(ACTION_BAR_DRAWABLE_TOGGLE_ATTRS); 613 final Drawable result = a.getDrawable(0); 614 a.recycle(); 615 return result; 616 } 617 618 @Override 619 public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) { 620 if (mActionBarView != null) { 621 mActionBarView.setHomeAsUpIndicator(upDrawable); 622 } 623 } 624 625 @Override 626 public void setActionBarDescription(int contentDescRes) { 627 // No support for setting Action Bar content description 628 } 629 } 630 631} 632