diff --git a/api/current.xml b/api/current.xml index 8a6b09a6dda4f..5ad31db516fe3 100644 --- a/api/current.xml +++ b/api/current.xml @@ -2088,6 +2088,28 @@ visibility="public" > + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + menu. - * - *

This is only called once, the first time the action bar is displayed. - * - * @param menu The action menu in which to place your items. - * @return You must return true for actions to be displayed; - * if you return false they will not be shown. - * - * @see #onActionItemClicked(MenuItem) - */ - public boolean onCreateActionMenu(Menu menu); - - /** - * Update the action bar. This is called in response to {@link #updateActionMenu()} - * calls, which may be application-initiated or the result of changing fragment state. - * - * @return true if the action bar should update based on altered menu contents, - * false if no changes are necessary. - */ - public boolean onUpdateActionMenu(Menu menu); - - /** - * This hook is called whenever an action item in your action bar is clicked. - * The default implementation simply returns false to have the normal - * processing happen (sending a message to its handler). You can use this - * method for any items for which you would like to do processing without - * those other facilities. - * - * @param item The action bar item that was selected. - * @return boolean Return false to allow normal menu processing to proceed, - * true to consume it here. - */ - public boolean onActionItemClicked(MenuItem item); - /** * This method is called whenever a navigation item in your action bar * is selected. @@ -276,18 +225,6 @@ public abstract class ActionBar { * Extend this if you only need a subset of Callback functionality. */ public static class SimpleCallback implements Callback { - public boolean onCreateActionMenu(Menu menu) { - return false; - } - - public boolean onUpdateActionMenu(Menu menu) { - return false; - } - - public boolean onActionItemClicked(MenuItem item) { - return false; - } - public boolean onCreateContextMode(int modeId, Menu menu) { return false; } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ed37058520579..382f1bd8a8f84 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -936,6 +936,10 @@ public class Activity extends ContextThemeWrapper mTitleReady = true; onTitleChanged(getTitle(), getTitleColor()); } + if (mWindow != null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { + // Invalidate the action bar menu so that it can initialize properly. + mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); + } mCalled = true; } diff --git a/core/java/android/view/ActionBarView.java b/core/java/android/view/ActionBarView.java index 3ed345e4aa242..39668474f985d 100644 --- a/core/java/android/view/ActionBarView.java +++ b/core/java/android/view/ActionBarView.java @@ -16,7 +16,10 @@ package android.view; -import java.util.ArrayList; +import com.android.internal.R; +import com.android.internal.view.menu.ActionMenu; +import com.android.internal.view.menu.ActionMenuItem; +import com.android.internal.view.menu.MenuBuilder; import android.app.ActionBar; import android.app.Activity; @@ -37,10 +40,6 @@ import android.widget.Spinner; import android.widget.SpinnerAdapter; import android.widget.TextView; -import com.android.internal.R; -import com.android.internal.view.menu.ActionMenu; -import com.android.internal.view.menu.ActionMenuItem; - /** * @hide */ @@ -75,7 +74,6 @@ public class ActionBarView extends ViewGroup { private CharSequence mSubtitle; private Drawable mIcon; private Drawable mLogo; - private Drawable mDivider; private ImageView mIconView; private ImageView mLogoView; @@ -86,25 +84,14 @@ public class ActionBarView extends ViewGroup { private boolean mShowMenu; + private MenuBuilder mOptionsMenu; + private View mMenuView; + private ActionMenuItem mLogoNavItem; - private ActionMenu mActionMenu; - private ActionMenu mOptionsMenu; private SparseArray mContextMenus; private Callback mCallback; - - private final ArrayList mActions = new ArrayList(); - private final OnClickListener mActionClickHandler = new OnClickListener() { - public void onClick(View v) { - ActionView av = (ActionView) v; - ActionMenuItem item = (ActionMenuItem) av.menuItem; - - if (mCallback == null || !mCallback.onActionItemClicked(item)) { - item.invoke(); - } - } - }; private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { @@ -151,7 +138,6 @@ public class ActionBarView extends ViewGroup { if (mIcon == null) { mIcon = info.loadIcon(pm); } - mDivider = a.getDrawable(R.styleable.ActionBar_divider); Drawable background = a.getDrawable(R.styleable.ActionBar_background); if (background != null) { @@ -178,8 +164,10 @@ public class ActionBarView extends ViewGroup { mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle); mHomeClickListener = new OnClickListener() { public void onClick(View v) { - if (mCallback != null) { - mCallback.onActionItemClicked(mLogoNavItem); + Context context = getContext(); + if (context instanceof Activity) { + Activity activity = (Activity) context; + activity.onOptionsItemSelected(mLogoNavItem); } } }; @@ -188,31 +176,22 @@ public class ActionBarView extends ViewGroup { mContextMenus = new SparseArray(); } - private boolean initOptionsMenu() { - final Context context = getContext(); - if (!(context instanceof Activity)) { - return false; - } - - final Activity activity = (Activity) context; - ActionMenu optionsMenu = new ActionMenu(context); - if (activity.onCreateOptionsMenu(optionsMenu)) { - mOptionsMenu = optionsMenu; - return true; - } - - return false; + public void setCallback(Callback callback) { + mCallback = callback; } - public void setCallback(Callback callback) { - final Context context = getContext(); - mCallback = callback; - - ActionMenu actionMenu = new ActionMenu(context); - if (callback.onCreateActionMenu(actionMenu)) { - mActionMenu = actionMenu; - performUpdateActionMenu(); + public void setMenu(Menu menu) { + MenuBuilder builder = (MenuBuilder) menu; + mOptionsMenu = builder; + if (mMenuView != null) { + removeView(mMenuView); } + final View menuView = builder.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, null); + final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.MATCH_PARENT); + menuView.setLayoutParams(layoutParams); + addView(menuView); + mMenuView = menuView; } public void setCustomNavigationView(View view) { @@ -222,10 +201,6 @@ public class ActionBarView extends ViewGroup { } } - public void setDividerDrawable(Drawable d) { - mDivider = d; - } - public CharSequence getTitle() { return mTitle; } @@ -301,9 +276,13 @@ public class ActionBarView extends ViewGroup { mSpinner = new Spinner(mContext, null, com.android.internal.R.attr.dropDownSpinnerStyle); mSpinner.setOnItemSelectedListener(mNavItemSelectedListener); + mSpinner.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT)); addView(mSpinner); break; case ActionBar.NAVIGATION_MODE_CUSTOM: + mCustomNavView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT)); addView(mCustomNavView); break; } @@ -328,18 +307,6 @@ public class ActionBarView extends ViewGroup { return mDisplayOptions; } - private ActionView findActionViewForItem(MenuItem item) { - final ArrayList actions = mActions; - final int actionCount = actions.size(); - for (int i = 0; i < actionCount; i++) { - ActionView av = actions.get(i); - if (av.menuItem.equals(item)) { - return av; - } - } - return null; - } - public void setContextMode(int mode) { Callback callback = mCallback; if (callback == null) { @@ -368,92 +335,6 @@ public class ActionBarView extends ViewGroup { // TODO Turn off context mode; go back to normal. } - public void updateActionMenu() { - final ActionMenu menu = mActionMenu; - if (menu == null || mCallback == null || !mCallback.onUpdateActionMenu(menu)) { - return; - } - performUpdateActionMenu(); - } - - private void performUpdateActionMenu() { - final ActionMenu menu = mActionMenu; - if (menu == null) { - return; - } - final Context context = getContext(); - - int childCount = getChildCount(); - int childIndex = 0; - while (childIndex < childCount) { - View v = getChildAt(childIndex); - if (v instanceof ActionView) { - detachViewFromParent(childIndex); - childCount--; - } else { - childIndex++; - } - } - - ArrayList detachedViews = new ArrayList(mActions); - final int itemCount = menu.size(); - for (int i = 0; i < itemCount; i++) { - final MenuItem item = menu.getItem(i); - - boolean newView = false; - ActionView actionView = findActionViewForItem(item); - if (actionView == null) { - actionView = new ActionView(context); - newView = true; - } - actionView.actionId = item.getItemId(); - actionView.menuItem = item; - actionView.actionLabel = item.getTitle(); - actionView.setAdjustViewBounds(true); - actionView.setImageDrawable(item.getIcon()); - actionView.setFocusable(true); - actionView.setOnClickListener(mActionClickHandler); - - LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ACTION); - actionView.setLayoutParams(layoutParams); - - if (newView) { - addView(actionView); - mActions.add(actionView); - } else { - attachViewToParent(actionView, -1, layoutParams); - detachedViews.remove(actionView); - actionView.invalidate(); - } - } - - final int detachedCount = detachedViews.size(); - for (int i = 0; i < detachedCount; i++) { - removeDetachedView(detachedViews.get(i), false); - } - - requestLayout(); - } - - public void addAction(int id, Drawable icon, CharSequence label, OnActionListener listener) { - ActionView actionView = new ActionView(getContext()); - actionView.actionId = id; - actionView.actionLabel = label; - actionView.actionListener = listener; - actionView.setAdjustViewBounds(true); - actionView.setImageDrawable(icon); - actionView.setOnClickListener(mActionClickHandler); - - actionView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ACTION)); - - addView(actionView); - mActions.add(actionView); - - requestLayout(); - } - @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -463,7 +344,7 @@ public class ActionBarView extends ViewGroup { mLogoView = new ImageView(getContext()); mLogoView.setAdjustViewBounds(true); mLogoView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ICON)); + LayoutParams.MATCH_PARENT)); mLogoView.setImageDrawable(mLogo); mLogoView.setClickable(true); mLogoView.setFocusable(true); @@ -473,7 +354,7 @@ public class ActionBarView extends ViewGroup { mIconView = new ImageView(getContext()); mIconView.setAdjustViewBounds(true); mIconView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, LayoutParams.ITEM_TYPE_ICON)); + LayoutParams.MATCH_PARENT)); mIconView.setImageDrawable(mIcon); mIconView.setClickable(true); mIconView.setFocusable(true); @@ -508,8 +389,8 @@ public class ActionBarView extends ViewGroup { private void initTitle() { LayoutInflater inflater = LayoutInflater.from(getContext()); mTitleView = (TextView) inflater.inflate(R.layout.action_bar_title_item, null); - mTitleView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT, LayoutParams.ITEM_TYPE_TITLE)); + mTitleView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT)); if (mTitle != null) { mTitleView.setText(mTitle); } @@ -542,12 +423,9 @@ public class ActionBarView extends ViewGroup { if (mIconView != null && mIconView.getVisibility() != GONE) { availableWidth = measureChildView(mIconView, availableWidth, childSpecHeight, mSpacing); } - - final ArrayList actions = mActions; - final int actionCount = actions.size(); - for (int i = 0; i < actionCount; i++) { - ActionView action = actions.get(i); - availableWidth = measureChildView(action, availableWidth, + + if (mMenuView != null) { + availableWidth = measureChildView(mMenuView, availableWidth, childSpecHeight, mActionSpacing); } @@ -619,10 +497,9 @@ public class ActionBarView extends ViewGroup { x = r - l - getPaddingRight(); - final int count = mActions.size(); - for (int i = count - 1; i >= 0; i--) { - ActionView action = mActions.get(i); - x -= (positionChildInverse(action, x, y, contentHeight) + mActionSpacing); + + if (mMenuView != null) { + x -= positionChildInverse(mMenuView, x, y, contentHeight) + mActionSpacing; } } @@ -645,58 +522,4 @@ public class ActionBarView extends ViewGroup { return childWidth; } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new ViewGroup.LayoutParams(getContext(), attrs); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p != null && p instanceof LayoutParams; - } - - private static class LayoutParams extends ViewGroup.LayoutParams { - static final int ITEM_TYPE_UNKNOWN = -1; - static final int ITEM_TYPE_ICON = 0; - static final int ITEM_TYPE_TITLE = 1; - static final int ITEM_TYPE_CUSTOM_NAV = 2; - static final int ITEM_TYPE_ACTION = 3; - static final int ITEM_TYPE_MORE = 4; - - int type = ITEM_TYPE_UNKNOWN; - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(int width, int height, int type) { - this(width, height); - this.type = type; - } - - - public LayoutParams(ViewGroup.LayoutParams source) { - super(source); - } - } - - public interface OnActionListener { - void onAction(int id); - } - - private static class ActionView extends ImageView { - int actionId; - CharSequence actionLabel; - OnActionListener actionListener; - MenuItem menuItem; - - public ActionView(Context context) { - super(context); - } - } } diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java index a37d83dd36dbe..3335e85436c8d 100644 --- a/core/java/android/view/MenuInflater.java +++ b/core/java/android/view/MenuInflater.java @@ -241,6 +241,14 @@ public class MenuInflater { private boolean itemVisible; private boolean itemEnabled; + /** + * Sync to attrs.xml enum: + * - 0: never + * - 1: ifRoom + * - 2: always + */ + private int itemShowAsAction; + private String itemListenerMethodName; private static final int defaultGroupId = NO_ID; @@ -314,6 +322,7 @@ public class MenuInflater { itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked); itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible); itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled); + itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, 0); itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick); a.recycle(); @@ -344,8 +353,12 @@ public class MenuInflater { new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName)); } - if (itemCheckable >= 2 && item instanceof MenuItemImpl) { - ((MenuItemImpl) item).setExclusiveCheckable(true); + if (item instanceof MenuItemImpl) { + MenuItemImpl impl = (MenuItemImpl) item; + if (itemCheckable >= 2) { + impl.setExclusiveCheckable(true); + } + impl.setShowAsAction(itemShowAsAction); } } diff --git a/core/java/com/android/internal/app/SplitActionBar.java b/core/java/com/android/internal/app/SplitActionBar.java index 81294922007f0..05ae793959305 100644 --- a/core/java/com/android/internal/app/SplitActionBar.java +++ b/core/java/com/android/internal/app/SplitActionBar.java @@ -83,10 +83,6 @@ public class SplitActionBar extends ActionBar { mActionView.setBackgroundDrawable(d); } - public void setDividerDrawable(Drawable d) { - mActionView.setDividerDrawable(d); - } - public View getCustomNavigationView() { return mActionView.getCustomNavigationView(); } @@ -106,9 +102,4 @@ public class SplitActionBar extends ActionBar { public int getDisplayOptions() { return mActionView.getDisplayOptions(); } - - public void updateActionMenu() { - mActionView.updateActionMenu(); - } - } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java new file mode 100644 index 0000000000000..f0d9f6065e8d9 --- /dev/null +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -0,0 +1,112 @@ +/* + * 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 android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.SoundEffectConstants; +import android.view.View; +import android.widget.ImageButton; + +/** + * @hide + */ +public class ActionMenuItemView extends ImageButton implements MenuView.ItemView { + private static final String TAG = "ActionMenuItemView"; + + private MenuItemImpl mItemData; + private CharSequence mTitle; + private MenuBuilder.ItemInvoker mItemInvoker; + + public ActionMenuItemView(Context context) { + this(context, null); + } + + public ActionMenuItemView(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.actionButtonStyle); + } + + public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public MenuItemImpl getItemData() { + return mItemData; + } + + public void initialize(MenuItemImpl itemData, int menuType) { + mItemData = itemData; + + setClickable(true); + setFocusable(true); + setTitle(itemData.getTitle()); + setIcon(itemData.getIcon()); + setId(itemData.getItemId()); + + setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE); + setEnabled(itemData.isEnabled()); + } + + @Override + public boolean performClick() { + // Let the view's listener have top priority + if (super.performClick()) { + return true; + } + + if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) { + playSoundEffect(SoundEffectConstants.CLICK); + return true; + } else { + return false; + } + } + + public void setItemInvoker(MenuBuilder.ItemInvoker invoker) { + mItemInvoker = invoker; + } + + public boolean prefersCondensedTitle() { + return false; + } + + public void setCheckable(boolean checkable) { + // TODO Support checkable action items + } + + public void setChecked(boolean checked) { + // TODO Support checkable action items + } + + public void setIcon(Drawable icon) { + setImageDrawable(icon); + } + + public void setShortcut(boolean showShortcut, char shortcutKey) { + // Action buttons don't show text for shortcut keys. + } + + public void setTitle(CharSequence title) { + mTitle = title; + } + + public boolean showsIcon() { + return true; + } + +} diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java new file mode 100644 index 0000000000000..596e3de6c65a6 --- /dev/null +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -0,0 +1,74 @@ +/* + * 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 android.content.Context; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +import java.util.ArrayList; + +/** + * @hide + */ +public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView { + private static final String TAG = "ActionMenuView"; + + // TODO: Make this a ViewConfiguration constant. + private static final int MAX_ACTION_ITEMS = 3; + + private MenuBuilder mMenu; + + public ActionMenuView(Context context) { + this(context, null); + } + + public ActionMenuView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public boolean invokeItem(MenuItemImpl item) { + return mMenu.performItemAction(item, 0); + } + + public int getWindowAnimations() { + return 0; + } + + public void initialize(MenuBuilder menu, int menuType) { + menu.setMaxActionItems(MAX_ACTION_ITEMS); + mMenu = menu; + updateChildren(true); + } + + public void updateChildren(boolean cleared) { + removeAllViews(); + + final ArrayList itemsToShow = mMenu.getActionItems(); + final int itemCount = itemsToShow.size(); + + for (int i = 0; i < itemCount; i++) { + final MenuItemImpl itemData = itemsToShow.get(i); + addItemView((ActionMenuItemView) itemData.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, + this)); + } + } + + private void addItemView(ActionMenuItemView view) { + view.setItemInvoker(this); + addView(view); + } +} diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java index beb57ba2340c7..bbf7c689e298a 100644 --- a/core/java/com/android/internal/view/menu/IconMenuView.java +++ b/core/java/com/android/internal/view/menu/IconMenuView.java @@ -337,7 +337,7 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi // This method does a clear refresh of children removeAllViews(); - final ArrayList itemsToShow = mMenu.getVisibleItems(); + final ArrayList itemsToShow = mMenu.getNonActionItems(); 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 228d5d09b0ab2..a9622128d6e94 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -27,16 +27,17 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; +import android.util.Log; import android.util.SparseArray; import android.view.ContextThemeWrapper; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView; import android.widget.BaseAdapter; @@ -54,7 +55,7 @@ public class MenuBuilder implements Menu { private static final String LOGTAG = "MenuBuilder"; /** The number of different menu types */ - public static final int NUM_TYPES = 3; + public static final int NUM_TYPES = 4; /** 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 */ @@ -65,6 +66,11 @@ public class MenuBuilder implements Menu { * 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; private static final String VIEWS_TAG = "android:views"; @@ -73,6 +79,7 @@ public class MenuBuilder implements Menu { com.android.internal.R.style.Theme_IconMenu, com.android.internal.R.style.Theme_ExpandedMenu, 0, + 0, }; // Order must be the same order as the TYPE_* @@ -80,6 +87,7 @@ public class MenuBuilder implements Menu { com.android.internal.R.layout.icon_menu_layout, com.android.internal.R.layout.expanded_menu_layout, 0, + com.android.internal.R.layout.action_menu_layout, }; // Order must be the same order as the TYPE_* @@ -87,6 +95,7 @@ public class MenuBuilder implements Menu { com.android.internal.R.layout.icon_menu_item_layout, 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, }; private static final int[] sCategoryToOrder = new int[] { @@ -130,6 +139,24 @@ public class MenuBuilder implements Menu { * fetched from {@link #getVisibleItems()} */ private boolean mIsVisibleItemsStale; + + /** + * Contains only the items that should appear in the Action Bar, if present. + */ + private ArrayList mActionItems; + /** + * Contains items that should NOT appear in the Action Bar, if present. + */ + private ArrayList mNonActionItems; + /** + * The number of visible action buttons permitted in this menu + */ + private int mMaxActionItems; + /** + * Whether or not the items (or any one item's action state) has changed since it was + * last fetched. + */ + private boolean mIsActionItemsStale; /** * Current use case is Context Menus: As Views populate the context menu, each one has @@ -281,6 +308,10 @@ public class MenuBuilder implements Menu { mVisibleItems = new ArrayList(); mIsVisibleItemsStale = true; + mActionItems = new ArrayList(); + mNonActionItems = new ArrayList(); + mIsActionItemsStale = true; + mShortcutsVisible = (mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS); } @@ -900,6 +931,7 @@ public class MenuBuilder implements Menu { private void onItemsChanged(boolean cleared) { if (!mPreventDispatchingItemsChanged) { if (mIsVisibleItemsStale == false) mIsVisibleItemsStale = true; + if (mIsActionItemsStale == false) mIsActionItemsStale = true; MenuType[] menuTypes = mMenuTypes; for (int i = 0; i < NUM_TYPES; i++) { @@ -920,6 +952,15 @@ public class MenuBuilder implements Menu { onItemsChanged(false); } + /** + * Called by {@link MenuItemImpl} when its action request status is changed. + * @param item The item that has gone through a change in action request status. + */ + void onItemActionRequestChanged(MenuItemImpl item) { + // Notify of items being changed + onItemsChanged(false); + } + ArrayList getVisibleItems() { if (!mIsVisibleItemsStale) return mVisibleItems; @@ -934,9 +975,64 @@ public class MenuBuilder implements Menu { } mIsVisibleItemsStale = false; + mIsActionItemsStale = true; return mVisibleItems; } + + private void flagActionItems() { + if (!mIsActionItemsStale) { + return; + } + + final ArrayList visibleItems = getVisibleItems(); + final int itemsSize = visibleItems.size(); + int maxActions = mMaxActionItems; + + for (int i = 0; i < itemsSize; i++) { + MenuItemImpl item = visibleItems.get(i); + if (item.requiresActionButton()) { + maxActions--; + } + } + + // Flag as many more requested items as will fit. + for (int i = 0; i < itemsSize; i++) { + MenuItemImpl item = visibleItems.get(i); + if (item.requestsActionButton()) { + item.setIsActionButton(maxActions > 0); + maxActions--; + } + } + + mActionItems.clear(); + mNonActionItems.clear(); + for (int i = 0; i < itemsSize; i++) { + MenuItemImpl item = visibleItems.get(i); + if (item.isActionButton()) { + mActionItems.add(item); + } else { + mNonActionItems.add(item); + } + } + + mIsActionItemsStale = false; + } + + ArrayList getActionItems() { + flagActionItems(); + return mActionItems; + } + + ArrayList getNonActionItems() { + flagActionItems(); + return mNonActionItems; + } + + void setMaxActionItems(int maxActionItems) { + mMaxActionItems = maxActionItems; + mIsActionItemsStale = true; + } public void clearHeader() { mHeaderIcon = null; diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index 9b582057fa7cd..dc1e81f04b1dc 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -74,6 +74,15 @@ public final class MenuItemImpl implements MenuItem { private static final int EXCLUSIVE = 0x00000004; private static final int HIDDEN = 0x00000008; private static final int ENABLED = 0x00000010; + private static final int IS_ACTION = 0x00000020; + + /* + * These should be kept in sync with attrs.xml enum constants for showAsAction + */ + private static final int SHOW_AS_ACTION_NEVER = 0; + private static final int SHOW_AS_ACTION_IF_ROOM = 1; + private static final int SHOW_AS_ACTION_ALWAYS = 2; + private int mShowAsAction = SHOW_AS_ACTION_NEVER; /** Used for the icon resource ID if this item does not have an icon */ static final int NO_ICON = 0; @@ -630,4 +639,35 @@ public final class MenuItemImpl implements MenuItem { public boolean shouldShowIcon(int menuType) { return menuType == MenuBuilder.TYPE_ICON || mMenu.getOptionalIconsVisible(); } + + public boolean isActionButton() { + return (mFlags & IS_ACTION) == IS_ACTION || requiresActionButton(); + } + + public boolean requestsActionButton() { + return mShowAsAction == SHOW_AS_ACTION_IF_ROOM; + } + + public boolean requiresActionButton() { + return mShowAsAction == SHOW_AS_ACTION_ALWAYS; + } + + public void setIsActionButton(boolean isActionButton) { + if (isActionButton) { + mFlags |= IS_ACTION; + } else { + mFlags &= ~IS_ACTION; + } + } + + /** + * Used by MenuInflater to set how this item should display in the presence of + * an Action Bar. + * + * @param actionEnum Enum flag as defined in attrs.xml/SHOW_AS_ constants. + */ + public void setShowAsAction(int actionEnum) { + mShowAsAction = actionEnum; + mMenu.onItemActionRequestChanged(this); + } } diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml new file mode 100644 index 0000000000000..3f06251e79015 --- /dev/null +++ b/core/res/res/layout/action_menu_item_layout.xml @@ -0,0 +1,20 @@ + + + + diff --git a/core/res/res/layout/action_menu_layout.xml b/core/res/res/layout/action_menu_layout.xml new file mode 100644 index 0000000000000..18d5531a81d5e --- /dev/null +++ b/core/res/res/layout/action_menu_layout.xml @@ -0,0 +1,20 @@ + + + + diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d3e66ee1150a5..d984575079840 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -397,8 +397,12 @@ - + + + + + @@ -3287,6 +3291,13 @@ called when the item is clicked. --> + + + + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 8b1c9d46f6699..3dba1a07c5a40 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1294,10 +1294,15 @@ + + + + + diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 4f74413f40f3c..5ed26d3e2b440 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -873,4 +873,7 @@ useLogo @android:drawable/action_bar_divider + + diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index bc5f610076d33..1f9f1367d2b41 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -170,6 +170,8 @@ @android:style/Widget.HorizontalScrollView @android:style/Widget.Spinner @android:style/Widget.Spinner.DropDown + @android:style/Widget.Spinner.DropDown + @android:style/Widget.ActionButton @android:style/Widget.CompoundButton.Star @android:style/Widget.TabWidget @android:style/Widget.TextView diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 1c78a01d1c79a..eea855d6f1799 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -44,7 +44,6 @@ import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; -import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -63,7 +62,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; @@ -81,12 +79,6 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -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.MenuView; -import com.android.internal.view.menu.SubMenuBuilder; - /** * Android-specific Window. *

@@ -307,7 +299,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Already prepared (isPrepared will be reset to false later) if (st.isPrepared) return true; - + if ((mPreparedPanel != null) && (mPreparedPanel != st)) { // Another Panel is prepared and possibly open, so close it closePanel(mPreparedPanel, false); @@ -334,7 +326,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } + st.refreshMenuContent = false; + + if (mActionBar != null) { + mActionBar.setMenu(st.menu); + } } // Callback and return if the callback does not want to show the menu @@ -383,11 +380,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { clearMenuViews(st); } } - } private static void clearMenuViews(PanelFeatureState st) { - // This can be called on config changes, so we should make sure // the views will be reconstructed based on the new orientation, etc. @@ -562,6 +557,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } st.refreshMenuContent = true; st.refreshDecorView = true; + + // Prepare the options panel if we have an action bar + if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL) + && mActionBar != null) { + st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false); + if (st != null) { + st.isPrepared = false; + preparePanel(st, null); + } + } } /** @@ -2295,10 +2300,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } else { mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); - if (mActionBar != null) { - if (mActionBar.getTitle() == null) { - mActionBar.setTitle(mTitle); - } + if (mActionBar != null && mActionBar.getTitle() == null) { + mActionBar.setTitle(mTitle); } } }