summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/calllog/database/Coalescer.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/calllog/database/Coalescer.java')
-rw-r--r--java/com/android/dialer/calllog/database/Coalescer.java151
1 files changed, 142 insertions, 9 deletions
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.
*
* <p>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<Cursor> coalesce(
+ @NonNull Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) {
+ ListenableFuture<Cursor> 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.
+ *
+ * <p>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.
+ *
+ * <p>The provided cursor should be one for {@link CoalescedAnnotatedCallLog}.
+ */
+ public static long getTimestamp(Cursor coalescedAnnotatedCallLogCursor) {
+ return coalescedAnnotatedCallLogCursor.getLong(TIMESTAMP);
+ }
}