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