Loading core/java/com/android/internal/view/menu/ActionMenuView.java +58 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading Loading @@ -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++) { Loading @@ -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; } } } core/java/com/android/internal/view/menu/IconMenuView.java +3 −1 Original line number Diff line number Diff line Loading @@ -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 Loading core/java/com/android/internal/view/menu/MenuBuilder.java +73 −11 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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(); } } } core/java/com/android/internal/view/menu/MenuPopupHelper.java +54 −9 Original line number Diff line number Diff line Loading @@ -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() { Loading @@ -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() { Loading @@ -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; Loading core/java/com/android/internal/widget/ActionBarView.java +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/com/android/internal/view/menu/ActionMenuView.java +58 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading Loading @@ -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++) { Loading @@ -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; } } }
core/java/com/android/internal/view/menu/IconMenuView.java +3 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/com/android/internal/view/menu/MenuBuilder.java +73 −11 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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(); } } }
core/java/com/android/internal/view/menu/MenuPopupHelper.java +54 −9 Original line number Diff line number Diff line Loading @@ -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() { Loading @@ -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() { Loading @@ -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; Loading
core/java/com/android/internal/widget/ActionBarView.java +11 −0 Original line number Diff line number Diff line Loading @@ -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