diff --git a/README.md b/README.md index bbf272b..e7942bf 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Download Hover is available through jCenter: ```groovy -compile 'io.mattcarroll.hover:hover:0.9.3' +compile 'io.mattcarroll.hover:hover:0.9.4' ``` License diff --git a/hover/build.gradle b/hover/build.gradle index 636e953..303a462 100644 --- a/hover/build.gradle +++ b/hover/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'com.android.library' -version = '0.9.3' +version = '0.9.4' android { compileSdkVersion 23 diff --git a/hover/src/main/java/io/mattcarroll/hover/HoverMenuAdapter.java b/hover/src/main/java/io/mattcarroll/hover/HoverMenuAdapter.java index 2d41769..c8ccda3 100644 --- a/hover/src/main/java/io/mattcarroll/hover/HoverMenuAdapter.java +++ b/hover/src/main/java/io/mattcarroll/hover/HoverMenuAdapter.java @@ -1,7 +1,10 @@ package io.mattcarroll.hover; +import android.support.annotation.NonNull; import android.view.View; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuAction; + /** * Adapter that provides {@code View}s for the tabs and the content within a Hover menu. */ @@ -23,11 +26,10 @@ public interface HoverMenuAdapter { View getTabView(int index); /** - * Returns the content that should be displayed for the {@code index}'th tab. + * Returns the {@link MenuAction} to activate for the tab at the given {@code index}. * - * @param index index of tab - * @return content for the {@code index}'th tab + * @param index index of tab to activate */ - NavigatorContent getContentView(int index); + MenuAction getTabMenuAction(int index); } diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuContentView.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuContentView.java index f5eaf93..d52359b 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuContentView.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuContentView.java @@ -9,6 +9,7 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; +import io.mattcarroll.hover.Navigator; import io.mattcarroll.hover.NavigatorContent; import io.mattcarroll.hover.R; @@ -17,7 +18,7 @@ * top of the content area. The content area itself can display anything provided by a given * {@link NavigatorContent}. */ -public class HoverMenuContentView extends FrameLayout { +public class HoverMenuContentView extends FrameLayout implements Navigator { private static final String TAG = "HoverMenuContentView"; @@ -67,15 +68,26 @@ public void setActiveTab(@NonNull View tabView) { updateTabSelectorPosition(); } - /** - * Displays the given {@code content} in the content area of this {@code View}. - * @param content content to display - */ - public void setContent(@NonNull NavigatorContent content) { - mContentContainer.clearContent(); + @Override + public void setTitle(@NonNull String title) { + mContentContainer.setTitle(title); + } + + @Override + public void pushContent(@NonNull NavigatorContent content) { mContentContainer.pushContent(content); } + @Override + public boolean popContent() { + return mContentContainer.popContent(); + } + + @Override + public void clearContent() { + mContentContainer.clearContent(); + } + /** * Tries to handle a back-press. * @return true if the back-press was handled, false otherwise diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuView.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuView.java index 0cde34e..8102380 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuView.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/HoverMenuView.java @@ -630,7 +630,12 @@ private void setActiveTab(String id) { mActiveTabId = id; mActiveTab = findViewById(id.hashCode()); mContentView.setActiveTab(mActiveTab); - mContentView.setContent(mAdapter.getContentView(Integer.parseInt(id))); + + // This is a top-level menu item so clear all content from the menu to start fresh. + mContentView.clearContent(); + + // Activate the chosen tab. + mAdapter.getTabMenuAction(Integer.parseInt(id)).execute(getContext(), mContentView); } public void setTabSelectionListener(@Nullable TabSelectionListener tabSelectionListener) { @@ -641,11 +646,6 @@ public void setTabSelectionListener(@Nullable TabSelectionListener tabSelectionL } } - // TODO: this should probably be private - public void setContentView(@NonNull NavigatorContent navigatorContent) { - mContentView.setContent(navigatorContent); - } - public boolean onBackPressed() { if (isExpanded() && !mContentView.onBackPressed()) { collapse(); diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/ToolbarNavigatorView.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/ToolbarNavigatorView.java index 677c90a..96ebf51 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/ToolbarNavigatorView.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/ToolbarNavigatorView.java @@ -88,6 +88,8 @@ public boolean popContent() { mContentStack.peek().onShown(this); } + updateToolbarBackButton(); + return true; } else { return false; diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/DoNothingMenuAction.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/DoNothingMenuAction.java new file mode 100644 index 0000000..0154e2f --- /dev/null +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/DoNothingMenuAction.java @@ -0,0 +1,18 @@ +package io.mattcarroll.hover.defaulthovermenu.menus; + +import android.content.Context; +import android.support.annotation.NonNull; + +import io.mattcarroll.hover.Navigator; + +/** + * {@link MenuAction} that does nothing. Use this for temporary stubbing of menu item behavior. + */ +public class DoNothingMenuAction implements MenuAction { + + @Override + public void execute(@NonNull Context context, @NonNull Navigator navigator) { + // Do nothing. + } + +} diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/Menu.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/Menu.java new file mode 100644 index 0000000..87e1516 --- /dev/null +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/Menu.java @@ -0,0 +1,30 @@ +package io.mattcarroll.hover.defaulthovermenu.menus; + +import android.support.annotation.NonNull; + +import java.util.List; + +/** + * A {@code Menu} contains {@link MenuItem}s. + */ +public class Menu { + + private final String mTitle; + private final List mMenuItemList; + + public Menu(@NonNull String title, @NonNull List menuItemList) { + mTitle = title; + mMenuItemList = menuItemList; + } + + @NonNull + public String getTitle() { + return mTitle; + } + + @NonNull + public List getMenuItemList() { + return mMenuItemList; + } + +} diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItem.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItem.java index ea4290e..63f66d8 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItem.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItem.java @@ -4,66 +4,36 @@ import android.support.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.UUID; /** * Represents a menu item that can act as a composite with submenu items. */ public class MenuItem { - private Type mType; - private String mId; - private String mTitle; - private List mItems; - private MenuItem mParent; - private String mPayload; + private final String mId; + private final String mTitle; + private final MenuAction mMenuAction; - public MenuItem(@NonNull Type type, @NonNull String id, @NonNull String title, @Nullable MenuItem parent) { - this(type, id, title, parent, null); - } - - public MenuItem(@NonNull Type type, @NonNull String id, @NonNull String title, @Nullable MenuItem parent, @Nullable String payload) { - mType = type; + public MenuItem(@NonNull String id, @NonNull String title, @NonNull MenuAction menuAction) { mId = id; mTitle = title; - mItems = new ArrayList<>(); - mParent = parent; - mPayload = payload; + mMenuAction = menuAction; } + @NonNull public String getId() { return mId; } - public Type getType() { - return mType; - } - + @NonNull public String getTitle() { return mTitle; } - @Nullable - public String getPayload() { - return mPayload; - } - - public List getItems() { - return new ArrayList<>(mItems); - } - - public void addItems(MenuItem... items) { - mItems.addAll(Arrays.asList(items)); - } - - public void removeItems(MenuItem... items) { - mItems.removeAll(Arrays.asList(items)); - } - - public MenuItem getParent() { - return mParent; + @NonNull + public MenuAction getMenuAction() { + return mMenuAction; } @Override @@ -82,10 +52,4 @@ public int hashCode() { return mId.hashCode(); } - public enum Type { - MENU, - DO_ACTION, - SHOW_VIEW, - GENERATE_MENU - } } diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItemView.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItemView.java index 12cec1a..a415c6c 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItemView.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuItemView.java @@ -10,7 +10,7 @@ /** - * View that represents a MenuItem as a list item. + * View that represents a {@link MenuItem} as a list item. */ public class MenuItemView extends FrameLayout { diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListAdapter.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListAdapter.java index 66851c7..5730024 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListAdapter.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListAdapter.java @@ -1,5 +1,6 @@ package io.mattcarroll.hover.defaulthovermenu.menus; +import android.support.annotation.Nullable; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; @@ -8,27 +9,25 @@ import java.util.List; /** - * Adapter that displays {@link MenuItem}s using {@link MenuItemView}s. + * Adapter that displays a {@link Menu} using {@link MenuItemView}s. */ public class MenuListAdapter extends BaseAdapter { - private List mMenuItems = new ArrayList<>(); - - public void setMenuItems(List menuItems) { - mMenuItems.clear(); - mMenuItems.addAll(menuItems); + private Menu mMenu; + public void setMenu(@Nullable Menu menu) { + mMenu = menu; notifyDataSetChanged(); } @Override public int getCount() { - return mMenuItems.size(); + return null == mMenu ? 0 : mMenu.getMenuItemList().size(); } @Override - public MenuItem getItem(int i) { - return mMenuItems.get(i); + public MenuItem getItem(int index) { + return mMenu.getMenuItemList().get(index); } @Override diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListNavigatorContent.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListNavigatorContent.java index 8b2e1e3..fe2de27 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListNavigatorContent.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListNavigatorContent.java @@ -16,36 +16,22 @@ public class MenuListNavigatorContent implements NavigatorContent { private static final String TAG = "MenuListNavigatorContent"; - private MenuItem mMenu; + private Menu mMenu; private MenuListView mMenuListView; private Navigator mNavigator; - public MenuListNavigatorContent(@NonNull Context context, @NonNull final MenuItem menu) { + public MenuListNavigatorContent(@NonNull Context context, @NonNull final Menu menu) { this(context, menu, null); } - public MenuListNavigatorContent(@NonNull Context context, @NonNull final MenuItem menu, @Nullable View emptyView) { + public MenuListNavigatorContent(@NonNull Context context, @NonNull final Menu menu, @Nullable View emptyView) { mMenu = menu; mMenuListView = new MenuListView(context); mMenuListView.setMenu(menu); mMenuListView.setMenuItemSelectionListener(new MenuListView.MenuItemSelectionListener() { @Override public void onMenuItemSelected(@NonNull MenuItem menuItem) { - switch (menuItem.getType()) { - case MENU: - MenuListNavigatorContent submenu = new MenuListNavigatorContent(getView().getContext(), menuItem); - mNavigator.pushContent(submenu); - break; - case DO_ACTION: - case SHOW_VIEW: - try { - runDevActionFromClasspath(menuItem.getPayload()); - } catch (Exception e) { - Log.e(TAG, "Failed to run action for menu item: " + menuItem.getTitle()); - e.printStackTrace(); - } - break; - } + menuItem.getMenuAction().execute(getView().getContext(), mNavigator); } }); @@ -74,21 +60,4 @@ public void onHidden() { mNavigator = null; } - private void runDevActionFromClasspath(@NonNull String devActionClassPath) { - try { - MenuAction menuAction = (MenuAction) Class.forName(devActionClassPath).newInstance(); - menuAction.execute(getView().getContext(), mNavigator); - } catch(ClassNotFoundException e) { - Log.w(TAG, "Could not locate class: " + devActionClassPath); - e.printStackTrace(); - } catch (InstantiationException e) { - Log.w(TAG, "InstantiationException: " + devActionClassPath + ", error: " + e.getMessage()); - e.printStackTrace(); - } catch (IllegalAccessException e) { - Log.w(TAG, "IllegalAccessException: " + devActionClassPath); - e.printStackTrace(); - } catch (ClassCastException e) { - Log.w(TAG, "Menu item's action is not a DevAction implementation: " + devActionClassPath); - } - } } diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListView.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListView.java index aa760af..bddd280 100755 --- a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListView.java +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/MenuListView.java @@ -10,10 +10,8 @@ import android.widget.FrameLayout; import android.widget.ListView; -import java.util.ArrayList; - /** - * View that displays all items in a given {@link MenuItem}. + * View that displays all items in a given {@link Menu}. */ public class MenuListView extends FrameLayout { @@ -47,24 +45,22 @@ public void onItemClick(AdapterView parent, View view, int position, long id) addView(mListView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } - public void setEmptyView(@NonNull View emptyView) { + public void setEmptyView(@Nullable View emptyView) { // Remove existing empty view. if (null != mEmptyView) { removeView(mEmptyView); } mEmptyView = emptyView; - addView(mEmptyView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + if (null != mEmptyView) { + addView(mEmptyView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + } + updateEmptyViewVisibility(); } - public void setMenu(@Nullable MenuItem menu) { - if (null == menu) { - mMenuListAdapter.setMenuItems(new ArrayList(0)); - } else { - mMenuListAdapter.setMenuItems(menu.getItems()); - } - + public void setMenu(@Nullable Menu menu) { + mMenuListAdapter.setMenu(menu); updateEmptyViewVisibility(); } diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/ShowSubmenuMenuAction.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/ShowSubmenuMenuAction.java new file mode 100644 index 0000000..2eb6905 --- /dev/null +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/ShowSubmenuMenuAction.java @@ -0,0 +1,38 @@ +package io.mattcarroll.hover.defaulthovermenu.menus; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.View; + +import io.mattcarroll.hover.Navigator; + +/** + * {@link MenuAction} that displays a submenu in a given {@link Navigator}. + */ +public class ShowSubmenuMenuAction implements MenuAction { + + private final Menu mMenu; + private final View mEmptyView; + private MenuListNavigatorContent mNavigatorContent; + + public ShowSubmenuMenuAction(@NonNull Menu menu) { + this(menu, null); + } + + public ShowSubmenuMenuAction(@NonNull Menu menu, @Nullable View emptyView) { + mMenu = menu; + mEmptyView = emptyView; + } + + @Override + public void execute(@NonNull Context context, @NonNull Navigator navigator) { + if (null == mNavigatorContent) { + // This is our first time being activated. Create our menu display. + mNavigatorContent = new MenuListNavigatorContent(context, mMenu, mEmptyView); + } + + navigator.pushContent(mNavigatorContent); + } + +} diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/serialization/MenuActionFactory.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/serialization/MenuActionFactory.java new file mode 100644 index 0000000..a947c4e --- /dev/null +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/serialization/MenuActionFactory.java @@ -0,0 +1,17 @@ +package io.mattcarroll.hover.defaulthovermenu.menus.serialization; + +import android.support.annotation.NonNull; + +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuAction; + +/** + * Creates {@link MenuAction}s given various action IDs. + */ +public interface MenuActionFactory { + + MenuAction createShowSubmenuMenuAction(@NonNull Menu menu); + + MenuAction createMenuActionForId(@NonNull String actionId); + +} diff --git a/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/serialization/MenuDeserializer.java b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/serialization/MenuDeserializer.java new file mode 100644 index 0000000..4ebde15 --- /dev/null +++ b/hover/src/main/java/io/mattcarroll/hover/defaulthovermenu/menus/serialization/MenuDeserializer.java @@ -0,0 +1,103 @@ +package io.mattcarroll.hover.defaulthovermenu.menus.serialization; + +import android.support.annotation.NonNull; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import io.mattcarroll.hover.defaulthovermenu.menus.DoNothingMenuAction; +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuAction; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuItem; + +/** + * Creates a {@link Menu} from a text-based configuration. + */ +public class MenuDeserializer { + + private final MenuActionFactory mMenuActionFactory; + + public MenuDeserializer(@NonNull MenuActionFactory menuActionFactory) { + mMenuActionFactory = menuActionFactory; + } + + public Menu deserializeMenu(InputStream in) throws IOException { + return doDeserialization(new BufferedReader(new InputStreamReader(in))); + } + + private Menu doDeserialization(@NonNull BufferedReader br) throws IOException { + // Read in all the menu configuration JSON. + StringBuilder sb = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + } + String jsonContent = sb.toString(); + + // Create Menu from JSON. + try { + JSONArray menuJson = new JSONArray(jsonContent); + return createMenuFromJson(menuJson); + } catch (JSONException e) { + throw new IOException(e); + } + } + + private Menu createMenuFromJson(@NonNull JSONArray menuJson) throws JSONException { + List menuItemList = new ArrayList<>(); + JSONObject menuItemJson; + MenuItem menuItem; + for (int i = 0; i < menuJson.length(); ++i) { + menuItemJson = menuJson.getJSONObject(i); + menuItem = recursivelyConstructMenuItem(menuItemJson); + menuItemList.add(menuItem); + } + + return new Menu("", menuItemList); + } + + private MenuItem recursivelyConstructMenuItem(@NonNull JSONObject menuItemJson) throws JSONException { + MenuItem menuItem; + + if (menuItemJson.has("items")) { + // This menu item contains a submenu. Recursively construct the submenu. + JSONArray submenuItemsJson = menuItemJson.getJSONArray("items"); + List submenuItems = new ArrayList<>(); + MenuItem submenuItem; + for (int i = 0; i < submenuItemsJson.length(); ++i) { + submenuItem = recursivelyConstructMenuItem(submenuItemsJson.getJSONObject(i)); + submenuItems.add(submenuItem); + } + + String id = menuItemJson.has("id") ? menuItemJson.getString("id") : UUID.randomUUID().toString(); + String title = menuItemJson.getString("title"); + Menu submenu = new Menu(title, submenuItems); + MenuAction showMenuAction = mMenuActionFactory.createShowSubmenuMenuAction(submenu); + menuItem = new MenuItem(id, title, showMenuAction); + } else if (menuItemJson.has("action")) { + // This item does not have a submenu, it just has a menu action. + String id = menuItemJson.has("id") ? menuItemJson.getString("id") : UUID.randomUUID().toString(); + String title = menuItemJson.getString("title"); + String menuActionId = menuItemJson.getString("action"); + MenuAction menuAction = mMenuActionFactory.createMenuActionForId(menuActionId); + menuItem = new MenuItem(id, title, menuAction); + } else { + // This menu item must a stub without any action. + String id = menuItemJson.has("id") ? menuItemJson.getString("id") : UUID.randomUUID().toString(); + String title = menuItemJson.getString("title"); + menuItem = new MenuItem(id, title, new DoNothingMenuAction()); + } + + return menuItem; + } + +} diff --git a/hoverdemo/src/main/assets/demo_menu.json b/hoverdemo/src/main/assets/demo_menu.json index 94432bd..eabc369 100644 --- a/hoverdemo/src/main/assets/demo_menu.json +++ b/hoverdemo/src/main/assets/demo_menu.json @@ -1,31 +1,35 @@ -{ - "nest":{ - "title":"Red", - "type":"menu", +[ + { + "id":"location", + "title":"Location", "items":[ { - "title":"Take Snapshot", - "type":"action", - "data":"TODO" + "title":"Location Methods", + "items":[ + { + "title":"GPS" + },{ + "title":"Cell Tower Triangulation" + },{ + "title":"Location Services" + } + ] } ] - }, - "camera":{ - "title":"Green", - "type":"menu", - "items":[ - - ] - }, - "protect":{ - "title":"Blue", - "type":"menu", + },{ + "id":"bluetooth", + "title":"Bluetooth Devices", "items":[ { - "title":"Test Alarms", - "type":"view", - "data":"TODO" + "title":"Estimote Beacon" + },{ + "title":"Moto 360" } ] + },{ + "id":"wifi", + "title":"WiFi", + "items":[ + ] } -} \ No newline at end of file +] \ No newline at end of file diff --git a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuActivity.java b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuActivity.java index 5afa4f3..12c4757 100644 --- a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuActivity.java +++ b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuActivity.java @@ -4,7 +4,13 @@ import android.os.Bundle; import android.util.Log; +import java.io.IOException; +import java.io.InputStream; + +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.serialization.MenuDeserializer; import io.mattcarroll.hover.defaulthovermenu.view.ViewHoverMenu; +import io.mattcarroll.hover.hoverdemo.menu.DemoMenuFromCode; /** * Presents a Hover menu within an Activity (instead of presenting it on top of all other Windows). @@ -21,7 +27,33 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hover_menu); - mHoverMenuView = (ViewHoverMenu) findViewById(R.id.hovermenu); - mHoverMenuView.setAdapter(new DemoHoverMenuAdapter(this)); + try { + mHoverMenuView = (ViewHoverMenu) findViewById(R.id.hovermenu); + mHoverMenuView.setAdapter(new DemoHoverMenuAdapter(this, createDemoMenuFromFile())); + } catch (IOException e) { + Log.e(TAG, "Failed to create demo menu from file."); + e.printStackTrace(); + } + } + + /** + * Example of how to create a menu from a configuration file. + * + * @return Menu + * @throws IOException + */ + private Menu createDemoMenuFromFile() throws IOException { + InputStream in = getAssets().open("demo_menu.json"); + MenuDeserializer menuDeserializer = new MenuDeserializer(new DemoMenuActionFactory(this)); + return menuDeserializer.deserializeMenu(in); + } + + /** + * Example of how to create a menu in code. + * @return Menu + */ + private Menu createDemoMenuFromCode() { + DemoMenuFromCode demoMenuFromCode = new DemoMenuFromCode(this); + return demoMenuFromCode.createMenu(); } } diff --git a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuAdapter.java b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuAdapter.java index cf2b5d8..69503e1 100644 --- a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuAdapter.java +++ b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuAdapter.java @@ -7,82 +7,51 @@ import android.view.View; import android.widget.ImageView; -import java.util.UUID; +import java.io.IOException; import io.mattcarroll.hover.HoverMenuAdapter; -import io.mattcarroll.hover.NavigatorContent; -import io.mattcarroll.hover.defaulthovermenu.menus.MenuItem; -import io.mattcarroll.hover.defaulthovermenu.menus.MenuListNavigatorContent; -import io.mattcarroll.hover.hoverdemo.menu.EmptyListView; +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuAction; /** * Demo implementation of a {@link HoverMenuAdapter}. */ public class DemoHoverMenuAdapter implements HoverMenuAdapter { + private static final String LOCATION_ID = "location"; + private static final String BLUETOOTH_ID = "bluetooth"; + private static final String WIFI_ID = "wifi"; + private final Context mContext; - private MenuListNavigatorContent mRedContent; - private MenuListNavigatorContent mGreenContent; - private MenuListNavigatorContent mBlueContent; + private final Menu mMenu; - public DemoHoverMenuAdapter(@NonNull Context context) { + public DemoHoverMenuAdapter(@NonNull Context context, @NonNull Menu demoMenu) throws IOException { mContext = context; - - MenuItem locationMenu = new MenuItem(MenuItem.Type.MENU, UUID.randomUUID().toString(), "User Location", null); - locationMenu.addItems( - new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "GPS", null), - new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Cell Tower Triangulation", null), - new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Location Services", null) - ); - mRedContent = new MenuListNavigatorContent(context, locationMenu, new EmptyListView(context)); - - MenuItem bluetoothMenu = new MenuItem(MenuItem.Type.MENU, UUID.randomUUID().toString(), "Bluetooth Devices", null); - bluetoothMenu.addItems( - new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Estimote Beacon", null), - new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Moto 360", null) - ); - mGreenContent = new MenuListNavigatorContent(context, bluetoothMenu, new EmptyListView(context)); - - MenuItem wifiMenu = new MenuItem(MenuItem.Type.MENU, UUID.randomUUID().toString(), "WiFi", null); -// wifiMenu.addItems( -// new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Wireless Access Point", null), -// new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "ATT-483759", null), -// new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Linksys-1294562", null) -// ); - mBlueContent = new MenuListNavigatorContent(context, wifiMenu, new EmptyListView(context)); + mMenu = demoMenu; } @Override public int getTabCount() { - return 3; + return mMenu.getMenuItemList().size(); } @Override public View getTabView(int index) { - switch (index) { - case 0: - return createTabView(R.drawable.ic_tab_location); - case 1: - return createTabView(R.drawable.ic_tab_bluetooth); - case 2: - return createTabView(R.drawable.ic_tab_wifi); - default: - throw new RuntimeException("Unknown tab selected: " + index); + String menuItemId = mMenu.getMenuItemList().get(index).getId(); + if (LOCATION_ID.equals(menuItemId)) { + return createTabView(R.drawable.ic_tab_location); + } else if (BLUETOOTH_ID.equals(menuItemId)) { + return createTabView(R.drawable.ic_tab_bluetooth); + } else if (WIFI_ID.equals(menuItemId)) { + return createTabView(R.drawable.ic_tab_wifi); + } else { + throw new RuntimeException("Unknown tab selected: " + index); } } @Override - public NavigatorContent getContentView(int index) { - switch (index) { - case 0: - return mRedContent; - case 1: - return mGreenContent; - case 2: - return mBlueContent; - default: - throw new RuntimeException("Unknown content selected: " + index); - } + public MenuAction getTabMenuAction(int index) { + return mMenu.getMenuItemList().get(index).getMenuAction(); } private View createTabView(@DrawableRes int tabBitmapRes) { diff --git a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuService.java b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuService.java index 960f947..9514298 100755 --- a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuService.java +++ b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoHoverMenuService.java @@ -4,8 +4,14 @@ import android.content.Intent; import android.view.ContextThemeWrapper; +import java.io.IOException; +import java.io.InputStream; + +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.serialization.MenuDeserializer; import io.mattcarroll.hover.defaulthovermenu.window.HoverMenuService; import io.mattcarroll.hover.HoverMenuAdapter; +import io.mattcarroll.hover.hoverdemo.menu.DemoMenuFromCode; /** * Demo {@link HoverMenuService}. @@ -26,7 +32,32 @@ protected int getMenuTheme() { @Override protected HoverMenuAdapter createHoverMenuAdapter() { final ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(this, R.style.AppTheme); - return new DemoHoverMenuAdapter(contextThemeWrapper); + try { + return new DemoHoverMenuAdapter(contextThemeWrapper, createDemoMenuFromFile()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Example of how to create a menu from a configuration file. + * + * @return Menu + * @throws IOException + */ + private Menu createDemoMenuFromFile() throws IOException { + InputStream in = getAssets().open("demo_menu.json"); + MenuDeserializer menuDeserializer = new MenuDeserializer(new DemoMenuActionFactory(this)); + return menuDeserializer.deserializeMenu(in); + } + + /** + * Example of how to create a menu in code. + * @return Menu + */ + private Menu createDemoMenuFromCode() { + DemoMenuFromCode demoMenuFromCode = new DemoMenuFromCode(this); + return demoMenuFromCode.createMenu(); } } diff --git a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoMenuActionFactory.java b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoMenuActionFactory.java new file mode 100644 index 0000000..f4d3ba1 --- /dev/null +++ b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/DemoMenuActionFactory.java @@ -0,0 +1,33 @@ +package io.mattcarroll.hover.hoverdemo; + +import android.content.Context; +import android.support.annotation.NonNull; + +import io.mattcarroll.hover.defaulthovermenu.menus.DoNothingMenuAction; +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuAction; +import io.mattcarroll.hover.defaulthovermenu.menus.ShowSubmenuMenuAction; +import io.mattcarroll.hover.defaulthovermenu.menus.serialization.MenuActionFactory; +import io.mattcarroll.hover.hoverdemo.menu.EmptyListView; + +/** + * Creates {@link MenuAction}s based on IDs. + */ +public class DemoMenuActionFactory implements MenuActionFactory { + + private final Context mContext; + + public DemoMenuActionFactory(@NonNull Context context) { + mContext = context; + } + + @Override + public MenuAction createShowSubmenuMenuAction(@NonNull Menu menu) { + return new ShowSubmenuMenuAction(menu, new EmptyListView(mContext)); + } + + @Override + public MenuAction createMenuActionForId(@NonNull String actionId) { + return new DoNothingMenuAction(); + } +} diff --git a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/menu/DemoMenuFromCode.java b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/menu/DemoMenuFromCode.java new file mode 100644 index 0000000..b4e5492 --- /dev/null +++ b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/menu/DemoMenuFromCode.java @@ -0,0 +1,59 @@ +package io.mattcarroll.hover.hoverdemo.menu; + +import android.content.Context; +import android.support.annotation.NonNull; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import io.mattcarroll.hover.defaulthovermenu.menus.DoNothingMenuAction; +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.MenuItem; +import io.mattcarroll.hover.defaulthovermenu.menus.ShowSubmenuMenuAction; + +/** + * Example of creating a {@link Menu} in code. + */ +public class DemoMenuFromCode { + + private static final String LOCATION_ID = "location"; + private static final String BLUETOOTH_ID = "bluetooth"; + private static final String WIFI_ID = "wifi"; + + private final Context mContext; + + public DemoMenuFromCode(@NonNull Context context) { + mContext = context; + } + + public Menu createMenu() { + List locationSubmenuItems = Arrays.asList( + new MenuItem(UUID.randomUUID().toString(), "GPS", new DoNothingMenuAction()), + new MenuItem(UUID.randomUUID().toString(), "Cell Tower Triangulation", new DoNothingMenuAction()), + new MenuItem(UUID.randomUUID().toString(), "Location Services", new DoNothingMenuAction()) + ); + Menu locationMenu = new Menu("User Location", locationSubmenuItems); + MenuItem locationMenuItem = new MenuItem(LOCATION_ID, "User Location", new ShowSubmenuMenuAction(locationMenu, new EmptyListView(mContext))); + + List bluetoothSubmenuItems = Arrays.asList( + new MenuItem(UUID.randomUUID().toString(), "Estimote Beacon", new DoNothingMenuAction()), + new MenuItem(UUID.randomUUID().toString(), "Moto 360", new DoNothingMenuAction()) + ); + Menu bluetoothMenu = new Menu("Bluetooth Devices", bluetoothSubmenuItems); + MenuItem bluetoothMenuItem = new MenuItem(BLUETOOTH_ID, "Bluetooth Devices", new ShowSubmenuMenuAction(bluetoothMenu, new EmptyListView(mContext))); + + List wifiSubmenuItems = Arrays.asList( +// wifiMenu.addItems( +// new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Wireless Access Point", null), +// new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "ATT-483759", null), +// new MenuItem(MenuItem.Type.DO_ACTION, UUID.randomUUID().toString(), "Linksys-1294562", null) +// ); + ); + Menu wifiMenu = new Menu("WiFi", wifiSubmenuItems); + MenuItem wifiMenuItem = new MenuItem(WIFI_ID, "WiFi", new ShowSubmenuMenuAction(wifiMenu, new EmptyListView(mContext))); + + return new Menu("", Arrays.asList(locationMenuItem, bluetoothMenuItem, wifiMenuItem)); + } + +} diff --git a/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/menu/DemoMenuFromFile.java b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/menu/DemoMenuFromFile.java new file mode 100644 index 0000000..b76a18e --- /dev/null +++ b/hoverdemo/src/main/java/io/mattcarroll/hover/hoverdemo/menu/DemoMenuFromFile.java @@ -0,0 +1,30 @@ +package io.mattcarroll.hover.hoverdemo.menu; + +import android.content.Context; +import android.support.annotation.NonNull; + +import java.io.IOException; + +import io.mattcarroll.hover.defaulthovermenu.menus.Menu; +import io.mattcarroll.hover.defaulthovermenu.menus.serialization.MenuActionFactory; +import io.mattcarroll.hover.defaulthovermenu.menus.serialization.MenuDeserializer; + +/** + * Example of how to deserialize a menu from a file. + */ +public class DemoMenuFromFile { + + private final Context mContext; + private final MenuActionFactory mMenuActionFactory; + + public DemoMenuFromFile(@NonNull Context context, @NonNull MenuActionFactory menuActionFactory) { + mContext = context; + mMenuActionFactory = menuActionFactory; + } + + public Menu createFromFile(@NonNull String assetFileName) throws IOException { + MenuDeserializer menuDeserializer = new MenuDeserializer(mMenuActionFactory); + return menuDeserializer.deserializeMenu(mContext.getAssets().open(assetFileName)); + } + +}