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