Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21f2f3e1 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Move coalescing logic out of AnnotatedCallLogContentProvider."

parents c6bd3f31 34daf89e
Loading
Loading
Loading
Loading
+1 −49
Original line number Diff line number Diff line
@@ -29,16 +29,12 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Build;
import android.provider.CallLog.Calls;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract;
import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.metrics.Metrics;
import com.android.dialer.metrics.MetricsComponent;
import java.util.ArrayList;
import java.util.Arrays;

@@ -50,7 +46,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
  private static final int ANNOTATED_CALL_LOG_TABLE_CODE = 1;
  private static final int ANNOTATED_CALL_LOG_TABLE_ID_CODE = 2;
  private static final int ANNOTATED_CALL_LOG_TABLE_DISTINCT_NUMBER_CODE = 3;
  private static final int COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE = 4;

  private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

@@ -65,10 +60,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
        AnnotatedCallLogContract.AUTHORITY,
        AnnotatedCallLog.DISTINCT_PHONE_NUMBERS,
        ANNOTATED_CALL_LOG_TABLE_DISTINCT_NUMBER_CODE);
    uriMatcher.addURI(
        AnnotatedCallLogContract.AUTHORITY,
        CoalescedAnnotatedCallLog.TABLE,
        COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE);
  }

  private AnnotatedCallLogDatabaseHelper databaseHelper;
@@ -142,33 +133,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
          LogUtil.w("AnnotatedCallLogContentProvider.query", "cursor was null");
        }
        return cursor;
      case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
        Assert.checkArgument(
            projection == CoalescedAnnotatedCallLog.ALL_COLUMNS,
            "only ALL_COLUMNS projection supported for coalesced call log");
        Assert.checkArgument(selection == null, "selection not supported for coalesced call log");
        Assert.checkArgument(
            selectionArgs == null, "selection args not supported for coalesced call log");
        Assert.checkArgument(sortOrder == null, "sort order not supported for coalesced call log");
        MetricsComponent.get(getContext()).metrics().startTimer(Metrics.NEW_CALL_LOG_COALESCE);
        try (Cursor allAnnotatedCallLogRows =
            queryBuilder.query(
                db,
                null,
                String.format("%s != ?", CoalescedAnnotatedCallLog.CALL_TYPE),
                new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)},
                null,
                null,
                AnnotatedCallLog.TIMESTAMP + " DESC")) {
          Cursor coalescedRows =
              CallLogDatabaseComponent.get(getContext())
                  .coalescer()
                  .coalesce(allAnnotatedCallLogRows);
          coalescedRows.setNotificationUri(
              getContext().getContentResolver(), CoalescedAnnotatedCallLog.CONTENT_URI);
          MetricsComponent.get(getContext()).metrics().stopTimer(Metrics.NEW_CALL_LOG_COALESCE);
          return coalescedRows;
        }
      default:
        throw new IllegalArgumentException("Unknown uri: " + uri);
    }
@@ -207,8 +171,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
        break;
      case ANNOTATED_CALL_LOG_TABLE_DISTINCT_NUMBER_CODE:
        throw new UnsupportedOperationException();
      case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
        throw new UnsupportedOperationException("coalesced call log does not support inserting");
      default:
        throw new IllegalArgumentException("Unknown uri: " + uri);
    }
@@ -245,8 +207,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
        break;
      case ANNOTATED_CALL_LOG_TABLE_DISTINCT_NUMBER_CODE:
        throw new UnsupportedOperationException();
      case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
        throw new UnsupportedOperationException("coalesced call log does not support deleting");
      default:
        throw new IllegalArgumentException("Unknown uri: " + uri);
    }
@@ -300,7 +260,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
        }
        return rows;
      case ANNOTATED_CALL_LOG_TABLE_DISTINCT_NUMBER_CODE:
      case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
        throw new UnsupportedOperationException();
      default:
        throw new IllegalArgumentException("Unknown uri: " + uri);
@@ -336,9 +295,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
            break;
          case ANNOTATED_CALL_LOG_TABLE_DISTINCT_NUMBER_CODE:
            throw new UnsupportedOperationException();
          case COALESCED_ANNOTATED_CALL_LOG_TABLE_CODE:
            throw new UnsupportedOperationException(
                "coalesced call log does not support applyBatch");
          default:
            throw new IllegalArgumentException("Unknown uri: " + operation.getUri());
        }
