michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.menu; michael@0: michael@0: import org.mozilla.gecko.AppConstants; michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.util.ThreadUtils; michael@0: import org.mozilla.gecko.util.ThreadUtils.AssertBehavior; michael@0: import org.mozilla.gecko.widget.GeckoActionProvider; michael@0: michael@0: import android.content.ComponentName; michael@0: import android.content.Context; michael@0: import android.content.Intent; michael@0: import android.util.AttributeSet; michael@0: import android.util.Log; michael@0: import android.view.ActionProvider; michael@0: import android.view.KeyEvent; michael@0: import android.view.LayoutInflater; michael@0: import android.view.Menu; michael@0: import android.view.MenuItem; michael@0: import android.view.SubMenu; michael@0: import android.view.View; michael@0: import android.view.ViewGroup; michael@0: import android.widget.AdapterView; michael@0: import android.widget.BaseAdapter; michael@0: import android.widget.LinearLayout; michael@0: import android.widget.ListView; michael@0: michael@0: import java.util.ArrayList; michael@0: import java.util.HashMap; michael@0: import java.util.List; michael@0: import java.util.Map; michael@0: michael@0: public class GeckoMenu extends ListView michael@0: implements Menu, michael@0: AdapterView.OnItemClickListener, michael@0: GeckoMenuItem.OnShowAsActionChangedListener { michael@0: private static final String LOGTAG = "GeckoMenu"; michael@0: michael@0: /** michael@0: * Controls whether off-UI-thread method calls in this class cause an michael@0: * exception or just logging. michael@0: */ michael@0: private static final AssertBehavior THREAD_ASSERT_BEHAVIOR = AppConstants.RELEASE_BUILD ? AssertBehavior.NONE : AssertBehavior.THROW; michael@0: michael@0: /* michael@0: * A callback for a menu item selected event. michael@0: */ michael@0: public static interface Callback { michael@0: // Called when a menu item is selected, with the actual menu item as the argument. michael@0: public boolean onMenuItemSelected(MenuItem item); michael@0: } michael@0: michael@0: /* michael@0: * An interface for a presenter to show the menu. michael@0: * Either an Activity or a View can be a presenter, that can watch for events michael@0: * and show/hide menu. michael@0: */ michael@0: public static interface MenuPresenter { michael@0: // Open the menu. michael@0: public void openMenu(); michael@0: michael@0: // Show the actual view containing the menu items. This can either be a parent or sub-menu. michael@0: public void showMenu(View menu); michael@0: michael@0: // Close the menu. michael@0: public void closeMenu(); michael@0: } michael@0: michael@0: /* michael@0: * An interface for a presenter of action-items. michael@0: * Either an Activity or a View can be a presenter, that can watch for events michael@0: * and add/remove action-items. If not ActionItemBarPresenter, the menu uses a michael@0: * DefaultActionItemBar, that shows the action-items as a header over list-view. michael@0: */ michael@0: public static interface ActionItemBarPresenter { michael@0: // Add an action-item. michael@0: public boolean addActionItem(View actionItem); michael@0: michael@0: // Remove an action-item. michael@0: public void removeActionItem(View actionItem); michael@0: } michael@0: michael@0: protected static final int NO_ID = 0; michael@0: michael@0: // List of all menu items. michael@0: private List mItems; michael@0: michael@0: // Map of "always" action-items in action-bar and their views. michael@0: private Map mPrimaryActionItems; michael@0: michael@0: // Map of "ifRoom" action-items in action-bar and their views. michael@0: private Map mSecondaryActionItems; michael@0: michael@0: // Reference to a callback for menu events. michael@0: private Callback mCallback; michael@0: michael@0: // Reference to menu presenter. michael@0: private MenuPresenter mMenuPresenter; michael@0: michael@0: // Reference to "always" action-items bar in action-bar. michael@0: private ActionItemBarPresenter mPrimaryActionItemBar; michael@0: michael@0: // Reference to "ifRoom" action-items bar in action-bar. michael@0: private final ActionItemBarPresenter mSecondaryActionItemBar; michael@0: michael@0: // Adapter to hold the list of menu items. michael@0: private MenuItemsAdapter mAdapter; michael@0: michael@0: // Show/hide icons in the list. michael@0: private boolean mShowIcons; michael@0: michael@0: public GeckoMenu(Context context) { michael@0: this(context, null); michael@0: } michael@0: michael@0: public GeckoMenu(Context context, AttributeSet attrs) { michael@0: this(context, attrs, R.attr.geckoMenuListViewStyle); michael@0: } michael@0: michael@0: public GeckoMenu(Context context, AttributeSet attrs, int defStyle) { michael@0: super(context, attrs, defStyle); michael@0: michael@0: setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, michael@0: LayoutParams.FILL_PARENT)); michael@0: michael@0: // Attach an adapter. michael@0: mAdapter = new MenuItemsAdapter(); michael@0: setAdapter(mAdapter); michael@0: setOnItemClickListener(this); michael@0: michael@0: mShowIcons = false; michael@0: mItems = new ArrayList(); michael@0: mPrimaryActionItems = new HashMap(); michael@0: mSecondaryActionItems = new HashMap(); michael@0: michael@0: mPrimaryActionItemBar = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null); michael@0: mSecondaryActionItemBar = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_secondary_action_bar, null); michael@0: } michael@0: michael@0: private static void assertOnUiThread() { michael@0: ThreadUtils.assertOnUiThread(THREAD_ASSERT_BEHAVIOR); michael@0: } michael@0: michael@0: @Override michael@0: public MenuItem add(CharSequence title) { michael@0: GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, title); michael@0: addItem(menuItem); michael@0: return menuItem; michael@0: } michael@0: michael@0: @Override michael@0: public MenuItem add(int groupId, int itemId, int order, int titleRes) { michael@0: GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, titleRes); michael@0: addItem(menuItem); michael@0: return menuItem; michael@0: } michael@0: michael@0: @Override michael@0: public MenuItem add(int titleRes) { michael@0: GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, titleRes); michael@0: addItem(menuItem); michael@0: return menuItem; michael@0: } michael@0: michael@0: @Override michael@0: public MenuItem add(int groupId, int itemId, int order, CharSequence title) { michael@0: GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, title); michael@0: addItem(menuItem); michael@0: return menuItem; michael@0: } michael@0: michael@0: private void addItem(GeckoMenuItem menuItem) { michael@0: assertOnUiThread(); michael@0: menuItem.setOnShowAsActionChangedListener(this); michael@0: mAdapter.addMenuItem(menuItem); michael@0: mItems.add(menuItem); michael@0: } michael@0: michael@0: private boolean addActionItem(final GeckoMenuItem menuItem) { michael@0: assertOnUiThread(); michael@0: menuItem.setOnShowAsActionChangedListener(this); michael@0: michael@0: final View actionView = menuItem.getActionView(); michael@0: final int actionEnum = menuItem.getActionEnum(); michael@0: boolean added = false; michael@0: michael@0: if (actionEnum == GeckoMenuItem.SHOW_AS_ACTION_ALWAYS) { michael@0: if (mPrimaryActionItems.size() == 0 && michael@0: mPrimaryActionItemBar instanceof DefaultActionItemBar) { michael@0: // Reset the adapter before adding the header view to a list. michael@0: setAdapter(null); michael@0: addHeaderView((DefaultActionItemBar) mPrimaryActionItemBar); michael@0: setAdapter(mAdapter); michael@0: } michael@0: michael@0: if (added = mPrimaryActionItemBar.addActionItem(actionView)) { michael@0: mPrimaryActionItems.put(menuItem, actionView); michael@0: mItems.add(menuItem); michael@0: } michael@0: } else if (actionEnum == GeckoMenuItem.SHOW_AS_ACTION_IF_ROOM) { michael@0: if (mSecondaryActionItems.size() == 0) { michael@0: // Reset the adapter before adding the header view to a list. michael@0: setAdapter(null); michael@0: addHeaderView((DefaultActionItemBar) mSecondaryActionItemBar); michael@0: setAdapter(mAdapter); michael@0: } michael@0: michael@0: if (added = mSecondaryActionItemBar.addActionItem(actionView)) { michael@0: mSecondaryActionItems.put(menuItem, actionView); michael@0: mItems.add(menuItem); michael@0: } michael@0: } michael@0: michael@0: // Set the listeners. michael@0: if (actionView instanceof MenuItemActionBar) { michael@0: ((MenuItemActionBar) actionView).setOnClickListener(new View.OnClickListener() { michael@0: @Override michael@0: public void onClick(View view) { michael@0: handleMenuItemClick(menuItem); michael@0: } michael@0: }); michael@0: } else if (actionView instanceof MenuItemActionView) { michael@0: ((MenuItemActionView) actionView).setMenuItemClickListener(new View.OnClickListener() { michael@0: @Override michael@0: public void onClick(View view) { michael@0: handleMenuItemClick(menuItem); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: return added; michael@0: } michael@0: michael@0: @Override michael@0: public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) { michael@0: return 0; michael@0: } michael@0: michael@0: @Override michael@0: public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { michael@0: MenuItem menuItem = add(groupId, itemId, order, title); michael@0: return addSubMenu(menuItem); michael@0: } michael@0: michael@0: @Override michael@0: public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { michael@0: MenuItem menuItem = add(groupId, itemId, order, titleRes); michael@0: return addSubMenu(menuItem); michael@0: } michael@0: michael@0: @Override michael@0: public SubMenu addSubMenu(CharSequence title) { michael@0: MenuItem menuItem = add(title); michael@0: return addSubMenu(menuItem); michael@0: } michael@0: michael@0: @Override michael@0: public SubMenu addSubMenu(int titleRes) { michael@0: MenuItem menuItem = add(titleRes); michael@0: return addSubMenu(menuItem); michael@0: } michael@0: michael@0: private SubMenu addSubMenu(MenuItem menuItem) { michael@0: GeckoSubMenu subMenu = new GeckoSubMenu(getContext()); michael@0: subMenu.setMenuItem(menuItem); michael@0: subMenu.setCallback(mCallback); michael@0: subMenu.setMenuPresenter(mMenuPresenter); michael@0: ((GeckoMenuItem) menuItem).setSubMenu(subMenu); michael@0: return subMenu; michael@0: } michael@0: michael@0: private void removePrimaryActionBarView() { michael@0: // Reset the adapter before removing the header view from a list. michael@0: setAdapter(null); michael@0: removeHeaderView((DefaultActionItemBar) mPrimaryActionItemBar); michael@0: setAdapter(mAdapter); michael@0: } michael@0: michael@0: private void removeSecondaryActionBarView() { michael@0: // Reset the adapter before removing the header view from a list. michael@0: setAdapter(null); michael@0: removeHeaderView((DefaultActionItemBar) mSecondaryActionItemBar); michael@0: setAdapter(mAdapter); michael@0: } michael@0: michael@0: @Override michael@0: public void clear() { michael@0: assertOnUiThread(); michael@0: for (GeckoMenuItem menuItem : mItems) { michael@0: if (menuItem.hasSubMenu()) { michael@0: SubMenu sub = menuItem.getSubMenu(); michael@0: if (sub == null) { michael@0: continue; michael@0: } michael@0: try { michael@0: sub.clear(); michael@0: } catch (Exception ex) { michael@0: Log.e(LOGTAG, "Couldn't clear submenu.", ex); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mAdapter.clear(); michael@0: mItems.clear(); michael@0: michael@0: /* michael@0: * Reinflating the menu will re-add any action items to the toolbar, so michael@0: * remove the old ones. This also ensures that any text associated with michael@0: * these is switched to the correct locale. michael@0: */ michael@0: if (mPrimaryActionItemBar != null) { michael@0: for (View item : mPrimaryActionItems.values()) { michael@0: mPrimaryActionItemBar.removeActionItem(item); michael@0: } michael@0: } michael@0: mPrimaryActionItems.clear(); michael@0: michael@0: if (mSecondaryActionItemBar != null) { michael@0: for (View item : mSecondaryActionItems.values()) { michael@0: mSecondaryActionItemBar.removeActionItem(item); michael@0: } michael@0: } michael@0: mSecondaryActionItems.clear(); michael@0: michael@0: // Remove the view, too -- the first addActionItem will re-add it, michael@0: // and this is simpler than changing that logic. michael@0: if (mPrimaryActionItemBar instanceof DefaultActionItemBar) { michael@0: removePrimaryActionBarView(); michael@0: } michael@0: michael@0: removeSecondaryActionBarView(); michael@0: } michael@0: michael@0: @Override michael@0: public void close() { michael@0: if (mMenuPresenter != null) michael@0: mMenuPresenter.closeMenu(); michael@0: } michael@0: michael@0: private void showMenu(View viewForMenu) { michael@0: if (mMenuPresenter != null) michael@0: mMenuPresenter.showMenu(viewForMenu); michael@0: } michael@0: michael@0: @Override michael@0: public MenuItem findItem(int id) { michael@0: for (GeckoMenuItem menuItem : mItems) { michael@0: if (menuItem.getItemId() == id) { michael@0: return menuItem; michael@0: } else if (menuItem.hasSubMenu()) { michael@0: if (!menuItem.hasActionProvider()) { michael@0: SubMenu subMenu = menuItem.getSubMenu(); michael@0: MenuItem item = subMenu.findItem(id); michael@0: if (item != null) michael@0: return item; michael@0: } michael@0: } michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: @Override michael@0: public MenuItem getItem(int index) { michael@0: if (index < mItems.size()) michael@0: return mItems.get(index); michael@0: michael@0: return null; michael@0: } michael@0: michael@0: @Override michael@0: public boolean hasVisibleItems() { michael@0: assertOnUiThread(); michael@0: for (GeckoMenuItem menuItem : mItems) { michael@0: if (menuItem.isVisible() && michael@0: !mPrimaryActionItems.containsKey(menuItem) && michael@0: !mSecondaryActionItems.containsKey(menuItem)) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: @Override michael@0: public boolean isShortcutKey(int keyCode, KeyEvent event) { michael@0: return true; michael@0: } michael@0: michael@0: @Override michael@0: public boolean performIdentifierAction(int id, int flags) { michael@0: return false; michael@0: } michael@0: michael@0: @Override michael@0: public boolean performShortcut(int keyCode, KeyEvent event, int flags) { michael@0: return false; michael@0: } michael@0: michael@0: @Override michael@0: public void removeGroup(int groupId) { michael@0: } michael@0: michael@0: @Override michael@0: public void removeItem(int id) { michael@0: assertOnUiThread(); michael@0: GeckoMenuItem item = (GeckoMenuItem) findItem(id); michael@0: if (item == null) michael@0: return; michael@0: michael@0: // Remove it from any sub-menu. michael@0: for (GeckoMenuItem menuItem : mItems) { michael@0: if (menuItem.hasSubMenu()) { michael@0: SubMenu subMenu = menuItem.getSubMenu(); michael@0: if (subMenu != null && subMenu.findItem(id) != null) { michael@0: subMenu.removeItem(id); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Remove it from own menu. michael@0: if (mPrimaryActionItems.containsKey(item)) { michael@0: if (mPrimaryActionItemBar != null) michael@0: mPrimaryActionItemBar.removeActionItem(mPrimaryActionItems.get(item)); michael@0: michael@0: mPrimaryActionItems.remove(item); michael@0: mItems.remove(item); michael@0: michael@0: if (mPrimaryActionItems.size() == 0 && michael@0: mPrimaryActionItemBar instanceof DefaultActionItemBar) { michael@0: removePrimaryActionBarView(); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (mSecondaryActionItems.containsKey(item)) { michael@0: if (mSecondaryActionItemBar != null) michael@0: mSecondaryActionItemBar.removeActionItem(mSecondaryActionItems.get(item)); michael@0: michael@0: mSecondaryActionItems.remove(item); michael@0: mItems.remove(item); michael@0: michael@0: if (mSecondaryActionItems.size() == 0) { michael@0: removeSecondaryActionBarView(); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: mAdapter.removeMenuItem(item); michael@0: mItems.remove(item); michael@0: } michael@0: michael@0: @Override michael@0: public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { michael@0: } michael@0: michael@0: @Override michael@0: public void setGroupEnabled(int group, boolean enabled) { michael@0: } michael@0: michael@0: @Override michael@0: public void setGroupVisible(int group, boolean visible) { michael@0: } michael@0: michael@0: @Override michael@0: public void setQwertyMode(boolean isQwerty) { michael@0: } michael@0: michael@0: @Override michael@0: public int size() { michael@0: return mItems.size(); michael@0: } michael@0: michael@0: @Override michael@0: public boolean hasActionItemBar() { michael@0: return (mPrimaryActionItemBar != null) && (mSecondaryActionItemBar != null); michael@0: } michael@0: michael@0: @Override michael@0: public void onShowAsActionChanged(GeckoMenuItem item) { michael@0: removeItem(item.getItemId()); michael@0: michael@0: if (item.isActionItem() && addActionItem(item)) { michael@0: return; michael@0: } michael@0: michael@0: addItem(item); michael@0: } michael@0: michael@0: public void onItemChanged(GeckoMenuItem item) { michael@0: assertOnUiThread(); michael@0: if (item.isActionItem()) { michael@0: final View actionView; michael@0: if (item.getActionEnum() == GeckoMenuItem.SHOW_AS_ACTION_ALWAYS) { michael@0: actionView = mPrimaryActionItems.get(item); michael@0: } else { michael@0: actionView = mSecondaryActionItems.get(item); michael@0: } michael@0: michael@0: if (actionView != null) { michael@0: // The update could be coming from the background thread. michael@0: // Post a runnable on the UI thread of the view for it to update. michael@0: final GeckoMenuItem menuItem = item; michael@0: actionView.post(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: if (menuItem.isVisible()) { michael@0: actionView.setVisibility(View.VISIBLE); michael@0: if (actionView instanceof MenuItemActionBar) { michael@0: ((MenuItemActionBar) actionView).initialize(menuItem); michael@0: } else { michael@0: ((MenuItemActionView) actionView).initialize(menuItem); michael@0: } michael@0: } else { michael@0: actionView.setVisibility(View.GONE); michael@0: } michael@0: } michael@0: }); michael@0: } michael@0: } else { michael@0: mAdapter.notifyDataSetChanged(); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onItemClick(AdapterView parent, View view, int position, long id) { michael@0: // We might be showing headers. Account them while using the position. michael@0: position -= getHeaderViewsCount(); michael@0: michael@0: GeckoMenuItem item = mAdapter.getItem(position); michael@0: handleMenuItemClick(item); michael@0: } michael@0: michael@0: private void handleMenuItemClick(GeckoMenuItem item) { michael@0: if (!item.isEnabled()) michael@0: return; michael@0: michael@0: if (item.invoke()) { michael@0: close(); michael@0: } else if (item.hasSubMenu()) { michael@0: // Refresh the submenu for the provider. michael@0: GeckoActionProvider provider = item.getGeckoActionProvider(); michael@0: if (provider != null) { michael@0: GeckoSubMenu subMenu = new GeckoSubMenu(getContext()); michael@0: subMenu.setShowIcons(true); michael@0: provider.onPrepareSubMenu(subMenu); michael@0: item.setSubMenu(subMenu); michael@0: } michael@0: michael@0: // Show the submenu. michael@0: GeckoSubMenu subMenu = (GeckoSubMenu) item.getSubMenu(); michael@0: showMenu(subMenu); michael@0: } else { michael@0: close(); michael@0: mCallback.onMenuItemSelected(item); michael@0: } michael@0: } michael@0: michael@0: public Callback getCallback() { michael@0: return mCallback; michael@0: } michael@0: michael@0: public MenuPresenter getMenuPresenter() { michael@0: return mMenuPresenter; michael@0: } michael@0: michael@0: public void setCallback(Callback callback) { michael@0: mCallback = callback; michael@0: michael@0: // Update the submenus just in case this changes on the fly. michael@0: for (GeckoMenuItem menuItem : mItems) { michael@0: if (menuItem.hasSubMenu()) { michael@0: GeckoSubMenu subMenu = (GeckoSubMenu) menuItem.getSubMenu(); michael@0: subMenu.setCallback(mCallback); michael@0: } michael@0: } michael@0: } michael@0: michael@0: public void setMenuPresenter(MenuPresenter presenter) { michael@0: mMenuPresenter = presenter; michael@0: michael@0: // Update the submenus just in case this changes on the fly. michael@0: for (GeckoMenuItem menuItem : mItems) { michael@0: if (menuItem.hasSubMenu()) { michael@0: GeckoSubMenu subMenu = (GeckoSubMenu) menuItem.getSubMenu(); michael@0: subMenu.setMenuPresenter(mMenuPresenter); michael@0: } michael@0: } michael@0: } michael@0: michael@0: public void setActionItemBarPresenter(ActionItemBarPresenter presenter) { michael@0: mPrimaryActionItemBar = presenter; michael@0: } michael@0: michael@0: public void setShowIcons(boolean show) { michael@0: if (mShowIcons != show) { michael@0: mShowIcons = show; michael@0: mAdapter.notifyDataSetChanged(); michael@0: } michael@0: } michael@0: michael@0: // Action Items are added to the header view by default. michael@0: // URL bar can register itself as a presenter, in case it has a different place to show them. michael@0: public static class DefaultActionItemBar extends LinearLayout michael@0: implements ActionItemBarPresenter { michael@0: private final int mRowHeight; michael@0: private float mWeightSum; michael@0: michael@0: public DefaultActionItemBar(Context context) { michael@0: this(context, null); michael@0: } michael@0: michael@0: public DefaultActionItemBar(Context context, AttributeSet attrs) { michael@0: super(context, attrs); michael@0: michael@0: mRowHeight = getResources().getDimensionPixelSize(R.dimen.menu_item_row_height); michael@0: } michael@0: michael@0: @Override michael@0: public boolean addActionItem(View actionItem) { michael@0: ViewGroup.LayoutParams actualParams = actionItem.getLayoutParams(); michael@0: LinearLayout.LayoutParams params; michael@0: michael@0: if (actualParams != null) { michael@0: params = new LinearLayout.LayoutParams(actionItem.getLayoutParams()); michael@0: params.width = 0; michael@0: } else { michael@0: params = new LinearLayout.LayoutParams(0, mRowHeight); michael@0: } michael@0: michael@0: if (actionItem instanceof MenuItemActionView) { michael@0: params.weight = ((MenuItemActionView) actionItem).getChildCount(); michael@0: } else { michael@0: params.weight = 1.0f; michael@0: } michael@0: michael@0: mWeightSum += params.weight; michael@0: michael@0: actionItem.setLayoutParams(params); michael@0: addView(actionItem); michael@0: setWeightSum(mWeightSum); michael@0: return true; michael@0: } michael@0: michael@0: @Override michael@0: public void removeActionItem(View actionItem) { michael@0: if (indexOfChild(actionItem) != -1) { michael@0: LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) actionItem.getLayoutParams(); michael@0: mWeightSum -= params.weight; michael@0: removeView(actionItem); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Adapter to bind menu items to the list. michael@0: private class MenuItemsAdapter extends BaseAdapter { michael@0: private static final int VIEW_TYPE_DEFAULT = 0; michael@0: private static final int VIEW_TYPE_ACTION_MODE = 1; michael@0: michael@0: private List mItems; michael@0: michael@0: public MenuItemsAdapter() { michael@0: mItems = new ArrayList(); michael@0: } michael@0: michael@0: @Override michael@0: public int getCount() { michael@0: if (mItems == null) michael@0: return 0; michael@0: michael@0: int visibleCount = 0; michael@0: for (GeckoMenuItem item : mItems) { michael@0: if (item.isVisible()) michael@0: visibleCount++; michael@0: } michael@0: michael@0: return visibleCount; michael@0: } michael@0: michael@0: @Override michael@0: public GeckoMenuItem getItem(int position) { michael@0: for (GeckoMenuItem item : mItems) { michael@0: if (item.isVisible()) { michael@0: position--; michael@0: michael@0: if (position < 0) michael@0: return item; michael@0: } michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: michael@0: @Override michael@0: public long getItemId(int position) { michael@0: return position; michael@0: } michael@0: michael@0: @Override michael@0: public View getView(int position, View convertView, ViewGroup parent) { michael@0: GeckoMenuItem item = getItem(position); michael@0: GeckoMenuItem.Layout view = null; michael@0: michael@0: // Try to re-use the view. michael@0: if (convertView == null && getItemViewType(position) == VIEW_TYPE_DEFAULT) { michael@0: view = new MenuItemDefault(parent.getContext(), null); michael@0: } else { michael@0: view = (GeckoMenuItem.Layout) convertView; michael@0: } michael@0: michael@0: if (view == null || view instanceof MenuItemActionView) { michael@0: // Always get from the menu item. michael@0: // This will ensure that the default activity is refreshed. michael@0: view = (MenuItemActionView) item.getActionView(); michael@0: michael@0: // ListView will not perform an item click if the row has a focusable view in it. michael@0: // Hence, forward the click event on the menu item in the action-view to the ListView. michael@0: final View actionView = (View) view; michael@0: final int pos = position; michael@0: final long id = getItemId(position); michael@0: ((MenuItemActionView) view).setMenuItemClickListener(new View.OnClickListener() { michael@0: @Override michael@0: public void onClick(View v) { michael@0: GeckoMenu listView = GeckoMenu.this; michael@0: listView.performItemClick(actionView, pos + listView.getHeaderViewsCount(), id); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: // Initialize the view. michael@0: view.setShowIcon(mShowIcons); michael@0: view.initialize(item); michael@0: return (View) view; michael@0: } michael@0: michael@0: @Override michael@0: public int getItemViewType(int position) { michael@0: return getItem(position).getGeckoActionProvider() == null ? VIEW_TYPE_DEFAULT : VIEW_TYPE_ACTION_MODE; michael@0: } michael@0: michael@0: @Override michael@0: public int getViewTypeCount() { michael@0: return 2; michael@0: } michael@0: michael@0: @Override michael@0: public boolean hasStableIds() { michael@0: return false; michael@0: } michael@0: michael@0: @Override michael@0: public boolean areAllItemsEnabled() { michael@0: // Setting this to true is a workaround to fix disappearing michael@0: // dividers in the menu (bug 963249). michael@0: return true; michael@0: } michael@0: michael@0: @Override michael@0: public boolean isEnabled(int position) { michael@0: return getItem(position).isEnabled(); michael@0: } michael@0: michael@0: public void addMenuItem(GeckoMenuItem menuItem) { michael@0: if (mItems.contains(menuItem)) michael@0: return; michael@0: michael@0: // Insert it in proper order. michael@0: int index = 0; michael@0: for (GeckoMenuItem item : mItems) { michael@0: if (item.getOrder() > menuItem.getOrder()) { michael@0: mItems.add(index, menuItem); michael@0: notifyDataSetChanged(); michael@0: return; michael@0: } else { michael@0: index++; michael@0: } michael@0: } michael@0: michael@0: // Add the menuItem at the end. michael@0: mItems.add(menuItem); michael@0: notifyDataSetChanged(); michael@0: } michael@0: michael@0: public void removeMenuItem(GeckoMenuItem menuItem) { michael@0: // Remove it from the list. michael@0: mItems.remove(menuItem); michael@0: notifyDataSetChanged(); michael@0: } michael@0: michael@0: public void clear() { michael@0: mItems.clear(); michael@0: notifyDataSetChanged(); michael@0: } michael@0: michael@0: public GeckoMenuItem getMenuItem(int id) { michael@0: for (GeckoMenuItem item : mItems) { michael@0: if (item.getItemId() == id) michael@0: return item; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: } michael@0: }