107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee/* 207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Copyright (C) 2015 The Android Open Source Project 307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * 407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Licensed under the Apache License, Version 2.0 (the "License"); 507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * you may not use this file except in compliance with the License. 607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * You may obtain a copy of the License at 707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * 807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * http://www.apache.org/licenses/LICENSE-2.0 907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * 1007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Unless required by applicable law or agreed to in writing, software 1107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * distributed under the License is distributed on an "AS IS" BASIS, 1207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * See the License for the specific language governing permissions and 1407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * limitations under the License. 1507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 1607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 1707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leepackage com.android.dialer.calllog; 1807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 1907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.content.Context; 2007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.database.ContentObserver; 2107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.database.Cursor; 2207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.database.DataSetObserver; 2307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.os.Handler; 24492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Leeimport android.support.v7.widget.RecyclerView; 25492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Leeimport android.util.Log; 2607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.util.SparseIntArray; 2707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.view.View; 2807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.view.ViewGroup; 2907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport android.widget.BaseAdapter; 3007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 3107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Leeimport com.android.contacts.common.testing.NeededForTesting; 3207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 3307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee/** 3407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Maintains a list that groups adjacent items sharing the same value of a "group-by" field. 3507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * 3607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * The list has three types of elements: stand-alone, group header and group child. Groups are 3707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * collapsible and collapsed by default. This is used by the call log to group related entries. 3807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 39492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Leeabstract class GroupingListAdapter extends RecyclerView.Adapter { 4007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 4107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private static final int GROUP_METADATA_ARRAY_INITIAL_SIZE = 16; 4207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private static final int GROUP_METADATA_ARRAY_INCREMENT = 128; 4307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private static final long GROUP_OFFSET_MASK = 0x00000000FFFFFFFFL; 4407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private static final long GROUP_SIZE_MASK = 0x7FFFFFFF00000000L; 4507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private static final long EXPANDED_GROUP_MASK = 0x8000000000000000L; 4607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 4707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public static final int ITEM_TYPE_STANDALONE = 0; 4807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public static final int ITEM_TYPE_GROUP_HEADER = 1; 4907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public static final int ITEM_TYPE_IN_GROUP = 2; 5007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 5107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 5207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Information about a specific list item: is it a group, if so is it expanded. 5307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Otherwise, is it a stand-alone item or a group member. 5407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 5507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee protected static class PositionMetadata { 5607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int itemType; 5707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee boolean isExpanded; 5807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int cursorPosition; 5907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int childCount; 6007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int groupPosition; 6107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int listPosition = -1; 6207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 6307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 6407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private Context mContext; 6507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private Cursor mCursor; 6607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 6707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 6807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Count of list items. 6907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 7007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int mCount; 7107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 7207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int mRowIdColumnIndex; 7307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 7407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 7507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Count of groups in the list. 7607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 7707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int mGroupCount; 7807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 7907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 8007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Information about where these groups are located in the list, how large they are 8107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * and whether they are expanded. 8207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 8307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private long[] mGroupMetadata; 8407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 8507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private SparseIntArray mPositionCache = new SparseIntArray(); 8607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int mLastCachedListPosition; 8707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int mLastCachedCursorPosition; 8807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int mLastCachedGroup; 8907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 9007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 9107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * A reusable temporary instance of PositionMetadata 9207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 9307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private PositionMetadata mPositionMetadata = new PositionMetadata(); 9407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 9507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) { 9607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 9707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee @Override 9807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public boolean deliverSelfNotifications() { 9907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return true; 10007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 10107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 10207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee @Override 10307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public void onChange(boolean selfChange) { 10407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee onContentChanged(); 10507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 10607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee }; 10707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 10807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee protected DataSetObserver mDataSetObserver = new DataSetObserver() { 10907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 11007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee @Override 11107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public void onChanged() { 11207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee notifyDataSetChanged(); 11307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 11407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee }; 11507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 11607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public GroupingListAdapter(Context context) { 11707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mContext = context; 11807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee resetCache(); 11907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 12007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 12107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 12207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Finds all groups of adjacent items in the cursor and calls {@link #addGroup} for 12307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * each of them. 12407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 12507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee protected abstract void addGroups(Cursor cursor); 12607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 127492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Lee protected abstract void onContentChanged(); 12807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 12907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 13007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Cache should be reset whenever the cursor changes or groups are expanded or collapsed. 13107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 13207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private void resetCache() { 13307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mCount = -1; 13407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mLastCachedListPosition = -1; 13507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mLastCachedCursorPosition = -1; 13607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mLastCachedGroup = -1; 13707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mPositionMetadata.listPosition = -1; 13807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mPositionCache.clear(); 13907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 14007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 14107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public void changeCursor(Cursor cursor) { 14207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (cursor == mCursor) { 14307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return; 14407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 14507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 14607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mCursor != null) { 14707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mCursor.unregisterContentObserver(mChangeObserver); 14807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mCursor.unregisterDataSetObserver(mDataSetObserver); 14907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mCursor.close(); 15007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 15107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mCursor = cursor; 15207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee resetCache(); 15307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee findGroups(); 15407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 15507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (cursor != null) { 15607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursor.registerContentObserver(mChangeObserver); 15707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursor.registerDataSetObserver(mDataSetObserver); 15807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mRowIdColumnIndex = cursor.getColumnIndexOrThrow("_id"); 15907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee notifyDataSetChanged(); 16007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 16107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 16207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 163492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Lee @NeededForTesting 16407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public Cursor getCursor() { 16507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mCursor; 16607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 16707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 16807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 16907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Scans over the entire cursor looking for duplicate phone numbers that need 17007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * to be collapsed. 17107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 17207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private void findGroups() { 17307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupCount = 0; 17407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupMetadata = new long[GROUP_METADATA_ARRAY_INITIAL_SIZE]; 17507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 17607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mCursor == null) { 17707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return; 17807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 17907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 18007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee addGroups(mCursor); 18107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 18207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 18307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 18407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Records information about grouping in the list. Should be called by the overridden 18507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * {@link #addGroups} method. 18607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 18707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee protected void addGroup(int cursorPosition, int size, boolean expanded) { 18807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mGroupCount >= mGroupMetadata.length) { 18907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int newSize = idealLongArraySize( 19007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupMetadata.length + GROUP_METADATA_ARRAY_INCREMENT); 19107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee long[] array = new long[newSize]; 19207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee System.arraycopy(mGroupMetadata, 0, array, 0, mGroupCount); 19307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupMetadata = array; 19407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 19507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 19607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee long metadata = ((long)size << 32) | cursorPosition; 19707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (expanded) { 19807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata |= EXPANDED_GROUP_MASK; 19907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 20007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupMetadata[mGroupCount++] = metadata; 20107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 20207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 20307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Copy/paste from ArrayUtils 20407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int idealLongArraySize(int need) { 20507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return idealByteArraySize(need * 8) / 8; 20607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 20707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 20807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Copy/paste from ArrayUtils 20907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee private int idealByteArraySize(int need) { 21007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee for (int i = 4; i < 32; i++) 21107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (need <= (1 << i) - 12) 21207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return (1 << i) - 12; 21307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 21407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return need; 21507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 21607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 217492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Lee @Override 218492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Lee public int getItemCount() { 21907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mCursor == null) { 22007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return 0; 22107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 22207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 22307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mCount != -1) { 22407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mCount; 22507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 22607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 22707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int cursorPosition = 0; 22807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int count = 0; 22907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee for (int i = 0; i < mGroupCount; i++) { 23007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee long metadata = mGroupMetadata[i]; 23107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int offset = (int)(metadata & GROUP_OFFSET_MASK); 23207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee boolean expanded = (metadata & EXPANDED_GROUP_MASK) != 0; 23307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int size = (int)((metadata & GROUP_SIZE_MASK) >> 32); 23407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 23507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee count += (offset - cursorPosition); 23607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 23707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (expanded) { 23807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee count += size + 1; 23907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } else { 24007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee count++; 24107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 24207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 24307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursorPosition = offset + size; 24407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 24507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 24607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mCount = count + mCursor.getCount() - cursorPosition; 24707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mCount; 24807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 24907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 25007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 25107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Figures out whether the item at the specified position represents a 25207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * stand-alone element, a group or a group child. Also computes the 25307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * corresponding cursor position. 25407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 25507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public void obtainPositionMetadata(PositionMetadata metadata, int position) { 25607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // If the description object already contains requested information, just return 25707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (metadata.listPosition == position) { 25807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return; 25907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 26007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 26107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int listPosition = 0; 26207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int cursorPosition = 0; 26307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int firstGroupToCheck = 0; 26407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 26507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Check cache for the supplied position. What we are looking for is 26607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // the group descriptor immediately preceding the supplied position. 26707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Once we have that, we will be able to tell whether the position 26807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // is the header of the group, a member of the group or a standalone item. 26907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mLastCachedListPosition != -1) { 27007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (position <= mLastCachedListPosition) { 27107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 27207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Have SparceIntArray do a binary search for us. 27307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int index = mPositionCache.indexOfKey(position); 27407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 27507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // If we get back a positive number, the position corresponds to 27607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // a group header. 27707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (index < 0) { 27807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 27907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // We had a cache miss, but we did obtain valuable information anyway. 28007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // The negative number will allow us to compute the location of 28107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // the group header immediately preceding the supplied position. 28207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee index = ~index - 1; 28307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 28407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (index >= mPositionCache.size()) { 28507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee index--; 28607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 28707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 28807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 28907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // A non-negative index gives us the position of the group header 29007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // corresponding or preceding the position, so we can 29107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // search for the group information at the supplied position 29207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // starting with the cached group we just found 29307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (index >= 0) { 29407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee listPosition = mPositionCache.keyAt(index); 29507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee firstGroupToCheck = mPositionCache.valueAt(index); 29607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee long descriptor = mGroupMetadata[firstGroupToCheck]; 29707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursorPosition = (int)(descriptor & GROUP_OFFSET_MASK); 29807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 29907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } else { 30007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 30107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // If we haven't examined groups beyond the supplied position, 30207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // we will start where we left off previously 30307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee firstGroupToCheck = mLastCachedGroup; 30407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee listPosition = mLastCachedListPosition; 30507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursorPosition = mLastCachedCursorPosition; 30607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 30707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 30807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 30907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee for (int i = firstGroupToCheck; i < mGroupCount; i++) { 31007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee long group = mGroupMetadata[i]; 31107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int offset = (int)(group & GROUP_OFFSET_MASK); 31207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 31307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Move pointers to the beginning of the group 31407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee listPosition += (offset - cursorPosition); 31507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursorPosition = offset; 31607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 31707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (i > mLastCachedGroup) { 31807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mPositionCache.append(listPosition, i); 31907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mLastCachedListPosition = listPosition; 32007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mLastCachedCursorPosition = cursorPosition; 32107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mLastCachedGroup = i; 32207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 32307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 32407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Now we have several possibilities: 32507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // A) The requested position precedes the group 32607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (position < listPosition) { 32707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.itemType = ITEM_TYPE_STANDALONE; 32807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.cursorPosition = cursorPosition - (listPosition - position); 329492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Lee metadata.childCount = 1; 33007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return; 33107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 33207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 33307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee boolean expanded = (group & EXPANDED_GROUP_MASK) != 0; 33407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee int size = (int) ((group & GROUP_SIZE_MASK) >> 32); 33507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 33607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // B) The requested position is a group header 33707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (position == listPosition) { 33807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.itemType = ITEM_TYPE_GROUP_HEADER; 33907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.groupPosition = i; 34007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.isExpanded = expanded; 34107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.childCount = size; 34207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.cursorPosition = offset; 34307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return; 34407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 34507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 34607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (expanded) { 34707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // C) The requested position is an element in the expanded group 34807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (position < listPosition + size + 1) { 34907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.itemType = ITEM_TYPE_IN_GROUP; 35007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.cursorPosition = cursorPosition + (position - listPosition) - 1; 35107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return; 35207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 35307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 35407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // D) The element is past the expanded group 35507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee listPosition += size + 1; 35607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } else { 35707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 35807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // E) The element is past the collapsed group 35907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee listPosition++; 36007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 36107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 36207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // Move cursor past the group 36307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee cursorPosition += size; 36407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 36507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 36607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee // The required item is past the last group 36707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.itemType = ITEM_TYPE_STANDALONE; 36807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee metadata.cursorPosition = cursorPosition + (position - listPosition); 369492cd7371c6d8fddc4de85887a4ed9a89d602767Andrew Lee metadata.childCount = 1; 37007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 37107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 37207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 37307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Returns true if the specified position in the list corresponds to a 37407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * group header. 37507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 37607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public boolean isGroupHeader(int position) { 37707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee obtainPositionMetadata(mPositionMetadata, position); 37807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mPositionMetadata.itemType == ITEM_TYPE_GROUP_HEADER; 37907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 38007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 38107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 38207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Given a position of a groups header in the list, returns the size of 38307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * the corresponding group. 38407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 38507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public int getGroupSize(int position) { 38607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee obtainPositionMetadata(mPositionMetadata, position); 38707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mPositionMetadata.childCount; 38807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 38907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 39007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee /** 39107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee * Mark group as expanded if it is collapsed and vice versa. 39207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee */ 39307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee @NeededForTesting 39407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public void toggleGroup(int position) { 39507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee obtainPositionMetadata(mPositionMetadata, position); 39607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mPositionMetadata.itemType != ITEM_TYPE_GROUP_HEADER) { 39707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee throw new IllegalArgumentException("Not a group at position " + position); 39807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 39907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 40007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mPositionMetadata.isExpanded) { 40107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupMetadata[mPositionMetadata.groupPosition] &= ~EXPANDED_GROUP_MASK; 40207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } else { 40307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee mGroupMetadata[mPositionMetadata.groupPosition] |= EXPANDED_GROUP_MASK; 40407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 40507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee resetCache(); 40607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee notifyDataSetChanged(); 40707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 40807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 40907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public int getItemViewType(int position) { 41007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee obtainPositionMetadata(mPositionMetadata, position); 41107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mPositionMetadata.itemType; 41207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 41307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 41407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public Object getItem(int position) { 41507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mCursor == null) { 41607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return null; 41707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 41807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 41907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee obtainPositionMetadata(mPositionMetadata, position); 42007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (mCursor.moveToPosition(mPositionMetadata.cursorPosition)) { 42107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mCursor; 42207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } else { 42307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return null; 42407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 42507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 42607a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee 42707a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee public long getItemId(int position) { 42807a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee Object item = getItem(position); 42907a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee if (item != null) { 43007a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return mCursor.getLong(mRowIdColumnIndex); 43107a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } else { 43207a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee return -1; 43307a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 43407a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee } 43507a5e0a68e95bc7e8098bb018004910dc97fa608Andrew Lee} 436