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