/* * 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.calllog.database; import android.content.ContentValues; import android.database.Cursor; import android.provider.CallLog.Calls; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.telecom.PhoneAccountHandle; import android.text.TextUtils; import com.android.dialer.CoalescedIds; import com.android.dialer.DialerPhoneNumber; import com.android.dialer.NumberAttributes; import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog; import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog; import com.android.dialer.calllog.datasources.CallLogDataSource; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.common.Assert; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.compat.telephony.TelephonyManagerCompat; import com.android.dialer.metrics.FutureTimer; import com.android.dialer.metrics.Metrics; import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil; import com.android.dialer.telecom.TelecomUtil; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.List; import java.util.Objects; import javax.inject.Inject; /** * Coalesces rows in {@link AnnotatedCallLog} by combining adjacent rows. * *
Applies the logic that determines which adjacent rows should be coalesced, and then delegates
* to each data source to determine how individual columns should be aggregated.
*/
public class Coalescer {
private final DataSources dataSources;
private final FutureTimer futureTimer;
private final ListeningExecutorService backgroundExecutorService;
@Inject
Coalescer(
@BackgroundExecutor ListeningExecutorService backgroundExecutorService,
DataSources dataSources,
FutureTimer futureTimer) {
this.backgroundExecutorService = backgroundExecutorService;
this.dataSources = dataSources;
this.futureTimer = futureTimer;
}
/**
* Given rows from {@link AnnotatedCallLog}, combine adjacent ones which should be collapsed for
* display purposes.
*
* @param allAnnotatedCallLogRowsSortedByTimestampDesc {@link AnnotatedCallLog} rows sorted in
* descending order of timestamp.
* @return a future of a list of {@link CoalescedRow coalesced rows}, which will be used to
* display call log entries.
*/
public ListenableFuture The provided {@link ContentValues} should be one for {@link CoalescedAnnotatedCallLog}.
*/
@VisibleForTesting
static CoalescedRow toCoalescedRowProto(ContentValues coalescedContentValues) {
DialerPhoneNumber number;
try {
number =
DialerPhoneNumber.parseFrom(
coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.NUMBER));
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Couldn't parse DialerPhoneNumber bytes");
}
CoalescedIds coalescedIds;
try {
coalescedIds =
CoalescedIds.parseFrom(
coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.COALESCED_IDS));
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Couldn't parse CoalescedIds bytes");
}
NumberAttributes numberAttributes;
try {
numberAttributes =
NumberAttributes.parseFrom(
coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.NUMBER_ATTRIBUTES));
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Couldn't parse NumberAttributes bytes");
}
CoalescedRow.Builder coalescedRowBuilder =
CoalescedRow.newBuilder()
.setId(coalescedContentValues.getAsLong(CoalescedAnnotatedCallLog._ID))
.setTimestamp(coalescedContentValues.getAsLong(CoalescedAnnotatedCallLog.TIMESTAMP))
.setNumber(number)
.setNumberPresentation(
coalescedContentValues.getAsInteger(CoalescedAnnotatedCallLog.NUMBER_PRESENTATION))
.setIsRead(coalescedContentValues.getAsInteger(CoalescedAnnotatedCallLog.IS_READ) == 1)
.setIsNew(coalescedContentValues.getAsInteger(CoalescedAnnotatedCallLog.NEW) == 1)
.setFeatures(coalescedContentValues.getAsInteger(CoalescedAnnotatedCallLog.FEATURES))
.setCallType(coalescedContentValues.getAsInteger(CoalescedAnnotatedCallLog.CALL_TYPE))
.setNumberAttributes(numberAttributes)
.setCoalescedIds(coalescedIds);
// TODO(linyuh): none of the boolean columns in the annotated call log should be null.
// This is a bug in VoicemailDataSource, but we should also fix the database constraints.
Integer isVoicemailCall =
coalescedContentValues.getAsInteger(CoalescedAnnotatedCallLog.IS_VOICEMAIL_CALL);
coalescedRowBuilder.setIsVoicemailCall(isVoicemailCall != null && isVoicemailCall == 1);
String formattedNumber =
coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.FORMATTED_NUMBER);
if (!TextUtils.isEmpty(formattedNumber)) {
coalescedRowBuilder.setFormattedNumber(formattedNumber);
}
String geocodedLocation =
coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.GEOCODED_LOCATION);
if (!TextUtils.isEmpty(geocodedLocation)) {
coalescedRowBuilder.setGeocodedLocation(geocodedLocation);
}
String phoneAccountComponentName =
coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME);
if (!TextUtils.isEmpty(phoneAccountComponentName)) {
coalescedRowBuilder.setPhoneAccountComponentName(
coalescedContentValues.getAsString(
CoalescedAnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME));
}
String phoneAccountId =
coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.PHONE_ACCOUNT_ID);
if (!TextUtils.isEmpty(phoneAccountId)) {
coalescedRowBuilder.setPhoneAccountId(phoneAccountId);
}
String voicemailCallTag =
coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.VOICEMAIL_CALL_TAG);
if (!TextUtils.isEmpty(voicemailCallTag)) {
coalescedRowBuilder.setVoicemailCallTag(voicemailCallTag);
}
return coalescedRowBuilder.build();
}
}