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 17 package android.support.v7.app; 18 19 import android.content.Context; 20 import android.content.res.Configuration; 21 import android.content.res.TypedArray; 22 import android.graphics.drawable.Drawable; 23 import android.support.v4.app.ActionBarDrawerToggle; 24 import android.support.v4.view.WindowCompat; 25 import android.support.v7.appcompat.R; 26 import android.support.v7.internal.view.menu.ListMenuPresenter; 27 import android.support.v7.internal.view.menu.MenuBuilder; 28 import android.support.v7.internal.view.menu.MenuPresenter; 29 import android.support.v7.internal.view.menu.MenuView; 30 import android.support.v7.internal.view.menu.MenuWrapperFactory; 31 import android.support.v7.internal.widget.ActionBarContainer; 32 import android.support.v7.internal.widget.ActionBarContextView; 33 import android.support.v7.internal.widget.ActionBarView; 34 import android.support.v7.internal.widget.ProgressBarICS; 35 import android.support.v7.view.ActionMode; 36 import android.view.LayoutInflater; 37 import android.view.Menu; 38 import android.view.MenuItem; 39 import android.view.View; 40 import android.view.ViewGroup; 41 import android.view.Window; 42 import android.widget.FrameLayout; 43 44 class 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 final 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 onTitleChanged(CharSequence title) { 249 if (mActionBarView != null) { 250 mActionBarView.setWindowTitle(title); 251 } 252 } 253 254 @Override 255 public View onCreatePanelView(int featureId) { 256 View createdPanelView = null; 257 258 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 259 boolean show = true; 260 MenuBuilder menu = mMenu; 261 262 if (mActionMode == null) { 263 // We only want to dispatch Activity/Fragment menu calls if there isn't 264 // currently an action mode 265 266 if (menu == null) { 267 // We don't have a menu created, so create one 268 menu = createMenu(); 269 setMenu(menu); 270 271 // Make sure we're not dispatching item changes to presenters 272 menu.stopDispatchingItemsChanged(); 273 // Dispatch onCreateSupportOptionsMenu 274 show = mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu); 275 } 276 277 if (show) { 278 // Make sure we're not dispatching item changes to presenters 279 menu.stopDispatchingItemsChanged(); 280 // Dispatch onPrepareSupportOptionsMenu 281 show = mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu); 282 } 283 } 284 285 if (show) { 286 createdPanelView = (View) getListMenuView(mActivity, this); 287 288 // Allow menu to start dispatching changes to presenters 289 menu.startDispatchingItemsChanged(); 290 } else { 291 // If the menu isn't being shown, we no longer need it 292 setMenu(null); 293 } 294 } 295 296 return createdPanelView; 297 } 298 299 @Override 300 public boolean onCreatePanelMenu(int featureId, Menu menu) { 301 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 302 return mActivity.superOnCreatePanelMenu(featureId, menu); 303 } 304 return false; 305 } 306 307 @Override 308 public boolean onPreparePanel(int featureId, View view, Menu menu) { 309 if (featureId != Window.FEATURE_OPTIONS_PANEL) { 310 return mActivity.superOnPreparePanel(featureId, view, menu); 311 } 312 return false; 313 } 314 315 @Override 316 public boolean onMenuItemSelected(int featureId, MenuItem item) { 317 if (featureId == Window.FEATURE_OPTIONS_PANEL) { 318 item = MenuWrapperFactory.createMenuItemWrapper(item); 319 } 320 return mActivity.superOnMenuItemSelected(featureId, item); 321 } 322 323 @Override 324 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 325 return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item); 326 } 327 328 @Override 329 public void onMenuModeChange(MenuBuilder menu) { 330 reopenMenu(menu, true); 331 } 332 333 @Override 334 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 335 mActivity.closeOptionsMenu(); 336 } 337 338 @Override 339 public boolean onOpenSubMenu(MenuBuilder subMenu) { 340 return false; 341 } 342 343 @Override 344 public ActionMode startSupportActionMode(ActionMode.Callback callback) { 345 if (callback == null) { 346 throw new IllegalArgumentException("ActionMode callback can not be null."); 347 } 348 349 if (mActionMode != null) { 350 mActionMode.finish(); 351 } 352 353 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback); 354 355 ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar(); 356 if (ab != null) { 357 mActionMode = ab.startActionMode(wrappedCallback); 358 } 359 360 if (mActionMode != null) { 361 mActivity.onSupportActionModeStarted(mActionMode); 362 } 363 return mActionMode; 364 } 365 366 @Override 367 public void supportInvalidateOptionsMenu() { 368 if (!mInvalidateMenuPosted) { 369 mInvalidateMenuPosted = true; 370 mActivity.getWindow().getDecorView().post(mInvalidateMenuRunnable); 371 } 372 } 373 374 private MenuBuilder createMenu() { 375 MenuBuilder menu = new MenuBuilder(getActionBarThemedContext()); 376 menu.setCallback(this); 377 return menu; 378 } 379 380 private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) { 381 if (mActionBarView != null && mActionBarView.isOverflowReserved()) { 382 if (!mActionBarView.isOverflowMenuShowing() || !toggleMenuMode) { 383 if (mActionBarView.getVisibility() == View.VISIBLE) { 384 mActionBarView.showOverflowMenu(); 385 } 386 } else { 387 mActionBarView.hideOverflowMenu(); 388 } 389 return; 390 } 391 392 menu.close(); 393 } 394 395 private MenuView getListMenuView(Context context, MenuPresenter.Callback cb) { 396 if (mMenu == null) { 397 return null; 398 } 399 400 if (mListMenuPresenter == null) { 401 TypedArray a = context.obtainStyledAttributes(R.styleable.Theme); 402 final int listPresenterTheme = a.getResourceId( 403 R.styleable.Theme_panelMenuListTheme, 404 R.style.Theme_AppCompat_CompactMenu); 405 a.recycle(); 406 407 mListMenuPresenter = new ListMenuPresenter( 408 R.layout.abc_list_menu_item_layout, listPresenterTheme); 409 mListMenuPresenter.setCallback(cb); 410 mMenu.addMenuPresenter(mListMenuPresenter); 411 } else { 412 // Make sure we update the ListView 413 mListMenuPresenter.updateMenuView(false); 414 } 415 416 return mListMenuPresenter.getMenuView(new FrameLayout(context)); 417 } 418 419 private void setMenu(MenuBuilder menu) { 420 if (menu == mMenu) { 421 return; 422 } 423 424 if (mMenu != null) { 425 mMenu.removeMenuPresenter(mListMenuPresenter); 426 } 427 mMenu = menu; 428 429 if (menu != null && mListMenuPresenter != null) { 430 // Only update list menu if there isn't an action mode menu 431 menu.addMenuPresenter(mListMenuPresenter); 432 } 433 if (mActionBarView != null) { 434 mActionBarView.setMenu(menu, this); 435 } 436 } 437 438 @Override 439 public boolean onBackPressed() { 440 // Back cancels action modes first. 441 if (mActionMode != null) { 442 mActionMode.finish(); 443 return true; 444 } 445 446 // Next collapse any expanded action views. 447 if (mActionBarView != null && mActionBarView.hasExpandedActionView()) { 448 mActionBarView.collapseActionView(); 449 return true; 450 } 451 452 return false; 453 } 454 455 @Override 456 void setSupportProgressBarVisibility(boolean visible) { 457 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 458 Window.PROGRESS_VISIBILITY_OFF); 459 } 460 461 @Override 462 void setSupportProgressBarIndeterminateVisibility(boolean visible) { 463 updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON : 464 Window.PROGRESS_VISIBILITY_OFF); 465 } 466 467 @Override 468 void setSupportProgressBarIndeterminate(boolean indeterminate) { 469 updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON 470 : Window.PROGRESS_INDETERMINATE_OFF); 471 } 472 473 @Override 474 void setSupportProgress(int progress) { 475 updateProgressBars(Window.PROGRESS_START + progress); 476 } 477 478 @Override 479 ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() { 480 return new ActionBarDrawableToggleImpl(); 481 } 482 483 /** 484 * Progress Bar function. Mostly extracted from PhoneWindow.java 485 */ 486 private void updateProgressBars(int value) { 487 ProgressBarICS circularProgressBar = getCircularProgressBar(); 488 ProgressBarICS horizontalProgressBar = getHorizontalProgressBar(); 489 490 if (value == Window.PROGRESS_VISIBILITY_ON) { 491 if (mFeatureProgress) { 492 int level = horizontalProgressBar.getProgress(); 493 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? 494 View.VISIBLE : View.INVISIBLE; 495 horizontalProgressBar.setVisibility(visibility); 496 } 497 if (mFeatureIndeterminateProgress) { 498 circularProgressBar.setVisibility(View.VISIBLE); 499 } 500 } else if (value == Window.PROGRESS_VISIBILITY_OFF) { 501 if (mFeatureProgress) { 502 horizontalProgressBar.setVisibility(View.GONE); 503 } 504 if (mFeatureIndeterminateProgress) { 505 circularProgressBar.setVisibility(View.GONE); 506 } 507 } else if (value == Window.PROGRESS_INDETERMINATE_ON) { 508 horizontalProgressBar.setIndeterminate(true); 509 } else if (value == Window.PROGRESS_INDETERMINATE_OFF) { 510 horizontalProgressBar.setIndeterminate(false); 511 } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) { 512 // We want to set the progress value before testing for visibility 513 // so that when the progress bar becomes visible again, it has the 514 // correct level. 515 horizontalProgressBar.setProgress(value - Window.PROGRESS_START); 516 517 if (value < Window.PROGRESS_END) { 518 showProgressBars(horizontalProgressBar, circularProgressBar); 519 } else { 520 hideProgressBars(horizontalProgressBar, circularProgressBar); 521 } 522 } 523 } 524 525 private void showProgressBars(ProgressBarICS horizontalProgressBar, 526 ProgressBarICS spinnyProgressBar) { 527 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) { 528 spinnyProgressBar.setVisibility(View.VISIBLE); 529 } 530 // Only show the progress bars if the primary progress is not complete 531 if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) { 532 horizontalProgressBar.setVisibility(View.VISIBLE); 533 } 534 } 535 536 private void hideProgressBars(ProgressBarICS horizontalProgressBar, 537 ProgressBarICS spinnyProgressBar) { 538 if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) { 539 spinnyProgressBar.setVisibility(View.INVISIBLE); 540 } 541 if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) { 542 horizontalProgressBar.setVisibility(View.INVISIBLE); 543 } 544 } 545 546 private ProgressBarICS getCircularProgressBar() { 547 ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_circular); 548 if (pb != null) { 549 pb.setVisibility(View.INVISIBLE); 550 } 551 return pb; 552 } 553 554 private ProgressBarICS getHorizontalProgressBar() { 555 ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_horizontal); 556 if (pb != null) { 557 pb.setVisibility(View.INVISIBLE); 558 } 559 return pb; 560 } 561 562 /** 563 * Clears out internal reference when the action mode is destroyed. 564 */ 565 private class ActionModeCallbackWrapper implements ActionMode.Callback { 566 private ActionMode.Callback mWrapped; 567 568 public ActionModeCallbackWrapper(ActionMode.Callback wrapped) { 569 mWrapped = wrapped; 570 } 571 572 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 573 return mWrapped.onCreateActionMode(mode, menu); 574 } 575 576 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 577 return mWrapped.onPrepareActionMode(mode, menu); 578 } 579 580 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 581 return mWrapped.onActionItemClicked(mode, item); 582 } 583 584 public void onDestroyActionMode(ActionMode mode) { 585 mWrapped.onDestroyActionMode(mode); 586 mActivity.onSupportActionModeFinished(mode); 587 mActionMode = null; 588 } 589 } 590 591 private class ActionBarDrawableToggleImpl 592 implements ActionBarDrawerToggle.Delegate { 593 594 @Override 595 public Drawable getThemeUpIndicator() { 596 final TypedArray a = mActivity.obtainStyledAttributes(ACTION_BAR_DRAWABLE_TOGGLE_ATTRS); 597 final Drawable result = a.getDrawable(0); 598 a.recycle(); 599 return result; 600 } 601 602 @Override 603 public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) { 604 if (mActionBarView != null) { 605 mActionBarView.setHomeAsUpIndicator(upDrawable); 606 } 607 } 608 609 @Override 610 public void setActionBarDescription(int contentDescRes) { 611 // No support for setting Action Bar content description 612 } 613 } 614 615 } 616