summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/phonelookup
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2018-01-11 16:50:48 -0800
committerCopybara-Service <copybara-piper@google.com>2018-01-11 17:03:34 -0800
commit01aac5de58903555a089d16a58b9346d34d54e7b (patch)
treeb90e5f582dcab0d3ad6393f4ffbc7a4dd78e81dc /java/com/android/dialer/phonelookup
parent188b42fd10644373175fc204b48da98125004985 (diff)
Implement PhoneLookup for CP2 remote contacts
Bug: 71763594 Test: Cp2LocalPhoneLookupTest, Cp2RemotePhoneLookupTest PiperOrigin-RevId: 181681435 Change-Id: I2e091371b6705390adf4be63c78344f78bd19d6e
Diffstat (limited to 'java/com/android/dialer/phonelookup')
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookupModule.java5
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java104
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2Projections.java119
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java248
-rw-r--r--java/com/android/dialer/phonelookup/phone_lookup_info.proto49
5 files changed, 428 insertions, 97 deletions
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupModule.java b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
index 5e215bafe..e93ca0f77 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupModule.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
@@ -19,6 +19,7 @@ package com.android.dialer.phonelookup;
import com.android.dialer.phonelookup.blockednumber.DialerBlockedNumberPhoneLookup;
import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2LocalPhoneLookup;
+import com.android.dialer.phonelookup.cp2.Cp2RemotePhoneLookup;
import com.google.common.collect.ImmutableList;
import dagger.Module;
import dagger.Provides;
@@ -31,8 +32,10 @@ public abstract class PhoneLookupModule {
@SuppressWarnings({"unchecked", "rawtype"})
static ImmutableList<PhoneLookup> providePhoneLookupList(
Cp2LocalPhoneLookup cp2LocalPhoneLookup,
+ Cp2RemotePhoneLookup cp2RemotePhoneLookup,
DialerBlockedNumberPhoneLookup dialerBlockedNumberPhoneLookup) {
- return ImmutableList.of(cp2LocalPhoneLookup, dialerBlockedNumberPhoneLookup);
+ return ImmutableList.of(
+ cp2LocalPhoneLookup, cp2RemotePhoneLookup, dialerBlockedNumberPhoneLookup);
}
@Provides
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
index 727977f5c..8879fee06 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
@@ -66,45 +66,7 @@ import javax.inject.Inject;
public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
private static final String PREF_LAST_TIMESTAMP_PROCESSED =
- "cp2PhoneLookupLastTimestampProcessed";
-
- /** Projection for performing batch lookups based on E164 numbers using the PHONE table. */
- private static final String[] PHONE_PROJECTION =
- new String[] {
- Phone.DISPLAY_NAME_PRIMARY, // 0
- Phone.PHOTO_THUMBNAIL_URI, // 1
- Phone.PHOTO_ID, // 2
- Phone.TYPE, // 3
- Phone.LABEL, // 4
- Phone.NORMALIZED_NUMBER, // 5
- Phone.CONTACT_ID, // 6
- Phone.LOOKUP_KEY // 7
- };
-
- /**
- * Projection for performing individual lookups of non-E164 numbers using the PHONE_LOOKUP table.
- */
- private static final String[] PHONE_LOOKUP_PROJECTION =
- new String[] {
- ContactsContract.PhoneLookup.DISPLAY_NAME_PRIMARY, // 0
- ContactsContract.PhoneLookup.PHOTO_THUMBNAIL_URI, // 1
- ContactsContract.PhoneLookup.PHOTO_ID, // 2
- ContactsContract.PhoneLookup.TYPE, // 3
- ContactsContract.PhoneLookup.LABEL, // 4
- ContactsContract.PhoneLookup.NORMALIZED_NUMBER, // 5
- ContactsContract.PhoneLookup.CONTACT_ID, // 6
- ContactsContract.PhoneLookup.LOOKUP_KEY // 7
- };
-
- // The following indexes should match both PHONE_PROJECTION and PHONE_LOOKUP_PROJECTION above.
- private static final int CP2_INFO_NAME_INDEX = 0;
- private static final int CP2_INFO_PHOTO_URI_INDEX = 1;
- private static final int CP2_INFO_PHOTO_ID_INDEX = 2;
- private static final int CP2_INFO_TYPE_INDEX = 3;
- private static final int CP2_INFO_LABEL_INDEX = 4;
- private static final int CP2_INFO_NORMALIZED_NUMBER_INDEX = 5;
- private static final int CP2_INFO_CONTACT_ID_INDEX = 6;
- private static final int CP2_INFO_LOOKUP_KEY_INDEX = 7;
+ "cp2LocalPhoneLookupLastTimestampProcessed";
// We cannot efficiently process invalid numbers because batch queries cannot be constructed which
// accomplish the necessary loose matching. We'll attempt to process a limited number of them,
@@ -146,14 +108,15 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
// ensure consistency when the batch methods are used to update data.
try (Cursor cursor =
e164.isPresent()
- ? queryPhoneTableBasedOnE164(PHONE_PROJECTION, ImmutableSet.of(e164.get()))
- : queryPhoneLookup(PHONE_LOOKUP_PROJECTION, rawNumber)) {
+ ? queryPhoneTableBasedOnE164(
+ Cp2Projections.getProjectionForPhoneTable(), ImmutableSet.of(e164.get()))
+ : queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), rawNumber)) {
if (cursor == null) {
LogUtil.w("Cp2LocalPhoneLookup.lookupInternal", "null cursor");
return Cp2Info.getDefaultInstance();
}
while (cursor.moveToNext()) {
- cp2ContactInfos.add(buildCp2ContactInfoFromPhoneCursor(appContext, cursor));
+ cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
}
}
return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
@@ -174,13 +137,14 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
return Cp2Info.getDefaultInstance();
}
Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
- try (Cursor cursor = queryPhoneLookup(PHONE_LOOKUP_PROJECTION, rawNumber)) {
+ try (Cursor cursor =
+ queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), rawNumber)) {
if (cursor == null) {
LogUtil.w("Cp2LocalPhoneLookup.lookup", "null cursor");
return Cp2Info.getDefaultInstance();
}
while (cursor.moveToNext()) {
- cp2ContactInfos.add(buildCp2ContactInfoFromPhoneCursor(appContext, cursor));
+ cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
}
}
return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
@@ -767,18 +731,21 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
if (e164Numbers.isEmpty()) {
return cp2ContactInfosByNumber;
}
- try (Cursor cursor = queryPhoneTableBasedOnE164(PHONE_PROJECTION, e164Numbers)) {
+ try (Cursor cursor =
+ queryPhoneTableBasedOnE164(
+ Cp2Projections.getProjectionForPhoneTable(), e164Numbers)) {
if (cursor == null) {
LogUtil.w("Cp2LocalPhoneLookup.batchQueryForValidNumbers", "null cursor");
} else {
while (cursor.moveToNext()) {
- String e164Number = cursor.getString(CP2_INFO_NORMALIZED_NUMBER_INDEX);
+ String e164Number = Cp2Projections.getNormalizedNumberFromCursor(cursor);
Set<Cp2ContactInfo> cp2ContactInfos = cp2ContactInfosByNumber.get(e164Number);
if (cp2ContactInfos == null) {
cp2ContactInfos = new ArraySet<>();
cp2ContactInfosByNumber.put(e164Number, cp2ContactInfos);
}
- cp2ContactInfos.add(buildCp2ContactInfoFromPhoneCursor(appContext, cursor));
+ cp2ContactInfos.add(
+ Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
}
}
}
@@ -794,12 +761,14 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
if (invalidNumber.isEmpty()) {
return cp2ContactInfos;
}
- try (Cursor cursor = queryPhoneLookup(PHONE_LOOKUP_PROJECTION, invalidNumber)) {
+ try (Cursor cursor =
+ queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), invalidNumber)) {
if (cursor == null) {
LogUtil.w("Cp2LocalPhoneLookup.individualQueryForInvalidNumber", "null cursor");
} else {
while (cursor.moveToNext()) {
- cp2ContactInfos.add(buildCp2ContactInfoFromPhoneCursor(appContext, cursor));
+ cp2ContactInfos.add(
+ Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
}
}
}
@@ -843,43 +812,6 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
return appContext.getContentResolver().query(uri, projection, null, null, null);
}
- /**
- * @param cursor with projection {@link #PHONE_PROJECTION}.
- * @return new {@link Cp2ContactInfo} based on current row of {@code cursor}.
- */
- private static Cp2ContactInfo buildCp2ContactInfoFromPhoneCursor(
- Context appContext, Cursor cursor) {
- String displayName = cursor.getString(CP2_INFO_NAME_INDEX);
- String photoUri = cursor.getString(CP2_INFO_PHOTO_URI_INDEX);
- int photoId = cursor.getInt(CP2_INFO_PHOTO_ID_INDEX);
- int type = cursor.getInt(CP2_INFO_TYPE_INDEX);
- String label = cursor.getString(CP2_INFO_LABEL_INDEX);
- int contactId = cursor.getInt(CP2_INFO_CONTACT_ID_INDEX);
- String lookupKey = cursor.getString(CP2_INFO_LOOKUP_KEY_INDEX);
-
- Cp2ContactInfo.Builder infoBuilder = Cp2ContactInfo.newBuilder();
- if (!TextUtils.isEmpty(displayName)) {
- infoBuilder.setName(displayName);
- }
- if (!TextUtils.isEmpty(photoUri)) {
- infoBuilder.setPhotoUri(photoUri);
- }
- if (photoId > 0) {
- infoBuilder.setPhotoId(photoId);
- }
-
- // Phone.getTypeLabel returns "Custom" if given (0, null) which is not of any use. Just
- // omit setting the label if there's no information for it.
- if (type != 0 || !TextUtils.isEmpty(label)) {
- infoBuilder.setLabel(Phone.getTypeLabel(appContext.getResources(), type, label).toString());
- }
- infoBuilder.setContactId(contactId);
- if (!TextUtils.isEmpty(lookupKey)) {
- infoBuilder.setLookupUri(Contacts.getLookupUri(contactId, lookupKey).toString());
- }
- return infoBuilder.build();
- }
-
/** Returns set of DialerPhoneNumbers that were associated with now deleted contacts. */
private ListenableFuture<Set<DialerPhoneNumber>> getDeletedPhoneNumbers(
ImmutableMap<DialerPhoneNumber, Cp2Info> existingInfoMap, long lastModified) {
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java b/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java
new file mode 100644
index 000000000..e3929990e
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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.phonelookup.cp2;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.text.TextUtils;
+import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo;
+
+/**
+ * A class providing projection-related functionality for {@link
+ * com.android.dialer.phonelookup.PhoneLookup} implementations for ContactsProvider2 (CP2).
+ */
+final class Cp2Projections {
+
+ // Projection for performing lookups using the PHONE table
+ private static final String[] PHONE_PROJECTION =
+ new String[] {
+ Phone.DISPLAY_NAME_PRIMARY, // 0
+ Phone.PHOTO_THUMBNAIL_URI, // 1
+ Phone.PHOTO_ID, // 2
+ Phone.TYPE, // 3
+ Phone.LABEL, // 4
+ Phone.NORMALIZED_NUMBER, // 5
+ Phone.CONTACT_ID, // 6
+ Phone.LOOKUP_KEY // 7
+ };
+
+ // Projection for performing lookups using the PHONE_LOOKUP table
+ private static final String[] PHONE_LOOKUP_PROJECTION =
+ new String[] {
+ PhoneLookup.DISPLAY_NAME_PRIMARY, // 0
+ PhoneLookup.PHOTO_THUMBNAIL_URI, // 1
+ PhoneLookup.PHOTO_ID, // 2
+ PhoneLookup.TYPE, // 3
+ PhoneLookup.LABEL, // 4
+ PhoneLookup.NORMALIZED_NUMBER, // 5
+ PhoneLookup.CONTACT_ID, // 6
+ PhoneLookup.LOOKUP_KEY // 7
+ };
+
+ // The following indexes should match both PHONE_PROJECTION and PHONE_LOOKUP_PROJECTION above.
+ private static final int CP2_INFO_NAME_INDEX = 0;
+ private static final int CP2_INFO_PHOTO_URI_INDEX = 1;
+ private static final int CP2_INFO_PHOTO_ID_INDEX = 2;
+ private static final int CP2_INFO_TYPE_INDEX = 3;
+ private static final int CP2_INFO_LABEL_INDEX = 4;
+ private static final int CP2_INFO_NORMALIZED_NUMBER_INDEX = 5;
+ private static final int CP2_INFO_CONTACT_ID_INDEX = 6;
+ private static final int CP2_INFO_LOOKUP_KEY_INDEX = 7;
+
+ private Cp2Projections() {}
+
+ static String[] getProjectionForPhoneTable() {
+ return PHONE_PROJECTION;
+ }
+
+ static String[] getProjectionForPhoneLookupTable() {
+ return PHONE_LOOKUP_PROJECTION;
+ }
+
+ /**
+ * Builds a {@link Cp2ContactInfo} based on the current row of {@code cursor}, of which the
+ * projection is either {@link #PHONE_PROJECTION} or {@link #PHONE_LOOKUP_PROJECTION}.
+ */
+ static Cp2ContactInfo buildCp2ContactInfoFromCursor(Context appContext, Cursor cursor) {
+ String displayName = cursor.getString(CP2_INFO_NAME_INDEX);
+ String photoUri = cursor.getString(CP2_INFO_PHOTO_URI_INDEX);
+ int photoId = cursor.getInt(CP2_INFO_PHOTO_ID_INDEX);
+ int type = cursor.getInt(CP2_INFO_TYPE_INDEX);
+ String label = cursor.getString(CP2_INFO_LABEL_INDEX);
+ int contactId = cursor.getInt(CP2_INFO_CONTACT_ID_INDEX);
+ String lookupKey = cursor.getString(CP2_INFO_LOOKUP_KEY_INDEX);
+
+ Cp2ContactInfo.Builder infoBuilder = Cp2ContactInfo.newBuilder();
+ if (!TextUtils.isEmpty(displayName)) {
+ infoBuilder.setName(displayName);
+ }
+ if (!TextUtils.isEmpty(photoUri)) {
+ infoBuilder.setPhotoUri(photoUri);
+ }
+ if (photoId > 0) {
+ infoBuilder.setPhotoId(photoId);
+ }
+
+ // Phone.getTypeLabel returns "Custom" if given (0, null) which is not of any use. Just
+ // omit setting the label if there's no information for it.
+ if (type != 0 || !TextUtils.isEmpty(label)) {
+ infoBuilder.setLabel(Phone.getTypeLabel(appContext.getResources(), type, label).toString());
+ }
+ infoBuilder.setContactId(contactId);
+ if (!TextUtils.isEmpty(lookupKey)) {
+ infoBuilder.setLookupUri(Contacts.getLookupUri(contactId, lookupKey).toString());
+ }
+ return infoBuilder.build();
+ }
+
+ /** Returns the normalized number in the current row of {@code cursor}. */
+ static String getNormalizedNumberFromCursor(Cursor cursor) {
+ return cursor.getString(CP2_INFO_NORMALIZED_NUMBER_INDEX);
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
new file mode 100644
index 000000000..6a4682958
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2018 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.phonelookup.cp2;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Directory;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.Call;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.telecom.TelecomCallUtil;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Inject;
+
+/** PhoneLookup implementation for remote contacts. */
+public final class Cp2RemotePhoneLookup implements PhoneLookup<Cp2Info> {
+
+ private final Context appContext;
+ private final ListeningExecutorService backgroundExecutorService;
+ private final ListeningExecutorService lightweightExecutorService;
+
+ @Inject
+ Cp2RemotePhoneLookup(
+ @ApplicationContext Context appContext,
+ @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
+ @LightweightExecutor ListeningExecutorService lightweightExecutorService) {
+ this.appContext = appContext;
+ this.backgroundExecutorService = backgroundExecutorService;
+ this.lightweightExecutorService = lightweightExecutorService;
+ }
+
+ @Override
+ public ListenableFuture<Cp2Info> lookup(Call call) {
+ String number = TelecomCallUtil.getNumber(call);
+ if (number == null) {
+ return Futures.immediateFuture(Cp2Info.getDefaultInstance());
+ }
+
+ return Futures.transformAsync(
+ queryCp2ForRemoteDirectoryIds(),
+ remoteDirectoryIds -> queryCp2ForRemoteContact(number, remoteDirectoryIds),
+ lightweightExecutorService);
+ }
+
+ private ListenableFuture<List<Long>> queryCp2ForRemoteDirectoryIds() {
+ return backgroundExecutorService.submit(
+ () -> {
+ List<Long> remoteDirectoryIds = new ArrayList<>();
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ getContentUriForDirectoryIds(),
+ /* projection = */ new String[] {ContactsContract.Directory._ID},
+ /* selection = */ null,
+ /* selectionArgs = */ null,
+ /* sortOrder = */ ContactsContract.Directory._ID)) {
+ if (cursor == null) {
+ LogUtil.e("Cp2RemotePhoneLookup.queryCp2ForDirectoryIds", "null cursor");
+ return remoteDirectoryIds;
+ }
+
+ if (!cursor.moveToFirst()) {
+ LogUtil.i("Cp2RemotePhoneLookup.queryCp2ForDirectoryIds", "empty cursor");
+ return remoteDirectoryIds;
+ }
+
+ int idColumnIndex = cursor.getColumnIndexOrThrow(ContactsContract.Directory._ID);
+ do {
+ long directoryId = cursor.getLong(idColumnIndex);
+
+ // Note that IDs of non-remote directories will be included in the result, such as
+ // android.provider.ContactsContract.Directory.DEFAULT (the default directory that
+ // represents locally stored contacts).
+ if (isRemoteDirectory(directoryId)) {
+ remoteDirectoryIds.add(cursor.getLong(idColumnIndex));
+ }
+ } while (cursor.moveToNext());
+ return remoteDirectoryIds;
+ }
+ });
+ }
+
+ private ListenableFuture<Cp2Info> queryCp2ForRemoteContact(
+ String number, List<Long> remoteDirectoryIds) {
+ if (remoteDirectoryIds.isEmpty()) {
+ return Futures.immediateFuture(Cp2Info.getDefaultInstance());
+ }
+
+ List<ListenableFuture<Cp2Info>> cp2InfoFutures = new ArrayList<>();
+ for (long remoteDirectoryId : remoteDirectoryIds) {
+ cp2InfoFutures.add(queryCp2ForRemoteContact(number, remoteDirectoryId));
+ }
+
+ return Futures.transform(
+ Futures.allAsList(cp2InfoFutures),
+ cp2InfoList -> {
+ Cp2Info.Builder cp2InfoBuilder = Cp2Info.newBuilder();
+ for (Cp2Info cp2Info : cp2InfoList) {
+ cp2InfoBuilder.addAllCp2ContactInfo(cp2Info.getCp2ContactInfoList());
+ }
+ return cp2InfoBuilder.build();
+ },
+ lightweightExecutorService);
+ }
+
+ private ListenableFuture<Cp2Info> queryCp2ForRemoteContact(
+ String number, long remoteDirectoryId) {
+ return backgroundExecutorService.submit(
+ () -> {
+ Cp2Info.Builder cp2InfoBuilder = Cp2Info.newBuilder();
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ getContentUriForContacts(number, remoteDirectoryId),
+ Cp2Projections.getProjectionForPhoneLookupTable(),
+ /* selection = */ null,
+ /* selectionArgs = */ null,
+ /* sortOrder = */ null)) {
+ if (cursor == null) {
+ LogUtil.e(
+ "Cp2RemotePhoneLookup.queryCp2ForRemoteContact",
+ "null cursor returned when querying directory %d",
+ remoteDirectoryId);
+ return cp2InfoBuilder.build();
+ }
+
+ if (!cursor.moveToFirst()) {
+ LogUtil.i(
+ "Cp2RemotePhoneLookup.queryCp2ForRemoteContact",
+ "empty cursor returned when querying directory %d",
+ remoteDirectoryId);
+ return cp2InfoBuilder.build();
+ }
+
+ do {
+ cp2InfoBuilder.addCp2ContactInfo(
+ Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
+ } while (cursor.moveToNext());
+ }
+
+ return cp2InfoBuilder.build();
+ });
+ }
+
+ @VisibleForTesting
+ static Uri getContentUriForDirectoryIds() {
+ return VERSION.SDK_INT >= VERSION_CODES.N
+ ? ContactsContract.Directory.ENTERPRISE_CONTENT_URI
+ : ContactsContract.Directory.CONTENT_URI;
+ }
+
+ @VisibleForTesting
+ static Uri getContentUriForContacts(String number, long directoryId) {
+ Uri baseUri =
+ VERSION.SDK_INT >= VERSION_CODES.N
+ ? ContactsContract.PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI
+ : ContactsContract.PhoneLookup.CONTENT_FILTER_URI;
+
+ Uri.Builder builder =
+ baseUri
+ .buildUpon()
+ .appendPath(number)
+ .appendQueryParameter(
+ ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
+ String.valueOf(PhoneNumberHelper.isUriNumber(number)))
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId));
+
+ return builder.build();
+ }
+
+ private static boolean isRemoteDirectory(long directoryId) {
+ return VERSION.SDK_INT >= VERSION_CODES.N
+ ? Directory.isRemoteDirectoryId(directoryId)
+ : (directoryId != Directory.DEFAULT
+ && directoryId != Directory.LOCAL_INVISIBLE
+ // Directory.ENTERPRISE_DEFAULT is the default work profile directory for locally stored
+ // contacts
+ && directoryId != Directory.ENTERPRISE_DEFAULT
+ && directoryId != Directory.ENTERPRISE_LOCAL_INVISIBLE);
+ }
+
+ @Override
+ public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
+ return Futures.immediateFuture(false);
+ }
+
+ @Override
+ public ListenableFuture<ImmutableMap<DialerPhoneNumber, Cp2Info>> getMostRecentInfo(
+ ImmutableMap<DialerPhoneNumber, Cp2Info> existingInfoMap) {
+ return Futures.immediateFuture(existingInfoMap);
+ }
+
+ @Override
+ public void setSubMessage(PhoneLookupInfo.Builder destination, Cp2Info subMessage) {
+ destination.setCp2RemoteInfo(subMessage);
+ }
+
+ @Override
+ public Cp2Info getSubMessage(PhoneLookupInfo phoneLookupInfo) {
+ return phoneLookupInfo.getCp2RemoteInfo();
+ }
+
+ @Override
+ public ListenableFuture<Void> onSuccessfulBulkUpdate() {
+ return Futures.immediateFuture(null);
+ }
+
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ // No content observer needed for remote contacts
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/phone_lookup_info.proto b/java/com/android/dialer/phonelookup/phone_lookup_info.proto
index f1497bdca..b5e73ccbe 100644
--- a/java/com/android/dialer/phonelookup/phone_lookup_info.proto
+++ b/java/com/android/dialer/phonelookup/phone_lookup_info.proto
@@ -13,29 +13,51 @@ package com.android.dialer.phonelookup;
// to an implementation of PhoneLookup. For example, field "cp2_local_info"
// corresponds to class Cp2LocalPhoneLookup, and class Cp2LocalPhoneLookup
// alone is responsible for populating it.
+// Next ID: 7
message PhoneLookupInfo {
- // Information about a PhoneNumber retrieved from CP2. Cp2LocalPhoneLookup is
- // responsible for populating the data in this message.
+ // Information about a PhoneNumber retrieved from CP2.
message Cp2Info {
- // Information about a single local contact.
+ // Information about a single contact, which can be a local contact or a
+ // remote one.
message Cp2ContactInfo {
- // android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY
+ // For a local contact:
+ // android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY
+ // For a remote contact:
+ // android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME_PRIMARY
optional string name = 1;
- // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_THUMBNAIL_URI
+ // For a local contact:
+ // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_THUMBNAIL_URI
+ // For a remote contact:
+ // android.provider.ContactsContract.PhoneLookup.PHOTO_THUMBNAIL_URI
optional string photo_uri = 2;
- // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_ID
+ // For a local contact:
+ // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_ID
+ // For a remote contact:
+ // android.provider.ContactsContract.PhoneLookup.PHOTO_ID
optional fixed64 photo_id = 3;
- // android.provider.ContactsContract.CommonDataKinds.Phone.LABEL
- // "Home", "Mobile", ect.
+ // For a local contact:
+ // android.provider.ContactsContract.CommonDataKinds.Phone.LABEL
+ // For a remote contact:
+ // android.provider.ContactsContract.PhoneLookup.LABEL
+ //
+ // The value can be "Home", "Mobile", ect.
optional string label = 4;
- // android.provider.ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ // For a local contact:
+ // android.provider.ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ // For a remote contact:
+ // android.provider.ContactsContract.PhoneLookup.CONTACT_ID
optional fixed64 contact_id = 5;
- // android.provider.ContactsContract.CONTENT_LOOKUP_URI
+ // For a local contact:
+ // constructed based on
+ // android.provider.ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY
+ // For a remote contact:
+ // constructed based on
+ // android.provider.ContactsContract.PhoneLookup.LOOKUP_KEY
optional string lookup_uri = 6;
}
// Repeated because one phone number can be associated with multiple CP2
@@ -50,8 +72,15 @@ message PhoneLookupInfo {
// log needs to query for the CP2 information at render time.
optional bool is_incomplete = 2;
}
+
+ // Information about a local contact retrieved via CP2.
+ // Cp2LocalPhoneLookup is responsible for populating this field.
optional Cp2Info cp2_local_info = 1;
+ // Information about a remote contact retrieved via CP2.
+ // Cp2RemotePhoneLookup is responsible for populating this field.
+ optional Cp2Info cp2_remote_info = 6;
+
// Message for APDL, a lookup for the proprietary Google dialer.
message ApdlInfo {
optional bool is_spam = 1;