ToolbarWidgetWrapper.java revision 5b84d15b2f5fb9b10532298c57eaebed32e3740a
1/* 2 * Copyright (C) 2014 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 18package android.support.v7.internal.widget; 19 20import android.app.ActionBar; 21import android.content.Context; 22import android.graphics.drawable.Drawable; 23import android.os.Parcelable; 24import android.support.v4.view.ViewCompat; 25import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; 26import android.support.v7.appcompat.R; 27import android.support.v7.internal.app.WindowCallback; 28import android.support.v7.internal.view.menu.ActionMenuItem; 29import android.support.v7.widget.ActionMenuPresenter; 30import android.support.v7.internal.view.menu.MenuBuilder; 31import android.support.v7.internal.view.menu.MenuPresenter; 32import android.support.v7.widget.Toolbar; 33import android.text.TextUtils; 34import android.util.Log; 35import android.util.SparseArray; 36import android.view.Gravity; 37import android.view.LayoutInflater; 38import android.view.Menu; 39import android.view.View; 40import android.view.ViewGroup; 41import android.view.Window; 42import android.widget.SpinnerAdapter; 43 44/** 45 * Internal class used to interact with the Toolbar widget without 46 * exposing interface methods to the public API. 47 * 48 * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView 49 * so that either variant acting as a 50 * {@link android.support.v7.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave 51 * in the same way.</p> 52 * 53 * @hide 54 */ 55public class ToolbarWidgetWrapper implements DecorToolbar { 56 private static final String TAG = "ToolbarWidgetWrapper"; 57 58 private static final int AFFECTS_LOGO_MASK = 59 ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO; 60 61 private Toolbar mToolbar; 62 63 private int mDisplayOpts; 64 private View mTabView; 65 private SpinnerCompat mSpinner; 66 private View mCustomView; 67 68 private Drawable mIcon; 69 private Drawable mLogo; 70 private Drawable mNavIcon; 71 72 private boolean mTitleSet; 73 private CharSequence mTitle; 74 private CharSequence mSubtitle; 75 private CharSequence mHomeDescription; 76 77 private WindowCallback mWindowCallback; 78 private boolean mMenuPrepared; 79 private ActionMenuPresenter mActionMenuPresenter; 80 81 private int mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; 82 83 private final TintManager mTintManager; 84 private int mDefaultNavigationContentDescription = 0; 85 86 public ToolbarWidgetWrapper(Toolbar toolbar, boolean style) { 87 this(toolbar, style, R.string.abc_action_bar_up_description); 88 } 89 90 public ToolbarWidgetWrapper(Toolbar toolbar, boolean style, 91 int defaultNavigationContentDescription) { 92 mToolbar = toolbar; 93 mTitle = toolbar.getTitle(); 94 mSubtitle = toolbar.getSubtitle(); 95 mTitleSet = mTitle != null; 96 97 if (style) { 98 final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(), 99 null, R.styleable.ActionBar, R.attr.actionBarStyle, 0); 100 101 final CharSequence title = a.getText(R.styleable.ActionBar_title); 102 if (!TextUtils.isEmpty(title)) { 103 setTitle(title); 104 } 105 106 final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); 107 if (!TextUtils.isEmpty(subtitle)) { 108 setSubtitle(subtitle); 109 } 110 111 final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo); 112 if (logo != null) { 113 setLogo(logo); 114 } 115 116 final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon); 117 if (icon != null) { 118 setIcon(icon); 119 } 120 121 final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator); 122 if (navIcon != null) { 123 setNavigationIcon(navIcon); 124 } 125 126 setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0)); 127 128 final int customNavId = a.getResourceId( 129 R.styleable.ActionBar_customNavigationLayout, 0); 130 if (customNavId != 0) { 131 setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId, 132 mToolbar, false)); 133 setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM); 134 } 135 136 final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0); 137 if (height > 0) { 138 final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams(); 139 lp.height = height; 140 mToolbar.setLayoutParams(lp); 141 } 142 143 final int contentInsetStart = a.getDimensionPixelOffset( 144 R.styleable.ActionBar_contentInsetStart, -1); 145 final int contentInsetEnd = a.getDimensionPixelOffset( 146 R.styleable.ActionBar_contentInsetEnd, -1); 147 if (contentInsetStart >= 0 || contentInsetEnd >= 0) { 148 mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0), 149 Math.max(contentInsetEnd, 0)); 150 } 151 152 final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); 153 if (titleTextStyle != 0) { 154 mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle); 155 } 156 157 final int subtitleTextStyle = a.getResourceId( 158 R.styleable.ActionBar_subtitleTextStyle, 0); 159 if (subtitleTextStyle != 0) { 160 mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle); 161 } 162 163 final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0); 164 if (popupTheme != 0) { 165 mToolbar.setPopupTheme(popupTheme); 166 } 167 168 a.recycle(); 169 // Keep the TintManager in case we need it later 170 mTintManager = a.getTintManager(); 171 } else { 172 mDisplayOpts = detectDisplayOptions(); 173 // Create a TintManager in case we need it later 174 mTintManager = new TintManager(toolbar.getContext()); 175 } 176 177 setDefaultNavigationContentDescription(defaultNavigationContentDescription); 178 mHomeDescription = mToolbar.getNavigationContentDescription(); 179 180 mToolbar.setNavigationOnClickListener(new View.OnClickListener() { 181 final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(), 182 0, android.R.id.home, 0, 0, mTitle); 183 @Override 184 public void onClick(View v) { 185 if (mWindowCallback != null && mMenuPrepared) { 186 mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem); 187 } 188 } 189 }); 190 } 191 192 /** 193 * Sets the default content description for the navigation button. 194 * <p> 195 * It changes the current content description if and only if the provided resource id is 196 * different than the current default resource id and the current content description is empty. 197 * 198 * @param defaultNavigationContentDescription The resource id for the default content 199 * description 200 */ 201 @Override 202 public void setDefaultNavigationContentDescription(int defaultNavigationContentDescription) { 203 if (defaultNavigationContentDescription == mDefaultNavigationContentDescription) { 204 return; 205 } 206 mDefaultNavigationContentDescription = defaultNavigationContentDescription; 207 if (TextUtils.isEmpty(mToolbar.getNavigationContentDescription())) { 208 setNavigationContentDescription(mDefaultNavigationContentDescription); 209 } 210 } 211 212 private int detectDisplayOptions() { 213 int opts = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME | 214 ActionBar.DISPLAY_USE_LOGO; 215 if (mToolbar.getNavigationIcon() != null) { 216 opts |= ActionBar.DISPLAY_HOME_AS_UP; 217 } 218 return opts; 219 } 220 221 @Override 222 public ViewGroup getViewGroup() { 223 return mToolbar; 224 } 225 226 @Override 227 public Context getContext() { 228 return mToolbar.getContext(); 229 } 230 231 @Override 232 public boolean isSplit() { 233 return false; 234 } 235 236 @Override 237 public boolean hasExpandedActionView() { 238 return mToolbar.hasExpandedActionView(); 239 } 240 241 @Override 242 public void collapseActionView() { 243 mToolbar.collapseActionView(); 244 } 245 246 @Override 247 public void setWindowCallback(WindowCallback cb) { 248 mWindowCallback = cb; 249 } 250 251 @Override 252 public void setWindowTitle(CharSequence title) { 253 // "Real" title always trumps window title. 254 if (!mTitleSet) { 255 setTitleInt(title); 256 } 257 } 258 259 @Override 260 public CharSequence getTitle() { 261 return mToolbar.getTitle(); 262 } 263 264 @Override 265 public void setTitle(CharSequence title) { 266 mTitleSet = true; 267 setTitleInt(title); 268 } 269 270 private void setTitleInt(CharSequence title) { 271 mTitle = title; 272 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 273 mToolbar.setTitle(title); 274 } 275 } 276 277 @Override 278 public CharSequence getSubtitle() { 279 return mToolbar.getSubtitle(); 280 } 281 282 @Override 283 public void setSubtitle(CharSequence subtitle) { 284 mSubtitle = subtitle; 285 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 286 mToolbar.setSubtitle(subtitle); 287 } 288 } 289 290 @Override 291 public void initProgress() { 292 Log.i(TAG, "Progress display unsupported"); 293 } 294 295 @Override 296 public void initIndeterminateProgress() { 297 Log.i(TAG, "Progress display unsupported"); 298 } 299 300 @Override 301 public boolean canSplit() { 302 return false; 303 } 304 305 @Override 306 public void setSplitView(ViewGroup splitView) { 307 } 308 309 @Override 310 public void setSplitToolbar(boolean split) { 311 if (split) { 312 throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar"); 313 } 314 } 315 316 @Override 317 public void setSplitWhenNarrow(boolean splitWhenNarrow) { 318 // Ignore. 319 } 320 321 @Override 322 public boolean hasIcon() { 323 return mIcon != null; 324 } 325 326 @Override 327 public boolean hasLogo() { 328 return mLogo != null; 329 } 330 331 @Override 332 public void setIcon(int resId) { 333 setIcon(resId != 0 ? mTintManager.getDrawable(resId) : null); 334 } 335 336 @Override 337 public void setIcon(Drawable d) { 338 mIcon = d; 339 updateToolbarLogo(); 340 } 341 342 @Override 343 public void setLogo(int resId) { 344 setLogo(resId != 0 ? mTintManager.getDrawable(resId) : null); 345 } 346 347 @Override 348 public void setLogo(Drawable d) { 349 mLogo = d; 350 updateToolbarLogo(); 351 } 352 353 private void updateToolbarLogo() { 354 Drawable logo = null; 355 if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) { 356 if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) { 357 logo = mLogo != null ? mLogo : mIcon; 358 } else { 359 logo = mIcon; 360 } 361 } 362 mToolbar.setLogo(logo); 363 } 364 365 @Override 366 public boolean canShowOverflowMenu() { 367 return mToolbar.canShowOverflowMenu(); 368 } 369 370 @Override 371 public boolean isOverflowMenuShowing() { 372 return mToolbar.isOverflowMenuShowing(); 373 } 374 375 @Override 376 public boolean isOverflowMenuShowPending() { 377 return mToolbar.isOverflowMenuShowPending(); 378 } 379 380 @Override 381 public boolean showOverflowMenu() { 382 return mToolbar.showOverflowMenu(); 383 } 384 385 @Override 386 public boolean hideOverflowMenu() { 387 return mToolbar.hideOverflowMenu(); 388 } 389 390 @Override 391 public void setMenuPrepared() { 392 mMenuPrepared = true; 393 } 394 395 @Override 396 public void setMenu(Menu menu, MenuPresenter.Callback cb) { 397 if (mActionMenuPresenter == null) { 398 mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext()); 399 mActionMenuPresenter.setId(R.id.action_menu_presenter); 400 } 401 mActionMenuPresenter.setCallback(cb); 402 mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter); 403 } 404 405 @Override 406 public void dismissPopupMenus() { 407 mToolbar.dismissPopupMenus(); 408 } 409 410 @Override 411 public int getDisplayOptions() { 412 return mDisplayOpts; 413 } 414 415 @Override 416 public void setDisplayOptions(int newOpts) { 417 final int oldOpts = mDisplayOpts; 418 final int changed = oldOpts ^ newOpts; 419 mDisplayOpts = newOpts; 420 if (changed != 0) { 421 if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 422 if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 423 mToolbar.setNavigationIcon(mNavIcon); 424 updateHomeAccessibility(); 425 } else { 426 mToolbar.setNavigationIcon(null); 427 } 428 } 429 430 if ((changed & AFFECTS_LOGO_MASK) != 0) { 431 updateToolbarLogo(); 432 } 433 434 if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 435 if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { 436 mToolbar.setTitle(mTitle); 437 mToolbar.setSubtitle(mSubtitle); 438 } else { 439 mToolbar.setTitle(null); 440 mToolbar.setSubtitle(null); 441 } 442 } 443 444 if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) { 445 if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 446 mToolbar.addView(mCustomView); 447 } else { 448 mToolbar.removeView(mCustomView); 449 } 450 } 451 } 452 } 453 454 @Override 455 public void setEmbeddedTabView(ScrollingTabContainerView tabView) { 456 if (mTabView != null && mTabView.getParent() == mToolbar) { 457 mToolbar.removeView(mTabView); 458 } 459 mTabView = tabView; 460 if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { 461 mToolbar.addView(mTabView, 0); 462 Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); 463 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; 464 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 465 lp.gravity = Gravity.START | Gravity.BOTTOM; 466 tabView.setAllowCollapse(true); 467 } 468 } 469 470 @Override 471 public boolean hasEmbeddedTabs() { 472 return mTabView != null; 473 } 474 475 @Override 476 public boolean isTitleTruncated() { 477 return mToolbar.isTitleTruncated(); 478 } 479 480 @Override 481 public void setCollapsible(boolean collapsible) { 482 mToolbar.setCollapsible(collapsible); 483 } 484 485 @Override 486 public void setHomeButtonEnabled(boolean enable) { 487 // Ignore 488 } 489 490 @Override 491 public int getNavigationMode() { 492 return mNavigationMode; 493 } 494 495 @Override 496 public void setNavigationMode(int mode) { 497 final int oldMode = mNavigationMode; 498 if (mode != oldMode) { 499 switch (oldMode) { 500 case ActionBar.NAVIGATION_MODE_LIST: 501 if (mSpinner != null && mSpinner.getParent() == mToolbar) { 502 mToolbar.removeView(mSpinner); 503 } 504 break; 505 case ActionBar.NAVIGATION_MODE_TABS: 506 if (mTabView != null && mTabView.getParent() == mToolbar) { 507 mToolbar.removeView(mTabView); 508 } 509 break; 510 } 511 512 mNavigationMode = mode; 513 514 switch (mode) { 515 case ActionBar.NAVIGATION_MODE_STANDARD: 516 break; 517 case ActionBar.NAVIGATION_MODE_LIST: 518 ensureSpinner(); 519 mToolbar.addView(mSpinner, 0); 520 break; 521 case ActionBar.NAVIGATION_MODE_TABS: 522 if (mTabView != null) { 523 mToolbar.addView(mTabView, 0); 524 Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); 525 lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; 526 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 527 lp.gravity = Gravity.START | Gravity.BOTTOM; 528 } 529 break; 530 default: 531 throw new IllegalArgumentException("Invalid navigation mode " + mode); 532 } 533 } 534 } 535 536 private void ensureSpinner() { 537 if (mSpinner == null) { 538 mSpinner = new SpinnerCompat(getContext(), null, R.attr.actionDropDownStyle); 539 Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 540 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL); 541 mSpinner.setLayoutParams(lp); 542 } 543 } 544 545 @Override 546 public void setDropdownParams(SpinnerAdapter adapter, 547 AdapterViewCompat.OnItemSelectedListener listener) { 548 ensureSpinner(); 549 mSpinner.setAdapter(adapter); 550 mSpinner.setOnItemSelectedListener(listener); 551 } 552 553 @Override 554 public void setDropdownSelectedPosition(int position) { 555 if (mSpinner == null) { 556 throw new IllegalStateException( 557 "Can't set dropdown selected position without an adapter"); 558 } 559 mSpinner.setSelection(position); 560 } 561 562 @Override 563 public int getDropdownSelectedPosition() { 564 return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0; 565 } 566 567 @Override 568 public int getDropdownItemCount() { 569 return mSpinner != null ? mSpinner.getCount() : 0; 570 } 571 572 @Override 573 public void setCustomView(View view) { 574 if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 575 mToolbar.removeView(mCustomView); 576 } 577 mCustomView = view; 578 if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { 579 mToolbar.addView(mCustomView); 580 } 581 } 582 583 @Override 584 public View getCustomView() { 585 return mCustomView; 586 } 587 588 @Override 589 public void animateToVisibility(int visibility) { 590 if (visibility == View.GONE) { 591 ViewCompat.animate(mToolbar).translationY(mToolbar.getHeight()).alpha(0) 592 .setListener(new ViewPropertyAnimatorListenerAdapter() { 593 private boolean mCanceled = false; 594 @Override 595 public void onAnimationEnd(View view) { 596 if (!mCanceled) { 597 mToolbar.setVisibility(View.GONE); 598 } 599 } 600 601 @Override 602 public void onAnimationCancel(View view) { 603 mCanceled = true; 604 } 605 }); 606 } else if (visibility == View.VISIBLE) { 607 ViewCompat.animate(mToolbar).translationY(0).alpha(1) 608 .setListener(new ViewPropertyAnimatorListenerAdapter() { 609 @Override 610 public void onAnimationStart(View view) { 611 mToolbar.setVisibility(View.VISIBLE); 612 } 613 }); 614 } 615 } 616 617 @Override 618 public void setNavigationIcon(Drawable icon) { 619 mNavIcon = icon; 620 if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 621 mToolbar.setNavigationIcon(icon); 622 } 623 } 624 625 @Override 626 public void setNavigationIcon(int resId) { 627 setNavigationIcon(resId != 0 628 ? mTintManager.getDrawable(resId) 629 : null); 630 } 631 632 @Override 633 public void setNavigationContentDescription(CharSequence description) { 634 mHomeDescription = description; 635 updateHomeAccessibility(); 636 } 637 638 @Override 639 public void setNavigationContentDescription(int resId) { 640 setNavigationContentDescription(resId == 0 ? null : getContext().getString(resId)); 641 } 642 643 private void updateHomeAccessibility() { 644 if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { 645 if (TextUtils.isEmpty(mHomeDescription)) { 646 mToolbar.setNavigationContentDescription(mDefaultNavigationContentDescription); 647 } else { 648 mToolbar.setNavigationContentDescription(mHomeDescription); 649 } 650 } 651 } 652 653 @Override 654 public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) { 655 mToolbar.saveHierarchyState(toolbarStates); 656 } 657 658 @Override 659 public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) { 660 mToolbar.restoreHierarchyState(toolbarStates); 661 } 662 663}