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