@@ -380,10 +336,6 @@ public class AnnotatedCallLogContentProvider extends ContentProvider {
  }

  private void notifyChange(Uri uri) {
    getContext().getContentResolver().notifyChange(uri, null);
    // Any time the annotated call log changes, we need to also notify observers of the
    // CoalescedAnnotatedCallLog, since that is just a massaged in-memory view of the real annotated
    // call log table.
    getContext().getContentResolver().notifyChange(CoalescedAnnotatedCallLog.CONTENT_URI, null);
    getContext().getContentResolver().notifyChange(uri, /* observer = */ null);
  }
}
+142 −9
Original line number Diff line number Diff line
@@ -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);
  }
}
+1 −11
Original line number Diff line number Diff line
@@ -225,7 +225,7 @@ public class AnnotatedCallLogContract {
    /**
     * An unique id to associate this call log row to a {@link android.telecom.Call}.
     *
     * <p>For pre-Q device, this is same as {@link TIMESTAMP}.
     * <p>For pre-Q device, this is same as {@link #TIMESTAMP}.
     *
     * <p>For Q+ device, this will be copied from {@link android.provider.CallLog.Calls}.
     *
@@ -244,16 +244,6 @@ public class AnnotatedCallLogContract {
   */
  public static final class CoalescedAnnotatedCallLog implements CommonColumns {

    public static final String TABLE = "CoalescedAnnotatedCallLog";

    /** The content URI for this table. */
    public static final Uri CONTENT_URI =
        Uri.withAppendedPath(AnnotatedCallLogContract.CONTENT_URI, TABLE);

    /** The MIME type of a {@link android.content.ContentProvider#getType(Uri)} single entry. */
    public static final String CONTENT_ITEM_TYPE =
        "vnd.android.cursor.item/coalesced_annotated_call_log";

    /**
     * IDs of rows in {@link AnnotatedCallLog} that are coalesced into one row in {@link
     * CoalescedAnnotatedCallLog}, encoded as a {@link com.android.dialer.CoalescedIds} proto.
+36 −0
Original line number Diff line number Diff line
/*
 * 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.ui;

import android.content.Context;
import android.provider.CallLog.Calls;
import android.support.v4.content.CursorLoader;
import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;

/** Cursor loader for {@link AnnotatedCallLog}. */
final class AnnotatedCallLogCursorLoader extends CursorLoader {

  AnnotatedCallLogCursorLoader(Context context) {
    super(
        context,
        AnnotatedCallLog.CONTENT_URI,
        /* projection = */ null,
        /* selection = */ AnnotatedCallLog.CALL_TYPE + " != ?",
        /* selectionArgs = */ new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)},
        /* sortOrder = */ AnnotatedCallLog.TIMESTAMP + " DESC");
  }
}
+0 −132
Original line number Diff line number Diff line
/*
 * 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.ui;

import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.CursorLoader;
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.CoalescedAnnotatedCallLog;
import com.android.dialer.calllog.model.CoalescedRow;
import com.google.protobuf.InvalidProtocolBufferException;

/** CursorLoader for the coalesced annotated call log. */
final class CoalescedAnnotatedCallLogCursorLoader extends CursorLoader {

  // 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;

  CoalescedAnnotatedCallLogCursorLoader(Context context) {
    // CoalescedAnnotatedCallLog requires that PROJECTION be ALL_COLUMNS and the following params be
    // null.
    super(
        context,
        CoalescedAnnotatedCallLog.CONTENT_URI,
        CoalescedAnnotatedCallLog.ALL_COLUMNS,
        null,
        null,
        null);
  }

  /** Creates a new {@link CoalescedRow} from the provided cursor using the current position. */
  static CoalescedRow toRow(Cursor cursor) {
    DialerPhoneNumber number;
    try {
      number = DialerPhoneNumber.parseFrom(cursor.getBlob(NUMBER));
    } catch (InvalidProtocolBufferException e) {
      throw new IllegalStateException("Couldn't parse DialerPhoneNumber bytes");
    }

    CoalescedIds coalescedIds;
    try {
      coalescedIds = CoalescedIds.parseFrom(cursor.getBlob(COALESCED_IDS));
    } catch (InvalidProtocolBufferException e) {
      throw new IllegalStateException("Couldn't parse CoalescedIds bytes");
    }

    NumberAttributes numberAttributes;
    try {
      numberAttributes = NumberAttributes.parseFrom(cursor.getBlob(NUMBER_ATTRIBUTES));
    } catch (InvalidProtocolBufferException e) {
      throw new IllegalStateException("Couldn't parse NumberAttributes bytes");
    }

    CoalescedRow.Builder coalescedRowBuilder =
        CoalescedRow.newBuilder()
            .setId(cursor.getLong(ID))
            .setTimestamp(cursor.getLong(TIMESTAMP))
            .setNumber(number)
            .setNumberPresentation(cursor.getInt(NUMBER_PRESENTATION))
            .setIsRead(cursor.getInt(IS_READ) == 1)
            .setIsNew(cursor.getInt(NEW) == 1)
            .setFeatures(cursor.getInt(FEATURES))
            .setCallType(cursor.getInt(CALL_TYPE))
            .setNumberAttributes(numberAttributes)
            .setIsVoicemailCall(cursor.getInt(IS_VOICEMAIL_CALL) == 1)
            .setCoalescedIds(coalescedIds);

    String formattedNumber = cursor.getString(FORMATTED_NUMBER);
    if (!TextUtils.isEmpty(formattedNumber)) {
      coalescedRowBuilder.setFormattedNumber(formattedNumber);
    }

    String geocodedLocation = cursor.getString(GEOCODED_LOCATION);
    if (!TextUtils.isEmpty(geocodedLocation)) {
      coalescedRowBuilder.setGeocodedLocation(geocodedLocation);
    }

    String phoneAccountComponentName = cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME);
    if (!TextUtils.isEmpty(phoneAccountComponentName)) {
      coalescedRowBuilder.setPhoneAccountComponentName(
          cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME));
    }

    String phoneAccountId = cursor.getString(PHONE_ACCOUNT_ID);
    if (!TextUtils.isEmpty(phoneAccountId)) {
      coalescedRowBuilder.setPhoneAccountId(phoneAccountId);
    }

    String voicemailCallTag = cursor.getString(VOICEMAIL_CALL_TAG);
    if (!TextUtils.isEmpty(voicemailCallTag)) {
      coalescedRowBuilder.setVoicemailCallTag(voicemailCallTag);
    }

    return coalescedRowBuilder.build();
  }

  static long getTimestamp(Cursor cursor) {
    return cursor.getLong(TIMESTAMP);
  }
}
Loading