ToolbarActionBar.java revision c46125042493e66f7dfdccec24e3b0739600b194
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.app;
19
20import android.annotation.Nullable;
21import android.app.ActionBar;
22import android.content.Context;
23import android.content.res.Configuration;
24import android.graphics.drawable.Drawable;
25import android.text.TextUtils;
26import android.view.ActionMode;
27import android.view.KeyEvent;
28import android.view.LayoutInflater;
29import android.view.Menu;
30import android.view.MenuItem;
31import android.view.View;
32import android.view.Window;
33import android.view.WindowCallbackWrapper;
34import android.widget.SpinnerAdapter;
35import android.widget.Toolbar;
36import com.android.internal.R;
37import com.android.internal.view.menu.MenuBuilder;
38import com.android.internal.view.menu.MenuPresenter;
39import com.android.internal.widget.DecorToolbar;
40import com.android.internal.widget.ToolbarWidgetWrapper;
41
42import java.util.ArrayList;
43
44public class ToolbarActionBar extends ActionBar {
45    private Toolbar mToolbar;
46    private DecorToolbar mDecorToolbar;
47    private boolean mToolbarMenuPrepared;
48    private Window.Callback mWindowCallback;
49    private boolean mMenuCallbackSet;
50
51    private CharSequence mHomeDescription;
52
53    private boolean mLastMenuVisibility;
54    private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
55            new ArrayList<OnMenuVisibilityListener>();
56
57    private final Runnable mMenuInvalidator = new Runnable() {
58        @Override
59        public void run() {
60            populateOptionsMenu();
61        }
62    };
63
64    private final Toolbar.OnMenuItemClickListener mMenuClicker =
65            new Toolbar.OnMenuItemClickListener() {
66        @Override
67        public boolean onMenuItemClick(MenuItem item) {
68            return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
69        }
70    };
71
72    public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
73        mToolbar = toolbar;
74        mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
75        mWindowCallback = new ToolbarCallbackWrapper(windowCallback);
76        mDecorToolbar.setWindowCallback(mWindowCallback);
77        toolbar.setOnMenuItemClickListener(mMenuClicker);
78        mDecorToolbar.setWindowTitle(title);
79        mHomeDescription = mToolbar.getNavigationContentDescription();
80        updateNavDescription();
81    }
82
83    public Window.Callback getWrappedWindowCallback() {
84        return mWindowCallback;
85    }
86
87    @Override
88    public void setCustomView(View view) {
89        setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
90    }
91
92    @Override
93    public void setCustomView(View view, LayoutParams layoutParams) {
94        view.setLayoutParams(layoutParams);
95        mDecorToolbar.setCustomView(view);
96    }
97
98    @Override
99    public void setCustomView(int resId) {
100        final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext());
101        setCustomView(inflater.inflate(resId, mToolbar, false));
102    }
103
104    @Override
105    public void setIcon(int resId) {
106        mDecorToolbar.setIcon(resId);
107    }
108
109    @Override
110    public void setIcon(Drawable icon) {
111        mDecorToolbar.setIcon(icon);
112    }
113
114    @Override
115    public void setLogo(int resId) {
116        mDecorToolbar.setLogo(resId);
117    }
118
119    @Override
120    public void setLogo(Drawable logo) {
121        mDecorToolbar.setLogo(logo);
122    }
123
124    @Override
125    public void setStackedBackgroundDrawable(Drawable d) {
126        // This space for rent (do nothing)
127    }
128
129    @Override
130    public void setSplitBackgroundDrawable(Drawable d) {
131        // This space for rent (do nothing)
132    }
133
134    @Override
135    public void setHomeButtonEnabled(boolean enabled) {
136        // If the nav button on a Toolbar is present, it's enabled. No-op.
137    }
138
139    @Override
140    public void setElevation(float elevation) {
141        mToolbar.setElevation(elevation);
142    }
143
144    @Override
145    public float getElevation() {
146        return mToolbar.getElevation();
147    }
148
149    @Override
150    public Context getThemedContext() {
151        return mToolbar.getContext();
152    }
153
154    @Override
155    public boolean isTitleTruncated() {
156        return super.isTitleTruncated();
157    }
158
159    @Override
160    public void setHomeAsUpIndicator(Drawable indicator) {
161        mToolbar.setNavigationIcon(indicator);
162    }
163
164    @Override
165    public void setHomeAsUpIndicator(int resId) {
166        mToolbar.setNavigationIcon(resId);
167    }
168
169    @Override
170    public void setHomeActionContentDescription(CharSequence description) {
171        mToolbar.setNavigationContentDescription(description);
172        mHomeDescription = description;
173    }
174
175    @Override
176    public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) {
177        // Do nothing
178    }
179
180    @Override
181    public void setHomeActionContentDescription(int resId) {
182        mToolbar.setNavigationContentDescription(resId);
183        mHomeDescription = mToolbar.getNavigationContentDescription();
184    }
185
186    @Override
187    public void setShowHideAnimationEnabled(boolean enabled) {
188        // This space for rent; no-op.
189    }
190
191    @Override
192    public void onConfigurationChanged(Configuration config) {
193        super.onConfigurationChanged(config);
194    }
195
196    @Override
197    public ActionMode startActionMode(ActionMode.Callback callback) {
198        return null;
199    }
200
201    @Override
202    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
203        mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
204    }
205
206    @Override
207    public void setSelectedNavigationItem(int position) {
208        switch (mDecorToolbar.getNavigationMode()) {
209            case NAVIGATION_MODE_LIST:
210                mDecorToolbar.setDropdownSelectedPosition(position);
211                break;
212            default:
213                throw new IllegalStateException(
214                        "setSelectedNavigationIndex not valid for current navigation mode");
215        }
216    }
217
218    @Override
219    public int getSelectedNavigationIndex() {
220        return -1;
221    }
222
223    @Override
224    public int getNavigationItemCount() {
225        return 0;
226    }
227
228    @Override
229    public void setTitle(CharSequence title) {
230        mDecorToolbar.setTitle(title);
231    }
232
233    @Override
234    public void setTitle(int resId) {
235        mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
236    }
237
238    @Override
239    public void setWindowTitle(CharSequence title) {
240        mDecorToolbar.setWindowTitle(title);
241    }
242
243    @Override
244    public void setSubtitle(CharSequence subtitle) {
245        mDecorToolbar.setSubtitle(subtitle);
246    }
247
248    @Override
249    public void setSubtitle(int resId) {
250        mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
251    }
252
253    @Override
254    public void setDisplayOptions(@DisplayOptions int options) {
255        setDisplayOptions(options, 0xffffffff);
256    }
257
258    @Override
259    public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
260        final int currentOptions = mDecorToolbar.getDisplayOptions();
261        final int changed = (options ^ currentOptions) & mask;
262        mDecorToolbar.setDisplayOptions(options & mask | currentOptions & ~mask);
263        if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
264            updateNavDescription();
265        }
266    }
267
268    private void updateNavDescription() {
269        if ((mDecorToolbar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
270            if (TextUtils.isEmpty(mHomeDescription)) {
271                mToolbar.setNavigationContentDescription(R.string.action_bar_up_description);
272            } else {
273                mToolbar.setNavigationContentDescription(mHomeDescription);
274            }
275        }
276    }
277
278    @Override
279    public void setDisplayUseLogoEnabled(boolean useLogo) {
280        setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
281    }
282
283    @Override
284    public void setDisplayShowHomeEnabled(boolean showHome) {
285        setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
286    }
287
288    @Override
289    public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
290        setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
291    }
292
293    @Override
294    public void setDisplayShowTitleEnabled(boolean showTitle) {
295        setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
296    }
297
298    @Override
299    public void setDisplayShowCustomEnabled(boolean showCustom) {
300        setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
301    }
302
303    @Override
304    public void setBackgroundDrawable(@Nullable Drawable d) {
305        mToolbar.setBackground(d);
306    }
307
308    @Override
309    public View getCustomView() {
310        return mDecorToolbar.getCustomView();
311    }
312
313    @Override
314    public CharSequence getTitle() {
315        return mToolbar.getTitle();
316    }
317
318    @Override
319    public CharSequence getSubtitle() {
320        return mToolbar.getSubtitle();
321    }
322
323    @Override
324    public int getNavigationMode() {
325        return NAVIGATION_MODE_STANDARD;
326    }
327
328    @Override
329    public void setNavigationMode(@NavigationMode int mode) {
330        if (mode == ActionBar.NAVIGATION_MODE_TABS) {
331            throw new IllegalArgumentException("Tabs not supported in this configuration");
332        }
333        mDecorToolbar.setNavigationMode(mode);
334    }
335
336    @Override
337    public int getDisplayOptions() {
338        return mDecorToolbar.getDisplayOptions();
339    }
340
341    @Override
342    public Tab newTab() {
343        throw new UnsupportedOperationException(
344                "Tabs are not supported in toolbar action bars");
345    }
346
347    @Override
348    public void addTab(Tab tab) {
349        throw new UnsupportedOperationException(
350                "Tabs are not supported in toolbar action bars");
351    }
352
353    @Override
354    public void addTab(Tab tab, boolean setSelected) {
355        throw new UnsupportedOperationException(
356                "Tabs are not supported in toolbar action bars");
357    }
358
359    @Override
360    public void addTab(Tab tab, int position) {
361        throw new UnsupportedOperationException(
362                "Tabs are not supported in toolbar action bars");
363    }
364
365    @Override
366    public void addTab(Tab tab, int position, boolean setSelected) {
367        throw new UnsupportedOperationException(
368                "Tabs are not supported in toolbar action bars");
369    }
370
371    @Override
372    public void removeTab(Tab tab) {
373        throw new UnsupportedOperationException(
374                "Tabs are not supported in toolbar action bars");
375    }
376
377    @Override
378    public void removeTabAt(int position) {
379        throw new UnsupportedOperationException(
380                "Tabs are not supported in toolbar action bars");
381    }
382
383    @Override
384    public void removeAllTabs() {
385        throw new UnsupportedOperationException(
386                "Tabs are not supported in toolbar action bars");
387    }
388
389    @Override
390    public void selectTab(Tab tab) {
391        throw new UnsupportedOperationException(
392                "Tabs are not supported in toolbar action bars");
393    }
394
395    @Override
396    public Tab getSelectedTab() {
397        throw new UnsupportedOperationException(
398                "Tabs are not supported in toolbar action bars");
399    }
400
401    @Override
402    public Tab getTabAt(int index) {
403        throw new UnsupportedOperationException(
404                "Tabs are not supported in toolbar action bars");
405    }
406
407    @Override
408    public int getTabCount() {
409        return 0;
410    }
411
412    @Override
413    public int getHeight() {
414        return mToolbar.getHeight();
415    }
416
417    @Override
418    public void show() {
419        // TODO: Consider a better transition for this.
420        // Right now use no automatic transition so that the app can supply one if desired.
421        mToolbar.setVisibility(View.VISIBLE);
422    }
423
424    @Override
425    public void hide() {
426        // TODO: Consider a better transition for this.
427        // Right now use no automatic transition so that the app can supply one if desired.
428        mToolbar.setVisibility(View.GONE);
429    }
430
431    @Override
432    public boolean isShowing() {
433        return mToolbar.getVisibility() == View.VISIBLE;
434    }
435
436    @Override
437    public boolean openOptionsMenu() {
438        return mToolbar.showOverflowMenu();
439    }
440
441    @Override
442    public boolean invalidateOptionsMenu() {
443        mToolbar.removeCallbacks(mMenuInvalidator);
444        mToolbar.postOnAnimation(mMenuInvalidator);
445        return true;
446    }
447
448    @Override
449    public boolean collapseActionView() {
450        if (mToolbar.hasExpandedActionView()) {
451            mToolbar.collapseActionView();
452            return true;
453        }
454        return false;
455    }
456
457    void populateOptionsMenu() {
458        if (!mMenuCallbackSet) {
459            mToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback());
460            mMenuCallbackSet = true;
461        }
462        final Menu menu = mToolbar.getMenu();
463        final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
464        if (mb != null) {
465            mb.stopDispatchingItemsChanged();
466        }
467        try {
468            menu.clear();
469            if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) ||
470                    !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
471                menu.clear();
472            }
473        } finally {
474            if (mb != null) {
475                mb.startDispatchingItemsChanged();
476            }
477        }
478    }
479
480    @Override
481    public boolean onMenuKeyEvent(KeyEvent event) {
482        if (event.getAction() == KeyEvent.ACTION_UP) {
483            openOptionsMenu();
484        }
485        return true;
486    }
487
488    public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
489        mMenuVisibilityListeners.add(listener);
490    }
491
492    public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
493        mMenuVisibilityListeners.remove(listener);
494    }
495
496    public void dispatchMenuVisibilityChanged(boolean isVisible) {
497        if (isVisible == mLastMenuVisibility) {
498            return;
499        }
500        mLastMenuVisibility = isVisible;
501
502        final int count = mMenuVisibilityListeners.size();
503        for (int i = 0; i < count; i++) {
504            mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
505        }
506    }
507
508    private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
509        public ToolbarCallbackWrapper(Window.Callback wrapped) {
510            super(wrapped);
511        }
512
513        @Override
514        public boolean onPreparePanel(int featureId, View view, Menu menu) {
515            final boolean result = super.onPreparePanel(featureId, view, menu);
516            if (result && !mToolbarMenuPrepared) {
517                mDecorToolbar.setMenuPrepared();
518                mToolbarMenuPrepared = true;
519            }
520            return result;
521        }
522    }
523
524    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
525        private boolean mClosingActionMenu;
526
527        @Override
528        public boolean onOpenSubMenu(MenuBuilder subMenu) {
529            if (mWindowCallback != null) {
530                mWindowCallback.onMenuOpened(Window.FEATURE_ACTION_BAR, subMenu);
531                return true;
532            }
533            return false;
534        }
535
536        @Override
537        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
538            if (mClosingActionMenu) {
539                return;
540            }
541
542            mClosingActionMenu = true;
543            mToolbar.dismissPopupMenus();
544            if (mWindowCallback != null) {
545                mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu);
546            }
547            mClosingActionMenu = false;
548        }
549    }
550
551    private final class MenuBuilderCallback implements MenuBuilder.Callback {
552
553        @Override
554        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
555            return false;
556        }
557
558        @Override
559        public void onMenuModeChange(MenuBuilder menu) {
560            if (mWindowCallback != null) {
561                if (mToolbar.isOverflowMenuShowing()) {
562                    mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu);
563                } else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL,
564                        null, menu)) {
565                    mWindowCallback.onMenuOpened(Window.FEATURE_ACTION_BAR, menu);
566                }
567            }
568        }
569    }
570}
571