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