package org.edx.mobile.view.adapters; import android.content.Context; import android.util.SparseIntArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import org.edx.mobile.core.IEdxEnvironment; import org.edx.mobile.logger.Logger; import java.util.ArrayList; import java.util.Date; import java.util.List; public abstract class BaseListAdapter<T> extends ArrayAdapter<T> implements OnItemClickListener { // constants that define selection state of list rows public static final int STATE_NOT_SELECTED = 0; public static final int STATE_SELECTED = 1; private final int layoutResource; private final List<T> objects; private SparseIntArray selection = new SparseIntArray(); public static final long MIN_CLICK_INTERVAL = 1000; //in millis protected final Logger logger = new Logger(getClass().getName()); //simple way to avoid click on same row too quickly. protected LastClickedEvent lastClickedEvent = new LastClickedEvent(); protected IEdxEnvironment environment; public BaseListAdapter(Context context, int layoutResourceId, IEdxEnvironment environment) { this(context, layoutResourceId, environment, new ArrayList<T>()); } private BaseListAdapter(Context context, int layoutResourceId, IEdxEnvironment environment, List<T> list) { super(context, layoutResourceId, list); layoutResource = layoutResourceId; this.environment = environment; this.objects = list; } /** * Selects the list row at specified position. * * @param position */ public void select(int position) { selection.put(position, STATE_SELECTED); } /** * De-selects the list row at specified position. * * @param position */ public void unselect(int position) { selection.put(position, STATE_NOT_SELECTED); } /** * Selects all the items in this adapter. */ public void selectAll() { for (int i = 0; i < getCount(); i++) { select(i); } } /** * De-selects all the items from this adapter. */ public void unselectAll() { selection.clear(); } /** * Returns true if list row at specified position is selected, false otherwise. * * @param position * @return */ public boolean isSelected(int position) { Integer val = selection.get(position); if (val == null) return false; return (val == STATE_SELECTED); } /** * Returns list of selected items. * * @return */ public ArrayList<T> getSelectedItems() { ArrayList<T> selectedItems = new ArrayList<T>(); for (int i = 0; i < getCount(); i++) { if (isSelected(i)) { selectedItems.add(getItem(i)); } } return selectedItems; } /** * Clears existing items from the adapter and sets given list as the data. * If null is provided, this method clears the existing values. * This avoids null value errors. * * @param newItems */ public void setItems(List<T> newItems) { clear(); if (newItems != null) { addAll(newItems); } } public void replace(T item, int position) { objects.set(position, item); notifyDataSetChanged(); } /** * Clears all items from this adapter. */ public void clear() { super.clear(); selection.clear(); lastClickedEvent = new LastClickedEvent(); } @Override public long getItemId(int index) { return index; } @Override public View getView(int position, View convertView, ViewGroup adapter) { if (convertView == null) { // create list row convertView = LayoutInflater.from(getContext()).inflate(layoutResource, adapter, false); // apply a tag to this list row BaseViewHolder tag = getTag(convertView); convertView.setTag(tag); } // get the tag for this list row BaseViewHolder tag = (BaseViewHolder) convertView.getTag(); // put position into the holder object tag.position = position; // get model data for this list row T model = getItem(position); // now render data for this list row render(tag, model); return convertView; } /** * Sub-class should override this method to render model's data to ViewHolder tag. * * @param tag * @param model */ public abstract void render(BaseViewHolder tag, T model); /** * Sub-class should override this method and return ViewHolder tag that * is to be applied to the given convertView. * * @param convertView * @return */ public abstract BaseViewHolder getTag(View convertView); /** * help method to avoid quick click on the same row * * @param position the row it clicks * @return <code>true</code> click can pass through <code>false</code> click on * the same row moment ago */ protected final boolean testClickAcceptable(int position) { final int acceptableElapseTime = 500; if (lastClickedEvent.position != position) { lastClickedEvent.position = position; lastClickedEvent.timestamp = new Date().getTime(); return true; } else { long previousClickTime = lastClickedEvent.timestamp; lastClickedEvent.timestamp = new Date().getTime(); return lastClickedEvent.timestamp - previousClickTime > acceptableElapseTime; } } /** * Base class for ViewHolders in individual adapters. */ public static class BaseViewHolder { public int position; public String videoId; } /** * remember the last click for list view */ private class LastClickedEvent { public int position; public long timestamp; } }