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