From 4267534d1c42af847ed0cefd1c88c99f66b36571 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Fri, 9 Jul 2010 18:02:59 -0700 Subject: Action Bar now supports submenus as popups. Change-Id: I1691c16081b3474ed6d6e406f91f5f74a2dc8fcb --- api/current.xml | 13 ++++ core/java/android/widget/ListPopupWindow.java | 15 +++- .../android/internal/view/menu/MenuBuilder.java | 26 ++++++- .../android/internal/view/menu/MenuItemImpl.java | 9 ++- .../internal/view/menu/MenuPopupHelper.java | 91 ++++++++++++++++++++++ .../android/internal/policy/impl/PhoneWindow.java | 26 +++++-- 6 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 core/java/com/android/internal/view/menu/MenuPopupHelper.java diff --git a/api/current.xml b/api/current.xml index d093416..086a87a 100644 --- a/api/current.xml +++ b/api/current.xml @@ -214120,6 +214120,19 @@ + + + + (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 @@ public final class MenuItemImpl implements MenuItem { * @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.RootViewSurfaceTaker; 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.animation.AnimationUtils; 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. *

@@ -96,7 +100,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { *

  • 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; } -- cgit v1.1