diff options
author | zachh <zachh@google.com> | 2018-01-17 15:07:44 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-01-17 16:35:09 -0800 |
commit | 73c65793a6fc191d5124739d7b37c41d5f61c671 (patch) | |
tree | bd84298b0e64deac9b58882c750e3d04a286abbe | |
parent | 4e7284d6cef414f17580993658059a373c82a936 (diff) |
Show existing CP2 information for invalid numbers even if out of date.
This is an optimization to reduce popping in the new call log. Currently when Cp2LocalPhoneLookup determines a number to be "incomplete" (because it is an invalid number and there are too many invalid numbers in the call log to efficiently bulk update) we clear the existing data, which has been populated in PhoneLookupHistory (for example, from InCallUi). This means that we will show the number initially when displaying the call log, and then when the query completes we will "pop in" the new information.
This change makes it so that we don't clear the existing data from PhoneLookupHistory, and just add the "incomplete" bit. The result of this is that we immediately display the available information when initially displaying the call log (even though it may be out of date). When the query completes, the row will be updated with the most recent information; in most cases this is likely to be the same as the information used to initially display the row, and no update will need to be applied.
Additoinal changes to support this functionality:
-RealtimeRowProcessor is now just responsible for returning an updated row, and NewCallLogView holder will compare the result to the originally displayed row and only update the UI if there are differences.
-NewCallLogFragment now calls clears the RealtimeRowProcessor's cache and notifies data set changed during onResume. This is to account for the fact that AnnotatedCallLog no longer contains the complete set of information necessary to show the call log; there may be changes we need to show which can't be detected by the cursor loader. We now show those potential changes in onResume.
Additional notes:
-If there is real-time data that changes after onResume it won't be detected but there shouldn't be such cases; changes made to contact information from dialer are always done through contact cards which pause the fragment.
-This change has the effect that whatever information was written to PhoneLookupHistory during the previous invocation of InCallUi will always be (initially) shown. For example, if the contact name for number "123" is "Joe" when the call comes in, we'll write "Joe" to PhoneLookupHistory. If the user changes Joe's name to "Jane", the UI will pop from "Joe" to "Jane" until PhoneLookupHistory is updated (which is currently only done from InCallUi). If this turns out to be a problem it could be mitigated by writing updated results to PhoneLookupHistory from the UI.
Test: unit, manual
PiperOrigin-RevId: 182277145
Change-Id: I3d9916b7747390ff956f399fe84b26d578e5a07f
6 files changed, 43 insertions, 30 deletions
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java index 1d4a35a6c..a0874f0cd 100644 --- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java +++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java @@ -280,7 +280,10 @@ public final class PhoneLookupDataSource PhoneLookupHistory.contentUriForNumber(normalizedNumber)) .build()); } - appContext.getContentResolver().applyBatch(PhoneLookupHistoryContract.AUTHORITY, operations); + Assert.isNotNull( + appContext + .getContentResolver() + .applyBatch(PhoneLookupHistoryContract.AUTHORITY, operations)); return null; } diff --git a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java index 24324b073..5618c4d16 100644 --- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java +++ b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java @@ -70,6 +70,10 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> { notifyDataSetChanged(); } + void clearCache() { + this.realtimeRowProcessor.clearCache(); + } + private void setHeaderPositions() { // Calculate header adapter positions by reading cursor. long currentTimeMillis = clock.currentTimeMillis(); diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java index 6db7c5d6c..10f75ef07 100644 --- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java +++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java @@ -93,6 +93,16 @@ public final class NewCallLogFragment extends Fragment // TODO(zachh): Consider doing this when fragment becomes visible. refreshAnnotatedCallLog(true /* checkDirty */); + + // There are some types of data that we show in the call log that are not represented in the + // AnnotatedCallLog. For example, CP2 information for invalid numbers can sometimes only be + // fetched at display time. Because of this, we need to clear the adapter's cache and update it + // whenever the user arrives at the call log (rather than relying on changes to the CursorLoader + // alone). + if (recyclerView.getAdapter() != null) { + ((NewCallLogAdapter) recyclerView.getAdapter()).clearCache(); + recyclerView.getAdapter().notifyDataSetChanged(); + } } @Override diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java index 2938d083b..a0daae141 100644 --- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java +++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java @@ -38,7 +38,6 @@ import com.android.dialer.compat.telephony.TelephonyManagerCompat; import com.android.dialer.contactphoto.ContactPhotoManager; import com.android.dialer.oem.MotorolaUtils; import com.android.dialer.time.Clock; -import com.google.common.base.Optional; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import java.util.Locale; @@ -86,17 +85,17 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder { // 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. - handleRow(row); + displayRow(row); // 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.id()), + new RealtimeRowFutureCallback(row), uiExecutorService); } - private void handleRow(CoalescedRow row) { + private void displayRow(CoalescedRow row) { // TODO(zachh): Handle RTL properly. primaryTextView.setText(CallLogEntryText.buildPrimaryText(context, row)); secondaryTextView.setText(CallLogEntryText.buildSecondaryTextForEntries(context, clock, row)); @@ -189,23 +188,24 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder { menuButton.setOnClickListener(NewCallLogMenu.createOnClickListener(context, row)); } - private class RealtimeRowFutureCallback implements FutureCallback<Optional<CoalescedRow>> { - private final int id; + private class RealtimeRowFutureCallback implements FutureCallback<CoalescedRow> { + private final CoalescedRow originalRow; - RealtimeRowFutureCallback(int id) { - this.id = id; + RealtimeRowFutureCallback(CoalescedRow originalRow) { + this.originalRow = originalRow; } - /** - * @param updatedRow the updated row if an update is required, or absent if no updates are - * required - */ @Override - public void onSuccess(Optional<CoalescedRow> updatedRow) { + public void onSuccess(CoalescedRow updatedRow) { // If the user scrolled then this ViewHolder may not correspond to the completed task and // there's nothing to do. - if (updatedRow.isPresent() && id == currentRowId) { - handleRow(updatedRow.get()); + if (originalRow.id() != currentRowId) { + return; + } + // Only update the UI if the updated row differs from the original row (which has already + // been displayed). + if (!updatedRow.equals(originalRow)) { + displayRow(updatedRow); } } diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java index 6e214edc4..57ad9657c 100644 --- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java +++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java @@ -29,7 +29,6 @@ import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info; import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator; import com.android.dialer.phonelookup.cp2.Cp2LocalPhoneLookup; -import com.google.common.base.Optional; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; @@ -67,21 +66,18 @@ public final class RealtimeRowProcessor { /** * Converts a {@link CoalescedRow} to a future which is the result of performing additional work - * on the row. Returns {@link Optional#absent()} if no modifications were necessary. + * on the row. May simply return the original row if no modifications were necessary. */ @MainThread - ListenableFuture<Optional<CoalescedRow>> applyRealtimeProcessing(final CoalescedRow row) { + ListenableFuture<CoalescedRow> applyRealtimeProcessing(final CoalescedRow row) { // Cp2LocalPhoneLookup can not always efficiently process all rows. if (!row.numberAttributes().getIsCp2InfoIncomplete()) { - return Futures.immediateFuture(Optional.absent()); + return Futures.immediateFuture(row); } Cp2Info cachedCp2Info = cache.get(row.number()); if (cachedCp2Info != null) { - if (cachedCp2Info.equals(Cp2Info.getDefaultInstance())) { - return Futures.immediateFuture(Optional.absent()); - } - return Futures.immediateFuture(Optional.of(applyCp2LocalInfoToRow(cachedCp2Info, row))); + return Futures.immediateFuture(applyCp2LocalInfoToRow(cachedCp2Info, row)); } ListenableFuture<Cp2Info> cp2InfoFuture = cp2LocalPhoneLookup.lookupByNumber(row.number()); @@ -89,10 +85,7 @@ public final class RealtimeRowProcessor { cp2InfoFuture, cp2Info -> { cache.put(row.number(), cp2Info); - if (!cp2Info.equals(Cp2Info.getDefaultInstance())) { - return Optional.of(applyCp2LocalInfoToRow(cp2Info, row)); - } - return Optional.absent(); + return applyCp2LocalInfoToRow(cp2Info, row); }, uiExecutor /* ensures the cache is updated on a single thread */); } diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java index c6e7f5aa3..995950d0e 100644 --- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java +++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java @@ -140,7 +140,7 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> { try (Cursor cursor = queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), rawNumber)) { if (cursor == null) { - LogUtil.w("Cp2LocalPhoneLookup.lookup", "null cursor"); + LogUtil.w("Cp2LocalPhoneLookup.lookupByNumber", "null cursor"); return Cp2Info.getDefaultInstance(); } while (cursor.moveToNext()) { @@ -511,7 +511,10 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> { } else if (deletedPhoneNumbers.contains(dialerPhoneNumber)) { infoBuilder.clear(); } else if (unprocessableNumbers.contains(dialerPhoneNumber)) { - infoBuilder.clear().setIsIncomplete(true); + // Don't clear the existing info when the number is unprocessable. It's + // likely that the existing info is up-to-date so keep it in place so that + // the UI doesn't pop when the query is completed at display time. + infoBuilder.setIsIncomplete(true); } // If the DialerPhoneNumber didn't change, add the unchanged existing info. |