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