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