summaryrefslogtreecommitdiffstats
path: root/actionbarsherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'actionbarsherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java')
-rw-r--r--actionbarsherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java1335
1 files changed, 0 insertions, 1335 deletions
diff --git a/actionbarsherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java b/actionbarsherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
deleted file mode 100644
index 179b8f037..000000000
--- a/actionbarsherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
+++ /dev/null
@@ -1,1335 +0,0 @@
-/*
- * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu;
-
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.SparseArray;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.View;
-
-import com.actionbarsherlock.R;
-import com.actionbarsherlock.view.ActionProvider;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
-import com.actionbarsherlock.view.SubMenu;
-
-/**
- * Implementation of the {@link android.view.Menu} interface for creating a
- * standard menu UI.
- */
-public class MenuBuilder implements Menu {
- //UNUSED private static final String TAG = "MenuBuilder";
-
- private static final String PRESENTER_KEY = "android:menu:presenters";
- private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates";
- private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview";
-
- private static final int[] sCategoryToOrder = new int[] {
- 1, /* No category */
- 4, /* CONTAINER */
- 5, /* SYSTEM */
- 3, /* SECONDARY */
- 2, /* ALTERNATIVE */
- 0, /* SELECTED_ALTERNATIVE */
- };
-
- private final Context mContext;
- private final Resources mResources;
-
- /**
- * Whether the shortcuts should be qwerty-accessible. Use isQwertyMode()
- * instead of accessing this directly.
- */
- private boolean mQwertyMode;
-
- /**
- * Whether the shortcuts should be visible on menus. Use isShortcutsVisible()
- * instead of accessing this directly.
- */
- private boolean mShortcutsVisible;
-
- /**
- * Callback that will receive the various menu-related events generated by
- * this class. Use getCallback to get a reference to the callback.
- */
- private Callback mCallback;
-
- /** Contains all of the items for this menu */
- private ArrayList<MenuItemImpl> mItems;
-
- /** Contains only the items that are currently visible. This will be created/refreshed from
- * {@link #getVisibleItems()} */
- private ArrayList<MenuItemImpl> mVisibleItems;
- /**
- * Whether or not the items (or any one item's shown state) has changed since it was last
- * fetched from {@link #getVisibleItems()}
- */
- private boolean mIsVisibleItemsStale;
-
- /**
- * Contains only the items that should appear in the Action Bar, if present.
- */
- private ArrayList<MenuItemImpl> mActionItems;
- /**
- * Contains items that should NOT appear in the Action Bar, if present.
- */
- private ArrayList<MenuItemImpl> mNonActionItems;
-
- /**
- * Whether or not the items (or any one item's action state) has changed since it was
- * last fetched.
- */
- private boolean mIsActionItemsStale;
-
- /**
- * Default value for how added items should show in the action list.
- */
- private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
-
- /**
- * 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.
- */
- private ContextMenuInfo mCurrentMenuInfo;
-
- /** Header title for menu types that have a header (context and submenus) */
- CharSequence mHeaderTitle;
- /** Header icon for menu types that have a header and support icons (context) */
- Drawable mHeaderIcon;
- /** Header custom view for menu types that have a header and support custom views (context) */
- View mHeaderView;
-
- /**
- * Contains the state of the View hierarchy for all menu views when the menu
- * was frozen.
- */
- //UNUSED private SparseArray<Parcelable> mFrozenViewStates;
-
- /**
- * Prevents onItemsChanged from doing its junk, useful for batching commands
- * that may individually call onItemsChanged.
- */
- private boolean mPreventDispatchingItemsChanged = false;
- private boolean mItemsChangedWhileDispatchPrevented = false;
-
- private boolean mOptionalIconsVisible = false;
-
- private boolean mIsClosing = false;
-
- private ArrayList<MenuItemImpl> mTempShortcutItemList = new ArrayList<MenuItemImpl>();
-
- private CopyOnWriteArrayList<WeakReference<MenuPresenter>> mPresenters =
- new CopyOnWriteArrayList<WeakReference<MenuPresenter>>();
-
- /**
- * Currently expanded menu item; must be collapsed when we clear.
- */
- private MenuItemImpl mExpandedItem;
-
- /**
- * Called by menu to notify of close and selection changes.
- */
- public interface Callback {
- /**
- * Called when a menu item is selected.
- * @param menu The menu that is the parent of the item
- * @param item The menu item that is selected
- * @return whether the menu item selection was handled
- */
- public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item);
-
- /**
- * Called when the mode of the menu changes (for example, from icon to expanded).
- *
- * @param menu the menu that has changed modes
- */
- public void onMenuModeChange(MenuBuilder menu);
- }
-
- /**
- * Called by menu items to execute their associated action
- */
- public interface ItemInvoker {
- public boolean invokeItem(MenuItemImpl item);
- }
-
- public MenuBuilder(Context context) {
- mContext = context;
- mResources = context.getResources();
-
- mItems = new ArrayList<MenuItemImpl>();
-
- mVisibleItems = new ArrayList<MenuItemImpl>();
- mIsVisibleItemsStale = true;
-
- mActionItems = new ArrayList<MenuItemImpl>();
- mNonActionItems = new ArrayList<MenuItemImpl>();
- mIsActionItemsStale = true;
-
- setShortcutsVisibleInner(true);
- }
-
- public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
- mDefaultShowAsAction = defaultShowAsAction;
- return this;
- }
-
- /**
- * Add a presenter to this menu. This will only hold a WeakReference;
- * you do not need to explicitly remove a presenter, but you can using
- * {@link #removeMenuPresenter(MenuPresenter)}.
- *
- * @param presenter The presenter to add
- */
- public void addMenuPresenter(MenuPresenter presenter) {
- mPresenters.add(new WeakReference<MenuPresenter>(presenter));
- presenter.initForMenu(mContext, this);
- mIsActionItemsStale = true;
- }
-
- /**
- * Remove a presenter from this menu. That presenter will no longer
- * receive notifications of updates to this menu's data.
- *
- * @param presenter The presenter to remove
- */
- public void removeMenuPresenter(MenuPresenter presenter) {
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter item = ref.get();
- if (item == null || item == presenter) {
- mPresenters.remove(ref);
- }
- }
- }
-
- private void dispatchPresenterUpdate(boolean cleared) {
- if (mPresenters.isEmpty()) return;
-
- stopDispatchingItemsChanged();
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else {
- presenter.updateMenuView(cleared);
- }
- }
- startDispatchingItemsChanged();
- }
-
- private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) {
- if (mPresenters.isEmpty()) return false;
-
- boolean result = false;
-
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else if (!result) {
- result = presenter.onSubMenuSelected(subMenu);
- }
- }
- return result;
- }
-
- private void dispatchSaveInstanceState(Bundle outState) {
- if (mPresenters.isEmpty()) return;
-
- SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>();
-
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else {
- final int id = presenter.getId();
- if (id > 0) {
- final Parcelable state = presenter.onSaveInstanceState();
- if (state != null) {
- presenterStates.put(id, state);
- }
- }
- }
- }
-
- outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
- }
-
- private void dispatchRestoreInstanceState(Bundle state) {
- SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
-
- if (presenterStates == null || mPresenters.isEmpty()) return;
-
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else {
- final int id = presenter.getId();
- if (id > 0) {
- Parcelable parcel = presenterStates.get(id);
- if (parcel != null) {
- presenter.onRestoreInstanceState(parcel);
- }
- }
- }
- }
- }
-
- public void savePresenterStates(Bundle outState) {
- dispatchSaveInstanceState(outState);
- }
-
- public void restorePresenterStates(Bundle state) {
- dispatchRestoreInstanceState(state);
- }
-
- public void saveActionViewStates(Bundle outStates) {
- SparseArray<Parcelable> viewStates = null;
-
- final int itemCount = size();
- for (int i = 0; i < itemCount; i++) {
- final MenuItem item = getItem(i);
- final View v = item.getActionView();
- if (v != null && v.getId() != View.NO_ID) {
- if (viewStates == null) {
- viewStates = new SparseArray<Parcelable>();
- }
- v.saveHierarchyState(viewStates);
- if (item.isActionViewExpanded()) {
- outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId());
- }
- }
- if (item.hasSubMenu()) {
- final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
- subMenu.saveActionViewStates(outStates);
- }
- }
-
- if (viewStates != null) {
- outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates);
- }
- }
-
- public void restoreActionViewStates(Bundle states) {
- if (states == null) {
- return;
- }
-
- SparseArray<Parcelable> viewStates = states.getSparseParcelableArray(
- getActionViewStatesKey());
-
- final int itemCount = size();
- for (int i = 0; i < itemCount; i++) {
- final MenuItem item = getItem(i);
- final View v = item.getActionView();
- if (v != null && v.getId() != View.NO_ID) {
- v.restoreHierarchyState(viewStates);
- }
- if (item.hasSubMenu()) {
- final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
- subMenu.restoreActionViewStates(states);
- }
- }
-
- final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID);
- if (expandedId > 0) {
- MenuItem itemToExpand = findItem(expandedId);
- if (itemToExpand != null) {
- itemToExpand.expandActionView();
- }
- }
- }
-
- protected String getActionViewStatesKey() {
- return ACTION_VIEW_STATES_KEY;
- }
-
- public void setCallback(Callback cb) {
- mCallback = cb;
- }
-
- /**
- * Adds an item to the menu. The other add methods funnel to this.
- */
- private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
- final int ordering = getOrdering(categoryOrder);
-
- final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
- ordering, title, mDefaultShowAsAction);
-
- if (mCurrentMenuInfo != null) {
- // Pass along the current menu info
- item.setMenuInfo(mCurrentMenuInfo);
- }
-
- mItems.add(findInsertIndex(mItems, ordering), item);
- onItemsChanged(true);
-
- return item;
- }
-
- public MenuItem add(CharSequence title) {
- return addInternal(0, 0, 0, title);
- }
-
- public MenuItem add(int titleRes) {
- return addInternal(0, 0, 0, mResources.getString(titleRes));
- }
-
- public MenuItem add(int group, int id, int categoryOrder, CharSequence title) {
- return addInternal(group, id, categoryOrder, title);
- }
-
- public MenuItem add(int group, int id, int categoryOrder, int title) {
- return addInternal(group, id, categoryOrder, mResources.getString(title));
- }
-
- public SubMenu addSubMenu(CharSequence title) {
- return addSubMenu(0, 0, 0, title);
- }
-
- public SubMenu addSubMenu(int titleRes) {
- return addSubMenu(0, 0, 0, mResources.getString(titleRes));
- }
-
- public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
- final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title);
- final SubMenuBuilder subMenu = new SubMenuBuilder(mContext, this, item);
- item.setSubMenu(subMenu);
-
- return subMenu;
- }
-
- public SubMenu addSubMenu(int group, int id, int categoryOrder, int title) {
- return addSubMenu(group, id, categoryOrder, mResources.getString(title));
- }
-
- public int addIntentOptions(int group, int id, int categoryOrder, ComponentName caller,
- Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
- PackageManager pm = mContext.getPackageManager();
- final List<ResolveInfo> lri =
- pm.queryIntentActivityOptions(caller, specifics, intent, 0);
- final int N = lri != null ? lri.size() : 0;
-
- if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
- removeGroup(group);
- }
-
- for (int i=0; i<N; i++) {
- final ResolveInfo ri = lri.get(i);
- Intent rintent = new Intent(
- ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
- rintent.setComponent(new ComponentName(
- ri.activityInfo.applicationInfo.packageName,
- ri.activityInfo.name));
- final MenuItem item = add(group, id, categoryOrder, ri.loadLabel(pm))
- .setIcon(ri.loadIcon(pm))
- .setIntent(rintent);
- if (outSpecificItems != null && ri.specificIndex >= 0) {
- outSpecificItems[ri.specificIndex] = item;
- }
- }
-
- return N;
- }
-
- public void removeItem(int id) {
- removeItemAtInt(findItemIndex(id), true);
- }
-
- public void removeGroup(int group) {
- final int i = findGroupIndex(group);
-
- if (i >= 0) {
- final int maxRemovable = mItems.size() - i;
- int numRemoved = 0;
- while ((numRemoved++ < maxRemovable) && (mItems.get(i).getGroupId() == group)) {
- // Don't force update for each one, this method will do it at the end
- removeItemAtInt(i, false);
- }
-
- // Notify menu views
- onItemsChanged(true);
- }
- }
-
- /**
- * Remove the item at the given index and optionally forces menu views to
- * update.
- *
- * @param index The index of the item to be removed. If this index is
- * invalid an exception is thrown.
- * @param updateChildrenOnMenuViews Whether to force update on menu views.
- * Please make sure you eventually call this after your batch of
- * removals.
- */
- private void removeItemAtInt(int index, boolean updateChildrenOnMenuViews) {
- if ((index < 0) || (index >= mItems.size())) return;
-
- mItems.remove(index);
-
- if (updateChildrenOnMenuViews) onItemsChanged(true);
- }
-
- public void removeItemAt(int index) {
- removeItemAtInt(index, true);
- }
-
- public void clearAll() {
- mPreventDispatchingItemsChanged = true;
- clear();
- clearHeader();
- mPreventDispatchingItemsChanged = false;
- mItemsChangedWhileDispatchPrevented = false;
- onItemsChanged(true);
- }
-
- public void clear() {
- if (mExpandedItem != null) {
- collapseItemActionView(mExpandedItem);
- }
- mItems.clear();
-
- onItemsChanged(true);
- }
-
- void setExclusiveItemChecked(MenuItem item) {
- final int group = item.getGroupId();
-
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
- MenuItemImpl curItem = mItems.get(i);
- if (curItem.getGroupId() == group) {
- if (!curItem.isExclusiveCheckable()) continue;
- if (!curItem.isCheckable()) continue;
-
- // Check the item meant to be checked, uncheck the others (that are in the group)
- curItem.setCheckedInt(curItem == item);
- }
- }
- }
-
- public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
- final int N = mItems.size();
-
- for (int i = 0; i < N; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.getGroupId() == group) {
- item.setExclusiveCheckable(exclusive);
- item.setCheckable(checkable);
- }
- }
- }
-
- public void setGroupVisible(int group, boolean visible) {
- final int N = mItems.size();
-
- // We handle the notification of items being changed ourselves, so we use setVisibleInt rather
- // than setVisible and at the end notify of items being changed
-
- boolean changedAtLeastOneItem = false;
- for (int i = 0; i < N; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.getGroupId() == group) {
- if (item.setVisibleInt(visible)) changedAtLeastOneItem = true;
- }
- }
-
- if (changedAtLeastOneItem) onItemsChanged(true);
- }
-
- public void setGroupEnabled(int group, boolean enabled) {
- final int N = mItems.size();
-
- for (int i = 0; i < N; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.getGroupId() == group) {
- item.setEnabled(enabled);
- }
- }
- }
-
- public boolean hasVisibleItems() {
- final int size = size();
-
- for (int i = 0; i < size; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.isVisible()) {
- return true;
- }
- }
-
- return false;
- }
-
- public MenuItem findItem(int id) {
- final int size = size();
- for (int i = 0; i < size; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.getItemId() == id) {
- return item;
- } else if (item.hasSubMenu()) {
- MenuItem possibleItem = item.getSubMenu().findItem(id);
-
- if (possibleItem != null) {
- return possibleItem;
- }
- }
- }
-
- return null;
- }
-
- public int findItemIndex(int id) {
- final int size = size();
-
- for (int i = 0; i < size; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.getItemId() == id) {
- return i;
- }
- }
-
- return -1;
- }
-
- public int findGroupIndex(int group) {
- return findGroupIndex(group, 0);
- }
-
- public int findGroupIndex(int group, int start) {
- final int size = size();
-
- if (start < 0) {
- start = 0;
- }
-
- for (int i = start; i < size; i++) {
- final MenuItemImpl item = mItems.get(i);
-
- if (item.getGroupId() == group) {
- return i;
- }
- }
-
- return -1;
- }
-
- public int size() {
- return mItems.size();
- }
-
- /** {@inheritDoc} */
- public MenuItem getItem(int index) {
- return mItems.get(index);
- }
-
- public boolean isShortcutKey(int keyCode, KeyEvent event) {
- return findItemWithShortcutForKey(keyCode, event) != null;
- }
-
- public void setQwertyMode(boolean isQwerty) {
- mQwertyMode = isQwerty;
-
- onItemsChanged(false);
- }
-
- /**
- * Returns the ordering across all items. This will grab the category from
- * the upper bits, find out how to order the category with respect to other
- * categories, and combine it with the lower bits.
- *
- * @param categoryOrder The category order for a particular item (if it has
- * not been or/add with a category, the default category is
- * assumed).
- * @return An ordering integer that can be used to order this item across
- * all the items (even from other categories).
- */
- private static int getOrdering(int categoryOrder) {
- final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT;
-
- if (index < 0 || index >= sCategoryToOrder.length) {
- throw new IllegalArgumentException("order does not contain a valid category.");
- }
-
- return (sCategoryToOrder[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK);
- }
-
- /**
- * @return whether the menu shortcuts are in qwerty mode or not
- */
- boolean isQwertyMode() {
- return mQwertyMode;
- }
-
- /**
- * Sets whether the shortcuts should be visible on menus. Devices without hardware
- * key input will never make shortcuts visible even if this method is passed 'true'.
- *
- * @param shortcutsVisible Whether shortcuts should be visible (if true and a
- * menu item does not have a shortcut defined, that item will
- * still NOT show a shortcut)
- */
- public void setShortcutsVisible(boolean shortcutsVisible) {
- if (mShortcutsVisible == shortcutsVisible) return;
-
- setShortcutsVisibleInner(shortcutsVisible);
- onItemsChanged(false);
- }
-
- private void setShortcutsVisibleInner(boolean shortcutsVisible) {
- mShortcutsVisible = shortcutsVisible
- && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
- && mResources.getBoolean(
- R.bool.abs__config_showMenuShortcutsWhenKeyboardPresent);
- }
-
- /**
- * @return Whether shortcuts should be visible on menus.
- */
- public boolean isShortcutsVisible() {
- return mShortcutsVisible;
- }
-
- Resources getResources() {
- return mResources;
- }
-
- public Context getContext() {
- return mContext;
- }
-
- boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
- return mCallback != null && mCallback.onMenuItemSelected(menu, item);
- }
-
- /**
- * Dispatch a mode change event to this menu's callback.
- */
- public void changeMenuMode() {
- if (mCallback != null) {
- mCallback.onMenuModeChange(this);
- }
- }
-
- private static int findInsertIndex(ArrayList<MenuItemImpl> items, int ordering) {
- for (int i = items.size() - 1; i >= 0; i--) {
- MenuItemImpl item = items.get(i);
- if (item.getOrdering() <= ordering) {
- return i + 1;
- }
- }
-
- return 0;
- }
-
- public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
- final MenuItemImpl item = findItemWithShortcutForKey(keyCode, event);
-
- boolean handled = false;
-
- if (item != null) {
- handled = performItemAction(item, flags);
- }
-
- if ((flags & FLAG_ALWAYS_PERFORM_CLOSE) != 0) {
- close(true);
- }
-
- return handled;
- }
-
- /*
- * This function will return all the menu and sub-menu items that can
- * be directly (the shortcut directly corresponds) and indirectly
- * (the ALT-enabled char corresponds to the shortcut) associated
- * with the keyCode.
- */
- @SuppressWarnings("deprecation")
- void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) {
- final boolean qwerty = isQwertyMode();
- final int metaState = event.getMetaState();
- final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
- // Get the chars associated with the keyCode (i.e using any chording combo)
- final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
- // The delete key is not mapped to '\b' so we treat it specially
- if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) {
- return;
- }
-
- // Look for an item whose shortcut is this key.
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
- MenuItemImpl item = mItems.get(i);
- if (item.hasSubMenu()) {
- ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
- }
- final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
- if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) &&
- (shortcutChar != 0) &&
- (shortcutChar == possibleChars.meta[0]
- || shortcutChar == possibleChars.meta[2]
- || (qwerty && shortcutChar == '\b' &&
- keyCode == KeyEvent.KEYCODE_DEL)) &&
- item.isEnabled()) {
- items.add(item);
- }
- }
- }
-
- /*
- * We want to return the menu item associated with the key, but if there is no
- * ambiguity (i.e. there is only one menu item corresponding to the key) we want
- * to return it even if it's not an exact match; this allow the user to
- * _not_ use the ALT key for example, making the use of shortcuts slightly more
- * user-friendly. An example is on the G1, '!' and '1' are on the same key, and
- * in Gmail, Menu+1 will trigger Menu+! (the actual shortcut).
- *
- * On the other hand, if two (or more) shortcuts corresponds to the same key,
- * we have to only return the exact match.
- */
- @SuppressWarnings("deprecation")
- MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) {
- // Get all items that can be associated directly or indirectly with the keyCode
- ArrayList<MenuItemImpl> items = mTempShortcutItemList;
- items.clear();
- findItemsWithShortcutForKey(items, keyCode, event);
-
- if (items.isEmpty()) {
- return null;
- }
-
- final int metaState = event.getMetaState();
- final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
- // Get the chars associated with the keyCode (i.e using any chording combo)
- event.getKeyData(possibleChars);
-
- // If we have only one element, we can safely returns it
- final int size = items.size();
- if (size == 1) {
- return items.get(0);
- }
-
- final boolean qwerty = isQwertyMode();
- // If we found more than one item associated with the key,
- // we have to return the exact match
- for (int i = 0; i < size; i++) {
- final MenuItemImpl item = items.get(i);
- final char shortcutChar = qwerty ? item.getAlphabeticShortcut() :
- item.getNumericShortcut();
- if ((shortcutChar == possibleChars.meta[0] &&
- (metaState & KeyEvent.META_ALT_ON) == 0)
- || (shortcutChar == possibleChars.meta[2] &&
- (metaState & KeyEvent.META_ALT_ON) != 0)
- || (qwerty && shortcutChar == '\b' &&
- keyCode == KeyEvent.KEYCODE_DEL)) {
- return item;
- }
- }
- return null;
- }
-
- public boolean performIdentifierAction(int id, int flags) {
- // Look for an item whose identifier is the id.
- return performItemAction(findItem(id), flags);
- }
-
- public boolean performItemAction(MenuItem item, int flags) {
- MenuItemImpl itemImpl = (MenuItemImpl) item;
-
- if (itemImpl == null || !itemImpl.isEnabled()) {
- return false;
- }
-
- boolean invoked = itemImpl.invoke();
-
- if (itemImpl.hasCollapsibleActionView()) {
- invoked |= itemImpl.expandActionView();
- if (invoked) close(true);
- } else if (item.hasSubMenu()) {
- close(false);
-
- final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
- final ActionProvider provider = item.getActionProvider();
- if (provider != null && provider.hasSubMenu()) {
- provider.onPrepareSubMenu(subMenu);
- }
- invoked |= dispatchSubMenuSelected(subMenu);
- if (!invoked) close(true);
- } else {
- if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
- close(true);
- }
- }
-
- return invoked;
- }
-
- /**
- * Closes the visible menu.
- *
- * @param allMenusAreClosing Whether the menus are completely closing (true),
- * or whether there is another menu coming in this menu's place
- * (false). For example, if the menu is closing because a
- * sub menu is about to be shown, <var>allMenusAreClosing</var>
- * is false.
- */
- final void close(boolean allMenusAreClosing) {
- if (mIsClosing) return;
-
- mIsClosing = true;
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else {
- presenter.onCloseMenu(this, allMenusAreClosing);
- }
- }
- mIsClosing = false;
- }
-
- /** {@inheritDoc} */
- public void close() {
- close(true);
- }
-
- /**
- * Called when an item is added or removed.
- *
- * @param structureChanged true if the menu structure changed,
- * false if only item properties changed.
- * (Visibility is a structural property since it affects layout.)
- */
- void onItemsChanged(boolean structureChanged) {
- if (!mPreventDispatchingItemsChanged) {
- if (structureChanged) {
- mIsVisibleItemsStale = true;
- mIsActionItemsStale = true;
- }
-
- dispatchPresenterUpdate(structureChanged);
- } else {
- mItemsChangedWhileDispatchPrevented = true;
- }
- }
-
- /**
- * Stop dispatching item changed events to presenters until
- * {@link #startDispatchingItemsChanged()} is called. Useful when
- * many menu operations are going to be performed as a batch.
- */
- public void stopDispatchingItemsChanged() {
- if (!mPreventDispatchingItemsChanged) {
- mPreventDispatchingItemsChanged = true;
- mItemsChangedWhileDispatchPrevented = false;
- }
- }
-
- public void startDispatchingItemsChanged() {
- mPreventDispatchingItemsChanged = false;
-
- if (mItemsChangedWhileDispatchPrevented) {
- mItemsChangedWhileDispatchPrevented = false;
- onItemsChanged(true);
- }
- }
-
- /**
- * Called by {@link MenuItemImpl} when its visible flag is changed.
- * @param item The item that has gone through a visibility change.
- */
- void onItemVisibleChanged(MenuItemImpl item) {
- // Notify of items being changed
- mIsVisibleItemsStale = true;
- onItemsChanged(true);
- }
-
- /**
- * 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
- mIsActionItemsStale = true;
- onItemsChanged(true);
- }
-
- ArrayList<MenuItemImpl> getVisibleItems() {
- if (!mIsVisibleItemsStale) return mVisibleItems;
-
- // Refresh the visible items
- mVisibleItems.clear();
-
- final int itemsSize = mItems.size();
- MenuItemImpl item;
- for (int i = 0; i < itemsSize; i++) {
- item = mItems.get(i);
- if (item.isVisible()) mVisibleItems.add(item);
- }
-
- mIsVisibleItemsStale = false;
- mIsActionItemsStale = true;
-
- return mVisibleItems;
- }
-
- /**
- * This method determines which menu items get to be 'action items' that will appear
- * in an action bar and which items should be 'overflow items' in a secondary menu.
- * The rules are as follows:
- *
- * <p>Items are considered for inclusion in the order specified within the menu.
- * There is a limit of mMaxActionItems as a total count, optionally including the overflow
- * menu button itself. This is a soft limit; if an item shares a group ID with an item
- * previously included as an action item, the new item will stay with its group and become
- * an action item itself even if it breaks the max item count limit. This is done to
- * limit the conceptual complexity of the items presented within an action bar. Only a few
- * unrelated concepts should be presented to the user in this space, and groups are treated
- * as a single concept.
- *
- * <p>There is also a hard limit of consumed measurable space: mActionWidthLimit. This
- * limit may be broken by a single item that exceeds the remaining space, but no further
- * items may be added. If an item that is part of a group cannot fit within the remaining
- * measured width, the entire group will be demoted to overflow. This is done to ensure room
- * for navigation and other affordances in the action bar as well as reduce general UI clutter.
- *
- * <p>The space freed by demoting a full group cannot be consumed by future menu items.
- * Once items begin to overflow, all future items become overflow items as well. This is
- * to avoid inadvertent reordering that may break the app's intended design.
- */
- public void flagActionItems() {
- if (!mIsActionItemsStale) {
- return;
- }
-
- // Presenters flag action items as needed.
- boolean flagged = false;
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else {
- flagged |= presenter.flagActionItems();
- }
- }
-
- if (flagged) {
- mActionItems.clear();
- mNonActionItems.clear();
- ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
- final int itemsSize = visibleItems.size();
- for (int i = 0; i < itemsSize; i++) {
- MenuItemImpl item = visibleItems.get(i);
- if (item.isActionButton()) {
- mActionItems.add(item);
- } else {
- mNonActionItems.add(item);
- }
- }
- } else {
- // Nobody flagged anything, everything is a non-action item.
- // (This happens during a first pass with no action-item presenters.)
- mActionItems.clear();
- mNonActionItems.clear();
- mNonActionItems.addAll(getVisibleItems());
- }
- mIsActionItemsStale = false;
- }
-
- ArrayList<MenuItemImpl> getActionItems() {
- flagActionItems();
- return mActionItems;
- }
-
- ArrayList<MenuItemImpl> getNonActionItems() {
- flagActionItems();
- return mNonActionItems;
- }
-
- public void clearHeader() {
- mHeaderIcon = null;
- mHeaderTitle = null;
- mHeaderView = null;
-
- onItemsChanged(false);
- }
-
- private void setHeaderInternal(final int titleRes, final CharSequence title, final int iconRes,
- final Drawable icon, final View view) {
- final Resources r = getResources();
-
- if (view != null) {
- mHeaderView = view;
-
- // If using a custom view, then the title and icon aren't used
- mHeaderTitle = null;
- mHeaderIcon = null;
- } else {
- if (titleRes > 0) {
- mHeaderTitle = r.getText(titleRes);
- } else if (title != null) {
- mHeaderTitle = title;
- }
-
- if (iconRes > 0) {
- mHeaderIcon = r.getDrawable(iconRes);
- } else if (icon != null) {
- mHeaderIcon = icon;
- }
-
- // If using the title or icon, then a custom view isn't used
- mHeaderView = null;
- }
-
- // Notify of change
- onItemsChanged(false);
- }
-
- /**
- * Sets the header's title. This replaces the header view. Called by the
- * builder-style methods of subclasses.
- *
- * @param title The new title.
- * @return This MenuBuilder so additional setters can be called.
- */
- protected MenuBuilder setHeaderTitleInt(CharSequence title) {
- setHeaderInternal(0, title, 0, null, null);
- return this;
- }
-
- /**
- * Sets the header's title. This replaces the header view. Called by the
- * builder-style methods of subclasses.
- *
- * @param titleRes The new title (as a resource ID).
- * @return This MenuBuilder so additional setters can be called.
- */
- protected MenuBuilder setHeaderTitleInt(int titleRes) {
- setHeaderInternal(titleRes, null, 0, null, null);
- return this;
- }
-
- /**
- * Sets the header's icon. This replaces the header view. Called by the
- * builder-style methods of subclasses.
- *
- * @param icon The new icon.
- * @return This MenuBuilder so additional setters can be called.
- */
- protected MenuBuilder setHeaderIconInt(Drawable icon) {
- setHeaderInternal(0, null, 0, icon, null);
- return this;
- }
-
- /**
- * Sets the header's icon. This replaces the header view. Called by the
- * builder-style methods of subclasses.
- *
- * @param iconRes The new icon (as a resource ID).
- * @return This MenuBuilder so additional setters can be called.
- */
- protected MenuBuilder setHeaderIconInt(int iconRes) {
- setHeaderInternal(0, null, iconRes, null, null);
- return this;
- }
-
- /**
- * Sets the header's view. This replaces the title and icon. Called by the
- * builder-style methods of subclasses.
- *
- * @param view The new view.
- * @return This MenuBuilder so additional setters can be called.
- */
- protected MenuBuilder setHeaderViewInt(View view) {
- setHeaderInternal(0, null, 0, null, view);
- return this;
- }
-
- public CharSequence getHeaderTitle() {
- return mHeaderTitle;
- }
-
- public Drawable getHeaderIcon() {
- return mHeaderIcon;
- }
-
- public View getHeaderView() {
- return mHeaderView;
- }
-
- /**
- * Gets the root menu (if this is a submenu, find its root menu).
- * @return The root menu.
- */
- public MenuBuilder getRootMenu() {
- return this;
- }
-
- /**
- * Sets the current menu info that is set on all items added to this menu
- * (until this is called again with different menu info, in which case that
- * one will be added to all subsequent item additions).
- *
- * @param menuInfo The extra menu information to add.
- */
- public void setCurrentMenuInfo(ContextMenuInfo menuInfo) {
- mCurrentMenuInfo = menuInfo;
- }
-
- void setOptionalIconsVisible(boolean visible) {
- mOptionalIconsVisible = visible;
- }
-
- boolean getOptionalIconsVisible() {
- return mOptionalIconsVisible;
- }
-
- public boolean expandItemActionView(MenuItemImpl item) {
- if (mPresenters.isEmpty()) return false;
-
- boolean expanded = false;
-
- stopDispatchingItemsChanged();
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else if ((expanded = presenter.expandItemActionView(this, item))) {
- break;
- }
- }
- startDispatchingItemsChanged();
-
- if (expanded) {
- mExpandedItem = item;
- }
- return expanded;
- }
-
- public boolean collapseItemActionView(MenuItemImpl item) {
- if (mPresenters.isEmpty() || mExpandedItem != item) return false;
-
- boolean collapsed = false;
-
- stopDispatchingItemsChanged();
- for (WeakReference<MenuPresenter> ref : mPresenters) {
- final MenuPresenter presenter = ref.get();
- if (presenter == null) {
- mPresenters.remove(ref);
- } else if ((collapsed = presenter.collapseItemActionView(this, item))) {
- break;
- }
- }
- startDispatchingItemsChanged();
-
- if (collapsed) {
- mExpandedItem = null;
- }
- return collapsed;
- }
-
- public MenuItemImpl getExpandedItem() {
- return mExpandedItem;
- }
-
- public boolean bindNativeOverflow(android.view.Menu menu, android.view.MenuItem.OnMenuItemClickListener listener, HashMap<android.view.MenuItem, MenuItemImpl> map) {
- final List<MenuItemImpl> nonActionItems = getNonActionItems();
- if (nonActionItems == null || nonActionItems.size() == 0) {
- return false;
- }
-
- boolean visible = false;
- menu.clear();
- for (MenuItemImpl nonActionItem : nonActionItems) {
- if (!nonActionItem.isVisible()) {
- continue;
- }
- visible = true;
-
- android.view.MenuItem nativeItem;
- if (nonActionItem.hasSubMenu()) {
- android.view.SubMenu nativeSub = menu.addSubMenu(nonActionItem.getGroupId(), nonActionItem.getItemId(),
- nonActionItem.getOrder(), nonActionItem.getTitle());
-
- SubMenuBuilder subMenu = (SubMenuBuilder)nonActionItem.getSubMenu();
- for (MenuItemImpl subItem : subMenu.getVisibleItems()) {
- android.view.MenuItem nativeSubItem = nativeSub.add(subItem.getGroupId(), subItem.getItemId(),
- subItem.getOrder(), subItem.getTitle());
-
- nativeSubItem.setIcon(subItem.getIcon());
- nativeSubItem.setOnMenuItemClickListener(listener);
- nativeSubItem.setEnabled(subItem.isEnabled());
- nativeSubItem.setIntent(subItem.getIntent());
- nativeSubItem.setNumericShortcut(subItem.getNumericShortcut());
- nativeSubItem.setAlphabeticShortcut(subItem.getAlphabeticShortcut());
- nativeSubItem.setTitleCondensed(subItem.getTitleCondensed());
- nativeSubItem.setCheckable(subItem.isCheckable());
- nativeSubItem.setChecked(subItem.isChecked());
-
- if (subItem.isExclusiveCheckable()) {
- nativeSub.setGroupCheckable(subItem.getGroupId(), true, true);
- }
-
- map.put(nativeSubItem, subItem);
- }
-
- nativeItem = nativeSub.getItem();
- } else {
- nativeItem = menu.add(nonActionItem.getGroupId(), nonActionItem.getItemId(),
- nonActionItem.getOrder(), nonActionItem.getTitle());
- }
- nativeItem.setIcon(nonActionItem.getIcon());
- nativeItem.setOnMenuItemClickListener(listener);
- nativeItem.setEnabled(nonActionItem.isEnabled());
- nativeItem.setIntent(nonActionItem.getIntent());
- nativeItem.setNumericShortcut(nonActionItem.getNumericShortcut());
- nativeItem.setAlphabeticShortcut(nonActionItem.getAlphabeticShortcut());
- nativeItem.setTitleCondensed(nonActionItem.getTitleCondensed());
- nativeItem.setCheckable(nonActionItem.isCheckable());
- nativeItem.setChecked(nonActionItem.isChecked());
-
- if (nonActionItem.isExclusiveCheckable()) {
- menu.setGroupCheckable(nonActionItem.getGroupId(), true, true);
- }
-
- map.put(nativeItem, nonActionItem);
- }
- return visible;
- }
-}