package it.demo.twitterlike.android.ui.adapters;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
public abstract class ExtendibleArrayAdapter<T> extends BaseAdapter implements
Filterable {
/**
* Contains the list of objects that represent the data of this
* ArrayAdapter. The content of this list is referred to as "the array" in
* the documentation.
*/
private List<T> mObjects;
/**
* Lock used to modify the content of {@link #mObjects}. Any write operation
* performed on the array should be synchronized on this lock. This lock is
* also used by the filter (see {@link #getFilter()} to make a synchronized
* copy of the original array of data.
*/
private final Object mLock = new Object();
/**
* Indicates whether or not {@link #notifyDataSetChanged()} must be called
* whenever {@link #mObjects} is modified.
*/
private boolean mNotifyOnChange = true;
private T selectedElement;
public T getSelectedElement() {
return selectedElement;
}
public void setSelectedElement(T selectedElement) {
this.selectedElement = selectedElement;
}
// A copy of the original mObjects array, initialized from and then used
// instead as soon as
// the mFilter ArrayFilter is used. mObjects will then only contain the
// filtered values.
private ArrayList<T> mOriginalValues;
private ItemContentFilter mFilter;
protected final Context mContext;
private final LayoutInflater mInflater;
public ExtendibleArrayAdapter(Context context) {
this(context, new ArrayList<T>());
}
public ExtendibleArrayAdapter(Context context, List<T> elements) {
this.mObjects = elements;
this.mContext = context;
this.mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
protected LayoutInflater getLayoutInflater() {
return this.mInflater;
}
protected Context getContext() {
return this.mContext;
}
/**
* Adds the specified object at the end of the array.
*
* @param object
* The object to add at the end of the array.
*/
public void add(T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(object);
} else {
mObjects.add(object);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
/**
* Adds the specified Collection at the end of the array.
*
* @param collection
* The Collection to add at the end of the array.
*/
public void addAll(Collection<? extends T> collection) {
if (collection != null) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.addAll(collection);
} else {
mObjects.addAll(collection);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
}
/**
* Adds the specified items at the end of the array.
*
* @param items
* The items to add at the end of the array.
*/
public void addAll(T... items) {
synchronized (mLock) {
if (mOriginalValues != null) {
Collections.addAll(mOriginalValues, items);
} else {
Collections.addAll(mObjects, items);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
/**
* Inserts the specified object at the specified index in the array.
*
* @param object
* The object to insert into the array.
* @param index
* The index at which the object must be inserted.
*/
public void insert(T object, int index) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(index, object);
} else {
mObjects.add(index, object);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
public void remove(int position) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.remove(position);
} else {
mObjects.remove(position);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
/**
* Removes the specified object from the array.
*
* @param object
* The object to remove.
*/
public void remove(T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.remove(object);
} else {
mObjects.remove(object);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
/**
* Remove all elements from the list.
*/
public void clear() {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.clear();
} else {
mObjects.clear();
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
public List<T> getAll() {
return mObjects;
}
/**
* Sorts the content of this adapter using the specified comparator.
*
* @param comparator
* The comparator used to sort the objects contained in this
* adapter.
*/
public void sort(Comparator<? super T> comparator) {
synchronized (mLock) {
if (mOriginalValues != null) {
Collections.sort(mOriginalValues, comparator);
} else {
Collections.sort(mObjects, comparator);
}
}
if (mNotifyOnChange)
notifyDataSetChanged();
}
/**
* {@inheritDoc}
*/
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
mNotifyOnChange = true;
}
/**
* Control whether methods that change the list ({@link #add},
* {@link #insert}, {@link #remove}, {@link #clear}) automatically call
* {@link #notifyDataSetChanged}. If set to false, caller must manually call
* notifyDataSetChanged() to have the changes reflected in the attached
* view.
*
* The default is true, and calling notifyDataSetChanged() resets the flag
* to true.
*
* @param notifyOnChange
* if true, modifications to the list will automatically call
* {@link #notifyDataSetChanged}
*/
public void setNotifyOnChange(boolean notifyOnChange) {
mNotifyOnChange = notifyOnChange;
}
protected ViewGroup createView(ViewGroup parent, int resourceId) {
return (ViewGroup) LayoutInflater.from(mContext).inflate(resourceId,
parent, false);
}
/**
* {@inheritDoc}
*/
public int getCount() {
return mObjects == null ? 0 : mObjects.size();
}
/**
* {@inheritDoc}
*/
public T getItem(int position) {
return mObjects.get(position);
}
/**
* Returns the position of the specified item in the array.
*
* @param item
* The item to retrieve the position of.
*
* @return The position of the specified item.
*/
public int getPosition(T item) {
return mObjects.indexOf(item);
}
/**
* {@inheritDoc}
*/
public long getItemId(int position) {
return position;
}
@Override
public Filter getFilter() {
if (mFilter == null)
mFilter = new ItemContentFilter();
return mFilter;
}
protected boolean matchSingle(T value, String prefixString) {
return value != null && prefixString != null
&& value.toString().contains(prefixString);
}
private class ItemContentFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final boolean append = matchSingle(values.get(i),
prefixString);
if (append) {
newValues.add(value);
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
// noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}