Merge "Show existing CP2 information for invalid numbers even if out of date."
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 1d4a35a..a0874f0 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 @@
                   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 24324b0..5618c4d 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
@@ -70,6 +70,10 @@
     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 6db7c5d..10f75ef 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -93,6 +93,16 @@
 
     // 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 2938d08..a0daae1 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.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 @@
     // 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 @@
     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 6e214ed..57ad965 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.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 @@
 
   /**
    * 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 @@
         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 c6e7f5a..995950d 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
@@ -140,7 +140,7 @@
           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 @@
                         } 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.