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}