Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 250fd971 authored by Adam Powell's avatar Adam Powell Committed by Android (Google) Code Review
Browse files

Merge "Overflow menu for action bars."

parents 11811612 8028dd32
Loading
Loading
Loading
Loading
+58 −1
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@
package com.android.internal.view.menu;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;

import java.util.ArrayList;
@@ -35,6 +37,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
    private int mItemPadding;
    private int mItemMargin;
    private int mMaxItems;
    private boolean mReserveOverflow;
    private OverflowMenuButton mOverflowButton;
    
    public ActionMenuView(Context context) {
        this(context, null);
@@ -56,6 +60,15 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
        final int itemSpace = size + mItemPadding;
        
        mMaxItems = spaceAvailable / (itemSpace > 0 ? itemSpace : 1);

        // TODO There has to be a better way to indicate that we don't have a hard menu key.
        final int screen = res.getConfiguration().screenLayout;
        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
                Configuration.SCREENLAYOUT_SIZE_XLARGE;
    }

    public boolean isOverflowReserved() {
        return mReserveOverflow;
    }
    
    @Override
@@ -101,9 +114,10 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
    }

    public void updateChildren(boolean cleared) {
        final boolean reserveOverflow = mReserveOverflow;
        removeAllViews();
        
        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems();
        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(reserveOverflow);
        final int itemCount = itemsToShow.size();
        
        for (int i = 0; i < itemCount; i++) {
@@ -111,10 +125,53 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
            addItemView((ActionMenuItemView) itemData.getItemView(MenuBuilder.TYPE_ACTION_BUTTON,
                    this));
        }

        if (reserveOverflow) {
            if (mMenu.getNonActionItems(true).size() > 0) {
                OverflowMenuButton button = new OverflowMenuButton(mContext);
                addView(button);
                mOverflowButton = button;
            } else {
                mOverflowButton = null;
            }
        }
    }

    public boolean showOverflowMenu() {
        if (mOverflowButton != null) {
            MenuPopupHelper popup = new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
            popup.show();
            return true;
        }
        return false;
    }

    private void addItemView(ActionMenuItemView view) {
        view.setItemInvoker(this);
        addView(view);
    }

    private class OverflowMenuButton extends ImageButton {
        public OverflowMenuButton(Context context) {
            super(context, null, com.android.internal.R.attr.actionButtonStyle);

            final Resources res = context.getResources();
            setClickable(true);
            setFocusable(true);
            // TODO setTitle() to a localized string for accessibility
            setImageDrawable(res.getDrawable(com.android.internal.R.drawable.ic_menu_more));
            setVisibility(VISIBLE);
            setEnabled(true);
        }

        @Override
        public boolean performClick() {
            if (super.performClick()) {
                return true;
            }

            showOverflowMenu();
            return true;
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -337,7 +337,9 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
        // This method does a clear refresh of children
        removeAllViews();
        
        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getNonActionItems();
        // IconMenuView never wants content sorted for an overflow action button, since
        // it is never used in the presence of an overflow button.
        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getNonActionItems(false);
        final int numItems = itemsToShow.size();
        final int numItemsThatCanFit = mMaxItems;
        // Minimum of the num that can fit and the num that we have
+73 −11
Original line number Diff line number Diff line
@@ -164,6 +164,12 @@ public class MenuBuilder implements Menu {
     */
    private boolean mIsActionItemsStale;

    /**
     * Whether the process of granting space as action items should reserve a space for
     * an overflow option in the action list.
     */
    private boolean mReserveActionOverflow;

    /**
     * Current use case is Context Menus: As Views populate the context menu, each one has
     * extra information that should be passed along.  This is the current menu info that
@@ -670,6 +676,11 @@ public class MenuBuilder implements Menu {
        return mItems.get(index);
    }

    public MenuItem getOverflowItem(int index) {
        flagActionItems(true);
        return mNonActionItems.get(index);
    }

    public boolean isShortcutKey(int keyCode, KeyEvent event) {
        return findItemWithShortcutForKey(keyCode, event) != null;
    }
@@ -986,7 +997,12 @@ public class MenuBuilder implements Menu {
        return mVisibleItems;
    }
    
    private void flagActionItems() {
    private void flagActionItems(boolean reserveActionOverflow) {
        if (reserveActionOverflow != mReserveActionOverflow) {
            mReserveActionOverflow = reserveActionOverflow;
            mIsActionItemsStale = true;
        }

        if (!mIsActionItemsStale) {
            return;
        }
@@ -995,13 +1011,27 @@ public class MenuBuilder implements Menu {
        final int itemsSize = visibleItems.size();
        int maxActions = mMaxActionItems;

        int requiredItems = 0;
        int requestedItems = 0;
        boolean hasOverflow = false;
        for (int i = 0; i < itemsSize; i++) {
            MenuItemImpl item = visibleItems.get(i);
            if (item.requiresActionButton()) {
                maxActions--;
                requiredItems++;
            } else if (item.requestsActionButton()) {
                requestedItems++;
            } else {
                hasOverflow = true;
            }
        }

        // Reserve a spot for the overflow item if needed.
        if (reserveActionOverflow &&
                (hasOverflow || requiredItems + requestedItems > maxActions)) {
            maxActions--;
        }
        maxActions -= requiredItems;

        // Flag as many more requested items as will fit.
        for (int i = 0; i < itemsSize; i++) {
            MenuItemImpl item = visibleItems.get(i);
@@ -1025,13 +1055,13 @@ public class MenuBuilder implements Menu {
        mIsActionItemsStale = false;
    }
    
    ArrayList<MenuItemImpl> getActionItems() {
        flagActionItems();
    ArrayList<MenuItemImpl> getActionItems(boolean reserveActionOverflow) {
        flagActionItems(reserveActionOverflow);
        return mActionItems;
    }
    
    ArrayList<MenuItemImpl> getNonActionItems() {
        flagActionItems();
    ArrayList<MenuItemImpl> getNonActionItems(boolean reserveActionOverflow) {
        flagActionItems(reserveActionOverflow);
        return mNonActionItems;
    }
    
@@ -1180,6 +1210,16 @@ public class MenuBuilder implements Menu {
        return new MenuAdapter(menuType);
    }

    /**
     * Gets an adapter for providing overflow (non-action) items and their views.
     *
     * @param menuType The type of menu to get an adapter for.
     * @return A {@link MenuAdapter} for this menu with the given menu type.
     */
    public MenuAdapter getOverflowMenuAdapter(int menuType) {
        return new OverflowMenuAdapter(menuType);
    }

    void setOptionalIconsVisible(boolean visible) {
        mOptionalIconsVisible = visible;
    }
@@ -1271,6 +1311,28 @@ public class MenuBuilder implements Menu {
                return item.getItemView(mMenuType, parent);
            }
        }
    }

    /**
     * An adapter that allows an {@link AdapterView} to use this {@link MenuBuilder} as a data
     * source for overflow menu items that do not fit in the list of action items.
     */
    private class OverflowMenuAdapter extends MenuAdapter {
        private ArrayList<MenuItemImpl> mOverflowItems;

        public OverflowMenuAdapter(int menuType) {
            super(menuType);
            mOverflowItems = getNonActionItems(true);
        }

        @Override
        public MenuItemImpl getItem(int position) {
            return mOverflowItems.get(position);
        }

        @Override
        public int getCount() {
            return mOverflowItems.size();
        }
    }
}
+54 −9
Original line number Diff line number Diff line
@@ -20,28 +20,48 @@ import com.android.internal.view.menu.MenuBuilder.MenuAdapter;

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.View.MeasureSpec;
import android.widget.AdapterView;
import android.widget.ListPopupWindow;

