[go: nahoru, domu]

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