OSDN Git Service

c1ae9c24ea4e68125b331d123316c83c7ed2d641
[android-x86/frameworks-base.git] / core / java / com / android / internal / view / menu / ActionMenuPresenter.java
1 /*
2  * Copyright (C) 2011 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 package com.android.internal.view.menu;
18
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.content.res.Resources;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.util.SparseBooleanArray;
25 import android.view.ActionProvider;
26 import android.view.MenuItem;
27 import android.view.MotionEvent;
28 import android.view.SoundEffectConstants;
29 import android.view.View;
30 import android.view.ViewConfiguration;
31 import android.view.View.MeasureSpec;
32 import android.view.accessibility.AccessibilityNodeInfo;
33 import android.view.ViewGroup;
34 import android.widget.ImageButton;
35 import android.widget.ListPopupWindow;
36 import android.widget.ListPopupWindow.ForwardingListener;
37
38 import com.android.internal.view.ActionBarPolicy;
39 import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
40
41 import java.util.ArrayList;
42
43 /**
44  * MenuPresenter for building action menus as seen in the action bar and action modes.
45  */
46 public class ActionMenuPresenter extends BaseMenuPresenter
47         implements ActionProvider.SubUiVisibilityListener {
48     private static final String TAG = "ActionMenuPresenter";
49
50     private View mOverflowButton;
51     private boolean mReserveOverflow;
52     private boolean mReserveOverflowSet;
53     private int mWidthLimit;
54     private int mActionItemWidthLimit;
55     private int mMaxItems;
56     private boolean mMaxItemsSet;
57     private boolean mStrictWidthLimit;
58     private boolean mWidthLimitSet;
59     private boolean mExpandedActionViewsExclusive;
60
61     private int mMinCellSize;
62
63     // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
64     private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
65
66     private View mScrapActionButtonView;
67
68     private OverflowPopup mOverflowPopup;
69     private ActionButtonSubmenu mActionButtonPopup;
70
71     private OpenOverflowRunnable mPostedOpenRunnable;
72
73     final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
74     int mOpenSubMenuId;
75
76     public ActionMenuPresenter(Context context) {
77         super(context, com.android.internal.R.layout.action_menu_layout,
78                 com.android.internal.R.layout.action_menu_item_layout);
79     }
80
81     @Override
82     public void initForMenu(Context context, MenuBuilder menu) {
83         super.initForMenu(context, menu);
84
85         final Resources res = context.getResources();
86
87         final ActionBarPolicy abp = ActionBarPolicy.get(context);
88         if (!mReserveOverflowSet) {
89             mReserveOverflow = abp.showsOverflowMenuButton();
90         }
91
92         if (!mWidthLimitSet) {
93             mWidthLimit = abp.getEmbeddedMenuWidthLimit();
94         }
95
96         // Measure for initial configuration
97         if (!mMaxItemsSet) {
98             mMaxItems = abp.getMaxActionButtons();
99         }
100
101         int width = mWidthLimit;
102         if (mReserveOverflow) {
103             if (mOverflowButton == null) {
104                 mOverflowButton = new OverflowMenuButton(mSystemContext);
105                 final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
106                 mOverflowButton.measure(spec, spec);
107             }
108             width -= mOverflowButton.getMeasuredWidth();
109         } else {
110             mOverflowButton = null;
111         }
112
113         mActionItemWidthLimit = width;
114
115         mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
116
117         // Drop a scrap view as it may no longer reflect the proper context/config.
118         mScrapActionButtonView = null;
119     }
120
121     public void onConfigurationChanged(Configuration newConfig) {
122         if (!mMaxItemsSet) {
123             mMaxItems = mContext.getResources().getInteger(
124                     com.android.internal.R.integer.max_action_buttons);
125         }
126         if (mMenu != null) {
127             mMenu.onItemsChanged(true);
128         }
129     }
130
131     public void setWidthLimit(int width, boolean strict) {
132         mWidthLimit = width;
133         mStrictWidthLimit = strict;
134         mWidthLimitSet = true;
135     }
136
137     public void setReserveOverflow(boolean reserveOverflow) {
138         mReserveOverflow = reserveOverflow;
139         mReserveOverflowSet = true;
140     }
141
142     public void setItemLimit(int itemCount) {
143         mMaxItems = itemCount;
144         mMaxItemsSet = true;
145     }
146
147     public void setExpandedActionViewsExclusive(boolean isExclusive) {
148         mExpandedActionViewsExclusive = isExclusive;
149     }
150
151     @Override
152     public MenuView getMenuView(ViewGroup root) {
153         MenuView result = super.getMenuView(root);
154         ((ActionMenuView) result).setPresenter(this);
155         return result;
156     }
157
158     @Override
159     public View getItemView(final MenuItemImpl item, View convertView, ViewGroup parent) {
160         View actionView = item.getActionView();
161         if (actionView == null || item.hasCollapsibleActionView()) {
162             if (!(convertView instanceof ActionMenuItemView)) {
163                 convertView = null;
164             }
165             actionView = super.getItemView(item, convertView, parent);
166         }
167         actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
168
169         if (item.hasSubMenu()) {
170             actionView.setOnTouchListener(new ForwardingListener(actionView) {
171                 @Override
172                 public ListPopupWindow getPopup() {
173                     return mActionButtonPopup != null ? mActionButtonPopup.getPopup() : null;
174                 }
175
176                 @Override
177                 protected boolean onForwardingStarted() {
178                     return onSubMenuSelected((SubMenuBuilder) item.getSubMenu());
179                 }
180
181                 @Override
182                 protected boolean onForwardingStopped() {
183                     return dismissPopupMenus();
184                 }
185             });
186         } else {
187             actionView.setOnTouchListener(null);
188         }
189
190         final ActionMenuView menuParent = (ActionMenuView) parent;
191         final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
192         if (!menuParent.checkLayoutParams(lp)) {
193             actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
194         }
195         return actionView;
196     }
197
198     @Override
199     public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
200         itemView.initialize(item, 0);
201
202         final ActionMenuView menuView = (ActionMenuView) mMenuView;
203         ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
204         actionItemView.setItemInvoker(menuView);
205     }
206
207     @Override
208     public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
209         return item.isActionButton();
210     }
211
212     @Override
213     public void updateMenuView(boolean cleared) {
214         super.updateMenuView(cleared);
215
216         if (mMenu != null) {
217             final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems();
218             final int count = actionItems.size();
219             for (int i = 0; i < count; i++) {
220                 final ActionProvider provider = actionItems.get(i).getActionProvider();
221                 if (provider != null) {
222                     provider.setSubUiVisibilityListener(this);
223                 }
224             }
225         }
226
227         final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
228                 mMenu.getNonActionItems() : null;
229
230         boolean hasOverflow = false;
231         if (mReserveOverflow && nonActionItems != null) {
232             final int count = nonActionItems.size();
233             if (count == 1) {
234                 hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
235             } else {
236                 hasOverflow = count > 0;
237             }
238         }
239
240         if (hasOverflow) {
241             if (mOverflowButton == null) {
242                 mOverflowButton = new OverflowMenuButton(mSystemContext);
243             }
244             ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
245             if (parent != mMenuView) {
246                 if (parent != null) {
247                     parent.removeView(mOverflowButton);
248                 }
249                 ActionMenuView menuView = (ActionMenuView) mMenuView;
250                 menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
251             }
252         } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
253             ((ViewGroup) mMenuView).removeView(mOverflowButton);
254         }
255
256         ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
257     }
258
259     @Override
260     public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
261         if (parent.getChildAt(childIndex) == mOverflowButton) return false;
262         return super.filterLeftoverView(parent, childIndex);
263     }
264
265     public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
266         if (!subMenu.hasVisibleItems()) return false;
267
268         SubMenuBuilder topSubMenu = subMenu;
269         while (topSubMenu.getParentMenu() != mMenu) {
270             topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
271         }
272         View anchor = findViewForItem(topSubMenu.getItem());
273         if (anchor == null) {
274             if (mOverflowButton == null) return false;
275             anchor = mOverflowButton;
276         }
277
278         mOpenSubMenuId = subMenu.getItem().getItemId();
279         mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
280         mActionButtonPopup.setAnchorView(anchor);
281         mActionButtonPopup.show();
282         super.onSubMenuSelected(subMenu);
283         return true;
284     }
285
286     private View findViewForItem(MenuItem item) {
287         final ViewGroup parent = (ViewGroup) mMenuView;
288         if (parent == null) return null;
289
290         final int count = parent.getChildCount();
291         for (int i = 0; i < count; i++) {
292             final View child = parent.getChildAt(i);
293             if (child instanceof MenuView.ItemView &&
294                     ((MenuView.ItemView) child).getItemData() == item) {
295                 return child;
296             }
297         }
298         return null;
299     }
300
301     /**
302      * Display the overflow menu if one is present.
303      * @return true if the overflow menu was shown, false otherwise.
304      */
305     public boolean showOverflowMenu() {
306         if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
307                 mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
308             OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
309             mPostedOpenRunnable = new OpenOverflowRunnable(popup);
310             // Post this for later; we might still need a layout for the anchor to be right.
311             ((View) mMenuView).post(mPostedOpenRunnable);
312
313             // ActionMenuPresenter uses null as a callback argument here
314             // to indicate overflow is opening.
315             super.onSubMenuSelected(null);
316
317             return true;
318         }
319         return false;
320     }
321
322     /**
323      * Hide the overflow menu if it is currently showing.
324      *
325      * @return true if the overflow menu was hidden, false otherwise.
326      */
327     public boolean hideOverflowMenu() {
328         if (mPostedOpenRunnable != null && mMenuView != null) {
329             ((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
330             mPostedOpenRunnable = null;
331             return true;
332         }
333
334         MenuPopupHelper popup = mOverflowPopup;
335         if (popup != null) {
336             popup.dismiss();
337             return true;
338         }
339         return false;
340     }
341
342     /**
343      * Dismiss all popup menus - overflow and submenus.
344      * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
345      */
346     public boolean dismissPopupMenus() {
347         boolean result = hideOverflowMenu();
348         result |= hideSubMenus();
349         return result;
350     }
351
352     /**
353      * Dismiss all submenu popups.
354      *
355      * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
356      */
357     public boolean hideSubMenus() {
358         if (mActionButtonPopup != null) {
359             mActionButtonPopup.dismiss();
360             return true;
361         }
362         return false;
363     }
364
365     /**
366      * @return true if the overflow menu is currently showing
367      */
368     public boolean isOverflowMenuShowing() {
369         return mOverflowPopup != null && mOverflowPopup.isShowing();
370     }
371
372     /**
373      * @return true if space has been reserved in the action menu for an overflow item.
374      */
375     public boolean isOverflowReserved() {
376         return mReserveOverflow;
377     }
378
379     public boolean flagActionItems() {
380         final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
381         final int itemsSize = visibleItems.size();
382         int maxActions = mMaxItems;
383         int widthLimit = mActionItemWidthLimit;
384         final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
385         final ViewGroup parent = (ViewGroup) mMenuView;
386
387         int requiredItems = 0;
388         int requestedItems = 0;
389         int firstActionWidth = 0;
390         boolean hasOverflow = false;
391         for (int i = 0; i < itemsSize; i++) {
392             MenuItemImpl item = visibleItems.get(i);
393             if (item.requiresActionButton()) {
394                 requiredItems++;
395             } else if (item.requestsActionButton()) {
396                 requestedItems++;
397             } else {
398                 hasOverflow = true;
399             }
400             if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
401                 // Overflow everything if we have an expanded action view and we're
402                 // space constrained.
403                 maxActions = 0;
404             }
405         }
406
407         // Reserve a spot for the overflow item if needed.
408         if (mReserveOverflow &&
409                 (hasOverflow || requiredItems + requestedItems > maxActions)) {
410             maxActions--;
411         }
412         maxActions -= requiredItems;
413
414         final SparseBooleanArray seenGroups = mActionButtonGroups;
415         seenGroups.clear();
416
417         int cellSize = 0;
418         int cellsRemaining = 0;
419         if (mStrictWidthLimit) {
420             cellsRemaining = widthLimit / mMinCellSize;
421             final int cellSizeRemaining = widthLimit % mMinCellSize;
422             cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
423         }
424
425         // Flag as many more requested items as will fit.
426         for (int i = 0; i < itemsSize; i++) {
427             MenuItemImpl item = visibleItems.get(i);
428
429             if (item.requiresActionButton()) {
430                 View v = getItemView(item, mScrapActionButtonView, parent);
431                 if (mScrapActionButtonView == null) {
432                     mScrapActionButtonView = v;
433                 }
434                 if (mStrictWidthLimit) {
435                     cellsRemaining -= ActionMenuView.measureChildForCells(v,
436                             cellSize, cellsRemaining, querySpec, 0);
437                 } else {
438                     v.measure(querySpec, querySpec);
439                 }
440                 final int measuredWidth = v.getMeasuredWidth();
441                 widthLimit -= measuredWidth;
442                 if (firstActionWidth == 0) {
443                     firstActionWidth = measuredWidth;
444                 }
445                 final int groupId = item.getGroupId();
446                 if (groupId != 0) {
447                     seenGroups.put(groupId, true);
448                 }
449                 item.setIsActionButton(true);
450             } else if (item.requestsActionButton()) {
451                 // Items in a group with other items that already have an action slot
452                 // can break the max actions rule, but not the width limit.
453                 final int groupId = item.getGroupId();
454                 final boolean inGroup = seenGroups.get(groupId);
455                 boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
456                         (!mStrictWidthLimit || cellsRemaining > 0);
457
458                 if (isAction) {
459                     View v = getItemView(item, mScrapActionButtonView, parent);
460                     if (mScrapActionButtonView == null) {
461                         mScrapActionButtonView = v;
462                     }
463                     if (mStrictWidthLimit) {
464                         final int cells = ActionMenuView.measureChildForCells(v,
465                                 cellSize, cellsRemaining, querySpec, 0);
466                         cellsRemaining -= cells;
467                         if (cells == 0) {
468                             isAction = false;
469                         }
470                     } else {
471                         v.measure(querySpec, querySpec);
472                     }
473                     final int measuredWidth = v.getMeasuredWidth();
474                     widthLimit -= measuredWidth;
475                     if (firstActionWidth == 0) {
476                         firstActionWidth = measuredWidth;
477                     }
478
479                     if (mStrictWidthLimit) {
480                         isAction &= widthLimit >= 0;
481                     } else {
482                         // Did this push the entire first item past the limit?
483                         isAction &= widthLimit + firstActionWidth > 0;
484                     }
485                 }
486
487                 if (isAction && groupId != 0) {
488                     seenGroups.put(groupId, true);
489                 } else if (inGroup) {
490                     // We broke the width limit. Demote the whole group, they all overflow now.
491                     seenGroups.put(groupId, false);
492                     for (int j = 0; j < i; j++) {
493                         MenuItemImpl areYouMyGroupie = visibleItems.get(j);
494                         if (areYouMyGroupie.getGroupId() == groupId) {
495                             // Give back the action slot
496                             if (areYouMyGroupie.isActionButton()) maxActions++;
497                             areYouMyGroupie.setIsActionButton(false);
498                         }
499                     }
500                 }
501
502                 if (isAction) maxActions--;
503
504                 item.setIsActionButton(isAction);
505             } else {
506                 // Neither requires nor requests an action button.
507                 item.setIsActionButton(false);
508             }
509         }
510         return true;
511     }
512
513     @Override
514     public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
515         dismissPopupMenus();
516         super.onCloseMenu(menu, allMenusAreClosing);
517     }
518
519     @Override
520     public Parcelable onSaveInstanceState() {
521         SavedState state = new SavedState();
522         state.openSubMenuId = mOpenSubMenuId;
523         return state;
524     }
525
526     @Override
527     public void onRestoreInstanceState(Parcelable state) {
528         SavedState saved = (SavedState) state;
529         if (saved.openSubMenuId > 0) {
530             MenuItem item = mMenu.findItem(saved.openSubMenuId);
531             if (item != null) {
532                 SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
533                 onSubMenuSelected(subMenu);
534             }
535         }
536     }
537
538     @Override
539     public void onSubUiVisibilityChanged(boolean isVisible) {
540         if (isVisible) {
541             // Not a submenu, but treat it like one.
542             super.onSubMenuSelected(null);
543         } else {
544             mMenu.close(false);
545         }
546     }
547
548     private static class SavedState implements Parcelable {
549         public int openSubMenuId;
550
551         SavedState() {
552         }
553
554         SavedState(Parcel in) {
555             openSubMenuId = in.readInt();
556         }
557
558         @Override
559         public int describeContents() {
560             return 0;
561         }
562
563         @Override
564         public void writeToParcel(Parcel dest, int flags) {
565             dest.writeInt(openSubMenuId);
566         }
567
568         public static final Parcelable.Creator<SavedState> CREATOR
569                 = new Parcelable.Creator<SavedState>() {
570             public SavedState createFromParcel(Parcel in) {
571                 return new SavedState(in);
572             }
573
574             public SavedState[] newArray(int size) {
575                 return new SavedState[size];
576             }
577         };
578     }
579
580     private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
581         public OverflowMenuButton(Context context) {
582             super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
583
584             setClickable(true);
585             setFocusable(true);
586             setVisibility(VISIBLE);
587             setEnabled(true);
588
589             setOnTouchListener(new ForwardingListener(this) {
590                 @Override
591                 public ListPopupWindow getPopup() {
592                     if (mOverflowPopup == null) {
593                         return null;
594                     }
595
596                     return mOverflowPopup.getPopup();
597                 }
598
599                 @Override
600                 public boolean onForwardingStarted() {
601                     showOverflowMenu();
602                     return true;
603                 }
604
605                 @Override
606                 public boolean onForwardingStopped() {
607                     // Displaying the popup occurs asynchronously, so wait for
608                     // the runnable to finish before deciding whether to stop
609                     // forwarding.
610                     if (mPostedOpenRunnable != null) {
611                         return false;
612                     }
613
614                     hideOverflowMenu();
615                     return true;
616                 }
617             });
618         }
619
620         @Override
621         public boolean performClick() {
622             if (super.performClick()) {
623                 return true;
624             }
625
626             playSoundEffect(SoundEffectConstants.CLICK);
627             showOverflowMenu();
628             return true;
629         }
630
631         @Override
632         public boolean needsDividerBefore() {
633             return false;
634         }
635
636         @Override
637         public boolean needsDividerAfter() {
638             return false;
639         }
640
641         @Override
642         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
643             if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
644                 // Fill available height
645                 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
646                         MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
647             }
648             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
649         }
650
651         @Override
652         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
653             super.onInitializeAccessibilityNodeInfo(info);
654             info.setCanOpenPopup(true);
655         }
656     }
657
658     private class OverflowPopup extends MenuPopupHelper {
659         public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
660                 boolean overflowOnly) {
661             super(context, menu, anchorView, overflowOnly);
662             setCallback(mPopupPresenterCallback);
663         }
664
665         @Override
666         public void onDismiss() {
667             super.onDismiss();
668             mMenu.close();
669             mOverflowPopup = null;
670         }
671     }
672
673     private class ActionButtonSubmenu extends MenuPopupHelper {
674         private SubMenuBuilder mSubMenu;
675
676         public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
677             super(context, subMenu);
678             mSubMenu = subMenu;
679
680             MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
681             if (!item.isActionButton()) {
682                 // Give a reasonable anchor to nested submenus.
683                 setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
684             }
685
686             setCallback(mPopupPresenterCallback);
687
688             boolean preserveIconSpacing = false;
689             final int count = subMenu.size();
690             for (int i = 0; i < count; i++) {
691                 MenuItem childItem = subMenu.getItem(i);
692                 if (childItem.isVisible() && childItem.getIcon() != null) {
693                     preserveIconSpacing = true;
694                     break;
695                 }
696             }
697             setForceShowIcon(preserveIconSpacing);
698         }
699
700         @Override
701         public void onDismiss() {
702             super.onDismiss();
703             mActionButtonPopup = null;
704             mOpenSubMenuId = 0;
705         }
706     }
707
708     private class PopupPresenterCallback implements MenuPresenter.Callback {
709
710         @Override
711         public boolean onOpenSubMenu(MenuBuilder subMenu) {
712             if (subMenu == null) return false;
713
714             mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
715             return false;
716         }
717
718         @Override
719         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
720             if (menu instanceof SubMenuBuilder) {
721                 ((SubMenuBuilder) menu).getRootMenu().close(false);
722             }
723         }
724     }
725
726     private class OpenOverflowRunnable implements Runnable {
727         private OverflowPopup mPopup;
728
729         public OpenOverflowRunnable(OverflowPopup popup) {
730             mPopup = popup;
731         }
732
733         public void run() {
734             mMenu.changeMenuMode();
735             final View menuView = (View) mMenuView;
736             if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
737                 mOverflowPopup = mPopup;
738             }
739             mPostedOpenRunnable = null;
740         }
741     }
742 }