package com.jbirdvegas.mgerrit.adapters;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import com.jbirdvegas.mgerrit.R;
import com.jbirdvegas.mgerrit.helpers.Tools;
import org.jetbrains.annotations.Nullable;
/**
* A wrapper for another adapter. This is a merge of the CommonsWare's
* AdapterWrapper and EndlessAdapter libraries of which neither support
* CursorAdapters.
*/
public abstract class EndlessAdapterWrapper extends BaseAdapter
implements AbsListView.OnScrollListener {
/**
* The child adapter that this wraps. Most of the work will be delegated to this adapter
* and we need to observe when its data changes.
*/
private BaseAdapter wrapped;
private Context mContext;
private View mPendingView;
private boolean mLoadingMoreData = false;
/**
* An adapter that wraps this adapter so we can notify it when either the child adapter's
* data or this wraper's data changes.
*/
private BaseAdapter mParentAdapter;
public EndlessAdapterWrapper(Context context, BaseAdapter wrapped) {
this(context, wrapped, R.layout.loading_placeholder);
}
/**
* Constructor wrapping a supplied Adapter and
* providing a id for a pending view.
*/
public EndlessAdapterWrapper(Context context, BaseAdapter wrapped, int pendingResource) {
this.wrapped = wrapped;
this.mContext = context;
// We need to intercept data change notifications from the underlying wrapper here
wrapped.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
finishedDataLoading();
}
public void onInvalidated() {
finishedDataLoading();
}
});
setPendingResource(pendingResource);
}
/**
* Override this method to perform the actual data loading. startDataLoading will be called
* for you automatically, and finishedDataLoading will be called when the child adapter's
* data changes.
*
* This method should not be called directly, instead call startDataLoading, which will
* check if we are already sent a request to load more data and update the listview.
*
* If after trying to load more data, no additional data was found, manually call
* finishedDataLoading.
*/
public abstract void loadData();
/**
* Shows the pending view signalling more data is being loaded. Calling this when data
* is already being loaded will have no effect.
* When manually loading data, be sure to call this.
*/
public void startDataLoading() {
// Don't load data if there is no connection
if (!Tools.isConnected(mContext)) return;
// No effect if we have already started loading or this is disabled
if (mLoadingMoreData) return;
mLoadingMoreData = true;
/* We need to notify the listview's adapter that the data has changed (i.e.
* we have added the pending row. */
if (mParentAdapter != null) mParentAdapter.notifyDataSetChanged();
loadData();
}
/**
* Hides the pending view signalling no data is being loaded. Call this if a load finished
* but there was no additional data to display.
*/
public void finishedDataLoading() {
mLoadingMoreData = false;
/* We need to notify the listview's adapter that the data has changed (i.e.
* we have removed the pending row. */
if (mParentAdapter != null) mParentAdapter.notifyDataSetChanged();
}
public void setParentAdatper(BaseAdapter parent) {
mParentAdapter = parent;
}
/**
* Sets the view to display when more data is being loaded.
* @param pendingResource Layout to be used for the pending row
*/
public void setPendingResource(int pendingResource) {
LayoutInflater inflater = LayoutInflater.from(mContext);
mPendingView = inflater.inflate(pendingResource, null);
}
/** Sets whether this adapter is enabled or not */
public void setEnabled(boolean enabled) {
if (!enabled) mLoadingMoreData = false;
}
@Override
public boolean areAllItemsEnabled() {
return false; // The pending row is never enabled
}
@Override
public boolean isEnabled(int position) {
return position < wrapped.getCount() && wrapped.isEnabled(position);
}
@Override
public int getCount() {
if (mLoadingMoreData) {
// Add an extra item for the pending row
return wrapped.getCount() + 1;
} else return wrapped.getCount();
}
@Override
public Object getItem(int position) {
if (position >= wrapped.getCount()) {
return null;
}
return wrapped.getItem(position);
}
@Override
public long getItemId(int position) {
if (position >= wrapped.getCount()) {
return -1;
}
return wrapped.getItemId(position);
}
@Nullable
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position >= wrapped.getCount()) {
return mPendingView;
} else {
return wrapped.getView(position, convertView, parent);
}
}
@Override
public int getItemViewType(int position) {
if (position >= wrapped.getCount()) return IGNORE_ITEM_VIEW_TYPE;
return wrapped.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
// Add another view type for the placeholder
return wrapped.getViewTypeCount() + 1;
}
@Override
public boolean isEmpty() {
return !mLoadingMoreData && wrapped.isEmpty();
}
/**
* Returns the ListAdapter that is wrapped by the endless
* logic.
*/
public BaseAdapter getWrappedAdapter() {
return wrapped;
}
@Override
public void onScrollStateChanged(AbsListView listView, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
if (listView.getLastVisiblePosition() == wrapped.getCount() - 1) {
startDataLoading();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Not used.
}
}