1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/menu/GeckoMenu.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,808 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.menu; 1.9 + 1.10 +import org.mozilla.gecko.AppConstants; 1.11 +import org.mozilla.gecko.R; 1.12 +import org.mozilla.gecko.util.ThreadUtils; 1.13 +import org.mozilla.gecko.util.ThreadUtils.AssertBehavior; 1.14 +import org.mozilla.gecko.widget.GeckoActionProvider; 1.15 + 1.16 +import android.content.ComponentName; 1.17 +import android.content.Context; 1.18 +import android.content.Intent; 1.19 +import android.util.AttributeSet; 1.20 +import android.util.Log; 1.21 +import android.view.ActionProvider; 1.22 +import android.view.KeyEvent; 1.23 +import android.view.LayoutInflater; 1.24 +import android.view.Menu; 1.25 +import android.view.MenuItem; 1.26 +import android.view.SubMenu; 1.27 +import android.view.View; 1.28 +import android.view.ViewGroup; 1.29 +import android.widget.AdapterView; 1.30 +import android.widget.BaseAdapter; 1.31 +import android.widget.LinearLayout; 1.32 +import android.widget.ListView; 1.33 + 1.34 +import java.util.ArrayList; 1.35 +import java.util.HashMap; 1.36 +import java.util.List; 1.37 +import java.util.Map; 1.38 + 1.39 +public class GeckoMenu extends ListView 1.40 + implements Menu, 1.41 + AdapterView.OnItemClickListener, 1.42 + GeckoMenuItem.OnShowAsActionChangedListener { 1.43 + private static final String LOGTAG = "GeckoMenu"; 1.44 + 1.45 + /** 1.46 + * Controls whether off-UI-thread method calls in this class cause an 1.47 + * exception or just logging. 1.48 + */ 1.49 + private static final AssertBehavior THREAD_ASSERT_BEHAVIOR = AppConstants.RELEASE_BUILD ? AssertBehavior.NONE : AssertBehavior.THROW; 1.50 + 1.51 + /* 1.52 + * A callback for a menu item selected event. 1.53 + */ 1.54 + public static interface Callback { 1.55 + // Called when a menu item is selected, with the actual menu item as the argument. 1.56 + public boolean onMenuItemSelected(MenuItem item); 1.57 + } 1.58 + 1.59 + /* 1.60 + * An interface for a presenter to show the menu. 1.61 + * Either an Activity or a View can be a presenter, that can watch for events 1.62 + * and show/hide menu. 1.63 + */ 1.64 + public static interface MenuPresenter { 1.65 + // Open the menu. 1.66 + public void openMenu(); 1.67 + 1.68 + // Show the actual view containing the menu items. This can either be a parent or sub-menu. 1.69 + public void showMenu(View menu); 1.70 + 1.71 + // Close the menu. 1.72 + public void closeMenu(); 1.73 + } 1.74 + 1.75 + /* 1.76 + * An interface for a presenter of action-items. 1.77 + * Either an Activity or a View can be a presenter, that can watch for events 1.78 + * and add/remove action-items. If not ActionItemBarPresenter, the menu uses a 1.79 + * DefaultActionItemBar, that shows the action-items as a header over list-view. 1.80 + */ 1.81 + public static interface ActionItemBarPresenter { 1.82 + // Add an action-item. 1.83 + public boolean addActionItem(View actionItem); 1.84 + 1.85 + // Remove an action-item. 1.86 + public void removeActionItem(View actionItem); 1.87 + } 1.88 + 1.89 + protected static final int NO_ID = 0; 1.90 + 1.91 + // List of all menu items. 1.92 + private List<GeckoMenuItem> mItems; 1.93 + 1.94 + // Map of "always" action-items in action-bar and their views. 1.95 + private Map<GeckoMenuItem, View> mPrimaryActionItems; 1.96 + 1.97 + // Map of "ifRoom" action-items in action-bar and their views. 1.98 + private Map<GeckoMenuItem, View> mSecondaryActionItems; 1.99 + 1.100 + // Reference to a callback for menu events. 1.101 + private Callback mCallback; 1.102 + 1.103 + // Reference to menu presenter. 1.104 + private MenuPresenter mMenuPresenter; 1.105 + 1.106 + // Reference to "always" action-items bar in action-bar. 1.107 + private ActionItemBarPresenter mPrimaryActionItemBar; 1.108 + 1.109 + // Reference to "ifRoom" action-items bar in action-bar. 1.110 + private final ActionItemBarPresenter mSecondaryActionItemBar; 1.111 + 1.112 + // Adapter to hold the list of menu items. 1.113 + private MenuItemsAdapter mAdapter; 1.114 + 1.115 + // Show/hide icons in the list. 1.116 + private boolean mShowIcons; 1.117 + 1.118 + public GeckoMenu(Context context) { 1.119 + this(context, null); 1.120 + } 1.121 + 1.122 + public GeckoMenu(Context context, AttributeSet attrs) { 1.123 + this(context, attrs, R.attr.geckoMenuListViewStyle); 1.124 + } 1.125 + 1.126 + public GeckoMenu(Context context, AttributeSet attrs, int defStyle) { 1.127 + super(context, attrs, defStyle); 1.128 + 1.129 + setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 1.130 + LayoutParams.FILL_PARENT)); 1.131 + 1.132 + // Attach an adapter. 1.133 + mAdapter = new MenuItemsAdapter(); 1.134 + setAdapter(mAdapter); 1.135 + setOnItemClickListener(this); 1.136 + 1.137 + mShowIcons = false; 1.138 + mItems = new ArrayList<GeckoMenuItem>(); 1.139 + mPrimaryActionItems = new HashMap<GeckoMenuItem, View>(); 1.140 + mSecondaryActionItems = new HashMap<GeckoMenuItem, View>(); 1.141 + 1.142 + mPrimaryActionItemBar = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null); 1.143 + mSecondaryActionItemBar = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_secondary_action_bar, null); 1.144 + } 1.145 + 1.146 + private static void assertOnUiThread() { 1.147 + ThreadUtils.assertOnUiThread(THREAD_ASSERT_BEHAVIOR); 1.148 + } 1.149 + 1.150 + @Override 1.151 + public MenuItem add(CharSequence title) { 1.152 + GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, title); 1.153 + addItem(menuItem); 1.154 + return menuItem; 1.155 + } 1.156 + 1.157 + @Override 1.158 + public MenuItem add(int groupId, int itemId, int order, int titleRes) { 1.159 + GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, titleRes); 1.160 + addItem(menuItem); 1.161 + return menuItem; 1.162 + } 1.163 + 1.164 + @Override 1.165 + public MenuItem add(int titleRes) { 1.166 + GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, titleRes); 1.167 + addItem(menuItem); 1.168 + return menuItem; 1.169 + } 1.170 + 1.171 + @Override 1.172 + public MenuItem add(int groupId, int itemId, int order, CharSequence title) { 1.173 + GeckoMenuItem menuItem = new GeckoMenuItem(this, itemId, order, title); 1.174 + addItem(menuItem); 1.175 + return menuItem; 1.176 + } 1.177 + 1.178 + private void addItem(GeckoMenuItem menuItem) { 1.179 + assertOnUiThread(); 1.180 + menuItem.setOnShowAsActionChangedListener(this); 1.181 + mAdapter.addMenuItem(menuItem); 1.182 + mItems.add(menuItem); 1.183 + } 1.184 + 1.185 + private boolean addActionItem(final GeckoMenuItem menuItem) { 1.186 + assertOnUiThread(); 1.187 + menuItem.setOnShowAsActionChangedListener(this); 1.188 + 1.189 + final View actionView = menuItem.getActionView(); 1.190 + final int actionEnum = menuItem.getActionEnum(); 1.191 + boolean added = false; 1.192 + 1.193 + if (actionEnum == GeckoMenuItem.SHOW_AS_ACTION_ALWAYS) { 1.194 + if (mPrimaryActionItems.size() == 0 && 1.195 + mPrimaryActionItemBar instanceof DefaultActionItemBar) { 1.196 + // Reset the adapter before adding the header view to a list. 1.197 + setAdapter(null); 1.198 + addHeaderView((DefaultActionItemBar) mPrimaryActionItemBar); 1.199 + setAdapter(mAdapter); 1.200 + } 1.201 + 1.202 + if (added = mPrimaryActionItemBar.addActionItem(actionView)) { 1.203 + mPrimaryActionItems.put(menuItem, actionView); 1.204 + mItems.add(menuItem); 1.205 + } 1.206 + } else if (actionEnum == GeckoMenuItem.SHOW_AS_ACTION_IF_ROOM) { 1.207 + if (mSecondaryActionItems.size() == 0) { 1.208 + // Reset the adapter before adding the header view to a list. 1.209 + setAdapter(null); 1.210 + addHeaderView((DefaultActionItemBar) mSecondaryActionItemBar); 1.211 + setAdapter(mAdapter); 1.212 + } 1.213 + 1.214 + if (added = mSecondaryActionItemBar.addActionItem(actionView)) { 1.215 + mSecondaryActionItems.put(menuItem, actionView); 1.216 + mItems.add(menuItem); 1.217 + } 1.218 + } 1.219 + 1.220 + // Set the listeners. 1.221 + if (actionView instanceof MenuItemActionBar) { 1.222 + ((MenuItemActionBar) actionView).setOnClickListener(new View.OnClickListener() { 1.223 + @Override 1.224 + public void onClick(View view) { 1.225 + handleMenuItemClick(menuItem); 1.226 + } 1.227 + }); 1.228 + } else if (actionView instanceof MenuItemActionView) { 1.229 + ((MenuItemActionView) actionView).setMenuItemClickListener(new View.OnClickListener() { 1.230 + @Override 1.231 + public void onClick(View view) { 1.232 + handleMenuItemClick(menuItem); 1.233 + } 1.234 + }); 1.235 + } 1.236 + 1.237 + return added; 1.238 + } 1.239 + 1.240 + @Override 1.241 + public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) { 1.242 + return 0; 1.243 + } 1.244 + 1.245 + @Override 1.246 + public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { 1.247 + MenuItem menuItem = add(groupId, itemId, order, title); 1.248 + return addSubMenu(menuItem); 1.249 + } 1.250 + 1.251 + @Override 1.252 + public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { 1.253 + MenuItem menuItem = add(groupId, itemId, order, titleRes); 1.254 + return addSubMenu(menuItem); 1.255 + } 1.256 + 1.257 + @Override 1.258 + public SubMenu addSubMenu(CharSequence title) { 1.259 + MenuItem menuItem = add(title); 1.260 + return addSubMenu(menuItem); 1.261 + } 1.262 + 1.263 + @Override 1.264 + public SubMenu addSubMenu(int titleRes) { 1.265 + MenuItem menuItem = add(titleRes); 1.266 + return addSubMenu(menuItem); 1.267 + } 1.268 + 1.269 + private SubMenu addSubMenu(MenuItem menuItem) { 1.270 + GeckoSubMenu subMenu = new GeckoSubMenu(getContext()); 1.271 + subMenu.setMenuItem(menuItem); 1.272 + subMenu.setCallback(mCallback); 1.273 + subMenu.setMenuPresenter(mMenuPresenter); 1.274 + ((GeckoMenuItem) menuItem).setSubMenu(subMenu); 1.275 + return subMenu; 1.276 + } 1.277 + 1.278 + private void removePrimaryActionBarView() { 1.279 + // Reset the adapter before removing the header view from a list. 1.280 + setAdapter(null); 1.281 + removeHeaderView((DefaultActionItemBar) mPrimaryActionItemBar); 1.282 + setAdapter(mAdapter); 1.283 + } 1.284 + 1.285 + private void removeSecondaryActionBarView() { 1.286 + // Reset the adapter before removing the header view from a list. 1.287 + setAdapter(null); 1.288 + removeHeaderView((DefaultActionItemBar) mSecondaryActionItemBar); 1.289 + setAdapter(mAdapter); 1.290 + } 1.291 + 1.292 + @Override 1.293 + public void clear() { 1.294 + assertOnUiThread(); 1.295 + for (GeckoMenuItem menuItem : mItems) { 1.296 + if (menuItem.hasSubMenu()) { 1.297 + SubMenu sub = menuItem.getSubMenu(); 1.298 + if (sub == null) { 1.299 + continue; 1.300 + } 1.301 + try { 1.302 + sub.clear(); 1.303 + } catch (Exception ex) { 1.304 + Log.e(LOGTAG, "Couldn't clear submenu.", ex); 1.305 + } 1.306 + } 1.307 + } 1.308 + 1.309 + mAdapter.clear(); 1.310 + mItems.clear(); 1.311 + 1.312 + /* 1.313 + * Reinflating the menu will re-add any action items to the toolbar, so 1.314 + * remove the old ones. This also ensures that any text associated with 1.315 + * these is switched to the correct locale. 1.316 + */ 1.317 + if (mPrimaryActionItemBar != null) { 1.318 + for (View item : mPrimaryActionItems.values()) { 1.319 + mPrimaryActionItemBar.removeActionItem(item); 1.320 + } 1.321 + } 1.322 + mPrimaryActionItems.clear(); 1.323 + 1.324 + if (mSecondaryActionItemBar != null) { 1.325 + for (View item : mSecondaryActionItems.values()) { 1.326 + mSecondaryActionItemBar.removeActionItem(item); 1.327 + } 1.328 + } 1.329 + mSecondaryActionItems.clear(); 1.330 + 1.331 + // Remove the view, too -- the first addActionItem will re-add it, 1.332 + // and this is simpler than changing that logic. 1.333 + if (mPrimaryActionItemBar instanceof DefaultActionItemBar) { 1.334 + removePrimaryActionBarView(); 1.335 + } 1.336 + 1.337 + removeSecondaryActionBarView(); 1.338 + } 1.339 + 1.340 + @Override 1.341 + public void close() { 1.342 + if (mMenuPresenter != null) 1.343 + mMenuPresenter.closeMenu(); 1.344 + } 1.345 + 1.346 + private void showMenu(View viewForMenu) { 1.347 + if (mMenuPresenter != null) 1.348 + mMenuPresenter.showMenu(viewForMenu); 1.349 + } 1.350 + 1.351 + @Override 1.352 + public MenuItem findItem(int id) { 1.353 + for (GeckoMenuItem menuItem : mItems) { 1.354 + if (menuItem.getItemId() == id) { 1.355 + return menuItem; 1.356 + } else if (menuItem.hasSubMenu()) { 1.357 + if (!menuItem.hasActionProvider()) { 1.358 + SubMenu subMenu = menuItem.getSubMenu(); 1.359 + MenuItem item = subMenu.findItem(id); 1.360 + if (item != null) 1.361 + return item; 1.362 + } 1.363 + } 1.364 + } 1.365 + return null; 1.366 + } 1.367 + 1.368 + @Override 1.369 + public MenuItem getItem(int index) { 1.370 + if (index < mItems.size()) 1.371 + return mItems.get(index); 1.372 + 1.373 + return null; 1.374 + } 1.375 + 1.376 + @Override 1.377 + public boolean hasVisibleItems() { 1.378 + assertOnUiThread(); 1.379 + for (GeckoMenuItem menuItem : mItems) { 1.380 + if (menuItem.isVisible() && 1.381 + !mPrimaryActionItems.containsKey(menuItem) && 1.382 + !mSecondaryActionItems.containsKey(menuItem)) 1.383 + return true; 1.384 + } 1.385 + 1.386 + return false; 1.387 + } 1.388 + 1.389 + @Override 1.390 + public boolean isShortcutKey(int keyCode, KeyEvent event) { 1.391 + return true; 1.392 + } 1.393 + 1.394 + @Override 1.395 + public boolean performIdentifierAction(int id, int flags) { 1.396 + return false; 1.397 + } 1.398 + 1.399 + @Override 1.400 + public boolean performShortcut(int keyCode, KeyEvent event, int flags) { 1.401 + return false; 1.402 + } 1.403 + 1.404 + @Override 1.405 + public void removeGroup(int groupId) { 1.406 + } 1.407 + 1.408 + @Override 1.409 + public void removeItem(int id) { 1.410 + assertOnUiThread(); 1.411 + GeckoMenuItem item = (GeckoMenuItem) findItem(id); 1.412 + if (item == null) 1.413 + return; 1.414 + 1.415 + // Remove it from any sub-menu. 1.416 + for (GeckoMenuItem menuItem : mItems) { 1.417 + if (menuItem.hasSubMenu()) { 1.418 + SubMenu subMenu = menuItem.getSubMenu(); 1.419 + if (subMenu != null && subMenu.findItem(id) != null) { 1.420 + subMenu.removeItem(id); 1.421 + return; 1.422 + } 1.423 + } 1.424 + } 1.425 + 1.426 + // Remove it from own menu. 1.427 + if (mPrimaryActionItems.containsKey(item)) { 1.428 + if (mPrimaryActionItemBar != null) 1.429 + mPrimaryActionItemBar.removeActionItem(mPrimaryActionItems.get(item)); 1.430 + 1.431 + mPrimaryActionItems.remove(item); 1.432 + mItems.remove(item); 1.433 + 1.434 + if (mPrimaryActionItems.size() == 0 && 1.435 + mPrimaryActionItemBar instanceof DefaultActionItemBar) { 1.436 + removePrimaryActionBarView(); 1.437 + } 1.438 + 1.439 + return; 1.440 + } 1.441 + 1.442 + if (mSecondaryActionItems.containsKey(item)) { 1.443 + if (mSecondaryActionItemBar != null) 1.444 + mSecondaryActionItemBar.removeActionItem(mSecondaryActionItems.get(item)); 1.445 + 1.446 + mSecondaryActionItems.remove(item); 1.447 + mItems.remove(item); 1.448 + 1.449 + if (mSecondaryActionItems.size() == 0) { 1.450 + removeSecondaryActionBarView(); 1.451 + } 1.452 + 1.453 + return; 1.454 + } 1.455 + 1.456 + mAdapter.removeMenuItem(item); 1.457 + mItems.remove(item); 1.458 + } 1.459 + 1.460 + @Override 1.461 + public void setGroupCheckable(int group, boolean checkable, boolean exclusive) { 1.462 + } 1.463 + 1.464 + @Override 1.465 + public void setGroupEnabled(int group, boolean enabled) { 1.466 + } 1.467 + 1.468 + @Override 1.469 + public void setGroupVisible(int group, boolean visible) { 1.470 + } 1.471 + 1.472 + @Override 1.473 + public void setQwertyMode(boolean isQwerty) { 1.474 + } 1.475 + 1.476 + @Override 1.477 + public int size() { 1.478 + return mItems.size(); 1.479 + } 1.480 + 1.481 + @Override 1.482 + public boolean hasActionItemBar() { 1.483 + return (mPrimaryActionItemBar != null) && (mSecondaryActionItemBar != null); 1.484 + } 1.485 + 1.486 + @Override 1.487 + public void onShowAsActionChanged(GeckoMenuItem item) { 1.488 + removeItem(item.getItemId()); 1.489 + 1.490 + if (item.isActionItem() && addActionItem(item)) { 1.491 + return; 1.492 + } 1.493 + 1.494 + addItem(item); 1.495 + } 1.496 + 1.497 + public void onItemChanged(GeckoMenuItem item) { 1.498 + assertOnUiThread(); 1.499 + if (item.isActionItem()) { 1.500 + final View actionView; 1.501 + if (item.getActionEnum() == GeckoMenuItem.SHOW_AS_ACTION_ALWAYS) { 1.502 + actionView = mPrimaryActionItems.get(item); 1.503 + } else { 1.504 + actionView = mSecondaryActionItems.get(item); 1.505 + } 1.506 + 1.507 + if (actionView != null) { 1.508 + // The update could be coming from the background thread. 1.509 + // Post a runnable on the UI thread of the view for it to update. 1.510 + final GeckoMenuItem menuItem = item; 1.511 + actionView.post(new Runnable() { 1.512 + @Override 1.513 + public void run() { 1.514 + if (menuItem.isVisible()) { 1.515 + actionView.setVisibility(View.VISIBLE); 1.516 + if (actionView instanceof MenuItemActionBar) { 1.517 + ((MenuItemActionBar) actionView).initialize(menuItem); 1.518 + } else { 1.519 + ((MenuItemActionView) actionView).initialize(menuItem); 1.520 + } 1.521 + } else { 1.522 + actionView.setVisibility(View.GONE); 1.523 + } 1.524 + } 1.525 + }); 1.526 + } 1.527 + } else { 1.528 + mAdapter.notifyDataSetChanged(); 1.529 + } 1.530 + } 1.531 + 1.532 + @Override 1.533 + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1.534 + // We might be showing headers. Account them while using the position. 1.535 + position -= getHeaderViewsCount(); 1.536 + 1.537 + GeckoMenuItem item = mAdapter.getItem(position); 1.538 + handleMenuItemClick(item); 1.539 + } 1.540 + 1.541 + private void handleMenuItemClick(GeckoMenuItem item) { 1.542 + if (!item.isEnabled()) 1.543 + return; 1.544 + 1.545 + if (item.invoke()) { 1.546 + close(); 1.547 + } else if (item.hasSubMenu()) { 1.548 + // Refresh the submenu for the provider. 1.549 + GeckoActionProvider provider = item.getGeckoActionProvider(); 1.550 + if (provider != null) { 1.551 + GeckoSubMenu subMenu = new GeckoSubMenu(getContext()); 1.552 + subMenu.setShowIcons(true); 1.553 + provider.onPrepareSubMenu(subMenu); 1.554 + item.setSubMenu(subMenu); 1.555 + } 1.556 + 1.557 + // Show the submenu. 1.558 + GeckoSubMenu subMenu = (GeckoSubMenu) item.getSubMenu(); 1.559 + showMenu(subMenu); 1.560 + } else { 1.561 + close(); 1.562 + mCallback.onMenuItemSelected(item); 1.563 + } 1.564 + } 1.565 + 1.566 + public Callback getCallback() { 1.567 + return mCallback; 1.568 + } 1.569 + 1.570 + public MenuPresenter getMenuPresenter() { 1.571 + return mMenuPresenter; 1.572 + } 1.573 + 1.574 + public void setCallback(Callback callback) { 1.575 + mCallback = callback; 1.576 + 1.577 + // Update the submenus just in case this changes on the fly. 1.578 + for (GeckoMenuItem menuItem : mItems) { 1.579 + if (menuItem.hasSubMenu()) { 1.580 + GeckoSubMenu subMenu = (GeckoSubMenu) menuItem.getSubMenu(); 1.581 + subMenu.setCallback(mCallback); 1.582 + } 1.583 + } 1.584 + } 1.585 + 1.586 + public void setMenuPresenter(MenuPresenter presenter) { 1.587 + mMenuPresenter = presenter; 1.588 + 1.589 + // Update the submenus just in case this changes on the fly. 1.590 + for (GeckoMenuItem menuItem : mItems) { 1.591 + if (menuItem.hasSubMenu()) { 1.592 + GeckoSubMenu subMenu = (GeckoSubMenu) menuItem.getSubMenu(); 1.593 + subMenu.setMenuPresenter(mMenuPresenter); 1.594 + } 1.595 + } 1.596 + } 1.597 + 1.598 + public void setActionItemBarPresenter(ActionItemBarPresenter presenter) { 1.599 + mPrimaryActionItemBar = presenter; 1.600 + } 1.601 + 1.602 + public void setShowIcons(boolean show) { 1.603 + if (mShowIcons != show) { 1.604 + mShowIcons = show; 1.605 + mAdapter.notifyDataSetChanged(); 1.606 + } 1.607 + } 1.608 + 1.609 + // Action Items are added to the header view by default. 1.610 + // URL bar can register itself as a presenter, in case it has a different place to show them. 1.611 + public static class DefaultActionItemBar extends LinearLayout 1.612 + implements ActionItemBarPresenter { 1.613 + private final int mRowHeight; 1.614 + private float mWeightSum; 1.615 + 1.616 + public DefaultActionItemBar(Context context) { 1.617 + this(context, null); 1.618 + } 1.619 + 1.620 + public DefaultActionItemBar(Context context, AttributeSet attrs) { 1.621 + super(context, attrs); 1.622 + 1.623 + mRowHeight = getResources().getDimensionPixelSize(R.dimen.menu_item_row_height); 1.624 + } 1.625 + 1.626 + @Override 1.627 + public boolean addActionItem(View actionItem) { 1.628 + ViewGroup.LayoutParams actualParams = actionItem.getLayoutParams(); 1.629 + LinearLayout.LayoutParams params; 1.630 + 1.631 + if (actualParams != null) { 1.632 + params = new LinearLayout.LayoutParams(actionItem.getLayoutParams()); 1.633 + params.width = 0; 1.634 + } else { 1.635 + params = new LinearLayout.LayoutParams(0, mRowHeight); 1.636 + } 1.637 + 1.638 + if (actionItem instanceof MenuItemActionView) { 1.639 + params.weight = ((MenuItemActionView) actionItem).getChildCount(); 1.640 + } else { 1.641 + params.weight = 1.0f; 1.642 + } 1.643 + 1.644 + mWeightSum += params.weight; 1.645 + 1.646 + actionItem.setLayoutParams(params); 1.647 + addView(actionItem); 1.648 + setWeightSum(mWeightSum); 1.649 + return true; 1.650 + } 1.651 + 1.652 + @Override 1.653 + public void removeActionItem(View actionItem) { 1.654 + if (indexOfChild(actionItem) != -1) { 1.655 + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) actionItem.getLayoutParams(); 1.656 + mWeightSum -= params.weight; 1.657 + removeView(actionItem); 1.658 + } 1.659 + } 1.660 + } 1.661 + 1.662 + // Adapter to bind menu items to the list. 1.663 + private class MenuItemsAdapter extends BaseAdapter { 1.664 + private static final int VIEW_TYPE_DEFAULT = 0; 1.665 + private static final int VIEW_TYPE_ACTION_MODE = 1; 1.666 + 1.667 + private List<GeckoMenuItem> mItems; 1.668 + 1.669 + public MenuItemsAdapter() { 1.670 + mItems = new ArrayList<GeckoMenuItem>(); 1.671 + } 1.672 + 1.673 + @Override 1.674 + public int getCount() { 1.675 + if (mItems == null) 1.676 + return 0; 1.677 + 1.678 + int visibleCount = 0; 1.679 + for (GeckoMenuItem item : mItems) { 1.680 + if (item.isVisible()) 1.681 + visibleCount++; 1.682 + } 1.683 + 1.684 + return visibleCount; 1.685 + } 1.686 + 1.687 + @Override 1.688 + public GeckoMenuItem getItem(int position) { 1.689 + for (GeckoMenuItem item : mItems) { 1.690 + if (item.isVisible()) { 1.691 + position--; 1.692 + 1.693 + if (position < 0) 1.694 + return item; 1.695 + } 1.696 + } 1.697 + 1.698 + return null; 1.699 + } 1.700 + 1.701 + @Override 1.702 + public long getItemId(int position) { 1.703 + return position; 1.704 + } 1.705 + 1.706 + @Override 1.707 + public View getView(int position, View convertView, ViewGroup parent) { 1.708 + GeckoMenuItem item = getItem(position); 1.709 + GeckoMenuItem.Layout view = null; 1.710 + 1.711 + // Try to re-use the view. 1.712 + if (convertView == null && getItemViewType(position) == VIEW_TYPE_DEFAULT) { 1.713 + view = new MenuItemDefault(parent.getContext(), null); 1.714 + } else { 1.715 + view = (GeckoMenuItem.Layout) convertView; 1.716 + } 1.717 + 1.718 + if (view == null || view instanceof MenuItemActionView) { 1.719 + // Always get from the menu item. 1.720 + // This will ensure that the default activity is refreshed. 1.721 + view = (MenuItemActionView) item.getActionView(); 1.722 + 1.723 + // ListView will not perform an item click if the row has a focusable view in it. 1.724 + // Hence, forward the click event on the menu item in the action-view to the ListView. 1.725 + final View actionView = (View) view; 1.726 + final int pos = position; 1.727 + final long id = getItemId(position); 1.728 + ((MenuItemActionView) view).setMenuItemClickListener(new View.OnClickListener() { 1.729 + @Override 1.730 + public void onClick(View v) { 1.731 + GeckoMenu listView = GeckoMenu.this; 1.732 + listView.performItemClick(actionView, pos + listView.getHeaderViewsCount(), id); 1.733 + } 1.734 + }); 1.735 + } 1.736 + 1.737 + // Initialize the view. 1.738 + view.setShowIcon(mShowIcons); 1.739 + view.initialize(item); 1.740 + return (View) view; 1.741 + } 1.742 + 1.743 + @Override 1.744 + public int getItemViewType(int position) { 1.745 + return getItem(position).getGeckoActionProvider() == null ? VIEW_TYPE_DEFAULT : VIEW_TYPE_ACTION_MODE; 1.746 + } 1.747 + 1.748 + @Override 1.749 + public int getViewTypeCount() { 1.750 + return 2; 1.751 + } 1.752 + 1.753 + @Override 1.754 + public boolean hasStableIds() { 1.755 + return false; 1.756 + } 1.757 + 1.758 + @Override 1.759 + public boolean areAllItemsEnabled() { 1.760 + // Setting this to true is a workaround to fix disappearing 1.761 + // dividers in the menu (bug 963249). 1.762 + return true; 1.763 + } 1.764 + 1.765 + @Override 1.766 + public boolean isEnabled(int position) { 1.767 + return getItem(position).isEnabled(); 1.768 + } 1.769 + 1.770 + public void addMenuItem(GeckoMenuItem menuItem) { 1.771 + if (mItems.contains(menuItem)) 1.772 + return; 1.773 + 1.774 + // Insert it in proper order. 1.775 + int index = 0; 1.776 + for (GeckoMenuItem item : mItems) { 1.777 + if (item.getOrder() > menuItem.getOrder()) { 1.778 + mItems.add(index, menuItem); 1.779 + notifyDataSetChanged(); 1.780 + return; 1.781 + } else { 1.782 + index++; 1.783 + } 1.784 + } 1.785 + 1.786 + // Add the menuItem at the end. 1.787 + mItems.add(menuItem); 1.788 + notifyDataSetChanged(); 1.789 + } 1.790 + 1.791 + public void removeMenuItem(GeckoMenuItem menuItem) { 1.792 + // Remove it from the list. 1.793 + mItems.remove(menuItem); 1.794 + notifyDataSetChanged(); 1.795 + } 1.796 + 1.797 + public void clear() { 1.798 + mItems.clear(); 1.799 + notifyDataSetChanged(); 1.800 + } 1.801 + 1.802 + public GeckoMenuItem getMenuItem(int id) { 1.803 + for (GeckoMenuItem item : mItems) { 1.804 + if (item.getItemId() == id) 1.805 + return item; 1.806 + } 1.807 + 1.808 + return null; 1.809 + } 1.810 + } 1.811 +}