From cd3d49843431913c2ff042d2294a8b5201877882 Mon Sep 17 00:00:00 2001 From: twyen Date: Mon, 19 Mar 2018 15:20:59 -0700 Subject: Implement VoicemailDataSource This data source determines if the call is to the voicemail inbox. isVoicemail() is removed from NumberAttributes and PhoneLookup. It is yet decided how in call UI should handle voicemail calls in the future. TAG_CHANGE_OK=proto not in prod yet. Please clear app data. TYPE_CHANGE_OK=above Bug: 70989587 Test: Unit tests PiperOrigin-RevId: 189650273 Change-Id: Iafebf1abb18c74301b62a72d1d04deecd6d78d29 --- java/com/android/dialer/calllog/CallLogModule.java | 6 +- .../database/AnnotatedCallLogDatabaseHelper.java | 2 + .../contract/AnnotatedCallLogContract.java | 21 ++++ .../database/contract/number_attributes.proto | 15 ++- .../datasources/voicemail/VoicemailDataSource.java | 111 +++++++++++++++++++++ .../android/dialer/calllog/model/CoalescedRow.java | 10 ++ .../ui/CoalescedAnnotatedCallLogCursorLoader.java | 8 +- .../dialer/calllog/ui/NewCallLogViewHolder.java | 3 +- .../android/dialer/calllog/ui/menu/Modules.java | 3 +- .../dialer/calllogutils/CallLogEntryText.java | 4 + .../calllogutils/NumberAttributesConverter.java | 2 - .../consolidator/PhoneLookupInfoConsolidator.java | 9 -- 12 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java diff --git a/java/com/android/dialer/calllog/CallLogModule.java b/java/com/android/dialer/calllog/CallLogModule.java index 6c85fd631..9dd9a794d 100644 --- a/java/com/android/dialer/calllog/CallLogModule.java +++ b/java/com/android/dialer/calllog/CallLogModule.java @@ -20,6 +20,7 @@ import com.android.dialer.calllog.datasources.CallLogDataSource; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.calllog.datasources.phonelookup.PhoneLookupDataSource; import com.android.dialer.calllog.datasources.systemcalllog.SystemCallLogDataSource; +import com.android.dialer.calllog.datasources.voicemail.VoicemailDataSource; import com.google.common.collect.ImmutableList; import dagger.Module; import dagger.Provides; @@ -31,10 +32,11 @@ public abstract class CallLogModule { @Provides static DataSources provideCallLogDataSources( SystemCallLogDataSource systemCallLogDataSource, - PhoneLookupDataSource phoneLookupDataSource) { + PhoneLookupDataSource phoneLookupDataSource, + VoicemailDataSource voicemailDataSource) { // System call log must be first, see getDataSourcesExcludingSystemCallLog below. ImmutableList allDataSources = - ImmutableList.of(systemCallLogDataSource, phoneLookupDataSource); + ImmutableList.of(systemCallLogDataSource, phoneLookupDataSource, voicemailDataSource); return new DataSources() { @Override public SystemCallLogDataSource getSystemCallLogDataSource() { diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java index c8387377b..3b67ff2d7 100644 --- a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java +++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java @@ -56,6 +56,8 @@ class AnnotatedCallLogDatabaseHelper extends SQLiteOpenHelper { + (AnnotatedCallLog.VOICEMAIL_URI + " text, ") + (AnnotatedCallLog.CALL_TYPE + " integer not null, ") + (AnnotatedCallLog.NUMBER_ATTRIBUTES + " blob, ") + + (AnnotatedCallLog.IS_VOICEMAIL_CALL + " integer, ") + + (AnnotatedCallLog.VOICEMAIL_CALL_TAG + " text, ") + (AnnotatedCallLog.TRANSCRIPTION_STATE + " integer") + ");"; diff --git a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java index c181d7573..b1cf6e495 100644 --- a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java +++ b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java @@ -132,6 +132,25 @@ public class AnnotatedCallLogContract { */ String NUMBER_ATTRIBUTES = "number_attributes"; + /** + * Whether the call is to the voicemail inbox. + * + *

TYPE: INTEGER (boolean) + * + * @see android.telecom.TelecomManager#isVoiceMailNumber(android.telecom.PhoneAccountHandle, + * String) + */ + String IS_VOICEMAIL_CALL = "is_voicemail_call"; + + /** + * The "name" of the voicemail inbox. This is provided by the SIM to show as the caller ID + * + *

TYPE: TEXT + * + * @see android.telephony.TelephonyManager#getVoiceMailAlphaTag() + */ + String VOICEMAIL_CALL_TAG = "voicemail_call_tag"; + /** * Copied from {@link android.provider.CallLog.Calls#TYPE}. * @@ -155,6 +174,8 @@ public class AnnotatedCallLogContract { PHONE_ACCOUNT_COLOR, FEATURES, NUMBER_ATTRIBUTES, + IS_VOICEMAIL_CALL, + VOICEMAIL_CALL_TAG, CALL_TYPE }; } diff --git a/java/com/android/dialer/calllog/database/contract/number_attributes.proto b/java/com/android/dialer/calllog/database/contract/number_attributes.proto index e24f393f7..2e93291ab 100644 --- a/java/com/android/dialer/calllog/database/contract/number_attributes.proto +++ b/java/com/android/dialer/calllog/database/contract/number_attributes.proto @@ -24,7 +24,7 @@ package com.android.dialer; import "java/com/android/dialer/logging/contact_source.proto"; // Information related to the phone number of the call. -// Next ID: 13 +// Next ID: 12 message NumberAttributes { // The name (which may be a person's name or business name, but not a number) // formatted exactly as it should appear to the user. If the user's locale or @@ -52,22 +52,19 @@ message NumberAttributes { // The number is a call to a business from nearby places lookup. optional bool is_business = 6; - // The number is a call to the voicemail inbox. - optional bool is_voicemail = 7; - // Can the number be reported as invalid through People API - optional bool can_report_as_invalid_number = 8; + optional bool can_report_as_invalid_number = 7; // True if the CP2 information is incomplete and needs to be queried at // display time. - optional bool is_cp2_info_incomplete = 9; + optional bool is_cp2_info_incomplete = 8; // Whether the number is blocked. - optional bool is_blocked = 10; + optional bool is_blocked = 9; // Whether the number is spam. - optional bool is_spam = 11; + optional bool is_spam = 10; // Source of the contact associated with the number. - optional com.android.dialer.logging.ContactSource.Type contact_source = 12; + optional com.android.dialer.logging.ContactSource.Type contact_source = 11; } \ No newline at end of file diff --git a/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java b/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java new file mode 100644 index 000000000..e8dc3e1eb --- /dev/null +++ b/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java @@ -0,0 +1,111 @@ +/* + * 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.calllog.datasources.voicemail; + +import android.content.ContentValues; +import android.content.Context; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import android.telephony.TelephonyManager; +import com.android.dialer.DialerPhoneNumber; +import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog; +import com.android.dialer.calllog.datasources.CallLogDataSource; +import com.android.dialer.calllog.datasources.CallLogMutations; +import com.android.dialer.calllog.datasources.util.RowCombiner; +import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; +import com.android.dialer.compat.telephony.TelephonyManagerCompat; +import com.android.dialer.telecom.TelecomUtil; +import com.android.dialer.util.PermissionsUtil; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.List; +import java.util.Map.Entry; +import javax.inject.Inject; + +/** Provide information for whether the call is a call to the voicemail inbox. */ +public class VoicemailDataSource implements CallLogDataSource { + + private final ListeningExecutorService backgroundExecutor; + + @Inject + VoicemailDataSource(@BackgroundExecutor ListeningExecutorService backgroundExecutor) { + this.backgroundExecutor = backgroundExecutor; + } + + @Override + public ListenableFuture isDirty(Context appContext) { + // The isVoicemail status is immutable and permanent. The call will always show as "Voicemail" + // even if the SIM is swapped. Dialing the row will result in some unexpected number after a SIM + // swap but this is deemed acceptable. + return Futures.immediateFuture(false); + } + + @Override + @SuppressWarnings("missingPermission") + public ListenableFuture fill(Context appContext, CallLogMutations mutations) { + if (!PermissionsUtil.hasReadPhoneStatePermissions(appContext)) { + return Futures.immediateFuture(null); + } + return backgroundExecutor.submit( + () -> { + TelecomManager telecomManager = appContext.getSystemService(TelecomManager.class); + for (Entry insert : mutations.getInserts().entrySet()) { + ContentValues values = insert.getValue(); + PhoneAccountHandle phoneAccountHandle = + TelecomUtil.composePhoneAccountHandle( + values.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME), + values.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_ID)); + DialerPhoneNumber dialerPhoneNumber; + try { + dialerPhoneNumber = + DialerPhoneNumber.parseFrom(values.getAsByteArray(AnnotatedCallLog.NUMBER)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException(e); + } + + if (telecomManager.isVoiceMailNumber( + phoneAccountHandle, dialerPhoneNumber.getNormalizedNumber())) { + values.put(AnnotatedCallLog.IS_VOICEMAIL_CALL, 1); + TelephonyManager telephonyManager = + TelephonyManagerCompat.getTelephonyManagerForPhoneAccountHandle( + appContext, phoneAccountHandle); + values.put( + AnnotatedCallLog.VOICEMAIL_CALL_TAG, telephonyManager.getVoiceMailAlphaTag()); + } + } + return null; + }); + } + + @Override + public ListenableFuture onSuccessfulFill(Context appContext) { + return Futures.immediateFuture(null); + } + + @Override + public ContentValues coalesce(List individualRowsSortedByTimestampDesc) { + return new RowCombiner(individualRowsSortedByTimestampDesc) + .useMostRecentInt(AnnotatedCallLog.IS_VOICEMAIL_CALL) + .useMostRecentString(AnnotatedCallLog.VOICEMAIL_CALL_TAG) + .combine(); + } + + @Override + public void registerContentObservers(Context appContext) {} +} diff --git a/java/com/android/dialer/calllog/model/CoalescedRow.java b/java/com/android/dialer/calllog/model/CoalescedRow.java index 2b6db97a2..737e736d6 100644 --- a/java/com/android/dialer/calllog/model/CoalescedRow.java +++ b/java/com/android/dialer/calllog/model/CoalescedRow.java @@ -39,6 +39,7 @@ public abstract class CoalescedRow { .setFeatures(0) .setCallType(0) .setNumberAttributes(NumberAttributes.getDefaultInstance()) + .setIsVoicemailCall(false) .setCoalescedIds(CoalescedIds.getDefaultInstance()); } @@ -80,6 +81,11 @@ public abstract class CoalescedRow { public abstract NumberAttributes numberAttributes(); + public abstract boolean isVoicemailCall(); + + @Nullable + public abstract String voicemailCallTag(); + public abstract CoalescedIds coalescedIds(); /** Builder for {@link CoalescedRow}. */ @@ -117,6 +123,10 @@ public abstract class CoalescedRow { public abstract Builder setNumberAttributes(NumberAttributes numberAttributes); + public abstract Builder setIsVoicemailCall(boolean isVoicemail); + + public abstract Builder setVoicemailCallTag(@Nullable String tag); + public abstract Builder setCoalescedIds(CoalescedIds coalescedIds); public abstract CoalescedRow build(); diff --git a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java index 0b1c6c990..a5cfd3f59 100644 --- a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java +++ b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java @@ -44,8 +44,10 @@ final class CoalescedAnnotatedCallLogCursorLoader extends CursorLoader { private static final int PHONE_ACCOUNT_COLOR = 11; private static final int FEATURES = 12; private static final int NUMBER_ATTRIBUTES = 13; - private static final int CALL_TYPE = 14; - private static final int COALESCED_IDS = 15; + private static final int IS_VOICEMAIL_CALL = 14; + private static final int VOICEMAIL_CALL_TAG = 15; + private static final int CALL_TYPE = 16; + private static final int COALESCED_IDS = 17; CoalescedAnnotatedCallLogCursorLoader(Context context) { // CoalescedAnnotatedCallLog requires that PROJECTION be ALL_COLUMNS and the following params be @@ -98,6 +100,8 @@ final class CoalescedAnnotatedCallLogCursorLoader extends CursorLoader { .setFeatures(cursor.getInt(FEATURES)) .setCallType(cursor.getInt(CALL_TYPE)) .setNumberAttributes(numberAttributes) + .setIsVoicemailCall(cursor.getInt(IS_VOICEMAIL_CALL) == 1) + .setVoicemailCallTag(cursor.getString(VOICEMAIL_CALL_TAG)) .setCoalescedIds(coalescedIds) .build(); } diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java index 4c2d124d7..74be21b0c 100644 --- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java +++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java @@ -153,7 +153,8 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder { private void setPhoto(CoalescedRow row) { PhotoInfo.Builder photoInfoBuilder = - NumberAttributesConverter.toPhotoInfoBuilder(row.numberAttributes()); + NumberAttributesConverter.toPhotoInfoBuilder(row.numberAttributes()) + .setIsVoicemail(row.isVoicemailCall()); if (!TextUtils.isEmpty(row.formattedNumber())) { photoInfoBuilder.setFormattedNumber(row.formattedNumber()); } diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java index fd5f6a348..69b42e304 100644 --- a/java/com/android/dialer/calllog/ui/menu/Modules.java +++ b/java/com/android/dialer/calllog/ui/menu/Modules.java @@ -176,7 +176,8 @@ final class Modules { private static PhotoInfo createPhotoInfoFromRow(CoalescedRow row) { PhotoInfo.Builder photoInfoBuilder = NumberAttributesConverter.toPhotoInfoBuilder(row.numberAttributes()) - .setIsVideo((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO); + .setIsVideo((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) + .setIsVoicemail(row.isVoicemailCall()); if (!TextUtils.isEmpty(row.formattedNumber())) { photoInfoBuilder.setFormattedNumber(row.formattedNumber()); } diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java index ab851cbbd..6f1047cb7 100644 --- a/java/com/android/dialer/calllogutils/CallLogEntryText.java +++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java @@ -48,6 +48,10 @@ public final class CallLogEntryText { return presentationName.get(); } + if (row.isVoicemailCall() && !TextUtils.isEmpty(row.voicemailCallTag())) { + return row.voicemailCallTag(); + } + // Otherwise prefer the name. if (!TextUtils.isEmpty(row.numberAttributes().getName())) { return row.numberAttributes().getName(); diff --git a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java index df6b680db..24567e027 100644 --- a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java +++ b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java @@ -34,7 +34,6 @@ public final class NumberAttributesConverter { .setLookupUri(numberAttributes.getLookupUri()) .setIsBusiness(numberAttributes.getIsBusiness()) .setIsSpam(numberAttributes.getIsSpam()) - .setIsVoicemail(numberAttributes.getIsVoicemail()) .setIsBlocked(numberAttributes.getIsBlocked()); } @@ -52,7 +51,6 @@ public final class NumberAttributesConverter { .setLookupUri(phoneLookupInfoConsolidator.getLookupUri()) .setNumberTypeLabel(phoneLookupInfoConsolidator.getNumberLabel()) .setIsBusiness(phoneLookupInfoConsolidator.isBusiness()) - .setIsVoicemail(phoneLookupInfoConsolidator.isVoicemail()) .setIsBlocked(phoneLookupInfoConsolidator.isBlocked()) .setIsSpam(phoneLookupInfoConsolidator.isSpam()) .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java index 6e867560d..f9ffd0404 100644 --- a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java +++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java @@ -265,15 +265,6 @@ public final class PhoneLookupInfoConsolidator { && 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; - } - /** * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method * returns whether the number is blocked. -- cgit v1.2.3