package it.sephiroth.android.library.bottomnavigation; import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Color; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import java.util.ArrayList; import java.util.List; import it.sephiroth.android.library.bottonnavigation.R; /** * Created by alessandro crugnola on 4/3/16 at 7:59 PM. * Project: MaterialBottomNavigation */ class MenuParser { private static final String TAG = MenuParser.class.getSimpleName(); static class Menu { private final Context context; private BottomNavigationItem[] items; private int colorActive; private int background; private int rippleColor; private int colorInactive; private int colorDisabled; private int itemAnimationDuration; private boolean shifting; private boolean tablet; private int badgeColor; public boolean forceFixed; public Menu(final Context context) { this.context = context; } public int getItemAnimationDuration() { return itemAnimationDuration; } @Override public String toString() { return String.format("Menu{background:%x, colorActive:%x, colorInactive:%x, colorDisabled: %s, shifting:%b, tablet:%b}", background, colorActive, colorInactive, colorDisabled, shifting, tablet ); } public int getBadgeColor() { return badgeColor; } public int getBackground() { if (0 == background) { if (shifting && !tablet) { return MiscUtils.getColor(context, R.attr.colorPrimary); } else { return MiscUtils.getColor(context, android.R.attr.windowBackground); } } return background; } public int getColorActive() { if (0 == colorActive) { if (shifting && !tablet) { colorActive = MiscUtils.getColor(context, android.R.attr.colorForegroundInverse); } else { colorActive = MiscUtils.getColor(context, android.R.attr.colorForeground); } } return colorActive; } public int getColorInactive() { if (0 == colorInactive) { if (shifting && !tablet) { int color = getColorActive(); colorInactive = Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color), Color.blue(color)); } else { int color = getColorActive(); colorInactive = Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color), Color.blue(color)); } } return colorInactive; } public int getColorDisabled() { if (0 == colorDisabled) { int color = getColorInactive(); colorDisabled = Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color), Color.blue(color)); } return colorDisabled; } public int getRippleColor() { if (0 == rippleColor) { if (shifting && !tablet) { rippleColor = ContextCompat.getColor(context, R.color.bbn_shifting_item_ripple_color); } else { rippleColor = ContextCompat.getColor(context, R.color.bbn_fixed_item_ripple_color); } } return rippleColor; } public void setItems(final BottomNavigationItem[] items) { this.items = items; this.shifting = (null != items && items.length > 3 && !forceFixed); } public boolean isShifting() { return shifting; } public BottomNavigationItem[] getItems() { return items; } public BottomNavigationItem getItemAt(final int index) { return items[index]; } public int getItemsCount() { if (null != items) { return items.length; } return 0; } /** * Returns true if the first item of the menu * has a color defined */ @SuppressWarnings ("unused") public boolean hasChangingColor() { return items[0].hasColor(); } void setTabletMode(final boolean tablet) { this.tablet = tablet; } public boolean isTablet() { return tablet; } } static class MenuItem { private int itemId; private CharSequence itemTitle; private int itemIconResId; private boolean itemEnabled; private int itemColor; public int getItemId() { return itemId; } public CharSequence getItemTitle() { return itemTitle; } public int getItemIconResId() { return itemIconResId; } public boolean isItemEnabled() { return itemEnabled; } public int getItemColor() { return itemColor; } } private MenuItem item; private Menu menu; public MenuParser() { } private void readMenu(final Context context, final AttributeSet attrs) { menu = new Menu(context); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BottomNavigationMenu); menu.itemAnimationDuration = a.getInt( R.styleable.BottomNavigationMenu_bbn_itemAnimationDuration, context.getResources().getInteger(R.integer.bbn_item_animation_duration) ); menu.background = a.getColor(R.styleable.BottomNavigationMenu_android_background, 0); menu.rippleColor = a.getColor(R.styleable.BottomNavigationMenu_bbn_rippleColor, 0); menu.colorInactive = a.getColor(R.styleable.BottomNavigationMenu_bbn_itemColorInactive, 0); menu.colorDisabled = a.getColor(R.styleable.BottomNavigationMenu_bbn_itemColorDisabled, 0); menu.colorActive = a.getColor(R.styleable.BottomNavigationMenu_bbn_itemColorActive, 0); menu.badgeColor = a.getColor(R.styleable.BottomNavigationMenu_bbn_badgeColor, Color.RED); menu.forceFixed = a.getBoolean(R.styleable.BottomNavigationMenu_bbn_alwaysShowLabels, false); a.recycle(); } @SuppressWarnings ("checkstyle:cyclomaticcomplexity") protected static Menu inflateMenu(final Context context, int menuRes) { List<BottomNavigationItem> list = new ArrayList<>(); MenuParser menuParser = new MenuParser(); try { final XmlResourceParser parser = context.getResources().getLayout(menuRes); AttributeSet attrs = Xml.asAttributeSet(parser); String tagName; int eventType = parser.getEventType(); boolean lookingForEndOfUnknownTag = false; String unknownTagName = null; do { if (eventType == XmlPullParser.START_TAG) { tagName = parser.getName(); if (tagName.equals("menu")) { menuParser.readMenu(context, attrs); eventType = parser.next(); break; } throw new RuntimeException("Expecting menu, got " + tagName); } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); boolean reachedEndOfMenu = false; while (!reachedEndOfMenu) { switch (eventType) { case XmlPullParser.START_TAG: if (lookingForEndOfUnknownTag) { break; } tagName = parser.getName(); if (tagName.equals("item")) { menuParser.readItem(context, attrs); } else { lookingForEndOfUnknownTag = true; unknownTagName = tagName; } break; case XmlPullParser.END_TAG: tagName = parser.getName(); if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) { lookingForEndOfUnknownTag = false; unknownTagName = null; } else if (tagName.equals("item")) { if (menuParser.hasItem()) { MenuParser.MenuItem item = menuParser.pullItem(); BottomNavigationItem tab = new BottomNavigationItem(item.getItemId(), item.getItemIconResId(), String.valueOf(item.getItemTitle()) ); tab.setEnabled(item.isItemEnabled()); tab.setColor(item.getItemColor()); list.add(tab); } } else if (tagName.equals("menu")) { reachedEndOfMenu = true; } break; case XmlPullParser.END_DOCUMENT: throw new RuntimeException("Unexpected end of document"); default: break; } eventType = parser.next(); } } catch (Exception e) { return null; } if (menuParser.hasMenu()) { Menu menu = menuParser.pullMenu(); menu.setItems(list.toArray(new BottomNavigationItem[list.size()])); return menu; } return null; } public MenuItem pullItem() { MenuItem current = item; item = null; return current; } public boolean hasItem() { return null != item; } public boolean hasMenu() { return null != menu; } private Menu pullMenu() { Menu current = menu; menu = null; return current; } /** * Called when the parser is pointing to an item tag. */ public void readItem(Context mContext, AttributeSet attrs) { TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.BottomNavigationMenuItem); item = new MenuItem(); item.itemId = a.getResourceId(R.styleable.BottomNavigationMenuItem_android_id, 0); item.itemTitle = a.getText(R.styleable.BottomNavigationMenuItem_android_title); item.itemIconResId = a.getResourceId(R.styleable.BottomNavigationMenuItem_android_icon, 0); item.itemEnabled = a.getBoolean(R.styleable.BottomNavigationMenuItem_android_enabled, true); item.itemColor = a.getColor(R.styleable.BottomNavigationMenuItem_android_color, 0); a.recycle(); } }