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