Newly starred SpeedDialUiItems now have SpeedDialEntry ids set.

Bug: 78241895
Test: numerous
PiperOrigin-RevId: 194147693
Change-Id: I9e9947ad689c5bf24dd52e37787f4138a92f5238
diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java b/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java
index ce771c3..4d6ac2d 100644
--- a/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java
+++ b/java/com/android/dialer/speeddial/database/SpeedDialEntryDao.java
@@ -17,6 +17,7 @@
 package com.android.dialer.speeddial.database;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 /**
  * Interface that databases support speed dial entries should implement.
@@ -32,8 +33,10 @@
    * Insert new entries.
    *
    * <p>{@link SpeedDialEntry#id() ids} must be null.
+   *
+   * @return a map of the inserted entries to their new ids.
    */
-  void insert(ImmutableList<SpeedDialEntry> entries);
+  ImmutableMap<SpeedDialEntry, Long> insert(ImmutableList<SpeedDialEntry> entries);
 
   /**
    * Insert a new entry.
@@ -59,11 +62,12 @@
   /**
    * Inserts, updates and deletes rows all in on transaction.
    *
+   * @return a map of the inserted entries to their new ids.
    * @see #insert(ImmutableList)
    * @see #update(ImmutableList)
    * @see #delete(ImmutableList)
    */
-  void insertUpdateAndDelete(
+  ImmutableMap<SpeedDialEntry, Long> insertUpdateAndDelete(
       ImmutableList<SpeedDialEntry> entriesToInsert,
       ImmutableList<SpeedDialEntry> entriesToUpdate,
       ImmutableList<Long> entriesToDelete);
diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java b/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java
index 137933f..544bb36 100644
--- a/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java
+++ b/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java
@@ -26,6 +26,7 @@
 import com.android.dialer.common.database.Selection;
 import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -132,30 +133,39 @@
   }
 
   @Override
