summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java59
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuView.java4
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java84
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java63
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java11
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java8
6 files changed, 205 insertions, 24 deletions
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index c3fe5dcba0c7..7024a2748cd8 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -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;
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java
index bbf7c689e298..178dcde9a030 100644
--- a/core/java/com/android/internal/view/menu/IconMenuView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuView.java
@@ -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
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 94a9f65a0dbe..215d809524c6 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -165,6 +165,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
* should be set on all items added to this menu.
@@ -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,22 +997,41 @@ 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;
}
-
+
final ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
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);
@@ -1010,7 +1040,7 @@ public class MenuBuilder implements Menu {
maxActions--;
}
}
-
+
mActionItems.clear();
mNonActionItems.clear();
for (int i = 0; i < itemsSize; i++) {
@@ -1021,17 +1051,17 @@ public class MenuBuilder implements Menu {
mNonActionItems.add(item);
}
}
-
+
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();
+ }
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 751ecdae48bc..9913c0099406 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -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;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index fbff8ae4ae5e..8f8b3afe62f6 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -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) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 070d1e8fc3e1..cc91f319efb8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1381,8 +1381,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
case KeyEvent.KEYCODE_MENU: {
- onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
- event);
+ if (mActionBar != null && mActionBar.isOverflowReserved()) {
+ mActionBar.showOverflowMenu();
+ } else {
+ onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
+ event);
+ }
return true;
}