From 31e83f3eb7e6ce06c7fb93b621f6086e7d5cd8c2 Mon Sep 17 00:00:00 2001 From: linyuh Date: Wed, 17 Jan 2018 10:39:26 -0800 Subject: 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 --- .../phonelookup/PhoneLookupDataSource.java | 30 +- .../dialer/calllog/ui/RealtimeRowProcessor.java | 33 ++- .../phonelookup/consolidator/AndroidManifest.xml | 18 ++ .../consolidator/PhoneLookupInfoConsolidator.java | 306 +++++++++++++++++++++ .../consolidator/res/values/strings.xml | 22 ++ .../phonelookup/selector/AndroidManifest.xml | 18 -- .../phonelookup/selector/PhoneLookupSelector.java | 182 ------------ .../phonelookup/selector/res/values/strings.xml | 22 -- packages.mk | 2 +- 9 files changed, 381 insertions(+), 252 deletions(-) create mode 100644 java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml create mode 100644 java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java create mode 100644 java/com/android/dialer/phonelookup/consolidator/res/values/strings.xml delete mode 100644 java/com/android/dialer/phonelookup/selector/AndroidManifest.xml delete mode 100644 java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java delete mode 100644 java/com/android/dialer/phonelookup/selector/res/values/strings.xml diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java index b73c169b9..1d4a35a6c 100644 --- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java +++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java @@ -37,11 +37,12 @@ import com.android.dialer.common.Assert; 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.consolidator.PhoneLookupInfoConsolidator; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; -import com.android.dialer.phonelookup.selector.PhoneLookupSelector; import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -67,8 +68,8 @@ import javax.inject.Inject; public final class PhoneLookupDataSource implements CallLogDataSource, PhoneLookup.ContentObserverCallbacks { + private final Context appContext; private final PhoneLookup phoneLookup; - private final PhoneLookupSelector phoneLookupSelector; private final ListeningExecutorService backgroundExecutorService; private final ListeningExecutorService lightweightExecutorService; @@ -94,11 +95,11 @@ public final class PhoneLookupDataSource @Inject PhoneLookupDataSource( PhoneLookup phoneLookup, - PhoneLookupSelector phoneLookupSelector, + @ApplicationContext Context appContext, @BackgroundExecutor ListeningExecutorService backgroundExecutorService, @LightweightExecutor ListeningExecutorService lightweightExecutorService) { this.phoneLookup = phoneLookup; - this.phoneLookupSelector = phoneLookupSelector; + this.appContext = appContext; this.backgroundExecutorService = backgroundExecutorService; this.lightweightExecutorService = lightweightExecutorService; } @@ -579,19 +580,20 @@ public final class PhoneLookupDataSource } private void updateContentValues(ContentValues contentValues, PhoneLookupInfo phoneLookupInfo) { + PhoneLookupInfoConsolidator phoneLookupInfoConsolidator = + new PhoneLookupInfoConsolidator(appContext, phoneLookupInfo); contentValues.put( AnnotatedCallLog.NUMBER_ATTRIBUTES, NumberAttributes.newBuilder() - .setName(phoneLookupSelector.selectName(phoneLookupInfo)) - .setPhotoUri(phoneLookupSelector.selectPhotoUri(phoneLookupInfo)) - .setPhotoId(phoneLookupSelector.selectPhotoId(phoneLookupInfo)) - .setLookupUri(phoneLookupSelector.selectLookupUri(phoneLookupInfo)) - .setNumberTypeLabel(phoneLookupSelector.selectNumberLabel(phoneLookupInfo)) - .setIsBusiness(phoneLookupSelector.selectIsBusiness(phoneLookupInfo)) - .setIsVoicemail(phoneLookupSelector.selectIsVoicemail(phoneLookupInfo)) - .setCanReportAsInvalidNumber( - phoneLookupSelector.canReportAsInvalidNumber(phoneLookupInfo)) - .setIsCp2InfoIncomplete(phoneLookupSelector.selectIsCp2InfoIncomplete(phoneLookupInfo)) + .setName(phoneLookupInfoConsolidator.getName()) + .setPhotoUri(phoneLookupInfoConsolidator.getPhotoUri()) + .setPhotoId(phoneLookupInfoConsolidator.getPhotoId()) + .setLookupUri(phoneLookupInfoConsolidator.getLookupUri()) + .setNumberTypeLabel(phoneLookupInfoConsolidator.getNumberLabel()) + .setIsBusiness(phoneLookupInfoConsolidator.isBusiness()) + .setIsVoicemail(phoneLookupInfoConsolidator.isVoicemail()) + .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) + .setIsCp2InfoIncomplete(phoneLookupInfoConsolidator.isCp2LocalInfoIncomplete()) .build() .toByteArray()); } diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java index 393482ab0..6e214edc4 100644 --- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java +++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java @@ -16,6 +16,7 @@ package com.android.dialer.calllog.ui; +import android.content.Context; import android.support.annotation.MainThread; import android.util.ArrayMap; import com.android.dialer.DialerPhoneNumber; @@ -23,10 +24,11 @@ import com.android.dialer.NumberAttributes; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.common.Assert; import com.android.dialer.common.concurrent.Annotations.Ui; +import com.android.dialer.inject.ApplicationContext; import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info; +import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator; import com.android.dialer.phonelookup.cp2.Cp2LocalPhoneLookup; -import com.android.dialer.phonelookup.selector.PhoneLookupSelector; import com.google.common.base.Optional; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -47,20 +49,20 @@ import javax.inject.Inject; */ public final class RealtimeRowProcessor { - private final ListeningExecutorService uiExecutor; + private final Context appContext; private final Cp2LocalPhoneLookup cp2LocalPhoneLookup; - private final PhoneLookupSelector phoneLookupSelector; + private final ListeningExecutorService uiExecutor; private final Map cache = new ArrayMap<>(); @Inject RealtimeRowProcessor( + @ApplicationContext Context appContext, @Ui ListeningExecutorService uiExecutor, - Cp2LocalPhoneLookup cp2LocalPhoneLookup, - PhoneLookupSelector phoneLookupSelector) { + Cp2LocalPhoneLookup cp2LocalPhoneLookup) { + this.appContext = appContext; this.uiExecutor = uiExecutor; this.cp2LocalPhoneLookup = cp2LocalPhoneLookup; - this.phoneLookupSelector = phoneLookupSelector; } /** @@ -104,19 +106,20 @@ public final class RealtimeRowProcessor { private CoalescedRow applyCp2LocalInfoToRow(Cp2Info cp2Info, CoalescedRow row) { PhoneLookupInfo phoneLookupInfo = PhoneLookupInfo.newBuilder().setCp2LocalInfo(cp2Info).build(); + PhoneLookupInfoConsolidator phoneLookupInfoConsolidator = + new PhoneLookupInfoConsolidator(appContext, phoneLookupInfo); // It is safe to overwrite any existing data because CP2 always has highest priority. return row.toBuilder() .setNumberAttributes( NumberAttributes.newBuilder() - .setName(phoneLookupSelector.selectName(phoneLookupInfo)) - .setPhotoUri(phoneLookupSelector.selectPhotoUri(phoneLookupInfo)) - .setPhotoId(phoneLookupSelector.selectPhotoId(phoneLookupInfo)) - .setLookupUri(phoneLookupSelector.selectLookupUri(phoneLookupInfo)) - .setNumberTypeLabel(phoneLookupSelector.selectNumberLabel(phoneLookupInfo)) - .setIsBusiness(phoneLookupSelector.selectIsBusiness(phoneLookupInfo)) - .setIsVoicemail(phoneLookupSelector.selectIsVoicemail(phoneLookupInfo)) - .setCanReportAsInvalidNumber( - phoneLookupSelector.canReportAsInvalidNumber(phoneLookupInfo)) + .setName(phoneLookupInfoConsolidator.getName()) + .setPhotoUri(phoneLookupInfoConsolidator.getPhotoUri()) + .setPhotoId(phoneLookupInfoConsolidator.getPhotoId()) + .setLookupUri(phoneLookupInfoConsolidator.getLookupUri()) + .setNumberTypeLabel(phoneLookupInfoConsolidator.getNumberLabel()) + .setIsBusiness(phoneLookupInfoConsolidator.isBusiness()) + .setIsVoicemail(phoneLookupInfoConsolidator.isVoicemail()) + .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) .build()) .build(); } diff --git a/java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml b/java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml new file mode 100644 index 000000000..98e07e574 --- /dev/null +++ b/java/com/android/dialer/phonelookup/consolidator/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + 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}. + * + *

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. + * + *

Each source is one of the values in NameSource, as defined above. + * + *

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. + * + *

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 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. + * + *

Examples of this are a local contact's name or a business name received from caller ID. + * + *

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. + * + *

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. + * + *

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. + * + *

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/consolidator/res/values/strings.xml b/java/com/android/dialer/phonelookup/consolidator/res/values/strings.xml new file mode 100644 index 000000000..2080b3975 --- /dev/null +++ b/java/com/android/dialer/phonelookup/consolidator/res/values/strings.xml @@ -0,0 +1,22 @@ + + + + + + Blocked + + diff --git a/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml b/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml deleted file mode 100644 index 5d836c791..000000000 --- a/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - 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}. - * - *

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. - * - *

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. - * - *

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); - } -} diff --git a/java/com/android/dialer/phonelookup/selector/res/values/strings.xml b/java/com/android/dialer/phonelookup/selector/res/values/strings.xml deleted file mode 100644 index 2080b3975..000000000 --- a/java/com/android/dialer/phonelookup/selector/res/values/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - Blocked - - diff --git a/packages.mk b/packages.mk index 2a445a319..d4b225aea 100644 --- a/packages.mk +++ b/packages.mk @@ -41,7 +41,7 @@ LOCAL_AAPT_FLAGS := \ com.android.dialer.notification \ com.android.dialer.oem \ com.android.dialer.phonelookup.database \ - com.android.dialer.phonelookup.selector \ + com.android.dialer.phonelookup.consolidator \ com.android.dialer.phonenumberutil \ com.android.dialer.postcall \ com.android.dialer.precall.impl \ -- cgit v1.2.3