--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -165,22 +165,22 @@ import org.mozilla.gecko.switchboard.Asy
import org.mozilla.gecko.switchboard.SwitchBoard;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
-import java.util.Vector;
import java.util.regex.Pattern;
import static org.mozilla.gecko.Tab.TabType;
import static org.mozilla.gecko.Tabs.INVALID_TAB_ID;
public class BrowserApp extends GeckoApp
implements ActionModePresenter,
AnchoredPopup.OnVisibilityChangeListener,
@@ -266,23 +266,28 @@ public class BrowserApp extends GeckoApp
public boolean checkable;
public boolean checked;
public boolean enabled = true;
public boolean visible = true;
public int parent;
public boolean added; // So we can re-add after a locale change.
}
+ private static class BrowserActionItemInfo extends MenuItemInfo {
+ public String uuid;
+ }
+
// The types of guest mode dialogs we show.
public static enum GuestModeDialog {
ENTERING,
LEAVING
}
- private Vector<MenuItemInfo> mAddonMenuItemsCache;
+ private ArrayList<MenuItemInfo> mAddonMenuItemsCache;
+ private ArrayList<BrowserActionItemInfo> mBrowserActionItemsCache;
private PropertyAnimator mMainLayoutAnimator;
private static final Interpolator sTabsInterpolator = new Interpolator() {
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
@@ -754,16 +759,18 @@ public class BrowserApp extends GeckoApp
"Search:Keyword",
null);
EventDispatcher.getInstance().registerUiThreadListener(this,
"Menu:Open",
"Menu:Update",
"Menu:Add",
"Menu:Remove",
+ "Menu:AddBrowserAction",
+ "Menu:RemoveBrowserAction",
"LightweightTheme:Update",
"Tab:Added",
"Video:Play",
"CharEncoding:Data",
"CharEncoding:State",
"Settings:Show",
"Updater:Launch",
"Sanitize:Finished",
@@ -1555,16 +1562,18 @@ public class BrowserApp extends GeckoApp
"Search:Keyword",
null);
EventDispatcher.getInstance().unregisterUiThreadListener(this,
"Menu:Open",
"Menu:Update",
"Menu:Add",
"Menu:Remove",
+ "Menu:AddBrowserAction",
+ "Menu:RemoveBrowserAction",
"LightweightTheme:Update",
"Tab:Added",
"Video:Play",
"CharEncoding:Data",
"CharEncoding:State",
"Settings:Show",
"Updater:Launch",
"Sanitize:Finished",
@@ -1853,16 +1862,32 @@ public class BrowserApp extends GeckoApp
info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET;
addAddonMenuItem(info);
break;
case "Menu:Remove":
removeAddonMenuItem(message.getInt("id") + ADDON_MENU_OFFSET);
break;
+ case "Menu:AddBrowserAction":
+ final BrowserActionItemInfo browserAction = new BrowserActionItemInfo();
+ browserAction.label = message.getString("name");
+ if (TextUtils.isEmpty(browserAction.label)) {
+ Log.e(LOGTAG, "Invalid browser action name");
+ return;
+ }
+ browserAction.id = message.getInt("id") + ADDON_MENU_OFFSET;
+ browserAction.uuid = message.getString("uuid");
+ addBrowserActionMenuItem(browserAction);
+ break;
+
+ case "Menu:RemoveBrowserAction":
+ removeBrowserActionMenuItem(message.getString("uuid"));
+ break;
+
case "LightweightTheme:Update":
mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
break;
case "Search:Keyword":
storeSearchQuery(message.getString("query"));
recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"),
TelemetryContract.Method.ACTIONBAR);
@@ -3155,17 +3180,17 @@ public class BrowserApp extends GeckoApp
item.setCheckable(info.checkable);
item.setChecked(info.checked);
item.setEnabled(info.enabled);
item.setVisible(info.visible);
}
private void addAddonMenuItem(final MenuItemInfo info) {
if (mAddonMenuItemsCache == null) {
- mAddonMenuItemsCache = new Vector<MenuItemInfo>();
+ mAddonMenuItemsCache = new ArrayList<MenuItemInfo>();
}
// Mark it as added if the menu was ready.
info.added = (mMenu != null);
// Always cache so we can rebuild after a locale switch.
mAddonMenuItemsCache.add(info);
@@ -3175,20 +3200,20 @@ public class BrowserApp extends GeckoApp
addAddonMenuItemToMenu(mMenu, info);
}
private void removeAddonMenuItem(int id) {
// Remove add-on menu item from cache, if available.
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
for (MenuItemInfo item : mAddonMenuItemsCache) {
- if (item.id == id) {
- mAddonMenuItemsCache.remove(item);
- break;
- }
+ if (item.id == id) {
+ mAddonMenuItemsCache.remove(item);
+ break;
+ }
}
}
if (mMenu == null)
return;
final MenuItem menuItem = mMenu.findItem(id);
if (menuItem != null)
@@ -3220,30 +3245,110 @@ public class BrowserApp extends GeckoApp
menuItem.setTitle(options.getString("name", menuItem.getTitle().toString()));
menuItem.setCheckable(options.getBoolean("checkable", menuItem.isCheckable()));
menuItem.setChecked(options.getBoolean("checked", menuItem.isChecked()));
menuItem.setEnabled(options.getBoolean("enabled", menuItem.isEnabled()));
menuItem.setVisible(options.getBoolean("visible", menuItem.isVisible()));
}
}
+ /**
+ * Add the provided item to the provided menu, which should be
+ * the root (mMenu).
+ */
+ private void addBrowserActionMenuItemToMenu(final Menu menu, final BrowserActionItemInfo info) {
+ info.added = true;
+
+ final MenuItem item = menu.add(Menu.NONE, info.id, Menu.NONE, info.label);
+
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ final GeckoBundle data = new GeckoBundle(1);
+ data.putString("item", info.uuid);
+ EventDispatcher.getInstance().dispatch("Menu:BrowserActionClicked", data);
+ return true;
+ }
+ });
+
+ item.setCheckable(info.checkable);
+ item.setChecked(info.checked);
+ item.setEnabled(info.enabled);
+ item.setVisible(info.visible);
+ }
+
+ /**
+ * Adds a WebExtension browser action to the menu.
+ */
+ private void addBrowserActionMenuItem(final BrowserActionItemInfo info) {
+ if (mBrowserActionItemsCache == null) {
+ mBrowserActionItemsCache = new ArrayList<BrowserActionItemInfo>();
+ }
+
+ // Mark it as added if the menu was ready.
+ info.added = (mMenu != null);
+
+ // Always cache so we can rebuild after a locale switch.
+ mBrowserActionItemsCache.add(info);
+
+ if (mMenu == null) {
+ return;
+ }
+
+ addAddonMenuItemToMenu(mMenu, info);
+ }
+
+ /**
+ * Removes a WebExtension browser action from the menu by its UUID.
+ */
+ private void removeBrowserActionMenuItem(String uuid) {
+ int id = -1;
+
+ // Remove browser action menu item from cache, if available.
+ if (mBrowserActionItemsCache != null && !mBrowserActionItemsCache.isEmpty()) {
+ for (BrowserActionItemInfo item : mBrowserActionItemsCache) {
+ if (item.uuid.equals(uuid)) {
+ id = item.id;
+ mBrowserActionItemsCache.remove(item);
+ break;
+ }
+ }
+ }
+
+ if (mMenu == null || id == -1) {
+ return;
+ }
+
+ final MenuItem menuItem = mMenu.findItem(id);
+ if (menuItem != null) {
+ mMenu.removeItem(id);
+ }
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Sets mMenu = menu.
super.onCreateOptionsMenu(menu);
// Inform the menu about the action-items bar.
if (menu instanceof GeckoMenu &&
HardwareUtils.isTablet()) {
((GeckoMenu) menu).setActionItemBarPresenter(mBrowserToolbar);
}
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.browser_app_menu, mMenu);
+ // Add browser action menu items, if any exist.
+ if (mBrowserActionItemsCache != null && !mBrowserActionItemsCache.isEmpty()) {
+ for (BrowserActionItemInfo item : mBrowserActionItemsCache) {
+ addBrowserActionMenuItemToMenu(mMenu, item);
+ }
+ }
+
// Add add-on menu items, if any exist.
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
for (MenuItemInfo item : mAddonMenuItemsCache) {
addAddonMenuItemToMenu(mMenu, item);
}
}
// Action providers are available only ICS+.