From 34daf89e6a8db5b329424db85a79362536ee8245 Mon Sep 17 00:00:00 2001 From: linyuh Date: Fri, 11 May 2018 15:58:37 -0700 Subject: Move coalescing logic out of AnnotatedCallLogContentProvider. Bug: 79232964 Test: CoalescerTest, AnnotatedCallLogCursorLoaderTest, and manual testing. PiperOrigin-RevId: 196321995 Change-Id: I016bf28e0c09cf4fee5bc5a9115335fb35b7f7e9 --- .../android/dialer/calllog/database/Coalescer.java | 151 +++++++++++++++++++-- 1 file changed, 142 insertions(+), 9 deletions(-) (limited to 'java/com/android/dialer/calllog/database/Coalescer.java') diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java index 8a16be2da..a889b9fe6 100644 --- a/java/com/android/dialer/calllog/database/Coalescer.java +++ b/java/com/android/dialer/calllog/database/Coalescer.java @@ -22,17 +22,25 @@ import android.provider.CallLog.Calls; import android.support.annotation.NonNull; 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.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; import com.google.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.List; @@ -41,32 +49,76 @@ import java.util.Objects; import javax.inject.Inject; /** - * Coalesces call log rows by combining some adjacent rows. + * 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 { + + // Indexes for CoalescedAnnotatedCallLog.ALL_COLUMNS + private static final int ID = 0; + private static final int TIMESTAMP = 1; + private static final int NUMBER = 2; + private static final int FORMATTED_NUMBER = 3; + private static final int NUMBER_PRESENTATION = 4; + private static final int IS_READ = 5; + private static final int NEW = 6; + private static final int GEOCODED_LOCATION = 7; + private static final int PHONE_ACCOUNT_COMPONENT_NAME = 8; + private static final int PHONE_ACCOUNT_ID = 9; + private static final int FEATURES = 10; + private static final int NUMBER_ATTRIBUTES = 11; + private static final int IS_VOICEMAIL_CALL = 12; + private static final int VOICEMAIL_CALL_TAG = 13; + private static final int CALL_TYPE = 14; + private static final int COALESCED_IDS = 15; + private final DataSources dataSources; + private final FutureTimer futureTimer; + private final ListeningExecutorService backgroundExecutorService; @Inject - Coalescer(DataSources dataSources) { + 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 {@link MatrixCursor} containing the {@link CoalescedAnnotatedCallLog} + * rows to display + */ + public ListenableFuture coalesce( + @NonNull Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { + ListenableFuture coalescingFuture = + backgroundExecutorService.submit( + () -> coalesceInternal(Assert.isNotNull(allAnnotatedCallLogRowsSortedByTimestampDesc))); + futureTimer.applyTiming(coalescingFuture, Metrics.NEW_CALL_LOG_COALESCE); + return coalescingFuture; } /** - * Reads the entire {@link AnnotatedCallLog} database into memory from the provided {@code - * allAnnotatedCallLog} parameter and then builds and returns a new {@link MatrixCursor} which is - * the result of combining adjacent rows which should be collapsed for display purposes. + * Reads the entire {@link AnnotatedCallLog} into memory from the provided cursor and then builds + * and returns a new {@link MatrixCursor} of {@link CoalescedAnnotatedCallLog}, which is the + * result of combining adjacent rows which should be collapsed for display purposes. * - * @param allAnnotatedCallLogRowsSortedByTimestampDesc all {@link AnnotatedCallLog} rows, sorted - * by timestamp descending + * @param allAnnotatedCallLogRowsSortedByTimestampDesc {@link AnnotatedCallLog} rows sorted in + * descending order of timestamp. * @return a new {@link MatrixCursor} containing the {@link CoalescedAnnotatedCallLog} rows to * display */ @WorkerThread @NonNull - Cursor coalesce(@NonNull Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { + private Cursor coalesceInternal(Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { Assert.isWorkerThread(); // Note: This method relies on rowsShouldBeCombined to determine which rows should be combined, @@ -77,7 +129,7 @@ public class Coalescer { MatrixCursor allCoalescedRowsMatrixCursor = new MatrixCursor( CoalescedAnnotatedCallLog.ALL_COLUMNS, - Assert.isNotNull(allAnnotatedCallLogRowsSortedByTimestampDesc).getCount()); + allAnnotatedCallLogRowsSortedByTimestampDesc.getCount()); if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) { return allCoalescedRowsMatrixCursor; @@ -252,4 +304,85 @@ public class Coalescer { rowBuilder.add(entry.getKey(), entry.getValue()); } } + + /** + * Creates a new {@link CoalescedRow} based on the data at the provided cursor's current position. + * + *

The provided cursor should be one for {@link CoalescedAnnotatedCallLog}. + */ + public static CoalescedRow toRow(Cursor coalescedAnnotatedCallLogCursor) { + DialerPhoneNumber number; + try { + number = DialerPhoneNumber.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(NUMBER)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException("Couldn't parse DialerPhoneNumber bytes"); + } + + CoalescedIds coalescedIds; + try { + coalescedIds = CoalescedIds.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(COALESCED_IDS)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException("Couldn't parse CoalescedIds bytes"); + } + + NumberAttributes numberAttributes; + try { + numberAttributes = + NumberAttributes.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(NUMBER_ATTRIBUTES)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException("Couldn't parse NumberAttributes bytes"); + } + + CoalescedRow.Builder coalescedRowBuilder = + CoalescedRow.newBuilder() + .setId(coalescedAnnotatedCallLogCursor.getLong(ID)) + .setTimestamp(coalescedAnnotatedCallLogCursor.getLong(TIMESTAMP)) + .setNumber(number) + .setNumberPresentation(coalescedAnnotatedCallLogCursor.getInt(NUMBER_PRESENTATION)) + .setIsRead(coalescedAnnotatedCallLogCursor.getInt(IS_READ) == 1) + .setIsNew(coalescedAnnotatedCallLogCursor.getInt(NEW) == 1) + .setFeatures(coalescedAnnotatedCallLogCursor.getInt(FEATURES)) + .setCallType(coalescedAnnotatedCallLogCursor.getInt(CALL_TYPE)) + .setNumberAttributes(numberAttributes) + .setIsVoicemailCall(coalescedAnnotatedCallLogCursor.getInt(IS_VOICEMAIL_CALL) == 1) + .setCoalescedIds(coalescedIds); + + String formattedNumber = coalescedAnnotatedCallLogCursor.getString(FORMATTED_NUMBER); + if (!TextUtils.isEmpty(formattedNumber)) { + coalescedRowBuilder.setFormattedNumber(formattedNumber); + } + + String geocodedLocation = coalescedAnnotatedCallLogCursor.getString(GEOCODED_LOCATION); + if (!TextUtils.isEmpty(geocodedLocation)) { + coalescedRowBuilder.setGeocodedLocation(geocodedLocation); + } + + String phoneAccountComponentName = + coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_COMPONENT_NAME); + if (!TextUtils.isEmpty(phoneAccountComponentName)) { + coalescedRowBuilder.setPhoneAccountComponentName( + coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_COMPONENT_NAME)); + } + + String phoneAccountId = coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_ID); + if (!TextUtils.isEmpty(phoneAccountId)) { + coalescedRowBuilder.setPhoneAccountId(phoneAccountId); + } + + String voicemailCallTag = coalescedAnnotatedCallLogCursor.getString(VOICEMAIL_CALL_TAG); + if (!TextUtils.isEmpty(voicemailCallTag)) { + coalescedRowBuilder.setVoicemailCallTag(voicemailCallTag); + } + + return coalescedRowBuilder.build(); + } + + /** + * Returns the timestamp at the provided cursor's current position. + * + *

The provided cursor should be one for {@link CoalescedAnnotatedCallLog}. + */ + public static long getTimestamp(Cursor coalescedAnnotatedCallLogCursor) { + return coalescedAnnotatedCallLogCursor.getLong(TIMESTAMP); + } } -- cgit v1.2.3