Bug 936756 - Part 3: rebuild menus and action items on locale change. r=sriram
authorRichard Newman <rnewman@mozilla.com>
Sun, 01 Dec 2013 21:53:17 -0800
changeset 172852 c46514ce6886b72bc6bce0b40026bfb790a35abc
parent 172851 686b4b5a5be3a5da1ddee082319944ebf6a0e816
child 172853 9cbe6533c48016afb2ec86aa96e0b99dbab0e90e
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssriram
bugs936756
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 936756 - Part 3: rebuild menus and action items on locale change. r=sriram
mobile/android/base/BrowserApp.java
mobile/android/base/menu/GeckoMenu.java
mobile/android/base/menu/GeckoMenuItem.java
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -119,25 +119,26 @@ abstract public class BrowserApp extends
     private BrowserToolbar mBrowserToolbar;
     private HomePager mHomePager;
     private View mHomePagerContainer;
     protected Telemetry.Timer mAboutHomeStartupTimer = null;
     private ActionModeCompat mActionMode;
 
     private static final int GECKO_TOOLS_MENU = -1;
     private static final int ADDON_MENU_OFFSET = 1000;
-    private class MenuItemInfo {
+    private static class MenuItemInfo {
         public int id;
         public String label;
         public String icon;
         public boolean checkable = false;
         public boolean checked = false;
         public boolean enabled = true;
         public boolean visible = true;
         public int parent;
+        public boolean added = false;    // So we can re-add after a locale change.
     }
 
     // The types of guest mdoe dialogs we show
     private static enum GuestModeDialog {
         ENTERING,
         LEAVING
     }
 
@@ -1641,16 +1642,21 @@ abstract public class BrowserApp extends
 
     @Override
     public void onLocaleReady(final String locale) {
         super.onLocaleReady(locale);
         if (mHomePager != null) {
             // Blow it away and rebuild it with the right strings.
             mHomePager.redisplay(getSupportFragmentManager());
         }
+
+        if (mMenu != null) {
+            mMenu.clear();
+            onCreateOptionsMenu(mMenu);
+        }
     }
 
     private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) {
         if (isHomePagerVisible()) {
             return;
         }
 
         // Refresh toolbar height to possibly restore the toolbar padding
@@ -1815,17 +1821,17 @@ abstract public class BrowserApp extends
                     mIsHidingTabs = false;
                 }
                 return true;
             }
             return false;
         }
     }
 
-    private Menu findParentMenu(Menu menu, MenuItem item) {
+    private static Menu findParentMenu(Menu menu, MenuItem item) {
         final int itemId = item.getItemId();
 
         final int count = (menu != null) ? menu.size() : 0;
         for (int i = 0; i < count; i++) {
             MenuItem menuItem = menu.getItem(i);
             if (menuItem.getItemId() == itemId) {
                 return menu;
             }
@@ -1835,84 +1841,104 @@ abstract public class BrowserApp extends
                     return parent;
                 }
             }
         }
 
         return null;
     }
 
-    private void addAddonMenuItem(final MenuItemInfo info) {
-        if (mMenu == null) {
-            if (mAddonMenuItemsCache == null)
-                mAddonMenuItemsCache = new Vector<MenuItemInfo>();
-
-            mAddonMenuItemsCache.add(info);
-            return;
-        }
-
-        Menu menu;
+    /**
+     * Add the provided item to the provided menu, which should be
+     * the root (mMenu).
+     */
+    private void addAddonMenuItemToMenu(final Menu menu, final MenuItemInfo info) {
+        info.added = true;
+        
+        final Menu destination;
         if (info.parent == 0) {
-            menu = mMenu;
+            destination = menu;
         } else if (info.parent == GECKO_TOOLS_MENU) {
-            MenuItem tools = mMenu.findItem(R.id.tools);
-            menu = tools != null ? tools.getSubMenu() : mMenu;
+            MenuItem tools = menu.findItem(R.id.tools);
+            destination = tools != null ? tools.getSubMenu() : menu;
         } else {
-            MenuItem parent = mMenu.findItem(info.parent);
-            if (parent == null)
+            MenuItem parent = menu.findItem(info.parent);
+            if (parent == null) {
                 return;
+            }
 
-            Menu parentMenu = findParentMenu(mMenu, parent);
+            Menu parentMenu = findParentMenu(menu, parent);
 
             if (!parent.hasSubMenu()) {
                 parentMenu.removeItem(parent.getItemId());
-                menu = parentMenu.addSubMenu(Menu.NONE, parent.getItemId(), Menu.NONE, parent.getTitle());
-                if (parent.getIcon() != null)
-                    ((SubMenu) menu).getItem().setIcon(parent.getIcon());
+                destination = parentMenu.addSubMenu(Menu.NONE, parent.getItemId(), Menu.NONE, parent.getTitle());
+                if (parent.getIcon() != null) {
+                    ((SubMenu) destination).getItem().setIcon(parent.getIcon());
+                }
             } else {
-                menu = parent.getSubMenu();
+                destination = parent.getSubMenu();
             }
         }
 
-        MenuItem item = menu.add(Menu.NONE, info.id, Menu.NONE, info.label);
+        MenuItem item = destination.add(Menu.NONE, info.id, Menu.NONE, info.label);
+
         item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override
             public boolean onMenuItemClick(MenuItem item) {
-                Log.i(LOGTAG, "menu item clicked");
+                Log.i(LOGTAG, "Menu item clicked");
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Menu:Clicked", Integer.toString(info.id - ADDON_MENU_OFFSET)));
                 return true;
             }
         });
 
