package ca.josephroque.bowlingcompanion.adapter;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import ca.josephroque.bowlingcompanion.R;
import ca.josephroque.bowlingcompanion.utilities.NavigationUtils;
/**
* Created by Joseph Roque on 15-03-28. Manages the data which will be displayed by the RecyclerView in the Navigation
* Drawer. Offers a callback interface {@code NavigationDrawerAdapter.NavigationCallback} to handle interaction events.
*/
public class NavigationDrawerAdapter
extends RecyclerView.Adapter<NavigationDrawerAdapter.NavigationViewHolder>
implements View.OnClickListener {
/** Identifies output from this class in Logcat. */
@SuppressWarnings("unused")
private static final String TAG = "NavigationDrawerAdapter";
/** Represents an item in the navigation drawer which is a simple navigation item. */
private static final int VIEW_TYPE_NAVIGATION = 0;
/** Represents an item in the navigation drawer which features a subheader. */
private static final int VIEW_TYPE_SUBHEADER = 1;
/** Represents an item in the navigation drawer which is a header. */
private static final int VIEW_TYPE_HEADER = 2;
/** Background color of views for selected items. */
private static final int COLOR_SELECTED_BACKGROUND = 0x28000000;
/** Callback listener for user events. */
private NavigationCallback mCallback;
/** The {@link RecyclerView} this adapter is attached to. */
private RecyclerView mRecyclerView;
/** List of options to be displayed in the navigation drawer. */
private final List<String> mListNavigationItems;
/**
* If an item is of the type {@code VIEW_TYPE_NAVIGATION}, it can feature a subtitle, which can be found in this
* array by the item's position.
*/
private final HashMap<String, String> mArraySubtitle;
/** Set of positions which are subheader items. */
private final Set<String> mSetSubheaderItems;
/** String to display as header text in the navigation drawer. */
private String mHeaderTitle;
/** String to display as header subtitle in the navigation drawer. */
private String mHeaderSubtitle;
/** The most recently selected navigation item. */
private int mCurrentNavigationItem;
/**
* Calls super constructor and attempts to cast context to a listener for events.
*
* @param callback instance of callback interface
* @param listItems items which will be displayed in the drawer
*/
public NavigationDrawerAdapter(NavigationCallback callback,
List<String> listItems) {
this.mListNavigationItems = listItems;
mArraySubtitle = new HashMap<>();
mSetSubheaderItems = new TreeSet<>();
this.mCallback = callback;
}
@Override
public NavigationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView;
switch (viewType) {
case VIEW_TYPE_HEADER:
rootView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_navigation_header, parent, false);
break;
case VIEW_TYPE_SUBHEADER:
rootView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_navigation_subheader, parent, false);
break;
case VIEW_TYPE_NAVIGATION:
rootView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_navigation, parent, false);
break;
default:
throw new IllegalStateException("invalid view type: " + viewType);
}
return new NavigationViewHolder(rootView, viewType);
}
@Override
public void onBindViewHolder(NavigationViewHolder viewHolder, int position) {
final int viewType = getItemViewType(position);
switch (viewType) {
case VIEW_TYPE_HEADER:
viewHolder.mTextViewTitle.setText(mHeaderTitle);
viewHolder.mTextViewSubtitle.setText(mHeaderSubtitle);
break;
case VIEW_TYPE_NAVIGATION:
viewHolder.itemView.setOnClickListener(this);
int icon = getItemIcon(position);
if (icon != 0)
viewHolder.mImageViewIcon.setImageResource(icon);
viewHolder.mTextViewTitle.setText(mListNavigationItems.get(position));
String extra = mArraySubtitle.get(mListNavigationItems.get(position));
viewHolder.mTextViewSubtitle.setText(extra);
if (mCurrentNavigationItem == position)
viewHolder.itemView.setBackgroundColor(COLOR_SELECTED_BACKGROUND);
else
setDrawableBackground(viewHolder.itemView,
getUnselectedBackground(viewHolder.itemView.getContext()));
break;
case VIEW_TYPE_SUBHEADER:
setDrawableBackground(viewHolder.itemView,
getUnselectedBackground(viewHolder.itemView.getContext()));
viewHolder.mTextViewTitle.setText(mListNavigationItems.get(position));
break;
default:
throw new IllegalStateException("invalid view type: " + viewType);
}
}
@Override
public int getItemViewType(int position) {
if (position == 0)
return VIEW_TYPE_HEADER;
else if (mSetSubheaderItems.contains(mListNavigationItems.get(position)))
return VIEW_TYPE_SUBHEADER;
else
return VIEW_TYPE_NAVIGATION;
}
@Override
public int getItemCount() {
return mListNavigationItems.size();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
// Releasing references
mCallback = null;
mRecyclerView = null;
}
@Override
public void onClick(View src) {
final int position = mRecyclerView.getChildAdapterPosition(src);
if (mListNavigationItems.get(position).matches("\\w+ \\d+")) {
final int tempPosition = mCurrentNavigationItem;
mCurrentNavigationItem = position;
notifyItemChanged(tempPosition);
notifyItemChanged(mCurrentNavigationItem);
}
mCallback.onNavigationItemClicked(position);
}
/**
* Gets the icon for an item.
*
* @param position position to get icon of
* @return id of icon drawable
*/
private int getItemIcon(int position) {
if (mListNavigationItems.get(position).matches("\\w+ \\d+")) {
if (mCurrentNavigationItem == position)
return R.drawable.ic_radio_button_checked_black_24dp;
else
return R.drawable.ic_radio_button_unchecked_black_24dp;
}
switch (mListNavigationItems.get(position)) {
case NavigationUtils.NAVIGATION_ITEM_BOWLERS:
return R.drawable.ic_people_black_24dp;
case NavigationUtils.NAVIGATION_ITEM_LEAGUES:
return R.drawable.ic_list_black_24dp;
case NavigationUtils.NAVIGATION_ITEM_SERIES:
return R.drawable.ic_event_black_24dp;
case NavigationUtils.NAVIGATION_ITEM_SETTINGS:
return R.drawable.ic_settings_black_24dp;
case NavigationUtils.NAVIGATION_ITEM_FEEDBACK:
return R.drawable.ic_mail_black_24dp;
case NavigationUtils.NAVIGATION_ITEM_HELP:
return R.drawable.ic_help_outline_black_24dp;
default:
return 0;
}
}
/**
* Sets a position to be a subheader. If it is already a navigation item or a switch, it is reassigned.
*
* @param title item to set to subheader
*/
@SuppressWarnings("unused")
public void setPositionToSubheader(String title) {
if (!mListNavigationItems.contains(title))
throw new IllegalArgumentException("Must be valid navigation item");
if (mArraySubtitle.containsKey(title))
mArraySubtitle.remove(title);
mSetSubheaderItems.add(title);
notifyItemChanged(mListNavigationItems.indexOf(title));
}
/**
* Sets the subtitle of a navigation item.
*
* @param title item to get subtitle
* @param text subtitle text
*/
public void setSubtitle(String title, String text) {
if (mSetSubheaderItems.contains(title))
throw new IllegalArgumentException("Can't set subtitle of subheader item: " + title);
mArraySubtitle.put(title, text);
notifyItemChanged(mListNavigationItems.indexOf(title));
}
/**
* Sets the background of a view using the most current method, depending on the API available.
*
* @param view to set background of
* @param background new background drawable
*/
@SuppressWarnings("deprecation") // uses undeprecated methods in newer apis
private void setDrawableBackground(View view, Drawable background) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
view.setBackground(background.mutate());
else
view.setBackgroundDrawable(background.mutate());
}
/**
* Gets the drawable to set for unselected items in the drawer.
*
* @param context to create drawable
* @return selectable item background
*/
private Drawable getUnselectedBackground(Context context) {
int[] attrs = {android.R.attr.selectableItemBackground};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
Drawable unselectedBackground = typedArray.getDrawable(0);
typedArray.recycle();
return unselectedBackground;
}
/**
* Gets the currently selected navigation item.
*
* @return mCurrentNavigationItem
*/
public int getCurrentItem() {
return mCurrentNavigationItem;
}
/**
* Sets the current navigation item.
*
* @param position new item
*/
public void setCurrentItem(int position) {
final int tempPosition = mCurrentNavigationItem;
mCurrentNavigationItem = position;
notifyItemChanged(mCurrentNavigationItem);
notifyItemChanged(tempPosition);
}
/**
* Sets the text for the navigation drawer header title.
*
* @param title new title text
*/
public void setHeaderTitle(String title) {
this.mHeaderTitle = title;
}
/**
* Sets the text for the navigation drawer header subtitle.
*
* @param subtitle new subtitle text
*/
public void setHeaderSubtitle(String subtitle) {
this.mHeaderSubtitle = subtitle;
}
/**
* Callback interface to report user interaction.
*/
public interface NavigationCallback {
/**
* Invoked when a user clicks an item in the navigation drawer.
*
* @param position position of item clicked
*/
void onNavigationItemClicked(int position);
}
/**
* Recycles views for RecyclerView so they do not need to be reinflated.
*/
public static final class NavigationViewHolder
extends RecyclerView.ViewHolder {
/** Displays an icon for an item in the navigation drawer. */
private ImageView mImageViewIcon;
/** Displays a title for an item in the navigation drawer. */
private TextView mTextViewTitle;
/** Displays a subtitle for an item in the navigation drawer. */
private TextView mTextViewSubtitle;
/**
* Gets references to member variables depending on {@code viewType}.
*
* @param rootView root item layout
* @param viewType view type
*/
public NavigationViewHolder(View rootView, int viewType) {
super(rootView);
switch (viewType) {
case VIEW_TYPE_NAVIGATION:
mImageViewIcon = (ImageView) rootView.findViewById(R.id.iv_nav_icon);
case VIEW_TYPE_HEADER:
mTextViewSubtitle = (TextView) rootView.findViewById(R.id.tv_nav_subtitle);
default:
mTextViewTitle = (TextView) rootView.findViewById(R.id.tv_nav_title);
}
}
}
}