MenuItemImpl.java revision b31c3281d870e9abb673db239234d580dcc4feff
1/*
2 * Copyright (C) 2006 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
17package androidx.appcompat.view.menu;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.content.ActivityNotFoundException;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.ColorStateList;
25import android.content.res.Resources;
26import android.graphics.PorterDuff;
27import android.graphics.drawable.Drawable;
28import android.os.Build;
29import androidx.annotation.Nullable;
30import androidx.annotation.RestrictTo;
31import androidx.core.graphics.drawable.DrawableCompat;
32import androidx.core.internal.view.SupportMenuItem;
33import androidx.core.view.ActionProvider;
34import androidx.appcompat.content.res.AppCompatResources;
35import androidx.appcompat.R;
36import android.util.Log;
37import android.view.ContextMenu.ContextMenuInfo;
38import android.view.KeyEvent;
39import android.view.LayoutInflater;
40import android.view.MenuItem;
41import android.view.SubMenu;
42import android.view.View;
43import android.view.ViewConfiguration;
44import android.view.ViewDebug;
45import android.widget.LinearLayout;
46
47/**
48 * @hide
49 */
50@RestrictTo(LIBRARY_GROUP)
51public final class MenuItemImpl implements SupportMenuItem {
52
53    private static final String TAG = "MenuItemImpl";
54
55    private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
56            SHOW_AS_ACTION_IF_ROOM |
57            SHOW_AS_ACTION_ALWAYS;
58
59    private final int mId;
60    private final int mGroup;
61    private final int mCategoryOrder;
62    private final int mOrdering;
63    private CharSequence mTitle;
64    private CharSequence mTitleCondensed;
65    private Intent mIntent;
66    private char mShortcutNumericChar;
67    private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON;
68    private char mShortcutAlphabeticChar;
69    private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON;
70
71    /** The icon's drawable which is only created as needed */
72    private Drawable mIconDrawable;
73
74    /**
75     * The icon's resource ID which is used to get the Drawable when it is
76     * needed (if the Drawable isn't already obtained--only one of the two is
77     * needed).
78     */
79    private int mIconResId = NO_ICON;
80
81    /** The menu to which this item belongs */
82    MenuBuilder mMenu;
83    /** If this item should launch a sub menu, this is the sub menu to launch */
84    private SubMenuBuilder mSubMenu;
85
86    private Runnable mItemCallback;
87    private SupportMenuItem.OnMenuItemClickListener mClickListener;
88
89    private CharSequence mContentDescription;
90    private CharSequence mTooltipText;
91
92    private ColorStateList mIconTintList = null;
93    private PorterDuff.Mode mIconTintMode = null;
94    private boolean mHasIconTint = false;
95    private boolean mHasIconTintMode = false;
96    private boolean mNeedToApplyIconTint = false;
97
98    private int mFlags = ENABLED;
99    private static final int CHECKABLE = 0x00000001;
100    private static final int CHECKED = 0x00000002;
101    private static final int EXCLUSIVE = 0x00000004;
102    private static final int HIDDEN = 0x00000008;
103    private static final int ENABLED = 0x00000010;
104    private static final int IS_ACTION = 0x00000020;
105
106    private int mShowAsAction = SHOW_AS_ACTION_NEVER;
107
108    private View mActionView;
109    private ActionProvider mActionProvider;
110    private MenuItem.OnActionExpandListener mOnActionExpandListener;
111    private boolean mIsActionViewExpanded = false;
112
113    /** Used for the icon resource ID if this item does not have an icon */
114    static final int NO_ICON = 0;
115
116    /**
117     * Current use case is for context menu: Extra information linked to the
118     * View that added this item to the context menu.
119     */
120    private ContextMenuInfo mMenuInfo;
121
122
123    /**
124     * Instantiates this menu item.
125     *
126     * @param menu
127     * @param group Item ordering grouping control. The item will be added after
128     *            all other items whose order is <= this number, and before any
129     *            that are larger than it. This can also be used to define
130     *            groups of items for batch state changes. Normally use 0.
131     * @param id Unique item ID. Use 0 if you do not need a unique ID.
132     * @param categoryOrder The ordering for this item.
133     * @param title The text to display for the item.
134     */
135    MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
136            CharSequence title, int showAsAction) {
137
138        mMenu = menu;
139        mId = id;
140        mGroup = group;
141        mCategoryOrder = categoryOrder;
142        mOrdering = ordering;
143        mTitle = title;
144        mShowAsAction = showAsAction;
145    }
146
147    /**
148     * Invokes the item by calling various listeners or callbacks.
149     *
150     * @return true if the invocation was handled, false otherwise
151     */
152    public boolean invoke() {
153        if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
154            return true;
155        }
156
157        if (mMenu.dispatchMenuItemSelected(mMenu, this)) {
158            return true;
159        }
160
161        if (mItemCallback != null) {
162            mItemCallback.run();
163            return true;
164        }
165
166        if (mIntent != null) {
167            try {
168                mMenu.getContext().startActivity(mIntent);
169                return true;
170            } catch (ActivityNotFoundException e) {
171                Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
172            }
173        }
174
175        if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
176            return true;
177        }
178
179        return false;
180    }
181
182    @Override
183    public boolean isEnabled() {
184        return (mFlags & ENABLED) != 0;
185    }
186
187    @Override
188    public MenuItem setEnabled(boolean enabled) {
189        if (enabled) {
190            mFlags |= ENABLED;
191        } else {
192            mFlags &= ~ENABLED;
193        }
194
195        mMenu.onItemsChanged(false);
196
197        return this;
198    }
199
200    @Override
201    public int getGroupId() {
202        return mGroup;
203    }
204
205    @Override
206    @ViewDebug.CapturedViewProperty
207    public int getItemId() {
208        return mId;
209    }
210
211    @Override
212    public int getOrder() {
213        return mCategoryOrder;
214    }
215
216    public int getOrdering() {
217        return mOrdering;
218    }
219
220    @Override
221    public Intent getIntent() {
222        return mIntent;
223    }
224
225    @Override
226    public MenuItem setIntent(Intent intent) {
227        mIntent = intent;
228        return this;
229    }
230
231    Runnable getCallback() {
232        return mItemCallback;
233    }
234
235    public MenuItem setCallback(Runnable callback) {
236        mItemCallback = callback;
237        return this;
238    }
239
240    @Override
241    public char getAlphabeticShortcut() {
242        return mShortcutAlphabeticChar;
243    }
244
245    @Override
246    public MenuItem setAlphabeticShortcut(char alphaChar) {
247        if (mShortcutAlphabeticChar == alphaChar) {
248            return this;
249        }
250
251        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
252
253        mMenu.onItemsChanged(false);
254
255        return this;
256    }
257
258    @Override
259    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
260        if (mShortcutAlphabeticChar == alphaChar
261                && mShortcutAlphabeticModifiers == alphaModifiers) {
262            return this;
263        }
264
265        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
266        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
267
268        mMenu.onItemsChanged(false);
269        return this;
270    }
271
272    @Override
273    public int getAlphabeticModifiers() {
274        return mShortcutAlphabeticModifiers;
275    }
276
277    @Override
278    public char getNumericShortcut() {
279        return mShortcutNumericChar;
280    }
281
282    @Override
283    public int getNumericModifiers() {
284        return mShortcutNumericModifiers;
285    }
286
287    @Override
288    public MenuItem setNumericShortcut(char numericChar) {
289        if (mShortcutNumericChar == numericChar) {
290            return this;
291        }
292
293        mShortcutNumericChar = numericChar;
294
295        mMenu.onItemsChanged(false);
296
297        return this;
298    }
299
300    @Override
301    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
302        if (mShortcutNumericChar == numericChar && mShortcutNumericModifiers == numericModifiers) {
303            return this;
304        }
305
306        mShortcutNumericChar = numericChar;
307        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
308
309        mMenu.onItemsChanged(false);
310
311        return this;
312    }
313
314    @Override
315    public MenuItem setShortcut(char numericChar, char alphaChar) {
316        mShortcutNumericChar = numericChar;
317        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
318
319        mMenu.onItemsChanged(false);
320
321        return this;
322    }
323
324    @Override
325    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
326            int alphaModifiers) {
327        mShortcutNumericChar = numericChar;
328        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
329        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
330        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
331
332        mMenu.onItemsChanged(false);
333
334        return this;
335    }
336
337    /**
338     * @return The active shortcut (based on QWERTY-mode of the menu).
339     */
340    char getShortcut() {
341        return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar);
342    }
343
344    /**
345     * @return The label to show for the shortcut. This includes the chording key (for example
346     *         'Menu+a'). Also, any non-human readable characters should be human readable (for
347     *         example 'Menu+enter').
348     */
349    String getShortcutLabel() {
350
351        char shortcut = getShortcut();
352        if (shortcut == 0) {
353            return "";
354        }
355
356        Resources res = mMenu.getContext().getResources();
357
358        StringBuilder sb = new StringBuilder();
359        if (ViewConfiguration.get(mMenu.getContext()).hasPermanentMenuKey()) {
360            sb.append(res.getString(R.string.abc_prepend_shortcut_label));
361        }
362
363        final int modifiers =
364                mMenu.isQwertyMode() ? mShortcutAlphabeticModifiers : mShortcutNumericModifiers;
365        appendModifier(sb, modifiers, KeyEvent.META_META_ON,
366                res.getString(R.string.abc_menu_meta_shortcut_label));
367        appendModifier(sb, modifiers, KeyEvent.META_CTRL_ON,
368                res.getString(R.string.abc_menu_ctrl_shortcut_label));
369        appendModifier(sb, modifiers, KeyEvent.META_ALT_ON,
370                res.getString(R.string.abc_menu_alt_shortcut_label));
371        appendModifier(sb, modifiers, KeyEvent.META_SHIFT_ON,
372                res.getString(R.string.abc_menu_shift_shortcut_label));
373        appendModifier(sb, modifiers, KeyEvent.META_SYM_ON,
374                res.getString(R.string.abc_menu_sym_shortcut_label));
375        appendModifier(sb, modifiers, KeyEvent.META_FUNCTION_ON,
376                res.getString(R.string.abc_menu_function_shortcut_label));
377
378        switch (shortcut) {
379
380            case '\n':
381                sb.append(res.getString(R.string.abc_menu_enter_shortcut_label));
382                break;
383
384            case '\b':
385                sb.append(res.getString(R.string.abc_menu_delete_shortcut_label));
386                break;
387
388            case ' ':
389                sb.append(res.getString(R.string.abc_menu_space_shortcut_label));
390                break;
391
392            default:
393                sb.append(shortcut);
394                break;
395        }
396
397        return sb.toString();
398    }
399
400    private static void appendModifier(StringBuilder sb, int modifiers, int flag, String label) {
401        if ((modifiers & flag) == flag) {
402            sb.append(label);
403        }
404    }
405
406    /**
407     * @return Whether this menu item should be showing shortcuts (depends on
408     *         whether the menu should show shortcuts and whether this item has
409     *         a shortcut defined)
410     */
411    boolean shouldShowShortcut() {
412        // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
413        return mMenu.isShortcutsVisible() && (getShortcut() != 0);
414    }
415
416    @Override
417    public SubMenu getSubMenu() {
418        return mSubMenu;
419    }
420
421    @Override
422    public boolean hasSubMenu() {
423        return mSubMenu != null;
424    }
425
426    public void setSubMenu(SubMenuBuilder subMenu) {
427        mSubMenu = subMenu;
428
429        subMenu.setHeaderTitle(getTitle());
430    }
431
432    @Override
433    @ViewDebug.CapturedViewProperty
434    public CharSequence getTitle() {
435        return mTitle;
436    }
437
438    /**
439     * Gets the title for a particular {@link MenuView.ItemView}
440     *
441     * @param itemView The ItemView that is receiving the title
442     * @return Either the title or condensed title based on what the ItemView prefers
443     */
444    CharSequence getTitleForItemView(MenuView.ItemView itemView) {
445        return ((itemView != null) && itemView.prefersCondensedTitle())
446                ? getTitleCondensed()
447                : getTitle();
448    }
449
450    @Override
451    public MenuItem setTitle(CharSequence title) {
452        mTitle = title;
453
454        mMenu.onItemsChanged(false);
455
456        if (mSubMenu != null) {
457            mSubMenu.setHeaderTitle(title);
458        }
459
460        return this;
461    }
462
463    @Override
464    public MenuItem setTitle(int title) {
465        return setTitle(mMenu.getContext().getString(title));
466    }
467
468    @Override
469    public CharSequence getTitleCondensed() {
470        final CharSequence ctitle = mTitleCondensed != null ? mTitleCondensed : mTitle;
471
472        if (Build.VERSION.SDK_INT < 18 && ctitle != null && !(ctitle instanceof String)) {
473            // For devices pre-JB-MR2, where we have a non-String CharSequence, we need to
474            // convert this to a String so that EventLog.writeEvent() does not throw an exception
475            // in Activity.onMenuItemSelected()
476            return ctitle.toString();
477        } else {
478            // Else, we just return the condensed title
479            return ctitle;
480        }
481    }
482
483    @Override
484    public MenuItem setTitleCondensed(CharSequence title) {
485        mTitleCondensed = title;
486
487        // Could use getTitle() in the loop below, but just cache what it would do here
488        if (title == null) {
489            title = mTitle;
490        }
491
492        mMenu.onItemsChanged(false);
493
494        return this;
495    }
496
497    @Override
498    public Drawable getIcon() {
499        if (mIconDrawable != null) {
500            return applyIconTintIfNecessary(mIconDrawable);
501        }
502
503        if (mIconResId != NO_ICON) {
504            Drawable icon = AppCompatResources.getDrawable(mMenu.getContext(), mIconResId);
505            mIconResId = NO_ICON;
506            mIconDrawable = icon;
507            return applyIconTintIfNecessary(icon);
508        }
509
510        return null;
511    }
512
513    @Override
514    public MenuItem setIcon(Drawable icon) {
515        mIconResId = NO_ICON;
516        mIconDrawable = icon;
517        mNeedToApplyIconTint = true;
518        mMenu.onItemsChanged(false);
519
520        return this;
521    }
522
523    @Override
524    public MenuItem setIcon(int iconResId) {
525        mIconDrawable = null;
526        mIconResId = iconResId;
527        mNeedToApplyIconTint = true;
528
529        // If we have a view, we need to push the Drawable to them
530        mMenu.onItemsChanged(false);
531
532        return this;
533    }
534
535
536    @Override
537    public MenuItem setIconTintList(@Nullable ColorStateList iconTintList) {
538        mIconTintList = iconTintList;
539        mHasIconTint = true;
540        mNeedToApplyIconTint = true;
541
542        mMenu.onItemsChanged(false);
543
544        return this;
545    }
546
547    @Override
548    public ColorStateList getIconTintList() {
549        return mIconTintList;
550    }
551
552    @Override
553    public MenuItem setIconTintMode(PorterDuff.Mode iconTintMode) {
554        mIconTintMode = iconTintMode;
555        mHasIconTintMode = true;
556        mNeedToApplyIconTint = true;
557
558        mMenu.onItemsChanged(false);
559
560        return this;
561    }
562
563    @Override
564    public PorterDuff.Mode getIconTintMode() {
565        return mIconTintMode;
566    }
567
568    private Drawable applyIconTintIfNecessary(Drawable icon) {
569        if (icon != null && mNeedToApplyIconTint && (mHasIconTint || mHasIconTintMode)) {
570            icon = DrawableCompat.wrap(icon);
571            icon = icon.mutate();
572
573            if (mHasIconTint) {
574                DrawableCompat.setTintList(icon, mIconTintList);
575            }
576
577            if (mHasIconTintMode) {
578                DrawableCompat.setTintMode(icon, mIconTintMode);
579            }
580
581            mNeedToApplyIconTint = false;
582        }
583
584        return icon;
585    }
586
587    @Override
588    public boolean isCheckable() {
589        return (mFlags & CHECKABLE) == CHECKABLE;
590    }
591
592    @Override
593    public MenuItem setCheckable(boolean checkable) {
594        final int oldFlags = mFlags;
595        mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
596        if (oldFlags != mFlags) {
597            mMenu.onItemsChanged(false);
598        }
599
600        return this;
601    }
602
603    public void setExclusiveCheckable(boolean exclusive) {
604        mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
605    }
606
607    public boolean isExclusiveCheckable() {
608        return (mFlags & EXCLUSIVE) != 0;
609    }
610
611    @Override
612    public boolean isChecked() {
613        return (mFlags & CHECKED) == CHECKED;
614    }
615
616    @Override
617    public MenuItem setChecked(boolean checked) {
618        if ((mFlags & EXCLUSIVE) != 0) {
619            // Call the method on the Menu since it knows about the others in this
620            // exclusive checkable group
621            mMenu.setExclusiveItemChecked(this);
622        } else {
623            setCheckedInt(checked);
624        }
625
626        return this;
627    }
628
629    void setCheckedInt(boolean checked) {
630        final int oldFlags = mFlags;
631        mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
632        if (oldFlags != mFlags) {
633            mMenu.onItemsChanged(false);
634        }
635    }
636
637    @Override
638    public boolean isVisible() {
639        if (mActionProvider != null && mActionProvider.overridesItemVisibility()) {
640            return (mFlags & HIDDEN) == 0 && mActionProvider.isVisible();
641        }
642        return (mFlags & HIDDEN) == 0;
643    }
644
645    /**
646     * Changes the visibility of the item. This method DOES NOT notify the parent menu of a change
647     * in this item, so this should only be called from methods that will eventually trigger this
648     * change.  If unsure, use {@link #setVisible(boolean)} instead.
649     *
650     * @param shown Whether to show (true) or hide (false).
651     * @return Whether the item's shown state was changed
652     */
653    boolean setVisibleInt(boolean shown) {
654        final int oldFlags = mFlags;
655        mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
656        return oldFlags != mFlags;
657    }
658
659    @Override
660    public MenuItem setVisible(boolean shown) {
661        // Try to set the shown state to the given state. If the shown state was changed
662        // (i.e. the previous state isn't the same as given state), notify the parent menu that
663        // the shown state has changed for this item
664        if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this);
665
666        return this;
667    }
668
669    @Override
670    public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) {
671        mClickListener = clickListener;
672        return this;
673    }
674
675    @Override
676    public String toString() {
677        return mTitle != null ? mTitle.toString() : null;
678    }
679
680    void setMenuInfo(ContextMenuInfo menuInfo) {
681        mMenuInfo = menuInfo;
682    }
683
684    @Override
685    public ContextMenuInfo getMenuInfo() {
686        return mMenuInfo;
687    }
688
689    public void actionFormatChanged() {
690        mMenu.onItemActionRequestChanged(this);
691    }
692
693    /**
694     * @return Whether the menu should show icons for menu items.
695     */
696    public boolean shouldShowIcon() {
697        return mMenu.getOptionalIconsVisible();
698    }
699
700    public boolean isActionButton() {
701        return (mFlags & IS_ACTION) == IS_ACTION;
702    }
703
704    public boolean requestsActionButton() {
705        return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
706    }
707
708    public boolean requiresActionButton() {
709        return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
710    }
711
712    public void setIsActionButton(boolean isActionButton) {
713        if (isActionButton) {
714            mFlags |= IS_ACTION;
715        } else {
716            mFlags &= ~IS_ACTION;
717        }
718    }
719
720    public boolean showsTextAsAction() {
721        return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
722    }
723
724    @Override
725    public void setShowAsAction(int actionEnum) {
726        switch (actionEnum & SHOW_AS_ACTION_MASK) {
727            case SHOW_AS_ACTION_ALWAYS:
728            case SHOW_AS_ACTION_IF_ROOM:
729            case SHOW_AS_ACTION_NEVER:
730                // Looks good!
731                break;
732
733            default:
734                // Mutually exclusive options selected!
735                throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
736                        + " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
737        }
738        mShowAsAction = actionEnum;
739        mMenu.onItemActionRequestChanged(this);
740    }
741
742    @Override
743    public SupportMenuItem setActionView(View view) {
744        mActionView = view;
745        mActionProvider = null;
746        if (view != null && view.getId() == View.NO_ID && mId > 0) {
747            view.setId(mId);
748        }
749        mMenu.onItemActionRequestChanged(this);
750        return this;
751    }
752
753    @Override
754    public SupportMenuItem setActionView(int resId) {
755        final Context context = mMenu.getContext();
756        final LayoutInflater inflater = LayoutInflater.from(context);
757        setActionView(inflater.inflate(resId, new LinearLayout(context), false));
758        return this;
759    }
760
761    @Override
762    public View getActionView() {
763        if (mActionView != null) {
764            return mActionView;
765        } else if (mActionProvider != null) {
766            mActionView = mActionProvider.onCreateActionView(this);
767            return mActionView;
768        } else {
769            return null;
770        }
771    }
772
773    @Override
774    public MenuItem setActionProvider(android.view.ActionProvider actionProvider) {
775        throw new UnsupportedOperationException(
776                "This is not supported, use MenuItemCompat.setActionProvider()");
777    }
778
779    @Override
780    public android.view.ActionProvider getActionProvider() {
781        throw new UnsupportedOperationException(
782                "This is not supported, use MenuItemCompat.getActionProvider()");
783    }
784
785    @Override
786    public ActionProvider getSupportActionProvider() {
787        return mActionProvider;
788    }
789
790    @Override
791    public SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) {
792        if (mActionProvider != null) {
793            mActionProvider.reset();
794        }
795        mActionView = null;
796        mActionProvider = actionProvider;
797        mMenu.onItemsChanged(true); // Measurement can be changed
798        if (mActionProvider != null) {
799            mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
800                @Override
801                public void onActionProviderVisibilityChanged(boolean isVisible) {
802                    mMenu.onItemVisibleChanged(MenuItemImpl.this);
803                }
804            });
805        }
806        return this;
807    }
808
809    @Override
810    public SupportMenuItem setShowAsActionFlags(int actionEnum) {
811        setShowAsAction(actionEnum);
812        return this;
813    }
814
815    @Override
816    public boolean expandActionView() {
817        if (!hasCollapsibleActionView()) {
818            return false;
819        }
820
821        if (mOnActionExpandListener == null ||
822                mOnActionExpandListener.onMenuItemActionExpand(this)) {
823            return mMenu.expandItemActionView(this);
824        }
825
826        return false;
827    }
828
829    @Override
830    public boolean collapseActionView() {
831        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) {
832            return false;
833        }
834        if (mActionView == null) {
835            // We're already collapsed if we have no action view.
836            return true;
837        }
838
839        if (mOnActionExpandListener == null ||
840                mOnActionExpandListener.onMenuItemActionCollapse(this)) {
841            return mMenu.collapseItemActionView(this);
842        }
843
844        return false;
845    }
846
847    public boolean hasCollapsibleActionView() {
848        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) {
849            if (mActionView == null && mActionProvider != null) {
850                mActionView = mActionProvider.onCreateActionView(this);
851            }
852            return mActionView != null;
853        }
854        return false;
855    }
856
857    public void setActionViewExpanded(boolean isExpanded) {
858        mIsActionViewExpanded = isExpanded;
859        mMenu.onItemsChanged(false);
860    }
861
862    @Override
863    public boolean isActionViewExpanded() {
864        return mIsActionViewExpanded;
865    }
866
867    @Override
868    public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
869        mOnActionExpandListener = listener;
870        return this;
871    }
872
873    @Override
874    public SupportMenuItem setContentDescription(CharSequence contentDescription) {
875        mContentDescription = contentDescription;
876
877        mMenu.onItemsChanged(false);
878
879        return this;
880    }
881
882    @Override
883    public CharSequence getContentDescription() {
884        return mContentDescription;
885    }
886
887    @Override
888    public SupportMenuItem setTooltipText(CharSequence tooltipText) {
889        mTooltipText = tooltipText;
890
891        mMenu.onItemsChanged(false);
892
893        return this;
894    }
895
896    @Override
897    public CharSequence getTooltipText() {
898        return mTooltipText;
899    }
900}
901