/* * 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.searchfragment.cp2; import android.content.ContentResolver; import android.content.Context; import android.database.CharArrayBuffer; import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.database.MatrixCursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract.CommonDataKinds.Nickname; import android.provider.ContactsContract.CommonDataKinds.Organization; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v4.util.ArraySet; import android.text.TextUtils; import com.android.dialer.searchfragment.common.Projections; import com.android.dialer.searchfragment.common.QueryFilteringUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; /** * Wrapper for a cursor containing all on device contacts. * *
This cursor removes duplicate phone numbers associated with the same contact and can filter
* contacts based on a query by calling {@link #filter(String, Context)}.
*/
final class ContactFilterCursor implements Cursor {
private final Cursor cursor;
// List of cursor ids that are valid for displaying after filtering.
private final List Here are some sample rows and columns that might exist in cp2 database:
*
* These rows would be coalesced into new rows like so:
*
* The query can have at least 1 of 3 forms:
*
* A contact is considered a match if:
*
*
*
*
*
*
*/
private static Cursor createCursor(Cursor cursor) {
// Convert cursor rows into Cp2Contacts
List
*
*
*
*
*/
public void filter(@Nullable String query, Context context) {
if (query == null) {
query = "";
}
queryFilteredPositions.clear();
query = query.toLowerCase();
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
int position = cursor.getPosition();
String number = cursor.getString(Projections.PHONE_NUMBER);
String name = cursor.getString(Projections.DISPLAY_NAME);
String companyName = cursor.getString(Projections.COMPANY_NAME);
String nickName = cursor.getString(Projections.NICKNAME);
if (TextUtils.isEmpty(query)
|| QueryFilteringUtil.nameMatchesT9Query(query, name, context)
|| QueryFilteringUtil.numberMatchesNumberQuery(query, number)
|| QueryFilteringUtil.nameContainsQuery(query, name)
|| QueryFilteringUtil.nameContainsQuery(query, companyName)
|| QueryFilteringUtil.nameContainsQuery(query, nickName)) {
queryFilteredPositions.add(position);
}
}
currentPosition = 0;
cursor.moveToFirst();
}
@Override
public boolean moveToPosition(int position) {
currentPosition = position;
return currentPosition < getCount()
&& cursor.moveToPosition(queryFilteredPositions.get(currentPosition));
}
@Override
public boolean move(int offset) {
currentPosition += offset;
return moveToPosition(currentPosition);
}
@Override
public int getCount() {
return queryFilteredPositions.size();
}
@Override
public boolean isFirst() {
return currentPosition == 0;
}
@Override
public boolean isLast() {
return currentPosition == getCount() - 1;
}
@Override
public int getPosition() {
return currentPosition;
}
@Override
public boolean moveToFirst() {
return moveToPosition(0);
}
@Override
public boolean moveToLast() {
return moveToPosition(getCount() - 1);
}
@Override
public boolean moveToNext() {
return moveToPosition(++currentPosition);
}
@Override
public boolean moveToPrevious() {
return moveToPosition(--currentPosition);
}
// Methods below simply call the corresponding method in cursor.
@Override
public boolean isBeforeFirst() {
return cursor.isBeforeFirst();
}
@Override
public boolean isAfterLast() {
return cursor.isAfterLast();
}
@Override
public int getColumnIndex(String columnName) {
return cursor.getColumnIndex(columnName);
}
@Override
public int getColumnIndexOrThrow(String columnName) {
return cursor.getColumnIndexOrThrow(columnName);
}
@Override
public String getColumnName(int columnIndex) {
return cursor.getColumnName(columnIndex);
}
@Override
public String[] getColumnNames() {
return cursor.getColumnNames();
}
@Override
public int getColumnCount() {
return cursor.getColumnCount();
}
@Override
public byte[] getBlob(int columnIndex) {
return cursor.getBlob(columnIndex);
}
@Override
public String getString(int columnIndex) {
return cursor.getString(columnIndex);
}
@Override
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
cursor.copyStringToBuffer(columnIndex, buffer);
}
@Override
public short getShort(int columnIndex) {
return cursor.getShort(columnIndex);
}
@Override
public int getInt(int columnIndex) {
return cursor.getInt(columnIndex);
}
@Override
public long getLong(int columnIndex) {
return cursor.getLong(columnIndex);
}
@Override
public float getFloat(int columnIndex) {
return cursor.getFloat(columnIndex);
}
@Override
public double getDouble(int columnIndex) {
return cursor.getDouble(columnIndex);
}
@Override
public int getType(int columnIndex) {
return cursor.getType(columnIndex);
}
@Override
public boolean isNull(int columnIndex) {
return cursor.isNull(columnIndex);
}
@Override
public void deactivate() {
cursor.deactivate();
}
@Override
public boolean requery() {
return cursor.requery();
}
@Override
public void close() {
cursor.close();
}
@Override
public boolean isClosed() {
return cursor.isClosed();
}
@Override
public void registerContentObserver(ContentObserver observer) {
cursor.registerContentObserver(observer);
}
@Override
public void unregisterContentObserver(ContentObserver observer) {
cursor.unregisterContentObserver(observer);
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
cursor.registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
cursor.unregisterDataSetObserver(observer);
}
@Override
public void setNotificationUri(ContentResolver cr, Uri uri) {
cursor.setNotificationUri(cr, uri);
}
@Override
public Uri getNotificationUri() {
return cursor.getNotificationUri();
}
@Override
public boolean getWantsAllOnMoveCalls() {
return cursor.getWantsAllOnMoveCalls();
}
@Override
public void setExtras(Bundle extras) {
cursor.setExtras(extras);
}
@Override
public Bundle getExtras() {
return cursor.getExtras();
}
@Override
public Bundle respond(Bundle extras) {
return cursor.respond(extras);
}
}