Loading java/com/android/dialer/calllog/database/Coalescer.java +59 −81 Original line number Diff line number Diff line Loading @@ -17,9 +17,9 @@ package com.android.dialer.calllog.database; import android.content.ContentValues; import android.database.Cursor; import android.database.MatrixCursor; 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; Loading @@ -39,12 +39,12 @@ 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.Map; import java.util.Objects; import javax.inject.Inject; Loading @@ -56,24 +56,6 @@ import javax.inject.Inject; */ 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; Loading @@ -94,12 +76,12 @@ public class Coalescer { * * @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 * @return a future of a list of {@link CoalescedRow coalesced rows}, which will be used to * display call log entries. */ public ListenableFuture<Cursor> coalesce( public ListenableFuture<ImmutableList<CoalescedRow>> coalesce( @NonNull Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { ListenableFuture<Cursor> coalescingFuture = ListenableFuture<ImmutableList<CoalescedRow>> coalescingFuture = backgroundExecutorService.submit( () -> coalesceInternal(Assert.isNotNull(allAnnotatedCallLogRowsSortedByTimestampDesc))); futureTimer.applyTiming(coalescingFuture, Metrics.NEW_CALL_LOG_COALESCE); Loading @@ -108,34 +90,34 @@ public class Coalescer { /** * 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. * and returns a list of {@link CoalescedRow coalesced rows}, which is the result of combining * adjacent rows which should be collapsed for display purposes. * * @param allAnnotatedCallLogRowsSortedByTimestampDesc {@link AnnotatedCallLog} rows sorted in * descending order of timestamp. * @return a new {@link MatrixCursor} containing the {@link CoalescedAnnotatedCallLog} rows to * display * @return a list of {@link CoalescedRow coalesced rows}, which will be used to display call log * entries. */ @WorkerThread @NonNull private Cursor coalesceInternal(Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { private ImmutableList<CoalescedRow> coalesceInternal( Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { Assert.isWorkerThread(); if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) { return ImmutableList.of(); } // Note: This method relies on rowsShouldBeCombined to determine which rows should be combined, // but delegates to data sources to actually aggregate column values. DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil(); MatrixCursor allCoalescedRowsMatrixCursor = new MatrixCursor( CoalescedAnnotatedCallLog.ALL_COLUMNS, allAnnotatedCallLogRowsSortedByTimestampDesc.getCount()); if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) { return allCoalescedRowsMatrixCursor; } ImmutableList.Builder<CoalescedRow> coalescedRowListBuilder = new ImmutableList.Builder<>(); int coalescedRowId = 0; // TODO(a bug): Avoid using ContentValues as it doesn't make sense here. List<ContentValues> currentRowGroup = new ArrayList<>(); ContentValues firstRow = cursorRowToContentValues(allAnnotatedCallLogRowsSortedByTimestampDesc); Loading @@ -157,9 +139,10 @@ public class Coalescer { // Coalesce the group into a single row ContentValues coalescedRow = coalesceRowsForAllDataSources(currentRowGroup); coalescedRow.put(CoalescedAnnotatedCallLog._ID, coalescedRowId++); coalescedRow.put( CoalescedAnnotatedCallLog.COALESCED_IDS, getCoalescedIds(currentRowGroup).toByteArray()); addContentValuesToMatrixCursor(coalescedRow, allCoalescedRowsMatrixCursor, coalescedRowId++); coalescedRowListBuilder.add(toCoalescedRowProto(coalescedRow)); // Clear the current group after the rows are coalesced. currentRowGroup.clear(); Loading @@ -170,7 +153,7 @@ public class Coalescer { } } return allCoalescedRowsMatrixCursor; return coalescedRowListBuilder.build(); } private static ContentValues cursorRowToContentValues(Cursor cursor) { Loading Loading @@ -293,34 +276,26 @@ public class Coalescer { } /** * @param contentValues a {@link CoalescedAnnotatedCallLog} row * @param matrixCursor represents {@link CoalescedAnnotatedCallLog} */ private static void addContentValuesToMatrixCursor( ContentValues contentValues, MatrixCursor matrixCursor, int rowId) { MatrixCursor.RowBuilder rowBuilder = matrixCursor.newRow(); rowBuilder.add(CoalescedAnnotatedCallLog._ID, rowId); for (Map.Entry<String, Object> entry : contentValues.valueSet()) { rowBuilder.add(entry.getKey(), entry.getValue()); } } /** * Creates a new {@link CoalescedRow} based on the data at the provided cursor's current position. * Creates a new {@link CoalescedRow} proto based on the provided {@link ContentValues}. * * <p>The provided cursor should be one for {@link CoalescedAnnotatedCallLog}. * <p>The provided {@link ContentValues} should be one for {@link CoalescedAnnotatedCallLog}. */ public static CoalescedRow toRow(Cursor coalescedAnnotatedCallLogCursor) { @VisibleForTesting static CoalescedRow toCoalescedRowProto(ContentValues coalescedContentValues) { DialerPhoneNumber number; try { number = DialerPhoneNumber.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(NUMBER)); number = DialerPhoneNumber.parseFrom( coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.NUMBER)); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Couldn't parse DialerPhoneNumber bytes"); } CoalescedIds coalescedIds; try { coalescedIds = CoalescedIds.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(COALESCED_IDS)); coalescedIds = CoalescedIds.parseFrom( coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.COALESCED_IDS)); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Couldn't parse CoalescedIds bytes"); } Loading @@ -328,61 +303,64 @@ public class Coalescer { NumberAttributes numberAttributes; try { numberAttributes = NumberAttributes.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(NUMBER_ATTRIBUTES)); NumberAttributes.parseFrom( coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.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)) .setId(coalescedContentValues.getAsLong(CoalescedAnnotatedCallLog._ID)) .setTimestamp(coalescedContentValues.getAsLong(CoalescedAnnotatedCallLog.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)) .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) .setIsVoicemailCall(coalescedAnnotatedCallLogCursor.getInt(IS_VOICEMAIL_CALL) == 1) .setCoalescedIds(coalescedIds); String formattedNumber = coalescedAnnotatedCallLogCursor.getString(FORMATTED_NUMBER); // 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 = coalescedAnnotatedCallLogCursor.getString(GEOCODED_LOCATION); String geocodedLocation = coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.GEOCODED_LOCATION); if (!TextUtils.isEmpty(geocodedLocation)) { coalescedRowBuilder.setGeocodedLocation(geocodedLocation); } String phoneAccountComponentName = coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_COMPONENT_NAME); coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME); if (!TextUtils.isEmpty(phoneAccountComponentName)) { coalescedRowBuilder.setPhoneAccountComponentName( coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_COMPONENT_NAME)); coalescedContentValues.getAsString( CoalescedAnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME)); } String phoneAccountId = coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_ID); String phoneAccountId = coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.PHONE_ACCOUNT_ID); if (!TextUtils.isEmpty(phoneAccountId)) { coalescedRowBuilder.setPhoneAccountId(phoneAccountId); } String voicemailCallTag = coalescedAnnotatedCallLogCursor.getString(VOICEMAIL_CALL_TAG); String voicemailCallTag = coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.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); } } java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java +1 −43 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import android.os.Build; import android.provider.BaseColumns; import com.android.dialer.compat.android.provider.VoicemailCompat; import com.android.dialer.constants.Constants; import java.util.Arrays; /** Contract for the AnnotatedCallLog content provider. */ public class AnnotatedCallLogContract { Loading @@ -29,11 +28,7 @@ public class AnnotatedCallLogContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); /** * Columns shared by {@link AnnotatedCallLog} and {@link CoalescedAnnotatedCallLog}. * * <p>When adding columns be sure to update {@link #ALL_COMMON_COLUMNS}. */ /** Columns shared by {@link AnnotatedCallLog} and {@link CoalescedAnnotatedCallLog}. */ interface CommonColumns extends BaseColumns { /** Loading Loading @@ -143,25 +138,6 @@ public class AnnotatedCallLogContract { * <p>Type: INTEGER (int) */ String CALL_TYPE = "call_type"; String[] ALL_COMMON_COLUMNS = new String[] { _ID, TIMESTAMP, NUMBER, FORMATTED_NUMBER, NUMBER_PRESENTATION, IS_READ, NEW, GEOCODED_LOCATION, PHONE_ACCOUNT_COMPONENT_NAME, PHONE_ACCOUNT_ID, FEATURES, NUMBER_ATTRIBUTES, IS_VOICEMAIL_CALL, VOICEMAIL_CALL_TAG, CALL_TYPE }; } /** Loading Loading @@ -239,8 +215,6 @@ public class AnnotatedCallLogContract { * * <p>This is an in-memory view of the {@link AnnotatedCallLog} with some adjacent entries * collapsed. * * <p>When adding columns be sure to update {@link #COLUMNS_ONLY_IN_COALESCED_CALL_LOG}. */ public static final class CoalescedAnnotatedCallLog implements CommonColumns { Loading @@ -251,21 +225,5 @@ public class AnnotatedCallLogContract { * <p>Type: BLOB */ public static final String COALESCED_IDS = "coalesced_ids"; /** * Columns that are only in the {@link CoalescedAnnotatedCallLog} but not the {@link * AnnotatedCallLog}. */ private static final String[] COLUMNS_ONLY_IN_COALESCED_CALL_LOG = new String[] {COALESCED_IDS}; /** All columns in the {@link CoalescedAnnotatedCallLog}. */ public static final String[] ALL_COLUMNS = concat(ALL_COMMON_COLUMNS, COLUMNS_ONLY_IN_COALESCED_CALL_LOG); } private static String[] concat(String[] first, String[] second) { String[] result = Arrays.copyOf(first, first.length + second.length); System.arraycopy(second, 0, result, first.length, second.length); return result; } } java/com/android/dialer/calllog/ui/NewCallLogAdapter.java +23 −15 Original line number Diff line number Diff line Loading @@ -17,19 +17,19 @@ package com.android.dialer.calllog.ui; import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; import android.view.ViewGroup; import com.android.dialer.calllog.database.Coalescer; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllogutils.CallLogDates; import com.android.dialer.common.Assert; import com.android.dialer.logging.Logger; import com.android.dialer.promotion.Promotion; import com.android.dialer.time.Clock; import com.google.common.collect.ImmutableList; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -68,7 +68,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { private final PopCounts popCounts = new PopCounts(); @Nullable private final Promotion promotion; private Cursor cursor; private ImmutableList<CoalescedRow> coalescedRows; /** Position of the promotion card. Null when it should not be displayed. */ @Nullable private Integer promotionCardPosition; Loading @@ -82,9 +82,13 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { /** Position of the "Older" header. Null when it should not be displayed. */ @Nullable private Integer olderHeaderPosition; NewCallLogAdapter(Activity activity, Cursor cursor, Clock clock, @Nullable Promotion promotion) { NewCallLogAdapter( Activity activity, ImmutableList<CoalescedRow> coalescedRows, Clock clock, @Nullable Promotion promotion) { this.activity = activity; this.cursor = cursor; this.coalescedRows = coalescedRows; this.clock = clock; this.realtimeRowProcessor = CallLogUiComponent.get(activity).realtimeRowProcessor(); this.promotion = promotion; Loading @@ -92,8 +96,8 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { setCardAndHeaderPositions(); } void updateCursor(Cursor updatedCursor) { this.cursor = updatedCursor; void updateRows(ImmutableList<CoalescedRow> coalescedRows) { this.coalescedRows = coalescedRows; this.realtimeRowProcessor.clearCache(); this.popCounts.reset(); Loading @@ -119,7 +123,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { } // If there are no rows to display, set all header positions to null. if (!cursor.moveToFirst()) { if (coalescedRows.isEmpty()) { todayHeaderPosition = null; yesterdayHeaderPosition = null; olderHeaderPosition = null; Loading @@ -131,17 +135,19 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { int numItemsInToday = 0; int numItemsInYesterday = 0; do { long timestamp = Coalescer.getTimestamp(cursor); int numItemsInOlder = 0; for (CoalescedRow coalescedRow : coalescedRows) { long timestamp = coalescedRow.getTimestamp(); long dayDifference = CallLogDates.getDayDifference(currentTimeMillis, timestamp); if (dayDifference == 0) { numItemsInToday++; } else if (dayDifference == 1) { numItemsInYesterday++; } else { numItemsInOlder = coalescedRows.size() - numItemsInToday - numItemsInYesterday; break; } } while (cursor.moveToNext()); } if (numItemsInToday > 0) { numItemsInToday++; // including the "Today" header; Loading @@ -149,13 +155,16 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { if (numItemsInYesterday > 0) { numItemsInYesterday++; // including the "Yesterday" header; } if (numItemsInOlder > 0) { numItemsInOlder++; // include the "Older" header; } // Set all header positions. // A header position will be null if there is no item to be displayed under that header. todayHeaderPosition = numItemsInToday > 0 ? numCards : null; yesterdayHeaderPosition = numItemsInYesterday > 0 ? numItemsInToday + numCards : null; olderHeaderPosition = !cursor.isAfterLast() ? numItemsInToday + numItemsInYesterday + numCards : null; numItemsInOlder > 0 ? numItemsInToday + numItemsInYesterday + numCards : null; } @Override Loading Loading @@ -233,8 +242,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { if (olderHeaderPosition != null && position > olderHeaderPosition) { previousCardAndHeaders++; } cursor.moveToPosition(position - previousCardAndHeaders); newCallLogViewHolder.bind(cursor); newCallLogViewHolder.bind(coalescedRows.get(position - previousCardAndHeaders)); break; default: throw Assert.createIllegalStateFailException( Loading Loading @@ -277,7 +285,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { if (olderHeaderPosition != null) { numberOfHeaders++; } return cursor.getCount() + numberOfHeaders + numberOfCards; return coalescedRows.size() + numberOfHeaders + numberOfCards; } /** Loading java/com/android/dialer/calllog/ui/NewCallLogFragment.java +8 −6 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import android.view.ViewGroup; import com.android.dialer.calllog.CallLogComponent; import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver; import com.android.dialer.calllog.database.CallLogDatabaseComponent; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DefaultFutureCallback; Loading @@ -47,6 +48,7 @@ import com.android.dialer.promotion.PromotionComponent; import com.android.dialer.util.PermissionsUtil; import com.android.dialer.widget.EmptyContentView; import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; Loading @@ -65,7 +67,7 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback private RecyclerView recyclerView; private EmptyContentView emptyContentView; private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver; private SupportUiListener<Cursor> coalesingAnnotatedCallLogListener; private SupportUiListener<ImmutableList<CoalescedRow>> coalesingAnnotatedCallLogListener; private boolean shouldMarkCallsRead = false; private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true; Loading Loading @@ -304,13 +306,13 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback // Start combining adjacent rows which should be collapsed for display purposes. // This is a time-consuming process so we will do it in the background. ListenableFuture<Cursor> coalescedCursorFuture = ListenableFuture<ImmutableList<CoalescedRow>> coalescedRowsFuture = CallLogDatabaseComponent.get(getContext()).coalescer().coalesce(newCursor); coalesingAnnotatedCallLogListener.listen( getContext(), coalescedCursorFuture, coalescedCursor -> { coalescedRowsFuture, coalescedRows -> { LogUtil.i("NewCallLogFragment.onLoadFinished", "coalescing succeeded"); // TODO(zachh): Handle empty cursor by showing empty view. Loading @@ -323,14 +325,14 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback recyclerView.setAdapter( new NewCallLogAdapter( activity, coalescedCursor, coalescedRows, System::currentTimeMillis, PromotionComponent.get(getContext()) .promotionManager() .getHighestPriorityPromotion(PromotionType.CARD) .orElse(null))); } else { ((NewCallLogAdapter) recyclerView.getAdapter()).updateCursor(coalescedCursor); ((NewCallLogAdapter) recyclerView.getAdapter()).updateRows(coalescedRows); } }, throwable -> { Loading java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java +7 −13 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.dialer.calllog.ui; import android.app.Activity; import android.content.res.ColorStateList; import android.database.Cursor; import android.provider.CallLog.Calls; import android.support.annotation.ColorInt; import android.support.annotation.DrawableRes; Loading @@ -31,7 +30,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.ImageView; import android.widget.TextView; import com.android.dialer.calllog.database.Coalescer; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllog.ui.NewCallLogAdapter.PopCounts; import com.android.dialer.calllog.ui.menu.NewCallLogMenu; Loading Loading @@ -101,25 +99,21 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder { uiExecutorService = DialerExecutorComponent.get(activity).uiExecutor(); } /** * @param cursor a cursor for {@link * com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog}. */ void bind(Cursor cursor) { CoalescedRow row = Coalescer.toRow(cursor); currentRowId = row.getId(); // Used to make sure async updates are applied to the correct views void bind(CoalescedRow coalescedRow) { // The row ID is used to make sure async updates are applied to the correct views. currentRowId = coalescedRow.getId(); // Even if there is additional real time processing necessary, we still want to immediately show // what information we have, rather than an empty card. For example, if CP2 information needs to // be queried on the fly, we can still show the phone number until the contact name loads. displayRow(row); configA11yForRow(row); displayRow(coalescedRow); configA11yForRow(coalescedRow); // Note: This leaks the view holder via the callback (which is an inner class), but this is OK // because we only create ~10 of them (and they'll be collected assuming all jobs finish). Futures.addCallback( realtimeRowProcessor.applyRealtimeProcessing(row), new RealtimeRowFutureCallback(row), realtimeRowProcessor.applyRealtimeProcessing(coalescedRow), new RealtimeRowFutureCallback(coalescedRow), uiExecutorService); } Loading Loading
java/com/android/dialer/calllog/database/Coalescer.java +59 −81 Original line number Diff line number Diff line Loading @@ -17,9 +17,9 @@ package com.android.dialer.calllog.database; import android.content.ContentValues; import android.database.Cursor; import android.database.MatrixCursor; 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; Loading @@ -39,12 +39,12 @@ 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.Map; import java.util.Objects; import javax.inject.Inject; Loading @@ -56,24 +56,6 @@ import javax.inject.Inject; */ 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; Loading @@ -94,12 +76,12 @@ public class Coalescer { * * @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 * @return a future of a list of {@link CoalescedRow coalesced rows}, which will be used to * display call log entries. */ public ListenableFuture<Cursor> coalesce( public ListenableFuture<ImmutableList<CoalescedRow>> coalesce( @NonNull Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { ListenableFuture<Cursor> coalescingFuture = ListenableFuture<ImmutableList<CoalescedRow>> coalescingFuture = backgroundExecutorService.submit( () -> coalesceInternal(Assert.isNotNull(allAnnotatedCallLogRowsSortedByTimestampDesc))); futureTimer.applyTiming(coalescingFuture, Metrics.NEW_CALL_LOG_COALESCE); Loading @@ -108,34 +90,34 @@ public class Coalescer { /** * 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. * and returns a list of {@link CoalescedRow coalesced rows}, which is the result of combining * adjacent rows which should be collapsed for display purposes. * * @param allAnnotatedCallLogRowsSortedByTimestampDesc {@link AnnotatedCallLog} rows sorted in * descending order of timestamp. * @return a new {@link MatrixCursor} containing the {@link CoalescedAnnotatedCallLog} rows to * display * @return a list of {@link CoalescedRow coalesced rows}, which will be used to display call log * entries. */ @WorkerThread @NonNull private Cursor coalesceInternal(Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { private ImmutableList<CoalescedRow> coalesceInternal( Cursor allAnnotatedCallLogRowsSortedByTimestampDesc) { Assert.isWorkerThread(); if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) { return ImmutableList.of(); } // Note: This method relies on rowsShouldBeCombined to determine which rows should be combined, // but delegates to data sources to actually aggregate column values. DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil(); MatrixCursor allCoalescedRowsMatrixCursor = new MatrixCursor( CoalescedAnnotatedCallLog.ALL_COLUMNS, allAnnotatedCallLogRowsSortedByTimestampDesc.getCount()); if (!allAnnotatedCallLogRowsSortedByTimestampDesc.moveToFirst()) { return allCoalescedRowsMatrixCursor; } ImmutableList.Builder<CoalescedRow> coalescedRowListBuilder = new ImmutableList.Builder<>(); int coalescedRowId = 0; // TODO(a bug): Avoid using ContentValues as it doesn't make sense here. List<ContentValues> currentRowGroup = new ArrayList<>(); ContentValues firstRow = cursorRowToContentValues(allAnnotatedCallLogRowsSortedByTimestampDesc); Loading @@ -157,9 +139,10 @@ public class Coalescer { // Coalesce the group into a single row ContentValues coalescedRow = coalesceRowsForAllDataSources(currentRowGroup); coalescedRow.put(CoalescedAnnotatedCallLog._ID, coalescedRowId++); coalescedRow.put( CoalescedAnnotatedCallLog.COALESCED_IDS, getCoalescedIds(currentRowGroup).toByteArray()); addContentValuesToMatrixCursor(coalescedRow, allCoalescedRowsMatrixCursor, coalescedRowId++); coalescedRowListBuilder.add(toCoalescedRowProto(coalescedRow)); // Clear the current group after the rows are coalesced. currentRowGroup.clear(); Loading @@ -170,7 +153,7 @@ public class Coalescer { } } return allCoalescedRowsMatrixCursor; return coalescedRowListBuilder.build(); } private static ContentValues cursorRowToContentValues(Cursor cursor) { Loading Loading @@ -293,34 +276,26 @@ public class Coalescer { } /** * @param contentValues a {@link CoalescedAnnotatedCallLog} row * @param matrixCursor represents {@link CoalescedAnnotatedCallLog} */ private static void addContentValuesToMatrixCursor( ContentValues contentValues, MatrixCursor matrixCursor, int rowId) { MatrixCursor.RowBuilder rowBuilder = matrixCursor.newRow(); rowBuilder.add(CoalescedAnnotatedCallLog._ID, rowId); for (Map.Entry<String, Object> entry : contentValues.valueSet()) { rowBuilder.add(entry.getKey(), entry.getValue()); } } /** * Creates a new {@link CoalescedRow} based on the data at the provided cursor's current position. * Creates a new {@link CoalescedRow} proto based on the provided {@link ContentValues}. * * <p>The provided cursor should be one for {@link CoalescedAnnotatedCallLog}. * <p>The provided {@link ContentValues} should be one for {@link CoalescedAnnotatedCallLog}. */ public static CoalescedRow toRow(Cursor coalescedAnnotatedCallLogCursor) { @VisibleForTesting static CoalescedRow toCoalescedRowProto(ContentValues coalescedContentValues) { DialerPhoneNumber number; try { number = DialerPhoneNumber.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(NUMBER)); number = DialerPhoneNumber.parseFrom( coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.NUMBER)); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Couldn't parse DialerPhoneNumber bytes"); } CoalescedIds coalescedIds; try { coalescedIds = CoalescedIds.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(COALESCED_IDS)); coalescedIds = CoalescedIds.parseFrom( coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.COALESCED_IDS)); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Couldn't parse CoalescedIds bytes"); } Loading @@ -328,61 +303,64 @@ public class Coalescer { NumberAttributes numberAttributes; try { numberAttributes = NumberAttributes.parseFrom(coalescedAnnotatedCallLogCursor.getBlob(NUMBER_ATTRIBUTES)); NumberAttributes.parseFrom( coalescedContentValues.getAsByteArray(CoalescedAnnotatedCallLog.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)) .setId(coalescedContentValues.getAsLong(CoalescedAnnotatedCallLog._ID)) .setTimestamp(coalescedContentValues.getAsLong(CoalescedAnnotatedCallLog.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)) .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) .setIsVoicemailCall(coalescedAnnotatedCallLogCursor.getInt(IS_VOICEMAIL_CALL) == 1) .setCoalescedIds(coalescedIds); String formattedNumber = coalescedAnnotatedCallLogCursor.getString(FORMATTED_NUMBER); // 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 = coalescedAnnotatedCallLogCursor.getString(GEOCODED_LOCATION); String geocodedLocation = coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.GEOCODED_LOCATION); if (!TextUtils.isEmpty(geocodedLocation)) { coalescedRowBuilder.setGeocodedLocation(geocodedLocation); } String phoneAccountComponentName = coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_COMPONENT_NAME); coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME); if (!TextUtils.isEmpty(phoneAccountComponentName)) { coalescedRowBuilder.setPhoneAccountComponentName( coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_COMPONENT_NAME)); coalescedContentValues.getAsString( CoalescedAnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME)); } String phoneAccountId = coalescedAnnotatedCallLogCursor.getString(PHONE_ACCOUNT_ID); String phoneAccountId = coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.PHONE_ACCOUNT_ID); if (!TextUtils.isEmpty(phoneAccountId)) { coalescedRowBuilder.setPhoneAccountId(phoneAccountId); } String voicemailCallTag = coalescedAnnotatedCallLogCursor.getString(VOICEMAIL_CALL_TAG); String voicemailCallTag = coalescedContentValues.getAsString(CoalescedAnnotatedCallLog.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); } }
java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java +1 −43 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import android.os.Build; import android.provider.BaseColumns; import com.android.dialer.compat.android.provider.VoicemailCompat; import com.android.dialer.constants.Constants; import java.util.Arrays; /** Contract for the AnnotatedCallLog content provider. */ public class AnnotatedCallLogContract { Loading @@ -29,11 +28,7 @@ public class AnnotatedCallLogContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); /** * Columns shared by {@link AnnotatedCallLog} and {@link CoalescedAnnotatedCallLog}. * * <p>When adding columns be sure to update {@link #ALL_COMMON_COLUMNS}. */ /** Columns shared by {@link AnnotatedCallLog} and {@link CoalescedAnnotatedCallLog}. */ interface CommonColumns extends BaseColumns { /** Loading Loading @@ -143,25 +138,6 @@ public class AnnotatedCallLogContract { * <p>Type: INTEGER (int) */ String CALL_TYPE = "call_type"; String[] ALL_COMMON_COLUMNS = new String[] { _ID, TIMESTAMP, NUMBER, FORMATTED_NUMBER, NUMBER_PRESENTATION, IS_READ, NEW, GEOCODED_LOCATION, PHONE_ACCOUNT_COMPONENT_NAME, PHONE_ACCOUNT_ID, FEATURES, NUMBER_ATTRIBUTES, IS_VOICEMAIL_CALL, VOICEMAIL_CALL_TAG, CALL_TYPE }; } /** Loading Loading @@ -239,8 +215,6 @@ public class AnnotatedCallLogContract { * * <p>This is an in-memory view of the {@link AnnotatedCallLog} with some adjacent entries * collapsed. * * <p>When adding columns be sure to update {@link #COLUMNS_ONLY_IN_COALESCED_CALL_LOG}. */ public static final class CoalescedAnnotatedCallLog implements CommonColumns { Loading @@ -251,21 +225,5 @@ public class AnnotatedCallLogContract { * <p>Type: BLOB */ public static final String COALESCED_IDS = "coalesced_ids"; /** * Columns that are only in the {@link CoalescedAnnotatedCallLog} but not the {@link * AnnotatedCallLog}. */ private static final String[] COLUMNS_ONLY_IN_COALESCED_CALL_LOG = new String[] {COALESCED_IDS}; /** All columns in the {@link CoalescedAnnotatedCallLog}. */ public static final String[] ALL_COLUMNS = concat(ALL_COMMON_COLUMNS, COLUMNS_ONLY_IN_COALESCED_CALL_LOG); } private static String[] concat(String[] first, String[] second) { String[] result = Arrays.copyOf(first, first.length + second.length); System.arraycopy(second, 0, result, first.length, second.length); return result; } }
java/com/android/dialer/calllog/ui/NewCallLogAdapter.java +23 −15 Original line number Diff line number Diff line Loading @@ -17,19 +17,19 @@ package com.android.dialer.calllog.ui; import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; import android.view.ViewGroup; import com.android.dialer.calllog.database.Coalescer; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllogutils.CallLogDates; import com.android.dialer.common.Assert; import com.android.dialer.logging.Logger; import com.android.dialer.promotion.Promotion; import com.android.dialer.time.Clock; import com.google.common.collect.ImmutableList; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -68,7 +68,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { private final PopCounts popCounts = new PopCounts(); @Nullable private final Promotion promotion; private Cursor cursor; private ImmutableList<CoalescedRow> coalescedRows; /** Position of the promotion card. Null when it should not be displayed. */ @Nullable private Integer promotionCardPosition; Loading @@ -82,9 +82,13 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { /** Position of the "Older" header. Null when it should not be displayed. */ @Nullable private Integer olderHeaderPosition; NewCallLogAdapter(Activity activity, Cursor cursor, Clock clock, @Nullable Promotion promotion) { NewCallLogAdapter( Activity activity, ImmutableList<CoalescedRow> coalescedRows, Clock clock, @Nullable Promotion promotion) { this.activity = activity; this.cursor = cursor; this.coalescedRows = coalescedRows; this.clock = clock; this.realtimeRowProcessor = CallLogUiComponent.get(activity).realtimeRowProcessor(); this.promotion = promotion; Loading @@ -92,8 +96,8 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { setCardAndHeaderPositions(); } void updateCursor(Cursor updatedCursor) { this.cursor = updatedCursor; void updateRows(ImmutableList<CoalescedRow> coalescedRows) { this.coalescedRows = coalescedRows; this.realtimeRowProcessor.clearCache(); this.popCounts.reset(); Loading @@ -119,7 +123,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { } // If there are no rows to display, set all header positions to null. if (!cursor.moveToFirst()) { if (coalescedRows.isEmpty()) { todayHeaderPosition = null; yesterdayHeaderPosition = null; olderHeaderPosition = null; Loading @@ -131,17 +135,19 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { int numItemsInToday = 0; int numItemsInYesterday = 0; do { long timestamp = Coalescer.getTimestamp(cursor); int numItemsInOlder = 0; for (CoalescedRow coalescedRow : coalescedRows) { long timestamp = coalescedRow.getTimestamp(); long dayDifference = CallLogDates.getDayDifference(currentTimeMillis, timestamp); if (dayDifference == 0) { numItemsInToday++; } else if (dayDifference == 1) { numItemsInYesterday++; } else { numItemsInOlder = coalescedRows.size() - numItemsInToday - numItemsInYesterday; break; } } while (cursor.moveToNext()); } if (numItemsInToday > 0) { numItemsInToday++; // including the "Today" header; Loading @@ -149,13 +155,16 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { if (numItemsInYesterday > 0) { numItemsInYesterday++; // including the "Yesterday" header; } if (numItemsInOlder > 0) { numItemsInOlder++; // include the "Older" header; } // Set all header positions. // A header position will be null if there is no item to be displayed under that header. todayHeaderPosition = numItemsInToday > 0 ? numCards : null; yesterdayHeaderPosition = numItemsInYesterday > 0 ? numItemsInToday + numCards : null; olderHeaderPosition = !cursor.isAfterLast() ? numItemsInToday + numItemsInYesterday + numCards : null; numItemsInOlder > 0 ? numItemsInToday + numItemsInYesterday + numCards : null; } @Override Loading Loading @@ -233,8 +242,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { if (olderHeaderPosition != null && position > olderHeaderPosition) { previousCardAndHeaders++; } cursor.moveToPosition(position - previousCardAndHeaders); newCallLogViewHolder.bind(cursor); newCallLogViewHolder.bind(coalescedRows.get(position - previousCardAndHeaders)); break; default: throw Assert.createIllegalStateFailException( Loading Loading @@ -277,7 +285,7 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { if (olderHeaderPosition != null) { numberOfHeaders++; } return cursor.getCount() + numberOfHeaders + numberOfCards; return coalescedRows.size() + numberOfHeaders + numberOfCards; } /** Loading
java/com/android/dialer/calllog/ui/NewCallLogFragment.java +8 −6 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import android.view.ViewGroup; import com.android.dialer.calllog.CallLogComponent; import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver; import com.android.dialer.calllog.database.CallLogDatabaseComponent; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DefaultFutureCallback; Loading @@ -47,6 +48,7 @@ import com.android.dialer.promotion.PromotionComponent; import com.android.dialer.util.PermissionsUtil; import com.android.dialer.widget.EmptyContentView; import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; Loading @@ -65,7 +67,7 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback private RecyclerView recyclerView; private EmptyContentView emptyContentView; private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver; private SupportUiListener<Cursor> coalesingAnnotatedCallLogListener; private SupportUiListener<ImmutableList<CoalescedRow>> coalesingAnnotatedCallLogListener; private boolean shouldMarkCallsRead = false; private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true; Loading Loading @@ -304,13 +306,13 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback // Start combining adjacent rows which should be collapsed for display purposes. // This is a time-consuming process so we will do it in the background. ListenableFuture<Cursor> coalescedCursorFuture = ListenableFuture<ImmutableList<CoalescedRow>> coalescedRowsFuture = CallLogDatabaseComponent.get(getContext()).coalescer().coalesce(newCursor); coalesingAnnotatedCallLogListener.listen( getContext(), coalescedCursorFuture, coalescedCursor -> { coalescedRowsFuture, coalescedRows -> { LogUtil.i("NewCallLogFragment.onLoadFinished", "coalescing succeeded"); // TODO(zachh): Handle empty cursor by showing empty view. Loading @@ -323,14 +325,14 @@ public final class NewCallLogFragment extends Fragment implements LoaderCallback recyclerView.setAdapter( new NewCallLogAdapter( activity, coalescedCursor, coalescedRows, System::currentTimeMillis, PromotionComponent.get(getContext()) .promotionManager() .getHighestPriorityPromotion(PromotionType.CARD) .orElse(null))); } else { ((NewCallLogAdapter) recyclerView.getAdapter()).updateCursor(coalescedCursor); ((NewCallLogAdapter) recyclerView.getAdapter()).updateRows(coalescedRows); } }, throwable -> { Loading
java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java +7 −13 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.dialer.calllog.ui; import android.app.Activity; import android.content.res.ColorStateList; import android.database.Cursor; import android.provider.CallLog.Calls; import android.support.annotation.ColorInt; import android.support.annotation.DrawableRes; Loading @@ -31,7 +30,6 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.ImageView; import android.widget.TextView; import com.android.dialer.calllog.database.Coalescer; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.calllog.ui.NewCallLogAdapter.PopCounts; import com.android.dialer.calllog.ui.menu.NewCallLogMenu; Loading Loading @@ -101,25 +99,21 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder { uiExecutorService = DialerExecutorComponent.get(activity).uiExecutor(); } /** * @param cursor a cursor for {@link * com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog}. */ void bind(Cursor cursor) { CoalescedRow row = Coalescer.toRow(cursor); currentRowId = row.getId(); // Used to make sure async updates are applied to the correct views void bind(CoalescedRow coalescedRow) { // The row ID is used to make sure async updates are applied to the correct views. currentRowId = coalescedRow.getId(); // Even if there is additional real time processing necessary, we still want to immediately show // what information we have, rather than an empty card. For example, if CP2 information needs to // be queried on the fly, we can still show the phone number until the contact name loads. displayRow(row); configA11yForRow(row); displayRow(coalescedRow); configA11yForRow(coalescedRow); // Note: This leaks the view holder via the callback (which is an inner class), but this is OK // because we only create ~10 of them (and they'll be collected assuming all jobs finish). Futures.addCallback( realtimeRowProcessor.applyRealtimeProcessing(row), new RealtimeRowFutureCallback(row), realtimeRowProcessor.applyRealtimeProcessing(coalescedRow), new RealtimeRowFutureCallback(coalescedRow), uiExecutorService); } Loading