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