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