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