import java.lang.ref.WeakReference;

/**
 * @hide
 */
public class MenuPopupHelper implements AdapterView.OnItemClickListener {
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener {
    private static final String TAG = "MenuPopupHelper";

    private Context mContext;
    private ListPopupWindow mPopup;
    private SubMenuBuilder mSubMenu;
    private MenuBuilder mMenu;
    private int mPopupMaxWidth;
    private WeakReference<View> mAnchorView;
    private boolean mOverflowOnly;

    public MenuPopupHelper(Context context, MenuBuilder menu) {
        this(context, menu, null, false);
    }

    public MenuPopupHelper(Context context, SubMenuBuilder subMenu) {
    public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
        this(context, menu, anchorView, false);
    }

    public MenuPopupHelper(Context context, MenuBuilder menu,
            View anchorView, boolean overflowOnly) {
        mContext = context;
        mSubMenu = subMenu;
        mMenu = menu;
        mOverflowOnly = overflowOnly;

        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        mPopupMaxWidth = metrics.widthPixels / 2;

        if (anchorView != null) {
            mAnchorView = new WeakReference<View>(anchorView);
        }
    }

    public void show() {
@@ -50,16 +70,23 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener {
                com.android.internal.R.style.Widget_Spinner);
        mPopup.setOnItemClickListener(this);

        final MenuAdapter adapter = mSubMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP);
        final MenuAdapter adapter = mOverflowOnly ?
                mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
                mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP);
        mPopup.setAdapter(adapter);
        mPopup.setModal(true);

        final MenuItemImpl itemImpl = (MenuItemImpl) mSubMenu.getItem();
        final View anchorView = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null);
        mPopup.setAnchorView(anchorView);
        if (mMenu instanceof SubMenuBuilder) {
            SubMenuBuilder subMenu = (SubMenuBuilder) mMenu;
            final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem();
            mPopup.setAnchorView(itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null));
        } else if (mAnchorView != null) {
            mPopup.setAnchorView(mAnchorView.get());
        }

        mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth));
        mPopup.show();
        mPopup.getListView().setOnKeyListener(this);
    }

    public void dismiss() {
@@ -67,11 +94,29 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener {
        mPopup = null;
    }

    public boolean isShowing() {
        return mPopup != null && mPopup.isShowing();
    }

    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mSubMenu.performItemAction(mSubMenu.getItem(position), 0);
        MenuItem item = null;
        if (mOverflowOnly) {
            item = mMenu.getOverflowItem(position);
        } else {
            item = mMenu.getItem(position);
        }
        mMenu.performItemAction(item, 0);
        mPopup.dismiss();
    }

    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
            dismiss();
            return true;
        }
        return false;
    }

    private int measureContentWidth(MenuAdapter adapter) {
        // Menus don't tend to be long, so this is more sane than it looks.
        int width = 0;
+11 −0
Original line number Diff line number Diff line
@@ -202,6 +202,17 @@ public class ActionBarView extends ViewGroup {
        mMenuView = menuView;
    }

    public boolean showOverflowMenu() {
        if (mMenuView != null) {
            return mMenuView.showOverflowMenu();
        }
        return false;
    }

    public boolean isOverflowReserved() {
        return mMenuView != null && mMenuView.isOverflowReserved();
    }

    public void setCustomNavigationView(View view) {
        mCustomNavView = view;
        if (view != null) {
Loading