mobile/android/base/menu/GeckoMenu.java

changeset 0
6474c204b198
     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 +}

mercurial