-        if (info.icon != null) {
+        if (info.icon == null) {
+            item.setIcon(R.drawable.ic_menu_addons_filler);
+        } else {
             final int id = info.id;
             BitmapUtils.getDrawable(this, info.icon, new BitmapUtils.BitmapLoader() {
                 @Override
                 public void onBitmapFound(Drawable d) {
-                    MenuItem item = mMenu.findItem(id);
+                    // TODO: why do we re-find the item?
+                    MenuItem item = destination.findItem(id);
                     if (item == null) {
                         return;
                     }
                     if (d == null) {
                         item.setIcon(R.drawable.ic_menu_addons_filler);
                         return;
                     }
                     item.setIcon(d);
                 }
             });
-        } else {
-            item.setIcon(R.drawable.ic_menu_addons_filler);
         }
 
         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>();
+        }
+
+        // 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);
+
+        if (mMenu == null) {
+            return;
+        }
+
+        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;
                  }
@@ -1932,52 +1958,55 @@ abstract public class BrowserApp extends
         if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
             for (MenuItemInfo item : mAddonMenuItemsCache) {
                 if (item.id == id) {
                     item.label = options.optString("name", item.label);
                     item.checkable = options.optBoolean("checkable", item.checkable);
                     item.checked = options.optBoolean("checked", item.checked);
                     item.enabled = options.optBoolean("enabled", item.enabled);
                     item.visible = options.optBoolean("visible", item.visible);
+                    item.added = (mMenu != null);
                     break;
                 }
             }
         }
 
-        if (mMenu == null)
+        if (mMenu == null) {
             return;
+        }
 
         MenuItem menuItem = mMenu.findItem(id);
         if (menuItem != null) {
             menuItem.setTitle(options.optString("name", menuItem.getTitle().toString()));
             menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable()));
             menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked()));
             menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled()));
             menuItem.setVisible(options.optBoolean("visible", menuItem.isVisible()));
         }
     }
 
     @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())
+        if (menu instanceof GeckoMenu &&
+            HardwareUtils.isTablet()) {
             ((GeckoMenu) menu).setActionItemBarPresenter(mBrowserToolbar);
+        }
 
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.browser_app_menu, mMenu);
 
-        // Add add-on menu items if any.
+        // Add add-on menu items, if any exist.
         if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
             for (MenuItemInfo item : mAddonMenuItemsCache) {
-                 addAddonMenuItem(item);
+                addAddonMenuItemToMenu(mMenu, item);
             }
-
-            mAddonMenuItemsCache.clear();
         }
 
         // Action providers are available only ICS+.
         if (Build.VERSION.SDK_INT >= 14) {
             MenuItem share = mMenu.findItem(R.id.share);
             GeckoActionProvider provider = new GeckoActionProvider(this);
             share.setActionProvider(provider);
         }
--- a/mobile/android/base/menu/GeckoMenu.java
+++ b/mobile/android/base/menu/GeckoMenu.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.menu;
 
 import org.mozilla.gecko.R;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.ActionProvider;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
@@ -109,17 +110,17 @@ public class GeckoMenu extends ListView
         // Attach an adapter.
         mAdapter = new MenuItemsAdapter();
         setAdapter(mAdapter);
         setOnItemClickListener(this);
 
         mItems = new ArrayList<GeckoMenuItem>();
         mActionItems = new HashMap<GeckoMenuItem, View>();
 
-        mActionItemBarPresenter =  (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null);
+        mActionItemBarPresenter = (DefaultActionItemBar) LayoutInflater.from(context).inflate(R.layout.menu_action_bar, null);
     }
 
     @Override
     public MenuItem add(CharSequence title) {
         GeckoMenuItem menuItem = new GeckoMenuItem(this, NO_ID, 0, title);
         addItem(menuItem);
         return menuItem;
     }
@@ -214,24 +215,36 @@ public class GeckoMenu extends ListView
         ((GeckoMenuItem) menuItem).setSubMenu(subMenu);
         return subMenu;
     }
 
     @Override
     public void clear() {
         for (GeckoMenuItem menuItem : mItems) {
             if (menuItem.hasSubMenu()) {
-                menuItem.getSubMenu().clear();
+                SubMenu sub = menuItem.getSubMenu();
+                if (sub == null) {
+                    continue;
+                }
+                try {
+                    sub.clear();
+                } catch (Exception ex) {
+                    Log.e(LOGTAG, "Couldn't clear submenu.", ex);
+                }
             }
         }
 
         mAdapter.clear();
-
         mItems.clear();
 
+        /*
+         * Reinflating the menu will re-add any action items to the toolbar, so
+         * remove the old ones. This also ensures that any text associated with
+         * these is switched to the correct locale.
+         */
         if (mActionItemBarPresenter != null) {
             for (View item : mActionItems.values()) {
                 mActionItemBarPresenter.removeActionItem(item);
             }
         }
         mActionItems.clear();
     }
 
--- a/mobile/android/base/menu/GeckoMenuItem.java
+++ b/mobile/android/base/menu/GeckoMenuItem.java
@@ -137,17 +137,21 @@ public class GeckoMenuItem implements Me
 
     @Override
     public int getOrder() {
         return mOrder;
     }
 
     @Override
     public SubMenu getSubMenu() {
-        return mSubMenu;
+        // For consistency with hasSubMenu.
+        if (mActionProvider == null) {
+            return mSubMenu;
+        }
+        return null;
     }
 
     @Override
     public CharSequence getTitle() {
         return mTitle;
     }
 
     @Override