/** * galaxy inc. * meetup client for android */ package com.galaxy.meetup.client.android.common; import android.content.Context; import android.database.Cursor; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; /** * * @author sihai * */ public abstract class EsCompositeCursorAdapter extends BaseAdapter { private static final int INITIAL_CAPACITY = 2; protected final Context mContext; private Partition[] mPartitions; private int mSize = 0; private int mCount = 0; private boolean mCacheValid = true; private boolean mNotificationsEnabled = true; private boolean mNotificationNeeded; public EsCompositeCursorAdapter(Context context) { this(context, INITIAL_CAPACITY); } public EsCompositeCursorAdapter(Context context, int initialCapacity) { mContext = context; mPartitions = new Partition[INITIAL_CAPACITY]; } public Context getContext() { return mContext; } public void addPartition(boolean showIfEmpty, boolean hasHeader) { addPartition(new Partition(showIfEmpty, hasHeader)); } public void addPartition(Partition partition) { if (mSize >= mPartitions.length) { int newCapacity = mSize + 2; Partition[] newAdapters = new Partition[newCapacity]; System.arraycopy(mPartitions, 0, newAdapters, 0, mSize); mPartitions = newAdapters; } mPartitions[mSize++] = partition; invalidate(); notifyDataSetChanged(); } public void removePartition(int partitionIndex) { Cursor cursor = mPartitions[partitionIndex].cursor; if (cursor != null && !cursor.isClosed()) { cursor.close(); } System.arraycopy(mPartitions, partitionIndex + 1, mPartitions, partitionIndex, mSize - partitionIndex - 1); mSize--; invalidate(); notifyDataSetChanged(); } public void clearPartitions() { for (int i = 0; i < mSize; i++) { mPartitions[i].cursor = null; } invalidate(); notifyDataSetChanged(); } public void close() { for (int i = 0; i < mSize; i++) { Cursor cursor = mPartitions[i].cursor; if (cursor != null && !cursor.isClosed()) { cursor.close(); mPartitions[i].cursor = null; } } mSize = 0; invalidate(); notifyDataSetChanged(); } public void setHasHeader(int partitionIndex, boolean flag) { mPartitions[partitionIndex].hasHeader = flag; invalidate(); } public void setShowIfEmpty(int partitionIndex, boolean flag) { mPartitions[partitionIndex].showIfEmpty = flag; invalidate(); } public Partition getPartition(int partitionIndex) { if (partitionIndex >= mSize) { throw new ArrayIndexOutOfBoundsException(partitionIndex); } return mPartitions[partitionIndex]; } protected void invalidate() { mCacheValid = false; } public int getPartitionCount() { return mSize; } protected void ensureCacheValid() { if (mCacheValid) { return; } mCount = 0; for (int i = 0; i < mSize; i++) { Cursor cursor = mPartitions[i].cursor; int count = cursor != null ? cursor.getCount() : 0; if (mPartitions[i].hasHeader) { if (count != 0 || mPartitions[i].showIfEmpty) { count++; } } mPartitions[i].count = count; mCount += count; } mCacheValid = true; } public boolean hasHeader(int partition) { return mPartitions[partition].hasHeader; } public int getCount() { ensureCacheValid(); return mCount; } public final int getCount(int i) { ensureCacheValid(); int j; if(mPartitions[i].cursor == null) j = 0; else j = mPartitions[i].cursor.getCount(); return j; } public Cursor getCursor(int partition) { return mPartitions[partition].cursor; } public void changeCursor(int partition, Cursor cursor) { Cursor prevCursor = mPartitions[partition].cursor; if (prevCursor != cursor) { if (prevCursor != null && !prevCursor.isClosed()) { prevCursor.close(); } mPartitions[partition].cursor = cursor; if (cursor != null) { mPartitions[partition].idColumnIndex = cursor.getColumnIndex("_id"); } invalidate(); notifyDataSetChanged(); } } public boolean isPartitionEmpty(int partition) { Cursor cursor = mPartitions[partition].cursor; return cursor == null || cursor.getCount() == 0; } public int getPartitionForPosition(int position) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { return i; } start = end; } return -1; } public int getOffsetInPartition(int position) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { int offset = position - start; if (mPartitions[i].hasHeader) { offset--; } return offset; } start = end; } return -1; } public int getPositionForPartition(int partition) { ensureCacheValid(); int position = 0; for (int i = 0; i < partition; i++) { position += mPartitions[i].count; } return position; } @Override public int getViewTypeCount() { return getItemViewTypeCount() + 1; } public int getItemViewTypeCount() { return 1; } protected int getItemViewType(int partition, int position) { return 1; } @Override public int getItemViewType(int position) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { int offset = position - start; if (mPartitions[i].hasHeader && offset == 0) { return IGNORE_ITEM_VIEW_TYPE; } return getItemViewType(i, position); } start = end; } throw new ArrayIndexOutOfBoundsException(position); } public View getView(int position, View convertView, ViewGroup parent) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { int offset = position - start; if (mPartitions[i].hasHeader) { offset--; } View view; if (offset == -1) { view = getHeaderView(i, mPartitions[i].cursor, convertView, parent); } else { if (!mPartitions[i].cursor.moveToPosition(offset)) { throw new IllegalStateException("Couldn't move cursor to position " + offset); } view = getView(i, mPartitions[i].cursor, offset, convertView, parent); } if (view == null) { throw new NullPointerException("View should not be null, partition: " + i + " position: " + offset); } return view; } start = end; } throw new ArrayIndexOutOfBoundsException(position); } protected View getHeaderView(int partition, Cursor cursor, View convertView, ViewGroup parent) { View view = convertView != null ? convertView : newHeaderView(mContext, partition, cursor, parent); bindHeaderView(view, partition, cursor); return view; } protected View newHeaderView(Context context, int partition, Cursor cursor, ViewGroup parent) { return null; } protected void bindHeaderView(View view, int partition, Cursor cursor) { } protected View getView(int partition, Cursor cursor, int position, View convertView, ViewGroup parent) { View view; if (convertView != null) { view = convertView; } else { view = newView(mContext, partition, cursor, position, parent); } bindView(view, partition, cursor, position); return view; } protected abstract View newView(Context context, int partition, Cursor cursor, int position, ViewGroup parent); protected abstract void bindView(View v, int partition, Cursor cursor, int position); public Object getItem(int position) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { int offset = position - start; if (mPartitions[i].hasHeader) { offset--; } if (offset == -1) { return null; } Cursor cursor = mPartitions[i].cursor; cursor.moveToPosition(offset); return cursor; } start = end; } return null; } public long getItemId(int position) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { int offset = position - start; if (mPartitions[i].hasHeader) { offset--; } if (offset == -1) { return 0; } if (mPartitions[i].idColumnIndex == -1) { return 0; } Cursor cursor = mPartitions[i].cursor; if (cursor == null || cursor.isClosed() || !cursor.moveToPosition(offset)) { return 0; } return cursor.getLong(mPartitions[i].idColumnIndex); } start = end; } return 0; } @Override public boolean areAllItemsEnabled() { for (int i = 0; i < mSize; i++) { if (mPartitions[i].hasHeader) { return false; } } return true; } @Override public boolean isEnabled(int position) { ensureCacheValid(); int start = 0; for (int i = 0; i < mSize; i++) { int end = start + mPartitions[i].count; if (position >= start && position < end) { int offset = position - start; if (mPartitions[i].hasHeader && offset == 0) { return false; } else { return isEnabled(i, offset); } } start = end; } return false; } protected boolean isEnabled(int partition, int position) { return true; } public void setNotificationsEnabled(boolean flag) { mNotificationsEnabled = flag; if (flag && mNotificationNeeded) { notifyDataSetChanged(); } } @Override public void notifyDataSetChanged() { if (mNotificationsEnabled) { mNotificationNeeded = false; super.notifyDataSetChanged(); } else { mNotificationNeeded = true; } } public final void checkPartitions(String s, String s1) { int i = -1 + mSize; while(i >= 0) { Partition partition = mPartitions[i]; Cursor cursor; if(partition != null) cursor = partition.cursor; else cursor = null; if(cursor == null) Log.i(s, (new StringBuilder("partcheck s:")).append(s1).append(" emptypart:").append(i).toString()); else if(cursor.isClosed()) Log.i(s, (new StringBuilder("partcheck s:")).append(s1).append(" stalepart:").append(i).toString()); i--; } } //============================================================================================================================= // //============================================================================================================================= public static class Partition { boolean showIfEmpty; boolean hasHeader; Cursor cursor; int idColumnIndex; int count; public Partition(boolean showIfEmpty, boolean hasHeader) { this.showIfEmpty = showIfEmpty; this.hasHeader = hasHeader; } public boolean getShowIfEmpty() { return showIfEmpty; } public boolean getHasHeader() { return hasHeader; } } }