[go: nahoru, domu]

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