summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/searchfragment
diff options
context:
space:
mode:
authorcalderwoodra <calderwoodra@google.com>2017-10-17 15:54:45 -0700
committerEric Erfanian <erfanian@google.com>2017-10-17 20:10:38 -0700
commitfe7e9e1d5a083bfe376df0d54bcf632f60012dcf (patch)
tree903650b336b90175f9d9dad64dc36c21848cd65b /java/com/android/dialer/searchfragment
parente1c4dc99304e117325bc8c92731c2c64f2c79400 (diff)
Contacts are now searchable by company name.
This change coalesces Cp2 contacts into a new cursor so that they can be associated with the Company name. The following logs can help explain how the data is organizes in the original cursor: display Name (A Pixel), lookupKey (3535i7a9673fc89b77de3), mimeType (vnd.android.cursor.item/name), data1 (A Pixel) display Name (A Pixel), lookupKey (3535i7a9673fc89b77de3), mimeType (vnd.android.cursor.item/note), data1 () display Name (A Pixel), lookupKey (3535i7a9673fc89b77de3), mimeType (vnd.android.cursor.item/group_membership), data1 (1) display Name (A Pixel), lookupKey (3535i7a9673fc89b77de3), mimeType (vnd.android.cursor.item/phone_v2), data1 (+1 650-200-7932) display Name (A Pixel), lookupKey (3535i7a9673fc89b77de3), mimeType (vnd.android.cursor.item/phone_v2), data1 (+1 540-555-6666) display Name (A Pixel), lookupKey (3535i7a9673fc89b77de3), mimeType (vnd.android.cursor.item/organization), data1 (Walmart) This is an example of what is returned for a single contact. We can easily associate contact rows together using the lookup key and determine which rows have relevant data by checking the mime type. I use the data here to coalesce the contacts together into one row for easy parsing in ContactFilterCursor. Rows with mime type phone_v2 contain contact information (for example, this contact has 2 phone numbers). Rows with mime type organization contain contact's company information (for example, this contact works at Walmart). Bug: 67675742,64894607,67848713 Test: existing + SCCT.filter_companyName PiperOrigin-RevId: 172528797 Change-Id: I5c9f66ff0c27276869295eff97bb0216f92995be
Diffstat (limited to 'java/com/android/dialer/searchfragment')
-rw-r--r--java/com/android/dialer/searchfragment/common/Projections.java44
-rw-r--r--java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java4
-rw-r--r--java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java209
-rw-r--r--java/com/android/dialer/searchfragment/cp2/Cp2Contact.java132
-rw-r--r--java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java25
-rw-r--r--java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java22
-rw-r--r--java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java8
-rw-r--r--java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java2
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java14
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java2
-rw-r--r--java/com/android/dialer/searchfragment/testing/TestCursorSchema.java47
11 files changed, 400 insertions, 109 deletions
diff --git a/java/com/android/dialer/searchfragment/common/Projections.java b/java/com/android/dialer/searchfragment/common/Projections.java
index 078c3e5e6..aaf9e80f1 100644
--- a/java/com/android/dialer/searchfragment/common/Projections.java
+++ b/java/com/android/dialer/searchfragment/common/Projections.java
@@ -16,37 +16,47 @@
package com.android.dialer.searchfragment.common;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Data;
/** Class containing relevant projections for searching contacts. */
public class Projections {
- public static final int PHONE_ID = 0;
+ public static final int ID = 0;
public static final int PHONE_TYPE = 1;
public static final int PHONE_LABEL = 2;
public static final int PHONE_NUMBER = 3;
- public static final int PHONE_DISPLAY_NAME = 4;
- public static final int PHONE_PHOTO_ID = 5;
- public static final int PHONE_PHOTO_URI = 6;
- public static final int PHONE_LOOKUP_KEY = 7;
- public static final int PHONE_CARRIER_PRESENCE = 8;
- public static final int PHONE_CONTACT_ID = 9;
+ public static final int DISPLAY_NAME = 4;
+ public static final int PHOTO_ID = 5;
+ public static final int PHOTO_URI = 6;
+ public static final int LOOKUP_KEY = 7;
+ public static final int CARRIER_PRESENCE = 8;
+ public static final int CONTACT_ID = 9;
+ public static final int MIME_TYPE = 10;
@SuppressWarnings("unused")
- public static final int PHONE_SORT_KEY = 10;
+ public static final int SORT_KEY = 11;
- public static final String[] PHONE_PROJECTION =
+ public static final int COMPANY_NAME = 12;
+ public static final int NICKNAME = 13;
+
+ public static final String[] DATA_PROJECTION =
new String[] {
- Phone._ID, // 0
+ Data._ID, // 0
Phone.TYPE, // 1
Phone.LABEL, // 2
Phone.NUMBER, // 3
- Phone.DISPLAY_NAME_PRIMARY, // 4
- Phone.PHOTO_ID, // 5
- Phone.PHOTO_THUMBNAIL_URI, // 6
- Phone.LOOKUP_KEY, // 7
- Phone.CARRIER_PRESENCE, // 8
- Phone.CONTACT_ID, // 9
- Phone.SORT_KEY_PRIMARY // 10
+ Data.DISPLAY_NAME_PRIMARY, // 4
+ Data.PHOTO_ID, // 5
+ Data.PHOTO_THUMBNAIL_URI, // 6
+ Data.LOOKUP_KEY, // 7
+ Data.CARRIER_PRESENCE, // 8
+ Data.CONTACT_ID, // 9
+ Data.MIMETYPE, // 10
+ Data.SORT_KEY_PRIMARY, // 11
+ Organization.COMPANY, // 12
+ Nickname.NAME // 13
};
}
diff --git a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
index 775f8deec..6b5cea88d 100644
--- a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
+++ b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
@@ -81,6 +81,10 @@ public class QueryFilteringUtil {
* </ul>
*/
public static boolean nameContainsQuery(String query, String name) {
+ if (TextUtils.isEmpty(name)) {
+ return false;
+ }
+
return Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase()))
.matcher(name.toLowerCase())
.find();
diff --git a/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
index 6fd053cae..9a0ca0088 100644
--- a/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
+++ b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
@@ -21,17 +21,25 @@ 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.common.Assert;
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.
@@ -63,92 +71,109 @@ final class ContactFilterCursor implements Cursor {
}
/**
- * @param cursor with projection {@link Projections#PHONE_PROJECTION}.
+ * @param cursor with projection {@link Projections#DATA_PROJECTION}.
* @param query to filter cursor results.
*/
ContactFilterCursor(Cursor cursor, @Nullable String query) {
- // TODO(calderwoodra) investigate copying this into a MatrixCursor and holding in memory
- this.cursor = cursor;
+ this.cursor = createCursor(cursor);
filter(query);
}
/**
- * Filters out contacts that do not match the query.
+ * Returns a new cursor with contact information coalesced.
*
- * <p>The query can have at least 1 of 3 forms:
+ * <p>Here are some sample rows and columns that might exist in cp2 database:
*
* <ul>
- * <li>A phone number
- * <li>A T9 representation of a name (matches {@link QueryFilteringUtil#T9_PATTERN}).
- * <li>A name
+ * <li>display Name (William), contactID (202), mimeType (name), data1 (A Pixel)
+ * <li>display Name (William), contactID (202), mimeType (phone), data1 (+1 650-200-3333)
+ * <li>display Name (William), contactID (202), mimeType (phone), data1 (+1 540-555-6666)
+ * <li>display Name (William), contactID (202), mimeType (organization), data1 (Walmart)
+ * <li>display Name (William), contactID (202), mimeType (nickname), data1 (Will)
* </ul>
*
- * <p>A contact is considered a match if:
+ * <p>These rows would be coalesced into new rows like so:
*
* <ul>
- * <li>Its phone number contains the phone number query
- * <li>Its name represented in T9 contains the T9 query
- * <li>Its name contains the query
+ * <li>display Name (William), phoneNumber (+1 650-200-3333), organization (Walmart), nickname
+ * (Will)
+ * <li>display Name (William), phoneNumber (+1 540-555-6666), organization (Walmart), nickname
+ * (Will)
* </ul>
*/
- public void filter(@Nullable String query) {
- if (query == null) {
- query = "";
+ private static Cursor createCursor(Cursor cursor) {
+ // Convert cursor rows into Cp2Contacts
+ List<Cp2Contact> cp2Contacts = new ArrayList<>();
+ Set<Integer> contactIds = new ArraySet<>();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ Cp2Contact contact = Cp2Contact.fromCursor(cursor);
+ cp2Contacts.add(contact);
+ contactIds.add(contact.contactId());
}
- queryFilteredPositions.clear();
+ cursor.close();
- // On some devices, contacts have multiple rows with identical phone numbers. These numbers are
- // considered duplicates. Since the order might not be guaranteed, we compare all of the numbers
- // and hold onto the most qualified one as the one we want to display to the user.
- // See #getQualification for details on how qualification is determined.
- int previousMostQualifiedPosition = 0;
- String previousName = "";
- String previousMostQualifiedNumber = "";
+ // Group then combine contact data
+ List<Cp2Contact> coalescedContacts = new ArrayList<>();
+ for (Integer contactId : contactIds) {
+ List<Cp2Contact> duplicateContacts = getAllContactsWithContactId(contactId, cp2Contacts);
+ coalescedContacts.addAll(coalesceContacts(duplicateContacts));
+ }
- query = query.toLowerCase();
- cursor.moveToPosition(-1);
+ // Sort by display name, then build new cursor from coalesced contacts.
+ // We sort the contacts so that they are displayed to the user in lexicographic order.
+ Collections.sort(coalescedContacts, (o1, o2) -> o1.displayName().compareTo(o2.displayName()));
+ MatrixCursor newCursor =
+ new MatrixCursor(Projections.DATA_PROJECTION, coalescedContacts.size());
+ for (Cp2Contact contact : coalescedContacts) {
+ newCursor.addRow(contact.toCursorRow());
+ }
+ return newCursor;
+ }
+
+ private static List<Cp2Contact> coalesceContacts(List<Cp2Contact> contactsWithSameContactId) {
+ String companyName = null;
+ String nickName = null;
+ List<Cp2Contact> phoneContacts = new ArrayList<>();
+ for (Cp2Contact contact : contactsWithSameContactId) {
+ if (contact.mimeType().equals(Phone.CONTENT_ITEM_TYPE)) {
+ phoneContacts.add(contact);
+ } else if (contact.mimeType().equals(Organization.CONTENT_ITEM_TYPE)) {
+ Assert.checkArgument(TextUtils.isEmpty(companyName));
+ companyName = contact.companyName();
+ } else if (contact.mimeType().equals(Nickname.CONTENT_ITEM_TYPE)) {
+ Assert.checkArgument(TextUtils.isEmpty(nickName));
+ nickName = contact.nickName();
+ }
+ }
- while (cursor.moveToNext()) {
- int position = cursor.getPosition();
- String currentNumber = cursor.getString(Projections.PHONE_NUMBER);
- String currentName = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+ removeDuplicatePhoneNumbers(phoneContacts);
- if (!previousName.equals(currentName)) {
- previousName = currentName;
- previousMostQualifiedNumber = currentNumber;
- previousMostQualifiedPosition = position;
- } else {
- // Since the contact name is the same, check if this number is a duplicate
- switch (getQualification(currentNumber, previousMostQualifiedNumber)) {
- case Qualification.CURRENT_MORE_QUALIFIED:
- // Number is a less qualified duplicate, ignore it.
- continue;
- case Qualification.NEW_NUMBER_IS_MORE_QUALIFIED:
- // If number wasn't filtered out before, remove it and add it's more qualified version.
- int index = queryFilteredPositions.indexOf(previousMostQualifiedPosition);
- if (index != -1) {
- queryFilteredPositions.remove(index);
- queryFilteredPositions.add(position);
- }
- previousMostQualifiedNumber = currentNumber;
- previousMostQualifiedPosition = position;
- continue;
- case Qualification.NUMBERS_ARE_NOT_DUPLICATES:
- default:
- previousMostQualifiedNumber = currentNumber;
- previousMostQualifiedPosition = position;
+ List<Cp2Contact> coalescedContacts = new ArrayList<>();
+ for (Cp2Contact phoneContact : phoneContacts) {
+ coalescedContacts.add(
+ phoneContact.toBuilder().setCompanyName(companyName).setNickName(nickName).build());
+ }
+ return coalescedContacts;
+ }
+
+ private static void removeDuplicatePhoneNumbers(List<Cp2Contact> phoneContacts) {
+ for (int i = 0; i < phoneContacts.size(); i++) {
+ Cp2Contact contact1 = phoneContacts.get(i);
+ for (int j = i + 1; j < phoneContacts.size(); /* don't iterate by default */ ) {
+ Cp2Contact contact2 = phoneContacts.get(j);
+ int qualification = getQualification(contact2.phoneNumber(), contact1.phoneNumber());
+ if (qualification == Qualification.CURRENT_MORE_QUALIFIED) {
+ phoneContacts.remove(contact2);
+ } else if (qualification == Qualification.NEW_NUMBER_IS_MORE_QUALIFIED) {
+ phoneContacts.remove(contact1);
+ break;
+ } else if (qualification == Qualification.NUMBERS_ARE_NOT_DUPLICATES) {
+ // Keep both contacts
+ j++;
}
}
-
- if (TextUtils.isEmpty(query)
- || QueryFilteringUtil.nameMatchesT9Query(query, previousName)
- || QueryFilteringUtil.numberMatchesNumberQuery(query, previousMostQualifiedNumber)
- || QueryFilteringUtil.nameContainsQuery(query, previousName)) {
- queryFilteredPositions.add(previousMostQualifiedPosition);
- }
}
- currentPosition = 0;
- cursor.moveToFirst();
}
/**
@@ -157,7 +182,7 @@ final class ContactFilterCursor implements Cursor {
* @return {@link Qualification} where the more qualified number is the number with the most
* digits. If the digits are the same, the number with the most formatting is more qualified.
*/
- private @Qualification int getQualification(String number, String mostQualifiedNumber) {
+ private static @Qualification int getQualification(String number, String mostQualifiedNumber) {
// Ignore formatting
String numberDigits = QueryFilteringUtil.digitsOnly(number);
String qualifiedNumberDigits = QueryFilteringUtil.digitsOnly(mostQualifiedNumber);
@@ -182,6 +207,64 @@ final class ContactFilterCursor implements Cursor {
return Qualification.NUMBERS_ARE_NOT_DUPLICATES;
}
+ private static List<Cp2Contact> getAllContactsWithContactId(
+ int contactId, List<Cp2Contact> contacts) {
+ List<Cp2Contact> contactIdContacts = new ArrayList<>();
+ for (Cp2Contact contact : contacts) {
+ if (contact.contactId() == contactId) {
+ contactIdContacts.add(contact);
+ }
+ }
+ return contactIdContacts;
+ }
+
+ /**
+ * Filters out contacts that do not match the query.
+ *
+ * <p>The query can have at least 1 of 3 forms:
+ *
+ * <ul>
+ * <li>A phone number
+ * <li>A T9 representation of a name (matches {@link QueryFilteringUtil#T9_PATTERN}).
+ * <li>A name
+ * </ul>
+ *
+ * <p>A contact is considered a match if:
+ *
+ * <ul>
+ * <li>Its phone number contains the phone number query
+ * <li>Its name represented in T9 contains the T9 query
+ * <li>Its name contains the query
+ * <li>Its company contains the query
+ * </ul>
+ */
+ public void filter(@Nullable String query) {
+ 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)
+ || 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;
diff --git a/java/com/android/dialer/searchfragment/cp2/Cp2Contact.java b/java/com/android/dialer/searchfragment/cp2/Cp2Contact.java
new file mode 100644
index 000000000..f199f679b
--- /dev/null
+++ b/java/com/android/dialer/searchfragment/cp2/Cp2Contact.java
@@ -0,0 +1,132 @@
+/*
+ * 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.database.Cursor;
+import android.support.annotation.Nullable;
+import com.android.dialer.searchfragment.common.Projections;
+import com.google.auto.value.AutoValue;
+
+/** POJO Representation for contacts returned in {@link SearchContactsCursorLoader}. */
+@AutoValue
+public abstract class Cp2Contact {
+
+ public abstract long phoneId();
+
+ public abstract int phoneType();
+
+ @Nullable
+ public abstract String phoneLabel();
+
+ public abstract String phoneNumber();
+
+ @Nullable
+ public abstract String displayName();
+
+ public abstract int photoId();
+
+ @Nullable
+ public abstract String photoUri();
+
+ public abstract String lookupKey();
+
+ public abstract int carrierPresence();
+
+ public abstract int contactId();
+
+ @Nullable
+ public abstract String companyName();
+
+ @Nullable
+ public abstract String nickName();
+
+ public abstract String mimeType();
+
+ /** Builder for {@link Cp2Contact}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder setPhoneId(long id);
+
+ public abstract Builder setPhoneType(int type);
+
+ public abstract Builder setPhoneLabel(@Nullable String label);
+
+ public abstract Builder setPhoneNumber(String number);
+
+ public abstract Builder setDisplayName(@Nullable String name);
+
+ public abstract Builder setPhotoId(int id);
+
+ public abstract Builder setPhotoUri(@Nullable String uri);
+
+ public abstract Builder setLookupKey(String lookupKey);
+
+ public abstract Builder setCarrierPresence(int presence);
+
+ public abstract Builder setContactId(int id);
+
+ public abstract Builder setCompanyName(@Nullable String name);
+
+ public abstract Builder setNickName(@Nullable String nickName);
+
+ public abstract Builder setMimeType(String mimeType);
+
+ public abstract Cp2Contact build();
+ }
+
+ public static Builder builder() {
+ return new AutoValue_Cp2Contact.Builder();
+ }
+
+ public static Cp2Contact fromCursor(Cursor cursor) {
+ return Cp2Contact.builder()
+ .setPhoneId(cursor.getLong(Projections.CONTACT_ID))
+ .setPhoneType(cursor.getInt(Projections.PHONE_TYPE))
+ .setPhoneLabel(cursor.getString(Projections.PHONE_LABEL))
+ .setPhoneNumber(cursor.getString(Projections.PHONE_NUMBER))
+ .setDisplayName(cursor.getString(Projections.DISPLAY_NAME))
+ .setPhotoId(cursor.getInt(Projections.PHOTO_ID))
+ .setPhotoUri(cursor.getString(Projections.PHOTO_URI))
+ .setLookupKey(cursor.getString(Projections.LOOKUP_KEY))
+ .setCarrierPresence(cursor.getInt(Projections.CARRIER_PRESENCE))
+ .setContactId(cursor.getInt(Projections.CONTACT_ID))
+ .setCompanyName(cursor.getString(Projections.COMPANY_NAME))
+ .setNickName(cursor.getString(Projections.NICKNAME))
+ .setMimeType(cursor.getString(Projections.MIME_TYPE))
+ .build();
+ }
+
+ public Object[] toCursorRow() {
+ Object[] row = new Object[Projections.DATA_PROJECTION.length];
+ row[Projections.ID] = phoneId();
+ row[Projections.PHONE_TYPE] = phoneType();
+ row[Projections.PHONE_LABEL] = phoneLabel();
+ row[Projections.PHONE_NUMBER] = phoneNumber();
+ row[Projections.DISPLAY_NAME] = displayName();
+ row[Projections.PHOTO_ID] = photoId();
+ row[Projections.PHOTO_URI] = photoUri();
+ row[Projections.LOOKUP_KEY] = lookupKey();
+ row[Projections.CARRIER_PRESENCE] = carrierPresence();
+ row[Projections.CONTACT_ID] = contactId();
+ row[Projections.COMPANY_NAME] = companyName();
+ row[Projections.NICKNAME] = nickName();
+ row[Projections.MIME_TYPE] = mimeType();
+ return row;
+ }
+
+ public abstract Builder toBuilder();
+}
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
index b162a5e52..c09396c72 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
@@ -96,7 +96,7 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
dialerContact = getDialerContact(context, cursor);
position = cursor.getPosition();
number = cursor.getString(Projections.PHONE_NUMBER);
- String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+ String name = cursor.getString(Projections.DISPLAY_NAME);
String label = getLabel(context.getResources(), cursor);
String secondaryInfo =
TextUtils.isEmpty(label)
@@ -111,12 +111,12 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
if (shouldShowPhoto(cursor)) {
nameOrNumberView.setVisibility(View.VISIBLE);
photo.setVisibility(View.VISIBLE);
- String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+ String photoUri = cursor.getString(Projections.PHOTO_URI);
ContactPhotoManager.getInstance(context)
.loadDialerThumbnailOrPhoto(
photo,
getContactUri(cursor),
- cursor.getLong(Projections.PHONE_PHOTO_ID),
+ cursor.getLong(Projections.PHOTO_ID),
photoUri == null ? null : Uri.parse(photoUri),
name,
LetterTileDrawable.TYPE_DEFAULT);
@@ -129,11 +129,11 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
// Show the contact photo next to only the first number if a contact has multiple numbers
private boolean shouldShowPhoto(SearchCursor cursor) {
int currentPosition = cursor.getPosition();
- String currentLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ String currentLookupKey = cursor.getString(Projections.LOOKUP_KEY);
cursor.moveToPosition(currentPosition - 1);
if (!cursor.isHeader() && !cursor.isBeforeFirst()) {
- String previousLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ String previousLookupKey = cursor.getString(Projections.LOOKUP_KEY);
cursor.moveToPosition(currentPosition);
return !currentLookupKey.equals(previousLookupKey);
}
@@ -142,8 +142,8 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
}
private static Uri getContactUri(Cursor cursor) {
- long contactId = cursor.getLong(Projections.PHONE_ID);
- String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ long contactId = cursor.getLong(Projections.ID);
+ String lookupKey = cursor.getString(Projections.LOOKUP_KEY);
return Contacts.getLookupUri(contactId, lookupKey);
}
@@ -188,7 +188,7 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
private static @CallToAction int getCallToAction(
Context context, SearchCursor cursor, String query) {
- int carrierPresence = cursor.getInt(Projections.PHONE_CARRIER_PRESENCE);
+ int carrierPresence = cursor.getInt(Projections.CARRIER_PRESENCE);
String number = cursor.getString(Projections.PHONE_NUMBER);
if ((carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) == 1) {
return CallToAction.VIDEO_CALL;
@@ -262,16 +262,15 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
private static DialerContact getDialerContact(Context context, Cursor cursor) {
DialerContact.Builder contact = DialerContact.newBuilder();
- String displayName = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+ String displayName = cursor.getString(Projections.DISPLAY_NAME);
String number = cursor.getString(Projections.PHONE_NUMBER);
Uri contactUri =
Contacts.getLookupUri(
- cursor.getLong(Projections.PHONE_CONTACT_ID),
- cursor.getString(Projections.PHONE_LOOKUP_KEY));
+ cursor.getLong(Projections.CONTACT_ID), cursor.getString(Projections.LOOKUP_KEY));
contact
.setNumber(number)
- .setPhotoId(cursor.getLong(Projections.PHONE_PHOTO_ID))
+ .setPhotoId(cursor.getLong(Projections.PHOTO_ID))
.setContactType(LetterTileDrawable.TYPE_DEFAULT)
.setNameOrNumber(displayName)
.setNumberLabel(
@@ -281,7 +280,7 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick
cursor.getString(Projections.PHONE_LABEL))
.toString());
- String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+ String photoUri = cursor.getString(Projections.PHOTO_URI);
if (photoUri != null) {
contact.setPhotoUri(photoUri);
}
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
index b7fc9b5c5..f1230c6d9 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
@@ -19,7 +19,10 @@ package com.android.dialer.searchfragment.cp2;
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Data;
import android.support.annotation.Nullable;
import com.android.dialer.searchfragment.common.Projections;
@@ -32,14 +35,27 @@ public final class SearchContactsCursorLoader extends CursorLoader {
public SearchContactsCursorLoader(Context context, @Nullable String query) {
super(
context,
- Phone.CONTENT_URI,
- Projections.PHONE_PROJECTION,
- null,
+ Data.CONTENT_URI,
+ Projections.DATA_PROJECTION,
+ whereStatement(),
null,
Phone.SORT_KEY_PRIMARY + " ASC");
this.query = query;
}
+ private static String whereStatement() {
+ return (Phone.NUMBER + " IS NOT NULL")
+ + " AND "
+ + Data.MIMETYPE
+ + " IN (\'"
+ + Phone.CONTENT_ITEM_TYPE
+ + "\', \'"
+ + Nickname.CONTENT_ITEM_TYPE
+ + "\', \'"
+ + Organization.CONTENT_ITEM_TYPE
+ + "\')";
+ }
+
@Override
public Cursor loadInBackground() {
// All contacts
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
index fa0782623..5d5188059 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
@@ -61,17 +61,17 @@ public final class NearbyPlaceViewHolder extends RecyclerView.ViewHolder
*/
public void bind(SearchCursor cursor, String query) {
number = cursor.getString(Projections.PHONE_NUMBER);
- String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+ String name = cursor.getString(Projections.DISPLAY_NAME);
String address = cursor.getString(Projections.PHONE_LABEL);
placeName.setText(QueryBoldingUtil.getNameWithQueryBolded(query, name));
placeAddress.setText(QueryBoldingUtil.getNameWithQueryBolded(query, address));
- String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+ String photoUri = cursor.getString(Projections.PHOTO_URI);
ContactPhotoManager.getInstance(context)
.loadDialerThumbnailOrPhoto(
photo,
getContactUri(cursor),
- cursor.getLong(Projections.PHONE_PHOTO_ID),
+ cursor.getLong(Projections.PHOTO_ID),
photoUri == null ? null : Uri.parse(photoUri),
name,
LetterTileDrawable.TYPE_BUSINESS);
@@ -81,7 +81,7 @@ public final class NearbyPlaceViewHolder extends RecyclerView.ViewHolder
// Since the lookup key for Nearby Places is actually a JSON representation of the information,
// we need to pass it in as an encoded fragment in our contact uri.
// It includes information like display name, photo uri, phone number, ect.
- String businessInfoJson = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ String businessInfoJson = cursor.getString(Projections.LOOKUP_KEY);
return Contacts.CONTENT_LOOKUP_URI
.buildUpon()
.appendPath(Constants.LOOKUP_URI_ENCODED)
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
index c8bb36a73..0d52c108e 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
@@ -41,7 +41,7 @@ public final class NearbyPlacesCursorLoader extends CursorLoader {
*/
public NearbyPlacesCursorLoader(
Context context, String query, @NonNull List<Integer> directoryIds) {
- super(context, getContentUri(context, query), Projections.PHONE_PROJECTION, null, null, null);
+ super(context, getContentUri(context, query), Projections.DATA_PROJECTION, null, null, null);
this.directoryId = getDirectoryId(directoryIds);
}
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
index df3eacc5b..8a02eb9b9 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
@@ -64,7 +64,7 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
*/
public void bind(SearchCursor cursor, String query) {
number = cursor.getString(Projections.PHONE_NUMBER);
- String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+ String name = cursor.getString(Projections.DISPLAY_NAME);
String label = getLabel(context.getResources(), cursor);
String secondaryInfo =
TextUtils.isEmpty(label)
@@ -78,12 +78,12 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
if (shouldShowPhoto(cursor)) {
nameView.setVisibility(View.VISIBLE);
photo.setVisibility(View.VISIBLE);
- String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+ String photoUri = cursor.getString(Projections.PHOTO_URI);
ContactPhotoManager.getInstance(context)
.loadDialerThumbnailOrPhoto(
photo,
getContactUri(cursor),
- cursor.getLong(Projections.PHONE_PHOTO_ID),
+ cursor.getLong(Projections.PHOTO_ID),
photoUri == null ? null : Uri.parse(photoUri),
name,
LetterTileDrawable.TYPE_DEFAULT);
@@ -96,11 +96,11 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
// Show the contact photo next to only the first number if a contact has multiple numbers
private boolean shouldShowPhoto(SearchCursor cursor) {
int currentPosition = cursor.getPosition();
- String currentLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ String currentLookupKey = cursor.getString(Projections.LOOKUP_KEY);
cursor.moveToPosition(currentPosition - 1);
if (!cursor.isHeader() && !cursor.isBeforeFirst()) {
- String previousLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ String previousLookupKey = cursor.getString(Projections.LOOKUP_KEY);
cursor.moveToPosition(currentPosition);
return !currentLookupKey.equals(previousLookupKey);
}
@@ -121,8 +121,8 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
}
private static Uri getContactUri(SearchCursor cursor) {
- long contactId = cursor.getLong(Projections.PHONE_ID);
- String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ long contactId = cursor.getLong(Projections.ID);
+ String lookupKey = cursor.getString(Projections.LOOKUP_KEY);
return Contacts.getLookupUri(contactId, lookupKey)
.buildUpon()
.appendQueryParameter(
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
index 37695be50..eb472732f 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
@@ -54,7 +54,7 @@ public final class RemoteContactsCursorLoader extends CursorLoader {
super(
context,
null,
- Projections.PHONE_PROJECTION,
+ Projections.DATA_PROJECTION,
IGNORE_NUMBER_TOO_LONG_CLAUSE,
null,
Phone.SORT_KEY_PRIMARY);
diff --git a/java/com/android/dialer/searchfragment/testing/TestCursorSchema.java b/java/com/android/dialer/searchfragment/testing/TestCursorSchema.java
new file mode 100644
index 000000000..375fdb50c
--- /dev/null
+++ b/java/com/android/dialer/searchfragment/testing/TestCursorSchema.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testing;
+
+import android.provider.ContactsContract.Data;
+
+/** Testing class containing cp2 cursor testing utilities. */
+public final class TestCursorSchema {
+
+ /**
+ * If new rows are added to {@link
+ * com.android.dialer.searchfragment.common.Projections#DATA_PROJECTION}, this schema should be
+ * updated.
+ */
+ // TODO(67909522): remove these extra columns and remove all references to "Phone."
+ public static final String[] SCHEMA =
+ new String[] {
+ Data._ID, // 0
+ "data2", // 1 Phone Type
+ "data3", // 2 Phone Label
+ "data1", // 3 Phone Number, Organization Company
+ Data.DISPLAY_NAME_PRIMARY, // 4
+ Data.PHOTO_ID, // 5
+ Data.PHOTO_THUMBNAIL_URI, // 6
+ Data.LOOKUP_KEY, // 7
+ Data.CARRIER_PRESENCE, // 8
+ Data.CONTACT_ID, // 9
+ Data.MIMETYPE, // 10
+ Data.SORT_KEY_PRIMARY, // 11
+ "company_name", // 12, no constant because Organization.COMPANY.equals("data1")
+ "nick_name" // 13, no constant because Nickname.NAME.equals("data1")
+ };
+}