blob: 1208daefdf5d0b52bf45d410767d85044f721b5d [file] [log] [blame]
/*
* Copyright (C) 2017 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.speeddial;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.support.annotation.IntDef;
import android.support.annotation.StringRes;
import com.android.dialer.common.Assert;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/** Cursor for favorites contacts. */
final class SpeedDialCursor extends MergeCursor {
/**
* Caps the speed dial list to contain at most 20 contacts, including favorites and suggestions.
* It is only a soft limit though, for the case that there are more than 20 favorite contacts.
*/
private static final int SPEED_DIAL_CONTACT_LIST_SOFT_LIMIT = 20;
private static final String[] HEADER_CURSOR_PROJECTION = {"header"};
private static final int HEADER_COLUMN_POSITION = 0;
private boolean hasFavorites;
@Retention(RetentionPolicy.SOURCE)
@IntDef({RowType.HEADER, RowType.STARRED, RowType.SUGGESTION})
@interface RowType {
int HEADER = 0;
int STARRED = 1;
int SUGGESTION = 2;
}
public static SpeedDialCursor newInstance(Cursor strequentCursor) {
if (strequentCursor == null || strequentCursor.getCount() == 0) {
return null;
}
SpeedDialCursor cursor = new SpeedDialCursor(buildCursors(strequentCursor));
strequentCursor.close();
return cursor;
}
private static Cursor[] buildCursors(Cursor strequentCursor) {
MatrixCursor starred = new MatrixCursor(StrequentContactsCursorLoader.PHONE_PROJECTION);
MatrixCursor suggestions = new MatrixCursor(StrequentContactsCursorLoader.PHONE_PROJECTION);
strequentCursor.moveToPosition(-1);
while (strequentCursor.moveToNext()) {
if (strequentCursor.getPosition() != 0) {
long contactId = strequentCursor.getLong(StrequentContactsCursorLoader.PHONE_CONTACT_ID);
int position = strequentCursor.getPosition();
boolean duplicate = false;
// Iterate backwards through the cursor to check that this isn't a duplicate contact
// TODO(calderwoodra): improve this algorithm (currently O(n^2)).
while (strequentCursor.moveToPrevious() && !duplicate) {
duplicate |=
strequentCursor.getLong(StrequentContactsCursorLoader.PHONE_CONTACT_ID) == contactId;
}
strequentCursor.moveToPosition(position);
if (duplicate) {
continue;
}
}
if (strequentCursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
StrequentContactsCursorLoader.addToCursor(starred, strequentCursor);
} else if (starred.getCount() + suggestions.getCount() < SPEED_DIAL_CONTACT_LIST_SOFT_LIMIT) {
// Since all starred contacts come before each non-starred contact, it's safe to assume that
// this list will never exceed the soft limit unless there are more starred contacts than
// the limit permits.
StrequentContactsCursorLoader.addToCursor(suggestions, strequentCursor);
}
}
List<Cursor> cursorList = new ArrayList<>();
if (starred.getCount() > 0) {
cursorList.add(createHeaderCursor(R.string.favorites_header));
cursorList.add(starred);
}
if (suggestions.getCount() > 0) {
cursorList.add(createHeaderCursor(R.string.suggestions_header));
cursorList.add(suggestions);
}
return cursorList.toArray(new Cursor[cursorList.size()]);
}
private static Cursor createHeaderCursor(@StringRes int header) {
MatrixCursor cursor = new MatrixCursor(HEADER_CURSOR_PROJECTION);
cursor.newRow().add(HEADER_CURSOR_PROJECTION[HEADER_COLUMN_POSITION], header);
return cursor;
}
@RowType
int getRowType(int position) {
moveToPosition(position);
if (getColumnCount() == 1) {
return RowType.HEADER;
} else if (getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
return RowType.STARRED;
} else {
return RowType.SUGGESTION;
}
}
@SuppressLint("DefaultLocale")
@StringRes
int getHeader() {
if (getRowType(getPosition()) != RowType.HEADER) {
throw Assert.createIllegalStateFailException(
String.format("Current position (%d) is not a header.", getPosition()));
}
return getInt(HEADER_COLUMN_POSITION);
}
public boolean hasFavorites() {
return hasFavorites;
}
private SpeedDialCursor(Cursor[] cursors) {
super(cursors);
for (Cursor cursor : cursors) {
cursor.moveToFirst();
if (cursor.getColumnCount() != 1
&& cursor.getInt(StrequentContactsCursorLoader.PHONE_STARRED) == 1) {
hasFavorites = true;
break;
}
}
}
}