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