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