Moving GroupingListAdapter to frameworks/ex

Change-Id: Ic82f1603b82a63a3ce383b509dad8f043af7bfe4
diff --git a/src/com/android/contacts/GroupingListAdapter.java b/src/com/android/contacts/GroupingListAdapter.java
deleted file mode 100644
index 5937a6d..0000000
--- a/src/com/android/contacts/GroupingListAdapter.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-
-import com.android.internal.util.ArrayUtils;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-/**
- * Maintains a list that groups adjacent items sharing the same value of
- * a "group-by" field.  The list has three types of elements: stand-alone, group header and group
- * child. Groups are collapsible and collapsed by default.
- */
-public abstract class GroupingListAdapter extends BaseAdapter {
-
-    private static final int GROUP_METADATA_ARRAY_INITIAL_SIZE = 16;
-    private static final int GROUP_METADATA_ARRAY_INCREMENT = 128;
-    private static final long GROUP_OFFSET_MASK    = 0x00000000FFFFFFFFL;
-    private static final long GROUP_SIZE_MASK     = 0x7FFFFFFF00000000L;
-    private static final long EXPANDED_GROUP_MASK = 0x8000000000000000L;
-
-    public static final int ITEM_TYPE_STANDALONE = 0;
-    public static final int ITEM_TYPE_GROUP_HEADER = 1;
-    public static final int ITEM_TYPE_IN_GROUP = 2;
-
-    /**
-     * Information about a specific list item: is it a group, if so is it expanded.
-     * Otherwise, is it a stand-alone item or a group member.
-     */
-    protected static class PositionMetadata {
-        int itemType;
-        boolean isExpanded;
-        int cursorPosition;
-        int childCount;
-        private int groupPosition;
-        private int listPosition = -1;
-    }
-
-    private Context mContext;
-    private Cursor mCursor;
-
-    /**
-     * Count of list items.
-     */
-    private int mCount;
-
-    private int mRowIdColumnIndex;
-
-    /**
-     * Count of groups in the list.
-     */
-    private int mGroupCount;
-
-    /**
-     * Information about where these groups are located in the list, how large they are
-     * and whether they are expanded.
-     */
-    private long[] mGroupMetadata;
-
-    private SparseIntArray mPositionCache = new SparseIntArray();
-    private int mLastCachedListPosition;
-    private int mLastCachedCursorPosition;
-    private int mLastCachedGroup;
-
-    /**
-     * A reusable temporary instance of PositionMetadata
-     */
-    private PositionMetadata mPositionMetadata = new PositionMetadata();
-
-    protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) {
-
-        @Override
-        public boolean deliverSelfNotifications() {
-            return true;
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onContentChanged();
-        }
-    };
-
-    protected DataSetObserver mDataSetObserver = new DataSetObserver() {
-
-        @Override
-        public void onChanged() {
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public void onInvalidated() {
-            notifyDataSetInvalidated();
-        }
-    };
-
-    public GroupingListAdapter(Context context) {
-        mContext = context;
-        resetCache();
-    }
-
-    /**
-     * Finds all groups of adjacent items in the cursor and calls {@link #addGroup} for
-     * each of them.
-     */
-    protected abstract void addGroups(Cursor cursor);
-
-    protected abstract View newStandAloneView(Context context, ViewGroup parent);
-    protected abstract void bindStandAloneView(View view, Context context, Cursor cursor);
-
-    protected abstract View newGroupView(Context context, ViewGroup parent);
-    protected abstract void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
-            boolean expanded);
-
-    protected abstract View newChildView(Context context, ViewGroup parent);
-    protected abstract void bindChildView(View view, Context context, Cursor cursor);
-
-    /**
-     * Cache should be reset whenever the cursor changes or groups are expanded or collapsed.
-     */
-    private void resetCache() {
-        mCount = -1;
-        mLastCachedListPosition = -1;
-        mLastCachedCursorPosition = -1;
-        mLastCachedGroup = -1;
-        mPositionMetadata.listPosition = -1;
-        mPositionCache.clear();
-    }
-
-    protected void onContentChanged() {
-    }
-
-    public void changeCursor(Cursor cursor) {
-        if (cursor == mCursor) {
-            return;
-        }
-
-        if (mCursor != null) {
-            mCursor.unregisterContentObserver(mChangeObserver);
-            mCursor.unregisterDataSetObserver(mDataSetObserver);
-            mCursor.close();
-        }
-        mCursor = cursor;
-        resetCache();
-        findGroups();
-
-        if (cursor != null) {
-            cursor.registerContentObserver(mChangeObserver);
-            cursor.registerDataSetObserver(mDataSetObserver);
-            mRowIdColumnIndex = cursor.getColumnIndexOrThrow("_id");
-            notifyDataSetChanged();
-        } else {
-            // notify the observers about the lack of a data set
-            notifyDataSetInvalidated();
-        }
-
-    }
-
-    public Cursor getCursor() {
-        return mCursor;
-    }
-
-    /**
-     * Scans over the entire cursor looking for duplicate phone numbers that need
-     * to be collapsed.
-     */
-    private void findGroups() {
-        mGroupCount = 0;
-        mGroupMetadata = new long[GROUP_METADATA_ARRAY_INITIAL_SIZE];
-
-        if (mCursor == null) {
-            return;
-        }
-
-        addGroups(mCursor);
-    }
-
-    /**
-     * Records information about grouping in the list.  Should be called by the overridden
-     * {@link #addGroups} method.
-     */
-    protected void addGroup(int cursorPosition, int size, boolean expanded) {
-        if (mGroupCount >= mGroupMetadata.length) {
-            int newSize = ArrayUtils.idealLongArraySize(
-                    mGroupMetadata.length + GROUP_METADATA_ARRAY_INCREMENT);
-            long[] array = new long[newSize];
-            System.arraycopy(mGroupMetadata, 0, array, 0, mGroupCount);
-            mGroupMetadata = array;
-        }
-
-        long metadata = ((long)size << 32) | cursorPosition;
-        if (expanded) {
-            metadata |= EXPANDED_GROUP_MASK;
-        }
-        mGroupMetadata[mGroupCount++] = metadata;
-    }
-
-    public int getCount() {
-        if (mCursor == null) {
-            return 0;
-        }
-
-        if (mCount != -1) {
-            return mCount;
-        }
-
-        int cursorPosition = 0;
-        int count = 0;
-        for (int i = 0; i < mGroupCount; i++) {
-            long metadata = mGroupMetadata[i];
-            int offset = (int)(metadata & GROUP_OFFSET_MASK);
-            boolean expanded = (metadata & EXPANDED_GROUP_MASK) != 0;
-            int size = (int)((metadata & GROUP_SIZE_MASK) >> 32);
-
-            count += (offset - cursorPosition);
-
-            if (expanded) {
-                count += size + 1;
-            } else {
-                count++;
-            }
-
-            cursorPosition = offset + size;
-        }
-
-        mCount = count + mCursor.getCount() - cursorPosition;
-        return mCount;
-    }
-
-    /**
-     * Figures out whether the item at the specified position represents a
-     * stand-alone element, a group or a group child. Also computes the
-     * corresponding cursor position.
-     */
-    public void obtainPositionMetadata(PositionMetadata metadata, int position) {
-
-        // If the description object already contains requested information, just return
-        if (metadata.listPosition == position) {
-            return;
-        }
-
-        int listPosition = 0;
-        int cursorPosition = 0;
-        int firstGroupToCheck = 0;
-
-        // Check cache for the supplied position.  What we are looking for is
-        // the group descriptor immediately preceding the supplied position.
-        // Once we have that, we will be able to tell whether the position
-        // is the header of the group, a member of the group or a standalone item.
-        if (mLastCachedListPosition != -1) {
-            if (position <= mLastCachedListPosition) {
-
-                // Have SparceIntArray do a binary search for us.
-                int index = mPositionCache.indexOfKey(position);
-
-                // If we get back a positive number, the position corresponds to
-                // a group header.
-                if (index < 0) {
-
-                    // We had a cache miss, but we did obtain valuable information anyway.
-                    // The negative number will allow us to compute the location of
-                    // the group header immediately preceding the supplied position.
-                    index = ~index - 1;
-
-                    if (index >= mPositionCache.size()) {
-                        index--;
-                    }
-                }
-
-                // A non-negative index gives us the position of the group header
-                // corresponding or preceding the position, so we can
-                // search for the group information at the supplied position
-                // starting with the cached group we just found
-                if (index >= 0) {
-                    listPosition = mPositionCache.keyAt(index);
-                    firstGroupToCheck = mPositionCache.valueAt(index);
-                    long descriptor = mGroupMetadata[firstGroupToCheck];
-                    cursorPosition = (int)(descriptor & GROUP_OFFSET_MASK);
-                }
-            } else {
-
-                // If we haven't examined groups beyond the supplied position,
-                // we will start where we left off previously
-                firstGroupToCheck = mLastCachedGroup;
-                listPosition = mLastCachedListPosition;
-                cursorPosition = mLastCachedCursorPosition;
-            }
-        }
-
-        for (int i = firstGroupToCheck; i < mGroupCount; i++) {
-            long group = mGroupMetadata[i];
-            int offset = (int)(group & GROUP_OFFSET_MASK);
-
-            // Move pointers to the beginning of the group
-            listPosition += (offset - cursorPosition);
-            cursorPosition = offset;
-
-            if (i > mLastCachedGroup) {
-                mPositionCache.append(listPosition, i);
-                mLastCachedListPosition = listPosition;
-                mLastCachedCursorPosition = cursorPosition;
-                mLastCachedGroup = i;
-            }
-
-            // Now we have several possibilities:
-            // A) The requested position precedes the group
-            if (position < listPosition) {
-                metadata.itemType = ITEM_TYPE_STANDALONE;
-                metadata.cursorPosition = cursorPosition - (listPosition - position);
-                return;
-            }
-
-            boolean expanded = (group & EXPANDED_GROUP_MASK) != 0;
-            int size = (int) ((group & GROUP_SIZE_MASK) >> 32);
-
-            // B) The requested position is a group header
-            if (position == listPosition) {
-                metadata.itemType = ITEM_TYPE_GROUP_HEADER;
-                metadata.groupPosition = i;
-                metadata.isExpanded = expanded;
-                metadata.childCount = size;
-                metadata.cursorPosition = offset;
-                return;
-            }
-
-            if (expanded) {
-                // C) The requested position is an element in the expanded group
-                if (position < listPosition + size + 1) {
-                    metadata.itemType = ITEM_TYPE_IN_GROUP;
-                    metadata.cursorPosition = cursorPosition + (position - listPosition) - 1;
-                    return;
-                }
-
-                // D) The element is past the expanded group
-                listPosition += size + 1;
-            } else {
-
-                // E) The element is past the collapsed group
-                listPosition++;
-            }
-
-            // Move cursor past the group
-            cursorPosition += size;
-        }
-
-        // The required item is past the last group
-        metadata.itemType = ITEM_TYPE_STANDALONE;
-        metadata.cursorPosition = cursorPosition + (position - listPosition);
-    }
-
-    /**
-     * Returns true if the specified position in the list corresponds to a
-     * group header.
-     */
-    public boolean isGroupHeader(int position) {
-        obtainPositionMetadata(mPositionMetadata, position);
-        return mPositionMetadata.itemType == ITEM_TYPE_GROUP_HEADER;
-    }
-
-    /**
-     * Given a position of a groups header in the list, returns the size of
-     * the corresponding group.
-     */
-    public int getGroupSize(int position) {
-        obtainPositionMetadata(mPositionMetadata, position);
-        return mPositionMetadata.childCount;
-    }
-
-    /**
-     * Mark group as expanded if it is collapsed and vice versa.
-     */
-    public void toggleGroup(int position) {
-        obtainPositionMetadata(mPositionMetadata, position);
-        if (mPositionMetadata.itemType != ITEM_TYPE_GROUP_HEADER) {
-            throw new IllegalArgumentException("Not a group at position " + position);
-        }
-
-
-        if (mPositionMetadata.isExpanded) {
-            mGroupMetadata[mPositionMetadata.groupPosition] &= ~EXPANDED_GROUP_MASK;
-        } else {
-            mGroupMetadata[mPositionMetadata.groupPosition] |= EXPANDED_GROUP_MASK;
-        }
-        resetCache();
-        notifyDataSetChanged();
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return 3;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        obtainPositionMetadata(mPositionMetadata, position);
-        return mPositionMetadata.itemType;
-    }
-
-    public Object getItem(int position) {
-        if (mCursor == null) {
-            return null;
-        }
-
-        obtainPositionMetadata(mPositionMetadata, position);
-        if (mCursor.moveToPosition(mPositionMetadata.cursorPosition)) {
-            return mCursor;
-        } else {
-            return null;
-        }
-    }
-
-    public long getItemId(int position) {
-        Object item = getItem(position);
-        if (item != null) {
-            return mCursor.getLong(mRowIdColumnIndex);
-        } else {
-            return -1;
-        }
-    }
-
-    public View getView(int position, View convertView, ViewGroup parent) {
-        obtainPositionMetadata(mPositionMetadata, position);
-        View view = convertView;
-        if (view == null) {
-            switch (mPositionMetadata.itemType) {
-                case ITEM_TYPE_STANDALONE:
-                    view = newStandAloneView(mContext, parent);
-                    break;
-                case ITEM_TYPE_GROUP_HEADER:
-                    view = newGroupView(mContext, parent);
-                    break;
-                case ITEM_TYPE_IN_GROUP:
-                    view = newChildView(mContext, parent);
-                    break;
-            }
-        }
-
-        mCursor.moveToPosition(mPositionMetadata.cursorPosition);
-        switch (mPositionMetadata.itemType) {
-            case ITEM_TYPE_STANDALONE:
-                bindStandAloneView(view, mContext, mCursor);
-                break;
-            case ITEM_TYPE_GROUP_HEADER:
-                bindGroupView(view, mContext, mCursor, mPositionMetadata.childCount,
-                        mPositionMetadata.isExpanded);
-                break;
-            case ITEM_TYPE_IN_GROUP:
-                bindChildView(view, mContext, mCursor);
-                break;
-
-        }
-        return view;
-    }
-}
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/RecentCallsListActivity.java
index d29be6c..9275e52 100644
--- a/src/com/android/contacts/RecentCallsListActivity.java
+++ b/src/com/android/contacts/RecentCallsListActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts;
 
