summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/phonelookup
diff options
context:
space:
mode:
authorlinyuh <linyuh@google.com>2018-01-17 10:39:26 -0800
committerCopybara-Service <copybara-piper@google.com>2018-01-17 11:25:43 -0800
commit31e83f3eb7e6ce06c7fb93b621f6086e7d5cd8c2 (patch)
tree9e7ee23475307895f1862b1c1fc5cbb9a5731a86 /java/com/android/dialer/phonelookup
parentbb41b26b5d8b438f6d163c56e31f0ff3801c7ed0 (diff)
Replace PhoneLookupSelector with PhoneLookupInfoConsolidator.
PhoneLookupInfoConsolidator is designed for the following two purposes. (1) Different sub-messages in a PhoneLookupInfo proto can contain information for the same purpose. For example, all of cp2_local_info, cp2_remote_info, and people_api_info have the information for a contact's name. PhoneLookupInfoConsolidator defines the rules that determine which sub-message should be used to display the name in the UI. This is the same as PhoneLookupSelector. (2) Avoid mixing info from different sub-messages when we are supposed to stick with only one sub-message. For example, if a PhoneLookupInfo proto has both cp2_local_info and cp2_remote_info but only cp2_remote_info has a photo URI, PhoneLookupInfoConsolidator should return an *empty* photo URI as cp2_local_info has higher priority and we should not use cp2_remote_info's photo URI to display the contact's photo. This is what PhoneLookupSelector is unable to do. Bug: 71763594 Test: PhoneLookupInfoConsolidatorTest PiperOrigin-RevId: 182236013 Change-Id: If19cdc1a9e076f3ebc8f9e2901f050b519e273f2
Diffstat (limited to 'java/com/android/dialer/phonelookup')
-rw-r--r--java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml (renamed from java/com/android/dialer/phonelookup/selector/AndroidManifest.xml)2
-rw-r--r--java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java306
-rw-r--r--java/com/android/dialer/phonelookup/consolidator/res/values/strings.xml (renamed from java/com/android/dialer/phonelookup/selector/res/values/strings.xml)0
-rw-r--r--java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java182
4 files changed, 307 insertions, 183 deletions
diff --git a/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml b/java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml
index 5d836c791..98e07e574 100644
--- a/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml
+++ b/java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml
@@ -14,5 +14,5 @@
~ limitations under the License
-->
<manifest
- package="com.android.dialer.phonelookup.selector">
+ package="com.android.dialer.phonelookup.consolidator">
</manifest>
diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
new file mode 100644
index 000000000..ccad3e7bc
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
@@ -0,0 +1,306 @@
+/*
+ * 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.consolidator;
+
+import android.content.Context;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
+import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo.InfoType;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Consolidates information from a {@link PhoneLookupInfo}.
+ *
+ * <p>For example, a single {@link PhoneLookupInfo} may contain different name information from many
+ * different {@link PhoneLookup} implementations. This class defines the rules for deciding which
+ * name should be selected for display to the user, by prioritizing the data from some {@link
+ * PhoneLookup PhoneLookups} over others.
+ */
+public final class PhoneLookupInfoConsolidator {
+
+ /** Integers representing {@link PhoneLookup} implementations that can provide a contact's name */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NameSource.NONE, NameSource.CP2_LOCAL, NameSource.CP2_REMOTE, NameSource.PEOPLE_API})
+ @interface NameSource {
+ int NONE = 0; // used when none of the other sources can provide the name
+ int CP2_LOCAL = 1;
+ int CP2_REMOTE = 2;
+ int PEOPLE_API = 3;
+ }
+
+ /**
+ * Sources that can provide information about a contact's name.
+ *
+ * <p>Each source is one of the values in NameSource, as defined above.
+ *
+ * <p>Sources are sorted in the order of priority. For example, if source CP2_LOCAL can provide
+ * the name, we will use that name in the UI and ignore all the other sources. If source CP2_LOCAL
+ * can't provide the name, source CP2_REMOTE will be consulted.
+ *
+ * <p>The reason for defining a name source is to avoid mixing info from different sub-messages in
+ * PhoneLookupInfo proto when we are supposed to stick with only one sub-message. For example, if
+ * a PhoneLookupInfo proto has both cp2_local_info and cp2_remote_info but only cp2_remote_info
+ * has a photo URI, PhoneLookupInfoConsolidator should provide an empty photo URI as CP2_LOCAL has
+ * higher priority and we should not use cp2_remote_info's photo URI to display the contact's
+ * photo.
+ */
+ private static final ImmutableList<Integer> NAME_SOURCES_IN_PRIORITY_ORDER =
+ ImmutableList.of(NameSource.CP2_LOCAL, NameSource.CP2_REMOTE, NameSource.PEOPLE_API);
+
+ private final Context appContext;
+ private final @NameSource int nameSource;
+ private final PhoneLookupInfo phoneLookupInfo;
+
+ @Nullable private final Cp2ContactInfo firstCp2LocalContact;
+ @Nullable private final Cp2ContactInfo firstCp2RemoteContact;
+
+ public PhoneLookupInfoConsolidator(Context appContext, PhoneLookupInfo phoneLookupInfo) {
+ this.appContext = appContext;
+ this.phoneLookupInfo = phoneLookupInfo;
+
+ this.firstCp2LocalContact = getFirstLocalContact();
+ this.firstCp2RemoteContact = getFirstRemoteContact();
+ this.nameSource = selectNameSource();
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns the name associated with that number.
+ *
+ * <p>Examples of this are a local contact's name or a business name received from caller ID.
+ *
+ * <p>If no name can be obtained from the {@link PhoneLookupInfo}, an empty string will be
+ * returned.
+ */
+ public String getName() {
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ return Assert.isNotNull(firstCp2LocalContact).getName();
+ case NameSource.CP2_REMOTE:
+ return Assert.isNotNull(firstCp2RemoteContact).getName();
+ case NameSource.PEOPLE_API:
+ return phoneLookupInfo.getPeopleApiInfo().getDisplayName();
+ case NameSource.NONE:
+ return "";
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns the photo URI associated with that number.
+ *
+ * <p>If no photo URI can be obtained from the {@link PhoneLookupInfo}, an empty string will be
+ * returned.
+ */
+ public String getPhotoUri() {
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ return Assert.isNotNull(firstCp2LocalContact).getPhotoUri();
+ case NameSource.CP2_REMOTE:
+ return Assert.isNotNull(firstCp2RemoteContact).getPhotoUri();
+ case NameSource.PEOPLE_API:
+ case NameSource.NONE:
+ return "";
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns the photo ID associated with that number, or 0 if there is none.
+ */
+ public long getPhotoId() {
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ return Math.max(Assert.isNotNull(firstCp2LocalContact).getPhotoId(), 0);
+ case NameSource.CP2_REMOTE:
+ return Math.max(Assert.isNotNull(firstCp2RemoteContact).getPhotoId(), 0);
+ case NameSource.PEOPLE_API:
+ case NameSource.NONE:
+ return 0;
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns the lookup URI associated with that number, or an empty string if no lookup URI can be
+ * obtained.
+ */
+ public String getLookupUri() {
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ return Assert.isNotNull(firstCp2LocalContact).getLookupUri();
+ case NameSource.CP2_REMOTE:
+ return Assert.isNotNull(firstCp2RemoteContact).getLookupUri();
+ case NameSource.PEOPLE_API:
+ case NameSource.NONE:
+ return "";
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns a localized string representing the number type such as "Home" or "Mobile", or a custom
+ * value set by the user.
+ *
+ * <p>If no label can be obtained from the {@link PhoneLookupInfo}, an empty string will be
+ * returned.
+ */
+ public String getNumberLabel() {
+ if (phoneLookupInfo.hasDialerBlockedNumberInfo()
+ && phoneLookupInfo
+ .getDialerBlockedNumberInfo()
+ .getBlockedState()
+ .equals(BlockedState.BLOCKED)) {
+ return appContext.getString(R.string.blocked_number_new_call_log_label);
+ }
+
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ return Assert.isNotNull(firstCp2LocalContact).getLabel();
+ case NameSource.CP2_REMOTE:
+ return Assert.isNotNull(firstCp2RemoteContact).getLabel();
+ case NameSource.PEOPLE_API:
+ case NameSource.NONE:
+ return "";
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns whether the number belongs to a business place.
+ */
+ public boolean isBusiness() {
+ return phoneLookupInfo.hasPeopleApiInfo()
+ && phoneLookupInfo.getPeopleApiInfo().getInfoType() == InfoType.NEARBY_BUSINESS;
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns whether the number is a voicemail number.
+ */
+ public boolean isVoicemail() {
+ // TODO(twyen): implement
+ return false;
+ }
+
+ /**
+ * Returns true if the {@link PhoneLookupInfo} passed to the constructor has incomplete CP2 local
+ * info.
+ */
+ public boolean isCp2LocalInfoIncomplete() {
+ return phoneLookupInfo.getCp2LocalInfo().getIsIncomplete();
+ }
+
+ /**
+ * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+ * returns whether the number can be reported as invalid.
+ *
+ * <p>As we currently report invalid numbers via the People API, only numbers from the People API
+ * can be reported as invalid.
+ */
+ public boolean canReportAsInvalidNumber() {
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ case NameSource.CP2_REMOTE:
+ return false;
+ case NameSource.PEOPLE_API:
+ PeopleApiInfo peopleApiInfo = phoneLookupInfo.getPeopleApiInfo();
+ return peopleApiInfo.getInfoType() != InfoType.UNKNOWN
+ && !peopleApiInfo.getPersonId().isEmpty();
+ case NameSource.NONE:
+ return false;
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ /**
+ * Arbitrarily select the first local CP2 contact. In the future, it may make sense to display
+ * contact information from all contacts with the same number (for example show the name as "Mom,
+ * Dad" or show a synthesized photo containing photos of both "Mom" and "Dad").
+ */
+ @Nullable
+ private Cp2ContactInfo getFirstLocalContact() {
+ return phoneLookupInfo.getCp2LocalInfo().getCp2ContactInfoCount() > 0
+ ? phoneLookupInfo.getCp2LocalInfo().getCp2ContactInfo(0)
+ : null;
+ }
+
+ /**
+ * Arbitrarily select the first remote CP2 contact. In the future, it may make sense to display
+ * contact information from all contacts with the same number (for example show the name as "Mom,
+ * Dad" or show a synthesized photo containing photos of both "Mom" and "Dad").
+ */
+ @Nullable
+ private Cp2ContactInfo getFirstRemoteContact() {
+ return phoneLookupInfo.getCp2RemoteInfo().getCp2ContactInfoCount() > 0
+ ? phoneLookupInfo.getCp2RemoteInfo().getCp2ContactInfo(0)
+ : null;
+ }
+
+ /** Select the {@link PhoneLookup} source providing a contact's name. */
+ private @NameSource int selectNameSource() {
+ for (int nameSource : NAME_SOURCES_IN_PRIORITY_ORDER) {
+ switch (nameSource) {
+ case NameSource.CP2_LOCAL:
+ if (firstCp2LocalContact != null && !firstCp2LocalContact.getName().isEmpty()) {
+ return NameSource.CP2_LOCAL;
+ }
+ break;
+ case NameSource.CP2_REMOTE:
+ if (firstCp2RemoteContact != null && !firstCp2RemoteContact.getName().isEmpty()) {
+ return NameSource.CP2_REMOTE;
+ }
+ break;
+ case NameSource.PEOPLE_API:
+ if (phoneLookupInfo.hasPeopleApiInfo()
+ && !phoneLookupInfo.getPeopleApiInfo().getDisplayName().isEmpty()) {
+ return NameSource.PEOPLE_API;
+ }
+ break;
+ default:
+ throw Assert.createUnsupportedOperationFailException(
+ String.format("Unsupported name source: %s", nameSource));
+ }
+ }
+
+ return NameSource.NONE;
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/selector/res/values/strings.xml b/java/com/android/dialer/phonelookup/consolidator/res/values/strings.xml
index 2080b3975..2080b3975 100644
--- a/java/com/android/dialer/phonelookup/selector/res/values/strings.xml
+++ b/java/com/android/dialer/phonelookup/consolidator/res/values/strings.xml
diff --git a/java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java b/java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java
deleted file mode 100644
index 8d082911c..000000000
--- a/java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.phonelookup.selector;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import com.android.dialer.inject.ApplicationContext;
-import com.android.dialer.phonelookup.PhoneLookup;
-import com.android.dialer.phonelookup.PhoneLookupInfo;
-import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
-import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo;
-import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo;
-import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo.InfoType;
-import javax.inject.Inject;
-
-/**
- * Prioritizes information from a {@link PhoneLookupInfo}.
- *
- * <p>For example, a single {@link PhoneLookupInfo} may contain different name information from many
- * different {@link PhoneLookup} sources. This class defines the rules for deciding which name
- * should be selected for display to the user, by prioritizing the data from some {@link PhoneLookup
- * PhoneLookups} over others.
- *
- * <p>Note that the logic in this class may be highly coupled with the logic in {@code
- * CompositePhoneLookup}, because {@code CompositePhoneLookup} may also include prioritization logic
- * for short-circuiting low-priority {@link PhoneLookup PhoneLookups}.
- */
-public final class PhoneLookupSelector {
-
- private final Context appContext;
-
- @Inject
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- public PhoneLookupSelector(@ApplicationContext Context appContext) {
- this.appContext = appContext;
- }
-
- /**
- * Select the name associated with this number. Examples of this are a local contact's name or a
- * business name received from caller ID.
- */
- @NonNull
- public String selectName(PhoneLookupInfo phoneLookupInfo) {
- Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
- if (firstLocalContact != null) {
- String name = firstLocalContact.getName();
- if (!name.isEmpty()) {
- return firstLocalContact.getName();
- }
- }
- if (phoneLookupInfo.hasPeopleApiInfo()) {
- return phoneLookupInfo.getPeopleApiInfo().getDisplayName();
- }
- return "";
- }
-
- /** Select the photo URI associated with this number. */
- @NonNull
- public String selectPhotoUri(PhoneLookupInfo phoneLookupInfo) {
- Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
- if (firstLocalContact != null) {
- String photoUri = firstLocalContact.getPhotoUri();
- if (!photoUri.isEmpty()) {
- return photoUri;
- }
- }
- return "";
- }
-
- /** Select the photo ID associated with this number, or 0 if there is none. */
- public long selectPhotoId(PhoneLookupInfo phoneLookupInfo) {
- Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
- if (firstLocalContact != null) {
- long photoId = firstLocalContact.getPhotoId();
- if (photoId > 0) {
- return photoId;
- }
- }
- return 0;
- }
-
- /** Select the lookup URI associated with this number. */
- @NonNull
- public String selectLookupUri(PhoneLookupInfo phoneLookupInfo) {
- Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
- if (firstLocalContact != null) {
- String lookupUri = firstLocalContact.getLookupUri();
- if (!lookupUri.isEmpty()) {
- return lookupUri;
- }
- }
- return "";
- }
-
- /**
- * A localized string representing the number type such as "Home" or "Mobile", or a custom value
- * set by the user.
- */
- @NonNull
- public String selectNumberLabel(PhoneLookupInfo phoneLookupInfo) {
- if (isBlocked(phoneLookupInfo)) {
- return appContext.getString(R.string.blocked_number_new_call_log_label);
- }
-
- Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
- if (firstLocalContact != null) {
- String label = firstLocalContact.getLabel();
- if (!label.isEmpty()) {
- return label;
- }
- }
- return "";
- }
-
- public boolean selectIsBusiness(PhoneLookupInfo phoneLookupInfo) {
- return phoneLookupInfo.hasPeopleApiInfo()
- && phoneLookupInfo.getPeopleApiInfo().getInfoType() == InfoType.NEARBY_BUSINESS;
- }
-
- public boolean selectIsVoicemail(PhoneLookupInfo unused) {
- // TODO(twyen): implement
- return false;
- }
-
- public boolean selectIsCp2InfoIncomplete(PhoneLookupInfo phoneLookupInfo) {
- return phoneLookupInfo.getCp2LocalInfo().getIsIncomplete();
- }
-
- /**
- * Returns true if the number associated with the given {@link PhoneLookupInfo} can be reported as
- * invalid.
- *
- * <p>As we currently report invalid numbers via the People API, only numbers from the People API
- * can be reported as invalid.
- */
- public boolean canReportAsInvalidNumber(PhoneLookupInfo phoneLookupInfo) {
- // The presence of Cp2ContactInfo means the number associated with the given PhoneLookupInfo
- // matches that of a Cp2 (local) contact, and PeopleApiInfo will not be used to display
- // information like name, photo, etc. We should not allow the user to report the number in this
- // case as the info displayed is not from the People API.
- if (phoneLookupInfo.getCp2LocalInfo().getCp2ContactInfoCount() > 0) {
- return false;
- }
-
- PeopleApiInfo peopleApiInfo = phoneLookupInfo.getPeopleApiInfo();
- return peopleApiInfo.getInfoType() != InfoType.UNKNOWN
- && !peopleApiInfo.getPersonId().isEmpty();
- }
-
- /**
- * Arbitrarily select the first contact. In the future, it may make sense to display contact
- * information from all contacts with the same number (for example show the name as "Mom, Dad" or
- * show a synthesized photo containing photos of both "Mom" and "Dad").
- */
- @Nullable
- private Cp2ContactInfo firstLocalContact(PhoneLookupInfo phoneLookupInfo) {
- if (phoneLookupInfo.getCp2LocalInfo().getCp2ContactInfoCount() > 0) {
- return phoneLookupInfo.getCp2LocalInfo().getCp2ContactInfo(0);
- }
- return null;
- }
-
- private static boolean isBlocked(PhoneLookupInfo info) {
- return info.hasDialerBlockedNumberInfo()
- && info.getDialerBlockedNumberInfo().getBlockedState().equals(BlockedState.BLOCKED);
- }
-}