diff options
Diffstat (limited to 'java/com/android/dialer/app/calllog/GroupingListAdapter.java')
-rw-r--r-- | java/com/android/dialer/app/calllog/GroupingListAdapter.java | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/java/com/android/dialer/app/calllog/GroupingListAdapter.java b/java/com/android/dialer/app/calllog/GroupingListAdapter.java new file mode 100644 index 000000000..d1157206f --- /dev/null +++ b/java/com/android/dialer/app/calllog/GroupingListAdapter.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 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.dialer.app.calllog; + +import android.database.ContentObserver; +import android.database.Cursor; +import android.database.DataSetObserver; +import android.os.Handler; +import android.support.v7.widget.RecyclerView; +import android.util.SparseIntArray; + +/** + * Maintains a list that groups items into groups of consecutive elements which are disjoint, that + * is, an item can only belong to one group. This is leveraged for grouping calls in the call log + * received from or made to the same phone number. + * + * <p>There are two integers stored as metadata for every list item in the adapter. + */ +abstract class GroupingListAdapter extends RecyclerView.Adapter { + + 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(); + } + }; + private Cursor mCursor; + /** + * SparseIntArray, which maps the cursor position of the first element of a group to the size of + * the group. The index of a key in this map corresponds to the list position of that group. + */ + private SparseIntArray mGroupMetadata; + + private int mItemCount; + + public GroupingListAdapter() { + reset(); + } + + /** + * 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 void onContentChanged(); + + public void changeCursor(Cursor cursor) { + if (cursor == mCursor) { + return; + } + + if (mCursor != null) { + mCursor.unregisterContentObserver(mChangeObserver); + mCursor.unregisterDataSetObserver(mDataSetObserver); + mCursor.close(); + } + + // Reset whenever the cursor is changed. + reset(); + mCursor = cursor; + + if (cursor != null) { + addGroups(mCursor); + + // Calculate the item count by subtracting group child counts from the cursor count. + mItemCount = mGroupMetadata.size(); + + cursor.registerContentObserver(mChangeObserver); + cursor.registerDataSetObserver(mDataSetObserver); + notifyDataSetChanged(); + } + } + + /** + * Records information about grouping in the list. Should be called by the overridden {@link + * #addGroups} method. + */ + public void addGroup(int cursorPosition, int groupSize) { + int lastIndex = mGroupMetadata.size() - 1; + if (lastIndex < 0 || cursorPosition <= mGroupMetadata.keyAt(lastIndex)) { + mGroupMetadata.put(cursorPosition, groupSize); + } else { + // Optimization to avoid binary search if adding groups in ascending cursor position. + mGroupMetadata.append(cursorPosition, groupSize); + } + } + + @Override + public int getItemCount() { + return mItemCount; + } + + /** + * Given the position of a list item, returns the size of the group of items corresponding to that + * position. + */ + public int getGroupSize(int listPosition) { + if (listPosition < 0 || listPosition >= mGroupMetadata.size()) { + return 0; + } + + return mGroupMetadata.valueAt(listPosition); + } + + /** + * Given the position of a list item, returns the the first item in the group of items + * corresponding to that position. + */ + public Object getItem(int listPosition) { + if (mCursor == null || listPosition < 0 || listPosition >= mGroupMetadata.size()) { + return null; + } + + int cursorPosition = mGroupMetadata.keyAt(listPosition); + if (mCursor.moveToPosition(cursorPosition)) { + return mCursor; + } else { + return null; + } + } + + private void reset() { + mItemCount = 0; + mGroupMetadata = new SparseIntArray(); + } +} |