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