-  public void insert(ImmutableList<SpeedDialEntry> entries) {
+  public ImmutableMap<SpeedDialEntry, Long> insert(ImmutableList<SpeedDialEntry> entries) {
     if (entries.isEmpty()) {
-      return;
+      return ImmutableMap.of();
     }
 
     SQLiteDatabase db = getWritableDatabase();
     db.beginTransaction();
     try {
-      insert(db, entries);
+      ImmutableMap<SpeedDialEntry, Long> insertedEntriesToIdsMap = insert(db, entries);
       db.setTransactionSuccessful();
+      return insertedEntriesToIdsMap;
     } finally {
       db.endTransaction();
       db.close();
     }
   }
 
-  private void insert(SQLiteDatabase writeableDatabase, ImmutableList<SpeedDialEntry> entries) {
+  private ImmutableMap<SpeedDialEntry, Long> insert(
+      SQLiteDatabase writeableDatabase, ImmutableList<SpeedDialEntry> entries) {
+    ImmutableMap.Builder<SpeedDialEntry, Long> insertedEntriesToIdsMap = ImmutableMap.builder();
     for (SpeedDialEntry entry : entries) {
       Assert.checkArgument(entry.id() == null);
-      if (writeableDatabase.insert(TABLE_NAME, null, buildContentValuesWithoutId(entry)) == -1L) {
+      long id = writeableDatabase.insert(TABLE_NAME, null, buildContentValuesWithoutId(entry));
+      if (id == -1L) {
         throw Assert.createUnsupportedOperationFailException(
             "Attempted to insert a row that already exists.");
       }
+      // It's impossible to insert two identical entries but this is an important assumption we need
+      // to verify because there's an assumption that each entry will correspond to exactly one id.
+      // ImmutableMap#put verifies this check for us.
+      insertedEntriesToIdsMap.put(entry, id);
     }
+    return insertedEntriesToIdsMap.build();
   }
 
   @Override
@@ -255,20 +265,21 @@
   }
 
   @Override
-  public void insertUpdateAndDelete(
+  public ImmutableMap<SpeedDialEntry, Long> insertUpdateAndDelete(
       ImmutableList<SpeedDialEntry> entriesToInsert,
       ImmutableList<SpeedDialEntry> entriesToUpdate,
       ImmutableList<Long> entriesToDelete) {
     if (entriesToInsert.isEmpty() && entriesToUpdate.isEmpty() && entriesToDelete.isEmpty()) {
-      return;
+      return ImmutableMap.of();
     }
     SQLiteDatabase db = getWritableDatabase();
     db.beginTransaction();
     try {
-      insert(db, entriesToInsert);
+      ImmutableMap<SpeedDialEntry, Long> insertedEntriesToIdsMap = insert(db, entriesToInsert);
       update(db, entriesToUpdate);
       delete(db, entriesToDelete);
       db.setTransactionSuccessful();
+      return insertedEntriesToIdsMap;
     } finally {
       db.endTransaction();
       db.close();
diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
index 24bc776..9bda3fb 100644
--- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
@@ -136,6 +136,15 @@
     return builder.build();
   }
 
+  public SpeedDialEntry buildSpeedDialEntry() {
+    return SpeedDialEntry.builder()
+        .setId(speedDialEntryId())
+        .setLookupKey(lookupKey())
+        .setContactId(contactId())
+        .setDefaultChannel(defaultChannel())
+        .build();
+  }
+
   /**
    * Returns a video channel if there is exactly one video channel or the default channel is a video
    * channel.
diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
index 7107706..9214687 100644
--- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
@@ -44,6 +44,7 @@
 import com.android.dialer.speeddial.database.SpeedDialEntryDao;
 import com.android.dialer.speeddial.database.SpeedDialEntryDatabaseHelper;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import java.util.ArrayList;
@@ -146,13 +147,7 @@
       }
 
       // Insert a new entry into the SpeedDialEntry database
-      getSpeedDialEntryDao()
-          .insert(
-              SpeedDialEntry.builder()
-                  .setLookupKey(item.lookupKey())
-                  .setContactId(item.contactId())
-                  .setDefaultChannel(item.defaultChannel())
-                  .build());
+      getSpeedDialEntryDao().insert(item.buildSpeedDialEntry());
     }
     return loadSpeedDialUiItemsInternal();
   }
@@ -216,23 +211,55 @@
             contact.toBuilder().setDefaultChannel(contact.channels().get(0)).build());
 
       } else if (speedDialUiItems.stream().noneMatch(c -> c.contactId() == contact.contactId())) {
-        entriesToInsert.add(
-            SpeedDialEntry.builder()
-                .setLookupKey(contact.lookupKey())
-                .setContactId(contact.contactId())
-                .setDefaultChannel(contact.defaultChannel())
-                .build());
+        entriesToInsert.add(contact.buildSpeedDialEntry());
 
         // These are our newly starred contacts
         speedDialUiItems.add(contact);
       }
     }
 
-    db.insertUpdateAndDelete(
-        ImmutableList.copyOf(entriesToInsert),
-        ImmutableList.copyOf(entriesToUpdate),
-        ImmutableList.copyOf(entriesToDelete));
-    return ImmutableList.copyOf(speedDialUiItems);
+    ImmutableMap<SpeedDialEntry, Long> insertedEntriesToIdsMap =
+        db.insertUpdateAndDelete(
+            ImmutableList.copyOf(entriesToInsert),
+            ImmutableList.copyOf(entriesToUpdate),
+            ImmutableList.copyOf(entriesToDelete));
+    return speedDialUiItemsWithUpdatedIds(speedDialUiItems, insertedEntriesToIdsMap);
+  }
+
+  /**
+   * Since newly starred contacts sometimes aren't in the SpeedDialEntry database, we couldn't set
+   * their ids when we created our initial list of {@link SpeedDialUiItem speedDialUiItems}. Now
+   * that we've inserted the entries into the database and we have their ids, build a new list of
+   * speedDialUiItems with the now known ids.
+   */
+  private ImmutableList<SpeedDialUiItem> speedDialUiItemsWithUpdatedIds(
+      List<SpeedDialUiItem> speedDialUiItems,
+      ImmutableMap<SpeedDialEntry, Long> insertedEntriesToIdsMap) {
+    if (insertedEntriesToIdsMap.isEmpty()) {
+      // There were no newly inserted entries, so all entries ids are set already.
+      return ImmutableList.copyOf(speedDialUiItems);
+    }
+
+    ImmutableList.Builder<SpeedDialUiItem> updatedItems = ImmutableList.builder();
+    for (SpeedDialUiItem speedDialUiItem : speedDialUiItems) {
+      SpeedDialEntry entry = speedDialUiItem.buildSpeedDialEntry();
+      if (insertedEntriesToIdsMap.containsKey(entry)) {
+        // Get the id for newly inserted entry, update our SpeedDialUiItem and add it to our list
+        Long id = Assert.isNotNull(insertedEntriesToIdsMap.get(entry));
+        updatedItems.add(speedDialUiItem.toBuilder().setSpeedDialEntryId(id).build());
+        continue;
+      }
+
+      // Starred contacts that aren't in the map, should already have speed dial entry ids.
+      // Non-starred contacts (suggestions) aren't in the speed dial entry database, so they
+      // shouldn't have speed dial entry ids.
+      Assert.checkArgument(
+          speedDialUiItem.isStarred() == (speedDialUiItem.speedDialEntryId() != null),
+          "Contact must be starred with a speed dial entry id, or not starred with no id "
+              + "(suggested contacts)");
+      updatedItems.add(speedDialUiItem);
+    }
+    return updatedItems.build();
   }
 
   /**