diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 5d00bdd88bda..db08aab365e3 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -716,7 +716,11 @@ abstract public class BrowserApp extends GeckoApp @Override public void closeOptionsMenu() { - if (!mBrowserToolbar.closeOptionsMenu()) + boolean closed = mBrowserToolbar.closeOptionsMenu(); + + if (closed) + onOptionsMenuClosed(mMenu); + else super.closeOptionsMenu(); } diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 4c14faad82f4..5fd4a799f93d 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -304,6 +304,12 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, if (mHasSoftMenuButton) { mMenuPopup = new MenuPopup(mActivity); mMenuPopup.setPanelView(panel); + + mMenuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { + public void onDismiss() { + mActivity.onOptionsMenuClosed(null); + } + }); } } } @@ -696,7 +702,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, } // MenuPopup holds the MenuPanel in Honeycomb/ICS devices with no hardware key - public class MenuPopup extends PopupWindow { + public static class MenuPopup extends PopupWindow { private RelativeLayout mPanel; public MenuPopup(Context context) { diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 85ba1056b336..9310842bf03b 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -142,6 +142,7 @@ abstract public class GeckoApp public SurfaceView cameraView; public static GeckoApp mAppContext; public boolean mDOMFullScreen = false; + protected MenuPresenter mMenuPresenter; protected MenuPanel mMenuPanel; protected Menu mMenu; private static GeckoThread sGeckoThread; @@ -466,10 +467,14 @@ abstract public class GeckoApp return super.getMenuInflater(); } - public View getMenuPanel() { + public MenuPanel getMenuPanel() { return mMenuPanel; } + public MenuPresenter getMenuPresenter() { + return mMenuPresenter; + } + // MenuPanel holds the scrollable Menu public static class MenuPanel extends LinearLayout { public MenuPanel(Context context, AttributeSet attrs) { @@ -497,11 +502,41 @@ abstract public class GeckoApp } } + // MenuPresenter takes care of proper animation and inflation. + public class MenuPresenter { + GeckoApp mActivity; + + public MenuPresenter(GeckoApp activity) { + mActivity = activity; + } + + public void show(GeckoMenu menu) { + mActivity.closeOptionsMenu(); + + MenuPanel panel = mActivity.getMenuPanel(); + panel.removeAllViews(); + panel.addView(menu); + + mActivity.openOptionsMenu(); + } + + public void hide() { + mActivity.closeOptionsMenu(); + } + + public void onOptionsMenuClosed() { + MenuPanel panel = mActivity.getMenuPanel(); + panel.removeAllViews(); + panel.addView((GeckoMenu) mMenu); + } + } + @Override public View onCreatePanelView(int featureId) { if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) { if (mMenuPanel == null) { mMenuPanel = new MenuPanel(mAppContext, null); + mMenuPresenter = new MenuPresenter(this); } else { // Prepare the panel everytime before showing the menu. onPreparePanel(featureId, mMenuPanel, mMenu); @@ -573,6 +608,12 @@ abstract public class GeckoApp return super.onOptionsItemSelected(item); } } + + @Override + public void onOptionsMenuClosed(Menu menu) { + if (Build.VERSION.SDK_INT >= 11) + mMenuPresenter.onOptionsMenuClosed(); + } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { diff --git a/mobile/android/base/GeckoMenu.java b/mobile/android/base/GeckoMenu.java index 6475c0131ed0..d691b2432d2a 100644 --- a/mobile/android/base/GeckoMenu.java +++ b/mobile/android/base/GeckoMenu.java @@ -41,7 +41,7 @@ public class GeckoMenu extends ListView public int getActionItemsCount(); } - private static final int NO_ID = 0; + protected static final int NO_ID = 0; // List of all menu items. private List mItems; @@ -60,7 +60,6 @@ public class GeckoMenu extends ListView public GeckoMenu(Context context, AttributeSet attrs) { super(context, attrs); - mContext = context; setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, @@ -145,22 +144,38 @@ public class GeckoMenu extends ListView @Override public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) { - return null; + MenuItem menuItem = add(groupId, itemId, order, title); + GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null); + subMenu.setMenuItem(menuItem); + ((GeckoMenuItem) menuItem).setSubMenu(subMenu); + return subMenu; } @Override public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) { - return null; + MenuItem menuItem = add(groupId, itemId, order, titleRes); + GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null); + subMenu.setMenuItem(menuItem); + ((GeckoMenuItem) menuItem).setSubMenu(subMenu); + return subMenu; } @Override public SubMenu addSubMenu(CharSequence title) { - return null; + MenuItem menuItem = add(title); + GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null); + subMenu.setMenuItem(menuItem); + ((GeckoMenuItem) menuItem).setSubMenu(subMenu); + return subMenu; } @Override public SubMenu addSubMenu(int titleRes) { - return null; + MenuItem menuItem = add(titleRes); + GeckoSubMenu subMenu = new GeckoSubMenu(mContext, null); + subMenu.setMenuItem(menuItem); + ((GeckoMenuItem) menuItem).setSubMenu(subMenu); + return subMenu; } @Override @@ -174,8 +189,14 @@ public class GeckoMenu extends ListView @Override public MenuItem findItem(int id) { for (GeckoMenuItem menuItem : mItems) { - if (menuItem.getItemId() == id) + if (menuItem.getItemId() == id) { return menuItem; + } else if (menuItem.hasSubMenu()) { + SubMenu subMenu = menuItem.getSubMenu(); + MenuItem item = subMenu.findItem(id); + if (item != null) + return item; + } } return null; } @@ -298,10 +319,18 @@ public class GeckoMenu extends ListView @Override public boolean onMenuItemClick(MenuItem item) { - Activity activity = (Activity) mContext; - boolean result = activity.onOptionsItemSelected(item); - activity.closeOptionsMenu(); - return result; + GeckoApp activity = (GeckoApp) mContext; + + if (!item.hasSubMenu()) { + boolean result = activity.onOptionsItemSelected(item); + activity.closeOptionsMenu(); + return result; + } else { + // Dismiss this menu. + GeckoApp.MenuPresenter presenter = activity.getMenuPresenter(); + presenter.show((GeckoSubMenu) item.getSubMenu()); + return true; + } } public void setActionItemBarPresenter(ActionItemBarPresenter presenter) { diff --git a/mobile/android/base/GeckoMenuInflater.java b/mobile/android/base/GeckoMenuInflater.java index c488d6f267ea..342ec0021fa3 100644 --- a/mobile/android/base/GeckoMenuInflater.java +++ b/mobile/android/base/GeckoMenuInflater.java @@ -16,17 +16,21 @@ import android.view.InflateException; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.SubMenu; import java.io.IOException; public class GeckoMenuInflater extends MenuInflater { private static final String LOGTAG = "GeckoMenuInflater"; + private static final String TAG_MENU = "menu"; private static final String TAG_ITEM = "item"; private static final int NO_ID = 0; private Context mContext; + private boolean isSubMenu; + // Private class to hold the parsed menu item. private class ParsedItem { public int id; @@ -43,13 +47,13 @@ public class GeckoMenuInflater extends MenuInflater { public GeckoMenuInflater(Context context) { super(context); mContext = context; + + isSubMenu = false; } public void inflate(int menuRes, Menu menu) { - // This is a very minimal parser for the custom menu. - // This assumes that there is only one menu tag in the resource file. - // This does not support sub-menus. + // This does not check for a well-formed XML. XmlResourceParser parser = null; try { @@ -57,6 +61,8 @@ public class GeckoMenuInflater extends MenuInflater { AttributeSet attrs = Xml.asAttributeSet(parser); ParsedItem item = null; + SubMenu subMenu = null; + MenuItem menuItem = null; String tag; int eventType = parser.getEventType(); @@ -70,14 +76,35 @@ public class GeckoMenuInflater extends MenuInflater { // Parse the menu item. item = new ParsedItem(); parseItem(item, attrs); - } + } else if (tag.equals(TAG_MENU)) { + if (item != null) { + // Start parsing the sub menu. + isSubMenu = true; + subMenu = menu.addSubMenu(NO_ID, item.id, item.order, item.title); + menuItem = subMenu.getItem(); + + // Set the menu item in main menu. + setValues(item, menuItem); + } + } break; case XmlPullParser.END_TAG: if (parser.getName().equals(TAG_ITEM)) { - // Add the item. - MenuItem menuItem = menu.add(NO_ID, item.id, item.order, item.title); - setValues(item, menuItem); + if (isSubMenu && subMenu == null) { + isSubMenu = false; + } else { + // Add the item. + if (subMenu == null) + menuItem = menu.add(NO_ID, item.id, item.order, item.title); + else + menuItem = subMenu.add(NO_ID, item.id, item.order, item.title); + + setValues(item, menuItem); + } + } else if (tag.equals(TAG_MENU)) { + // End of sub menu. + subMenu = null; } break; } diff --git a/mobile/android/base/GeckoMenuItem.java b/mobile/android/base/GeckoMenuItem.java index ce9b82e333c9..f1e3142eae91 100644 --- a/mobile/android/base/GeckoMenuItem.java +++ b/mobile/android/base/GeckoMenuItem.java @@ -51,6 +51,7 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener { private boolean mEnabled; private Drawable mIcon; private int mIconRes; + private GeckoSubMenu mSubMenu; private MenuItem.OnMenuItemClickListener mMenuItemClickListener; private OnVisibilityChangedListener mVisibilityChangedListener; private OnShowAsActionChangedListener mShowAsActionChangedListener; @@ -141,7 +142,7 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener { @Override public SubMenu getSubMenu() { - return null; + return mSubMenu; } @Override @@ -156,7 +157,7 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener { @Override public boolean hasSubMenu() { - return false; + return (mSubMenu != null); } @Override @@ -308,6 +309,11 @@ public class GeckoMenuItem implements MenuItem, View.OnClickListener { return this; } + public MenuItem setSubMenu(GeckoSubMenu subMenu) { + mSubMenu = subMenu; + return this; + } + @Override public MenuItem setTitle(CharSequence title) { mTitle = title; diff --git a/mobile/android/base/GeckoSubMenu.java b/mobile/android/base/GeckoSubMenu.java new file mode 100644 index 000000000000..0432dafda558 --- /dev/null +++ b/mobile/android/base/GeckoSubMenu.java @@ -0,0 +1,85 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; + +import java.util.ArrayList; +import java.util.List; + +public class GeckoSubMenu extends GeckoMenu + implements SubMenu { + private static final String LOGTAG = "GeckoSubMenu"; + + private Context mContext; + + // MenuItem associated with this submenu. + private MenuItem mMenuItem; + + public GeckoSubMenu(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + } + + @Override + public void clearHeader() { + } + + public SubMenu setMenuItem(MenuItem item) { + mMenuItem = item; + return this; + } + + @Override + public MenuItem getItem() { + return mMenuItem; + } + + @Override + public SubMenu setHeaderIcon(Drawable icon) { + return this; + } + + @Override + public SubMenu setHeaderIcon(int iconRes) { + return this; + } + + @Override + public SubMenu setHeaderTitle(CharSequence title) { + return this; + } + + @Override + public SubMenu setHeaderTitle(int titleRes) { + return this; + } + + @Override + public SubMenu setHeaderView(View view) { + return this; + } + + @Override + public SubMenu setIcon(Drawable icon) { + return this; + } + + @Override + public SubMenu setIcon(int iconRes) { + return this; + } +} diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 725909938b4d..7d613695e5d8 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -81,6 +81,7 @@ FENNEC_JAVA_FILES = \ GeckoMenuInflater.java \ GeckoMenuItem.java \ GeckoMessageReceiver.java \ + GeckoSubMenu.java \ GeckoPreferences.java \ GeckoProfile.java \ GeckoThread.java \ diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index eb1e11f474f8..bda35167cab6 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -124,6 +124,7 @@ size. --> + diff --git a/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml.in b/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml.in index 880e45bb631b..ca9ed71bb307 100644 --- a/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml.in +++ b/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml.in @@ -28,10 +28,6 @@ gecko:icon="@drawable/ic_menu_reading_list_add" gecko:title="@string/reading_list" /> - - @@ -41,14 +37,26 @@ gecko:title="@string/desktop_mode" gecko:checkable="true" /> - + - + - + + + + + + + + + + + - - @@ -42,14 +38,26 @@ gecko:title="@string/desktop_mode" gecko:checkable="true" /> - + - + - + + + + + + + + + + + - - @@ -42,14 +38,26 @@ gecko:title="@string/desktop_mode" gecko:checkable="true" /> - + - + - + + + + + + + + + + + &save_as_pdf; &find_in_page; &desktop_mode; + &tools; &find_text; &find_prev;