Action Bar now supports submenus as popups.
Change-Id: I1691c16081b3474ed6d6e406f91f5f74a2dc8fcb
diff --git a/api/current.xml b/api/current.xml
index d093416..086a87a4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -214120,6 +214120,19 @@
<parameter name="d" type="android.graphics.drawable.Drawable">
</parameter>
</method>
+<method name="setContentWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+</method>
<method name="setHeight"
return="void"
abstract="false"
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index e9de385..5c34c2c 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -78,7 +78,7 @@
private AdapterView.OnItemClickListener mItemClickListener;
private AdapterView.OnItemSelectedListener mItemSelectedListener;
- private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
+ private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
private final PopupScrollListener mScrollListener = new PopupScrollListener();
private final ListSelectorHider mHideSelector = new ListSelectorHider();
@@ -432,6 +432,19 @@
}
/**
+ * Sets the width of the popup window by the size of its content. The final width may be
+ * larger to accommodate styled window dressing.
+ *
+ * @param width Desired width of content in pixels.
+ */
+ public void setContentWidth(int width) {
+ Drawable popupBackground = mPopup.getBackground();
+ if (popupBackground != null) {
+ mDropDownWidth = popupBackground.getIntrinsicWidth() + width;
+ }
+ }
+
+ /**
* @return The height of the popup window in pixels.
*/
public int getHeight() {
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index a962212..94a9f65 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -55,7 +55,7 @@
private static final String LOGTAG = "MenuBuilder";
/** The number of different menu types */
- public static final int NUM_TYPES = 4;
+ public static final int NUM_TYPES = 5;
/** The menu type that represents the icon menu view */
public static final int TYPE_ICON = 0;
/** The menu type that represents the expanded menu view */
@@ -66,20 +66,24 @@
* have an ItemView.
*/
public static final int TYPE_DIALOG = 2;
-
/**
* The menu type that represents a button in the application's action bar.
*/
public static final int TYPE_ACTION_BUTTON = 3;
+ /**
+ * The menu type that represents a menu popup.
+ */
+ public static final int TYPE_POPUP = 4;
private static final String VIEWS_TAG = "android:views";
-
+
// Order must be the same order as the TYPE_*
static final int THEME_RES_FOR_TYPE[] = new int[] {
com.android.internal.R.style.Theme_IconMenu,
com.android.internal.R.style.Theme_ExpandedMenu,
0,
0,
+ 0,
};
// Order must be the same order as the TYPE_*
@@ -88,6 +92,7 @@
com.android.internal.R.layout.expanded_menu_layout,
0,
com.android.internal.R.layout.action_menu_layout,
+ 0,
};
// Order must be the same order as the TYPE_*
@@ -96,6 +101,7 @@
com.android.internal.R.layout.list_menu_item_layout,
com.android.internal.R.layout.list_menu_item_layout,
com.android.internal.R.layout.action_menu_item_layout,
+ com.android.internal.R.layout.list_menu_item_layout,
};
private static final int[] sCategoryToOrder = new int[] {
@@ -1251,7 +1257,19 @@
}
public View getView(int position, View convertView, ViewGroup parent) {
- return ((MenuItemImpl) getItem(position)).getItemView(mMenuType, parent);
+ if (convertView != null) {
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ itemView.getItemData().setItemView(mMenuType, null);
+
+ MenuItemImpl item = (MenuItemImpl) getItem(position);
+ itemView.initialize(item, mMenuType);
+ item.setItemView(mMenuType, itemView);
+ return convertView;
+ } else {
+ MenuItemImpl item = (MenuItemImpl) getItem(position);
+ item.setItemView(mMenuType, null);
+ return item.getItemView(mMenuType, parent);
+ }
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 5fe75be..fecbd77 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -583,6 +583,10 @@
return (View) mItemViews[menuType].get();
}
+ void setItemView(int menuType, ItemView view) {
+ mItemViews[menuType] = new WeakReference<ItemView>(view);
+ }
+
/**
* Create and initializes a menu item view that implements {@link MenuView.ItemView}.
* @param menuType The type of menu to get a View for (must be one of
@@ -631,7 +635,10 @@
* @return Whether the given menu type should show icons for menu items.
*/
public boolean shouldShowIcon(int menuType) {
- return menuType == MenuBuilder.TYPE_ICON || mMenu.getOptionalIconsVisible();
+ return menuType == MenuBuilder.TYPE_ICON ||
+ menuType == MenuBuilder.TYPE_ACTION_BUTTON ||
+ menuType == MenuBuilder.TYPE_POPUP ||
+ mMenu.getOptionalIconsVisible();
}
public boolean isActionButton() {
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
new file mode 100644
index 0000000..751ecda
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import com.android.internal.view.menu.MenuBuilder.MenuAdapter;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.AdapterView;
+import android.widget.ListPopupWindow;
+
+/**
+ * @hide
+ */
+public class MenuPopupHelper implements AdapterView.OnItemClickListener {
+ private static final String TAG = "MenuPopupHelper";
+
+ private Context mContext;
+ private ListPopupWindow mPopup;
+ private SubMenuBuilder mSubMenu;
+ private int mPopupMaxWidth;
+
+ public MenuPopupHelper(Context context, SubMenuBuilder subMenu) {
+ mContext = context;
+ mSubMenu = subMenu;
+
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ mPopupMaxWidth = metrics.widthPixels / 2;
+ }
+
+ public void show() {
+ // TODO Use a style from the theme here
+ mPopup = new ListPopupWindow(mContext, null, 0,
+ com.android.internal.R.style.Widget_Spinner);
+ mPopup.setOnItemClickListener(this);
+
+ final MenuAdapter adapter = mSubMenu.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);
+
+ mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth));
+ mPopup.show();
+ }
+
+ public void dismiss() {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mSubMenu.performItemAction(mSubMenu.getItem(position), 0);
+ mPopup.dismiss();
+ }
+
+ private int measureContentWidth(MenuAdapter adapter) {
+ // Menus don't tend to be long, so this is more sane than it looks.
+ int width = 0;
+ View itemView = null;
+ final int widthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ itemView = adapter.getView(i, itemView, null);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ width = Math.max(width, itemView.getMeasuredWidth());
+ }
+ return width;
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 5c56d3c..879679f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -27,6 +27,7 @@
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuView;
import com.android.internal.view.menu.SubMenuBuilder;
import com.android.internal.widget.ActionBarView;
@@ -77,9 +78,12 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.ListPopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
+import java.lang.ref.WeakReference;
+
/**
* Android-specific Window.
* <p>
@@ -96,7 +100,7 @@
* Simple callback used by the context menu and its submenus. The options
* menu submenus do not use this (their behavior is more complex).
*/
- ContextMenuCallback mContextMenuCallback = new ContextMenuCallback(FEATURE_CONTEXT_MENU);
+ DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU);
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@@ -808,8 +812,20 @@
return true;
}
- // The window manager will give us a valid window token
- new MenuDialogHelper(subMenu).show(null);
+ final Menu parentMenu = subMenu.getRootMenu();
+ final PanelFeatureState panel = findMenuPanel(parentMenu);
+
+ /*
+ * Use the panel open state to determine whether this is coming from an open panel
+ * or an action button. If it's an open panel we want to use MenuDialogHelper.
+ * If it's closed we want to grab the relevant view and create a popup anchored to it.
+ */
+ if (panel.isOpen) {
+ // The window manager will give us a valid window token
+ new MenuDialogHelper(subMenu).show(null);
+ } else {
+ new MenuPopupHelper(getContext(), subMenu).show();
+ }
return true;
}
@@ -2797,11 +2813,11 @@
* <li> Calls back to the callback's onMenuItemSelected when an item is
* selected.
*/
- private final class ContextMenuCallback implements MenuBuilder.Callback {
+ private final class DialogMenuCallback implements MenuBuilder.Callback {
private int mFeatureId;
private MenuDialogHelper mSubMenuHelper;
- public ContextMenuCallback(int featureId) {
+ public DialogMenuCallback(int featureId) {
mFeatureId = featureId;
}