+import com.android.common.widget.GroupingListAdapter;
 import com.android.internal.telephony.CallerInfo;
 import com.android.internal.telephony.ITelephony;
 
diff --git a/tests/src/com/android/contacts/GroupingListAdapterTests.java b/tests/src/com/android/contacts/GroupingListAdapterTests.java
deleted file mode 100644
index 1877fac..0000000
--- a/tests/src/com/android/contacts/GroupingListAdapterTests.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-
-import android.content.Context;
-import android.database.CharArrayBuffer;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.provider.CallLog.Calls;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewGroup;
-
-import static com.android.contacts.GroupingListAdapter.ITEM_TYPE_STANDALONE;
-import static com.android.contacts.GroupingListAdapter.ITEM_TYPE_IN_GROUP;
-import static com.android.contacts.GroupingListAdapter.ITEM_TYPE_GROUP_HEADER;
-
-/**
- * Tests for the contact call list adapter.
- *
- * Running all tests:
- *
- *   runtest contacts
- * or
- *   adb shell am instrument \
- *     -w com.android.contacts.tests/android.test.InstrumentationTestRunner
- */
-public class GroupingListAdapterTests extends AndroidTestCase {
-
-    static private final String[] CALL_LOG_PROJECTION = new String[] {
-        Calls._ID,
-        Calls.NUMBER,
-        Calls.DATE,
-    };
-
-    private static final int CALLS_NUMBER_COLUMN_INDEX = 1;
-
-    private MatrixCursor mCursor;
-    private long mNextCall;
-
-    private GroupingListAdapter mAdapter = new GroupingListAdapter(null) {
-
-        @Override
-        protected void addGroups(Cursor cursor) {
-            int count = cursor.getCount();
-            int groupItemCount = 1;
-            cursor.moveToFirst();
-            String currentValue = cursor.getString(CALLS_NUMBER_COLUMN_INDEX);
-            for (int i = 1; i < count; i++) {
-                cursor.moveToNext();
-                String value = cursor.getString(CALLS_NUMBER_COLUMN_INDEX);
-                if (TextUtils.equals(value, currentValue)) {
-                    groupItemCount++;
-                } else {
-                    if (groupItemCount > 1) {
-                        addGroup(i - groupItemCount, groupItemCount, false);
-                    }
-
-                    groupItemCount = 1;
-                    currentValue = value;
-                }
-            }
-            if (groupItemCount > 1) {
-                addGroup(count - groupItemCount, groupItemCount, false);
-            }
-        }
-
-        @Override
-        protected void bindChildView(View view, Context context, Cursor cursor) {
-        }
-
-        @Override
-        protected void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
-                boolean expanded) {
-        }
-
-        @Override
-        protected void bindStandAloneView(View view, Context context, Cursor cursor) {
-        }
-
-        @Override
-        protected View newChildView(Context context, ViewGroup parent) {
-            return null;
-        }
-
-        @Override
-        protected View newGroupView(Context context, ViewGroup parent) {
-            return null;
-        }
-
-        @Override
-        protected View newStandAloneView(Context context, ViewGroup parent) {
-            return null;
-        }
-    };
-
-    private void buildCursor(String... numbers) {
-        mCursor = new MatrixCursor(CALL_LOG_PROJECTION);
-        mNextCall = 1;
-        for (String number : numbers) {
-            mCursor.addRow(new Object[]{mNextCall, number, 1000 - mNextCall});
-            mNextCall++;
-        }
-    }
-
-    public void testGroupingWithoutGroups() {
-        buildCursor("1", "2", "3");
-        mAdapter.changeCursor(mCursor);
-
-        assertEquals(3, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 2);
-    }
-
-    public void testGroupingWithCollapsedGroupAtTheBeginning() {
-        buildCursor("1", "1", "2");
-        mAdapter.changeCursor(mCursor);
-
-        assertEquals(2, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 2);
-    }
-
-    public void testGroupingWithExpandedGroupAtTheBeginning() {
-        buildCursor("1", "1", "2");
-        mAdapter.changeCursor(mCursor);
-        mAdapter.toggleGroup(0);
-
-        assertEquals(4, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, true, 0);
-        assertPositionMetadata(1, ITEM_TYPE_IN_GROUP, false, 0);
-        assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
-        assertPositionMetadata(3, ITEM_TYPE_STANDALONE, false, 2);
-    }
-
-    public void testGroupingWithExpandCollapseCycleAtTheBeginning() {
-        buildCursor("1", "1", "2");
-        mAdapter.changeCursor(mCursor);
-        mAdapter.toggleGroup(0);
-        mAdapter.toggleGroup(0);
-
-        assertEquals(2, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 2);
-    }
-
-    public void testGroupingWithCollapsedGroupInTheMiddle() {
-        buildCursor("1", "2", "2", "2", "3");
-        mAdapter.changeCursor(mCursor);
-
-        assertEquals(3, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 4);
-    }
-
-    public void testGroupingWithExpandedGroupInTheMiddle() {
-        buildCursor("1", "2", "2", "2", "3");
-        mAdapter.changeCursor(mCursor);
-        mAdapter.toggleGroup(1);
-
-        assertEquals(6, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
-        assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
-        assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
-        assertPositionMetadata(4, ITEM_TYPE_IN_GROUP, false, 3);
-        assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 4);
-    }
-
-    public void testGroupingWithCollapsedGroupAtTheEnd() {
-        buildCursor("1", "2", "3", "3", "3");
-        mAdapter.changeCursor(mCursor);
-
-        assertEquals(3, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_GROUP_HEADER, false, 2);
-    }
-
-    public void testGroupingWithExpandedGroupAtTheEnd() {
-        buildCursor("1", "2", "3", "3", "3");
-        mAdapter.changeCursor(mCursor);
-        mAdapter.toggleGroup(2);
-
-        assertEquals(6, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_GROUP_HEADER, true, 2);
-        assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
-        assertPositionMetadata(4, ITEM_TYPE_IN_GROUP, false, 3);
-        assertPositionMetadata(5, ITEM_TYPE_IN_GROUP, false, 4);
-    }
-
-    public void testGroupingWithMultipleCollapsedGroups() {
-        buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
-        mAdapter.changeCursor(mCursor);
-
-        assertEquals(6, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3);
-        assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4);
-        assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6);
-        assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8);
-    }
-
-    public void testGroupingWithMultipleExpandedGroups() {
-        buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
-        mAdapter.changeCursor(mCursor);
-        mAdapter.toggleGroup(1);
-
-        // Note that expanding the group of 2's shifted the group of 5's down from the
-        // 4th to the 6th position
-        mAdapter.toggleGroup(6);
-
-        assertEquals(10, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
-        assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
-        assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
-        assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3);
-        assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4);
-        assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, true, 6);
-        assertPositionMetadata(7, ITEM_TYPE_IN_GROUP, false, 6);
-        assertPositionMetadata(8, ITEM_TYPE_IN_GROUP, false, 7);
-        assertPositionMetadata(9, ITEM_TYPE_STANDALONE, false, 8);
-    }
-
-    public void testPositionCache() {
-        buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
-        mAdapter.changeCursor(mCursor);
-
-        // First pass - building up cache
-        assertEquals(6, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3);
-        assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4);
-        assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6);
-        assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8);
-
-        // Second pass - using cache
-        assertEquals(6, mAdapter.getCount());
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
-        assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3);
-        assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4);
-        assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6);
-        assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8);
-
-        // Invalidate cache by expanding a group
-        mAdapter.toggleGroup(1);
-
-        // First pass - building up cache
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
-        assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
-        assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
-        assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3);
-        assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4);
-        assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, false, 6);
-        assertPositionMetadata(7, ITEM_TYPE_STANDALONE, false, 8);
-
-        // Second pass - using cache
-        assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
-        assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
-        assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
-        assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
-        assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3);
-        assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4);
-        assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, false, 6);
-        assertPositionMetadata(7, ITEM_TYPE_STANDALONE, false, 8);
-    }
-
-    public void testGroupDescriptorArrayGrowth() {
-        String[] numbers = new String[500];
-        for (int i = 0; i < numbers.length; i++) {
-
-            // Make groups of 2
-            numbers[i] = String.valueOf((i / 2) * 2);
-        }
-
-        buildCursor(numbers);
-        mAdapter.changeCursor(mCursor);
-
-        assertEquals(250, mAdapter.getCount());
-    }
-
-    private void assertPositionMetadata(int position, int itemType, boolean isExpanded,
-            int cursorPosition) {
-        GroupingListAdapter.PositionMetadata metadata = new GroupingListAdapter.PositionMetadata();
-        mAdapter.obtainPositionMetadata(metadata, position);
-        assertEquals(itemType, metadata.itemType);
-        if (metadata.itemType == ITEM_TYPE_GROUP_HEADER) {
-            assertEquals(isExpanded, metadata.isExpanded);
-        }
-        assertEquals(cursorPosition, metadata.cursorPosition);
-    }
-}