ActionMenuView.java revision 04c0d4607897157f01c908475fa0c6d7301cf7e5
1/*
2 * Copyright (C) 2010 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 */
16package android.widget;
17
18import android.content.Context;
19import android.content.res.Configuration;
20import android.util.AttributeSet;
21import android.view.ContextThemeWrapper;
22import android.view.Gravity;
23import android.view.Menu;
24import android.view.MenuItem;
25import android.view.View;
26import android.view.ViewDebug;
27import android.view.ViewGroup;
28import android.view.accessibility.AccessibilityEvent;
29import com.android.internal.view.menu.ActionMenuItemView;
30import com.android.internal.view.menu.MenuBuilder;
31import com.android.internal.view.menu.MenuItemImpl;
32import com.android.internal.view.menu.MenuPresenter;
33import com.android.internal.view.menu.MenuView;
34
35/**
36 * ActionMenuView is a presentation of a series of menu options as a View. It provides
37 * several top level options as action buttons while spilling remaining options over as
38 * items in an overflow menu. This allows applications to present packs of actions inline with
39 * specific or repeating content.
40 */
41public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView {
42    private static final String TAG = "ActionMenuView";
43
44    static final int MIN_CELL_SIZE = 56; // dips
45    static final int GENERATED_ITEM_PADDING = 4; // dips
46
47    private MenuBuilder mMenu;
48
49    /** Context against which to inflate popup menus. */
50    private Context mPopupContext;
51
52    /** Theme resource against which to inflate popup menus. */
53    private int mPopupTheme;
54
55    private boolean mReserveOverflow;
56    private ActionMenuPresenter mPresenter;
57    private MenuPresenter.Callback mActionMenuPresenterCallback;
58    private boolean mFormatItems;
59    private int mFormatItemsWidth;
60    private int mMinCellSize;
61    private int mGeneratedItemPadding;
62
63    private OnMenuItemClickListener mOnMenuItemClickListener;
64
65    public ActionMenuView(Context context) {
66        this(context, null);
67    }
68
69    public ActionMenuView(Context context, AttributeSet attrs) {
70        super(context, attrs);
71        setBaselineAligned(false);
72        final float density = context.getResources().getDisplayMetrics().density;
73        mMinCellSize = (int) (MIN_CELL_SIZE * density);
74        mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
75        mPopupContext = context;
76        mPopupTheme = 0;
77    }
78
79    /**
80     * Specifies the theme to use when inflating popup menus. By default, uses
81     * the same theme as the action menu view itself.
82     *
83     * @param resId theme used to inflate popup menus
84     * @see #getPopupTheme()
85     */
86    public void setPopupTheme(int resId) {
87        if (mPopupTheme != resId) {
88            mPopupTheme = resId;
89            if (resId == 0) {
90                mPopupContext = mContext;
91            } else {
92                mPopupContext = new ContextThemeWrapper(mContext, resId);
93            }
94        }
95    }
96
97    /**
98     * @return resource identifier of the theme used to inflate popup menus, or
99     *         0 if menus are inflated against the action menu view theme
100     * @see #setPopupTheme(int)
101     */
102    public int getPopupTheme() {
103        return mPopupTheme;
104    }
105
106    /**
107     * @param presenter Menu presenter used to display popup menu
108     * @hide
109     */
110    public void setPresenter(ActionMenuPresenter presenter) {
111        mPresenter = presenter;
112        mPresenter.setMenuView(this);
113    }
114
115    @Override
116    public void onConfigurationChanged(Configuration newConfig) {
117        super.onConfigurationChanged(newConfig);
118        mPresenter.updateMenuView(false);
119
120        if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
121            mPresenter.hideOverflowMenu();
122            mPresenter.showOverflowMenu();
123        }
124    }
125
126    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
127        mOnMenuItemClickListener = listener;
128    }
129
130    @Override
131    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
132        // If we've been given an exact size to match, apply special formatting during layout.
133        final boolean wasFormatted = mFormatItems;
134        mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
135
136        if (wasFormatted != mFormatItems) {
137            mFormatItemsWidth = 0; // Reset this when switching modes
138        }
139
140        // Special formatting can change whether items can fit as action buttons.
141        // Kick the menu and update presenters when this changes.
142        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
143        if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
144            mFormatItemsWidth = widthSize;
145            mMenu.onItemsChanged(true);
146        }
147
148        final int childCount = getChildCount();
149        if (mFormatItems && childCount > 0) {
150            onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
151        } else {
152            // Previous measurement at exact format may have set margins - reset them.
153            for (int i = 0; i < childCount; i++) {
154                final View child = getChildAt(i);
155                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
156                lp.leftMargin = lp.rightMargin = 0;
157            }
158            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
159        }
160    }
161
162    private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
163        // We already know the width mode is EXACTLY if we're here.
164        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
165        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
166        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
167
168        final int widthPadding = getPaddingLeft() + getPaddingRight();
169        final int heightPadding = getPaddingTop() + getPaddingBottom();
170
171        final int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding,
172                ViewGroup.LayoutParams.WRAP_CONTENT);
173
174        widthSize -= widthPadding;
175
176        // Divide the view into cells.
177        final int cellCount = widthSize / mMinCellSize;
178        final int cellSizeRemaining = widthSize % mMinCellSize;
179
180        if (cellCount == 0) {
181            // Give up, nothing fits.
182            setMeasuredDimension(widthSize, 0);
183            return;
184        }
185
186        final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
187
188        int cellsRemaining = cellCount;
189        int maxChildHeight = 0;
190        int maxCellsUsed = 0;
191        int expandableItemCount = 0;
192        int visibleItemCount = 0;
193        boolean hasOverflow = false;
194
195        // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
196        long smallestItemsAt = 0;
197
198        final int childCount = getChildCount();
199        for (int i = 0; i < childCount; i++) {
200            final View child = getChildAt(i);
201            if (child.getVisibility() == GONE) continue;
202
203            final boolean isGeneratedItem = child instanceof ActionMenuItemView;
204            visibleItemCount++;
205
206            if (isGeneratedItem) {
207                // Reset padding for generated menu item views; it may change below
208                // and views are recycled.
209                child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
210            }
211
212            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
213            lp.expanded = false;
214            lp.extraPixels = 0;
215            lp.cellsUsed = 0;
216            lp.expandable = false;
217            lp.leftMargin = 0;
218            lp.rightMargin = 0;
219            lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
220
221            // Overflow always gets 1 cell. No more, no less.
222            final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
223
224            final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
225                    itemHeightSpec, heightPadding);
226
227            maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
228            if (lp.expandable) expandableItemCount++;
229            if (lp.isOverflowButton) hasOverflow = true;
230
231            cellsRemaining -= cellsUsed;
232            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
233            if (cellsUsed == 1) smallestItemsAt |= (1 << i);
234        }
235
236        // When we have overflow and a single expanded (text) item, we want to try centering it
237        // visually in the available space even though overflow consumes some of it.
238        final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
239
240        // Divide space for remaining cells if we have items that can expand.
241        // Try distributing whole leftover cells to smaller items first.
242
243        boolean needsExpansion = false;
244        while (expandableItemCount > 0 && cellsRemaining > 0) {
245            int minCells = Integer.MAX_VALUE;
246            long minCellsAt = 0; // Bit locations are indices of relevant child views
247            int minCellsItemCount = 0;
248            for (int i = 0; i < childCount; i++) {
249                final View child = getChildAt(i);
250                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
251
252                // Don't try to expand items that shouldn't.
253                if (!lp.expandable) continue;
254
255                // Mark indices of children that can receive an extra cell.
256                if (lp.cellsUsed < minCells) {
257                    minCells = lp.cellsUsed;
258                    minCellsAt = 1 << i;
259                    minCellsItemCount = 1;
260                } else if (lp.cellsUsed == minCells) {
261                    minCellsAt |= 1 << i;
262                    minCellsItemCount++;
263                }
264            }
265
266            // Items that get expanded will always be in the set of smallest items when we're done.
267            smallestItemsAt |= minCellsAt;
268
269            if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
270
271            // We have enough cells, all minimum size items will be incremented.
272            minCells++;
273
274            for (int i = 0; i < childCount; i++) {
275                final View child = getChildAt(i);
276                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
277                if ((minCellsAt & (1 << i)) == 0) {
278                    // If this item is already at our small item count, mark it for later.
279                    if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
280                    continue;
281                }
282
283                if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
284                    // Add padding to this item such that it centers.
285                    child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
286                }
287                lp.cellsUsed++;
288                lp.expanded = true;
289                cellsRemaining--;
290            }
291
292            needsExpansion = true;
293        }
294
295        // Divide any space left that wouldn't divide along cell boundaries
296        // evenly among the smallest items
297
298        final boolean singleItem = !hasOverflow && visibleItemCount == 1;
299        if (cellsRemaining > 0 && smallestItemsAt != 0 &&
300                (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
301            float expandCount = Long.bitCount(smallestItemsAt);
302
303            if (!singleItem) {
304                // The items at the far edges may only expand by half in order to pin to either side.
305                if ((smallestItemsAt & 1) != 0) {
306                    LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
307                    if (!lp.preventEdgeOffset) expandCount -= 0.5f;
308                }
309                if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
310                    LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
311                    if (!lp.preventEdgeOffset) expandCount -= 0.5f;
312                }
313            }
314
315            final int extraPixels = expandCount > 0 ?
316                    (int) (cellsRemaining * cellSize / expandCount) : 0;
317
318            for (int i = 0; i < childCount; i++) {
319                if ((smallestItemsAt & (1 << i)) == 0) continue;
320
321                final View child = getChildAt(i);
322                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
323                if (child instanceof ActionMenuItemView) {
324                    // If this is one of our views, expand and measure at the larger size.
325                    lp.extraPixels = extraPixels;
326                    lp.expanded = true;
327                    if (i == 0 && !lp.preventEdgeOffset) {
328                        // First item gets part of its new padding pushed out of sight.
329                        // The last item will get this implicitly from layout.
330                        lp.leftMargin = -extraPixels / 2;
331                    }
332                    needsExpansion = true;
333                } else if (lp.isOverflowButton) {
334                    lp.extraPixels = extraPixels;
335                    lp.expanded = true;
336                    lp.rightMargin = -extraPixels / 2;
337                    needsExpansion = true;
338                } else {
339                    // If we don't know what it is, give it some margins instead
340                    // and let it center within its space. We still want to pin
341                    // against the edges.
342                    if (i != 0) {
343                        lp.leftMargin = extraPixels / 2;
344                    }
345                    if (i != childCount - 1) {
346                        lp.rightMargin = extraPixels / 2;
347                    }
348                }
349            }
350
351            cellsRemaining = 0;
352        }
353
354        // Remeasure any items that have had extra space allocated to them.
355        if (needsExpansion) {
356            for (int i = 0; i < childCount; i++) {
357                final View child = getChildAt(i);
358                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
359
360                if (!lp.expanded) continue;
361
362                final int width = lp.cellsUsed * cellSize + lp.extraPixels;
363                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
364                        itemHeightSpec);
365            }
366        }
367
368        if (heightMode != MeasureSpec.EXACTLY) {
369            heightSize = maxChildHeight;
370        }
371
372        setMeasuredDimension(widthSize, heightSize);
373    }
374
375    /**
376     * Measure a child view to fit within cell-based formatting. The child's width
377     * will be measured to a whole multiple of cellSize.
378     *
379     * <p>Sets the expandable and cellsUsed fields of LayoutParams.
380     *
381     * @param child Child to measure
382     * @param cellSize Size of one cell
383     * @param cellsRemaining Number of cells remaining that this view can expand to fill
384     * @param parentHeightMeasureSpec MeasureSpec used by the parent view
385     * @param parentHeightPadding Padding present in the parent view
386     * @return Number of cells this child was measured to occupy
387     */
388    static int measureChildForCells(View child, int cellSize, int cellsRemaining,
389            int parentHeightMeasureSpec, int parentHeightPadding) {
390        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
391
392        final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
393                parentHeightPadding;
394        final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
395        final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
396
397        final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
398                (ActionMenuItemView) child : null;
399        final boolean hasText = itemView != null && itemView.hasText();
400
401        int cellsUsed = 0;
402        if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) {
403            final int childWidthSpec = MeasureSpec.makeMeasureSpec(
404                    cellSize * cellsRemaining, MeasureSpec.AT_MOST);
405            child.measure(childWidthSpec, childHeightSpec);
406
407            final int measuredWidth = child.getMeasuredWidth();
408            cellsUsed = measuredWidth / cellSize;
409            if (measuredWidth % cellSize != 0) cellsUsed++;
410            if (hasText && cellsUsed < 2) cellsUsed = 2;
411        }
412
413        final boolean expandable = !lp.isOverflowButton && hasText;
414        lp.expandable = expandable;
415
416        lp.cellsUsed = cellsUsed;
417        final int targetWidth = cellsUsed * cellSize;
418        child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
419                childHeightSpec);
420        return cellsUsed;
421    }
422
423    @Override
424    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
425        if (!mFormatItems) {
426            super.onLayout(changed, left, top, right, bottom);
427            return;
428        }
429
430        final int childCount = getChildCount();
431        final int midVertical = (top + bottom) / 2;
432        final int dividerWidth = getDividerWidth();
433        int overflowWidth = 0;
434        int nonOverflowWidth = 0;
435        int nonOverflowCount = 0;
436        int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
437        boolean hasOverflow = false;
438        final boolean isLayoutRtl = isLayoutRtl();
439        for (int i = 0; i < childCount; i++) {
440            final View v = getChildAt(i);
441            if (v.getVisibility() == GONE) {
442                continue;
443            }
444
445            LayoutParams p = (LayoutParams) v.getLayoutParams();
446            if (p.isOverflowButton) {
447                overflowWidth = v.getMeasuredWidth();
448                if (hasDividerBeforeChildAt(i)) {
449                    overflowWidth += dividerWidth;
450                }
451
452                int height = v.getMeasuredHeight();
453                int r;
454                int l;
455                if (isLayoutRtl) {
456                    l = getPaddingLeft() + p.leftMargin;
457                    r = l + overflowWidth;
458                } else {
459                    r = getWidth() - getPaddingRight() - p.rightMargin;
460                    l = r - overflowWidth;
461                }
462                int t = midVertical - (height / 2);
463                int b = t + height;
464                v.layout(l, t, r, b);
465
466                widthRemaining -= overflowWidth;
467                hasOverflow = true;
468            } else {
469                final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
470                nonOverflowWidth += size;
471                widthRemaining -= size;
472                if (hasDividerBeforeChildAt(i)) {
473                    nonOverflowWidth += dividerWidth;
474                }
475                nonOverflowCount++;
476            }
477        }
478
479        if (childCount == 1 && !hasOverflow) {
480            // Center a single child
481            final View v = getChildAt(0);
482            final int width = v.getMeasuredWidth();
483            final int height = v.getMeasuredHeight();
484            final int midHorizontal = (right - left) / 2;
485            final int l = midHorizontal - width / 2;
486            final int t = midVertical - height / 2;
487            v.layout(l, t, l + width, t + height);
488            return;
489        }
490
491        final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
492        final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
493
494        if (isLayoutRtl) {
495            int startRight = getWidth() - getPaddingRight();
496            for (int i = 0; i < childCount; i++) {
497                final View v = getChildAt(i);
498                final LayoutParams lp = (LayoutParams) v.getLayoutParams();
499                if (v.getVisibility() == GONE || lp.isOverflowButton) {
500                    continue;
501                }
502
503                startRight -= lp.rightMargin;
504                int width = v.getMeasuredWidth();
505                int height = v.getMeasuredHeight();
506                int t = midVertical - height / 2;
507                v.layout(startRight - width, t, startRight, t + height);
508                startRight -= width + lp.leftMargin + spacerSize;
509            }
510        } else {
511            int startLeft = getPaddingLeft();
512            for (int i = 0; i < childCount; i++) {
513                final View v = getChildAt(i);
514                final LayoutParams lp = (LayoutParams) v.getLayoutParams();
515                if (v.getVisibility() == GONE || lp.isOverflowButton) {
516                    continue;
517                }
518
519                startLeft += lp.leftMargin;
520                int width = v.getMeasuredWidth();
521                int height = v.getMeasuredHeight();
522                int t = midVertical - height / 2;
523                v.layout(startLeft, t, startLeft + width, t + height);
524                startLeft += width + lp.rightMargin + spacerSize;
525            }
526        }
527    }
528
529    @Override
530    public void onDetachedFromWindow() {
531        super.onDetachedFromWindow();
532        dismissPopupMenus();
533    }
534
535    /** @hide */
536    public boolean isOverflowReserved() {
537        return mReserveOverflow;
538    }
539
540    /** @hide */
541    public void setOverflowReserved(boolean reserveOverflow) {
542        mReserveOverflow = reserveOverflow;
543    }
544
545    @Override
546    protected LayoutParams generateDefaultLayoutParams() {
547        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
548                LayoutParams.WRAP_CONTENT);
549        params.gravity = Gravity.CENTER_VERTICAL;
550        return params;
551    }
552
553    @Override
554    public LayoutParams generateLayoutParams(AttributeSet attrs) {
555        return new LayoutParams(getContext(), attrs);
556    }
557
558    @Override
559    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
560        if (p != null) {
561            final LayoutParams result = p instanceof LayoutParams
562                    ? new LayoutParams((LayoutParams) p)
563                    : new LayoutParams(p);
564            if (result.gravity <= Gravity.NO_GRAVITY) {
565                result.gravity = Gravity.CENTER_VERTICAL;
566            }
567            return result;
568        }
569        return generateDefaultLayoutParams();
570    }
571
572    @Override
573    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
574        return p != null && p instanceof LayoutParams;
575    }
576
577    /** @hide */
578    public LayoutParams generateOverflowButtonLayoutParams() {
579        LayoutParams result = generateDefaultLayoutParams();
580        result.isOverflowButton = true;
581        return result;
582    }
583
584    /** @hide */
585    public boolean invokeItem(MenuItemImpl item) {
586        return mMenu.performItemAction(item, 0);
587    }
588
589    /** @hide */
590    public int getWindowAnimations() {
591        return 0;
592    }
593
594    /** @hide */
595    public void initialize(MenuBuilder menu) {
596        mMenu = menu;
597    }
598
599    /**
600     * Returns the Menu object that this ActionMenuView is currently presenting.
601     *
602     * <p>Applications should use this method to obtain the ActionMenuView's Menu object
603     * and inflate or add content to it as necessary.</p>
604     *
605     * @return the Menu presented by this view
606     */
607    public Menu getMenu() {
608        if (mMenu == null) {
609            final Context context = getContext();
610            mMenu = new MenuBuilder(context);
611            mMenu.setCallback(new MenuBuilderCallback());
612            mPresenter = new ActionMenuPresenter(context);
613            mPresenter.setCallback(mActionMenuPresenterCallback != null
614                    ? mActionMenuPresenterCallback : new ActionMenuPresenterCallback());
615            mMenu.addMenuPresenter(mPresenter, mPopupContext);
616            mPresenter.setMenuView(this);
617        }
618
619        return mMenu;
620    }
621
622    /**
623     * Must be called before the first call to getMenu()
624     * @hide
625     */
626    public void setActionMenuPresenterCallback(MenuPresenter.Callback cb) {
627        mActionMenuPresenterCallback = cb;
628    }
629
630    /**
631     * Returns the current menu or null if one has not yet been configured.
632     * @hide Internal use only for action bar integration
633     */
634    public MenuBuilder peekMenu() {
635        return mMenu;
636    }
637
638    /**
639     * Show the overflow items from the associated menu.
640     *
641     * @return true if the menu was able to be shown, false otherwise
642     */
643    public boolean showOverflowMenu() {
644        return mPresenter != null && mPresenter.showOverflowMenu();
645    }
646
647    /**
648     * Hide the overflow items from the associated menu.
649     *
650     * @return true if the menu was able to be hidden, false otherwise
651     */
652    public boolean hideOverflowMenu() {
653        return mPresenter != null && mPresenter.hideOverflowMenu();
654    }
655
656    /**
657     * Check whether the overflow menu is currently showing. This may not reflect
658     * a pending show operation in progress.
659     *
660     * @return true if the overflow menu is currently showing
661     */
662    public boolean isOverflowMenuShowing() {
663        return mPresenter != null && mPresenter.isOverflowMenuShowing();
664    }
665
666    /** @hide */
667    public boolean isOverflowMenuShowPending() {
668        return mPresenter != null && mPresenter.isOverflowMenuShowPending();
669    }
670
671    /**
672     * Dismiss any popups associated with this menu view.
673     */
674    public void dismissPopupMenus() {
675        if (mPresenter != null) {
676            mPresenter.dismissPopupMenus();
677        }
678    }
679
680    /**
681     * @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public.
682     */
683    @Override
684    protected boolean hasDividerBeforeChildAt(int childIndex) {
685        if (childIndex == 0) {
686            return false;
687        }
688        final View childBefore = getChildAt(childIndex - 1);
689        final View child = getChildAt(childIndex);
690        boolean result = false;
691        if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
692            result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
693        }
694        if (childIndex > 0 && child instanceof ActionMenuChildView) {
695            result |= ((ActionMenuChildView) child).needsDividerBefore();
696        }
697        return result;
698    }
699
700    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
701        return false;
702    }
703
704    /** @hide */
705    public void setExpandedActionViewsExclusive(boolean exclusive) {
706        mPresenter.setExpandedActionViewsExclusive(exclusive);
707    }
708
709    /**
710     * Interface responsible for receiving menu item click events if the items themselves
711     * do not have individual item click listeners.
712     */
713    public interface OnMenuItemClickListener {
714        /**
715         * This method will be invoked when a menu item is clicked if the item itself did
716         * not already handle the event.
717         *
718         * @param item {@link MenuItem} that was clicked
719         * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
720         */
721        public boolean onMenuItemClick(MenuItem item);
722    }
723
724    private class MenuBuilderCallback implements MenuBuilder.Callback {
725        @Override
726        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
727            return mOnMenuItemClickListener != null &&
728                    mOnMenuItemClickListener.onMenuItemClick(item);
729        }
730
731        @Override
732        public void onMenuModeChange(MenuBuilder menu) {
733        }
734    }
735
736    private class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback {
737        @Override
738        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
739        }
740
741        @Override
742        public boolean onOpenSubMenu(MenuBuilder subMenu) {
743            return false;
744        }
745    }
746
747    /** @hide */
748    public interface ActionMenuChildView {
749        public boolean needsDividerBefore();
750        public boolean needsDividerAfter();
751    }
752
753    public static class LayoutParams extends LinearLayout.LayoutParams {
754        /** @hide */
755        @ViewDebug.ExportedProperty(category = "layout")
756        public boolean isOverflowButton;
757
758        /** @hide */
759        @ViewDebug.ExportedProperty(category = "layout")
760        public int cellsUsed;
761
762        /** @hide */
763        @ViewDebug.ExportedProperty(category = "layout")
764        public int extraPixels;
765
766        /** @hide */
767        @ViewDebug.ExportedProperty(category = "layout")
768        public boolean expandable;
769
770        /** @hide */
771        @ViewDebug.ExportedProperty(category = "layout")
772        public boolean preventEdgeOffset;
773
774        /** @hide */
775        public boolean expanded;
776
777        public LayoutParams(Context c, AttributeSet attrs) {
778            super(c, attrs);
779        }
780
781        public LayoutParams(ViewGroup.LayoutParams other) {
782            super(other);
783        }
784
785        public LayoutParams(LayoutParams other) {
786            super((LinearLayout.LayoutParams) other);
787            isOverflowButton = other.isOverflowButton;
788        }
789
790        public LayoutParams(int width, int height) {
791            super(width, height);
792            isOverflowButton = false;
793        }
794
795        /** @hide */
796        public LayoutParams(int width, int height, boolean isOverflowButton) {
797            super(width, height);
798            this.isOverflowButton = isOverflowButton;
799        }
800    }
801}
802