blob: d1157206fc0e88eec69b8d549991747794d0af00 [file] [log] [blame]
/*
* 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();
}
}