Merge "Change RttTransciptUtil methods to return ListenableFuture."
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
index dec1230..8f850ae 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
@@ -30,7 +30,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresPermission;
-import android.support.annotation.WorkerThread;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -65,7 +64,7 @@
 import com.android.dialer.rtt.RttTranscriptActivity;
 import com.android.dialer.rtt.RttTranscriptUtil;
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.collect.ImmutableSet;
 import com.google.i18n.phonenumbers.NumberParseException;
 import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
@@ -100,7 +99,7 @@
 
   private CallDetailsAdapterCommon adapter;
   private CallDetailsEntries callDetailsEntries;
-  private UiListener<CallDetailsEntries> checkRttTranscriptAvailabilityListener;
+  private UiListener<ImmutableSet<String>> checkRttTranscriptAvailabilityListener;
 
   /**
    * Handles the intent that launches {@link OldCallDetailsActivity} or {@link CallDetailsActivity},
@@ -160,40 +159,28 @@
   }
 
   protected void loadRttTranscriptAvailability() {
+    ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+    for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
+      builder.add(entry.getCallMappingId());
+    }
     checkRttTranscriptAvailabilityListener.listen(
         this,
-        checkRttTranscriptAvailability(),
-        this::setCallDetailsEntries,
+        RttTranscriptUtil.getAvailableRttTranscriptIds(this, builder.build()),
+        this::updateCallDetailsEntriesWithRttTranscriptAvailability,
         throwable -> {
           throw new RuntimeException(throwable);
         });
   }
 
-  private ListenableFuture<CallDetailsEntries> checkRttTranscriptAvailability() {
-    return DialerExecutorComponent.get(this)
-        .backgroundExecutor()
-        .submit(() -> checkRttTranscriptAvailabilityInBackground(callDetailsEntries));
-  }
-
-  /**
-   * Check RTT transcript availability.
-   *
-   * @param input the original {@link CallDetailsEntries}
-   * @return {@link CallDetailsEntries} with updated RTT transcript availability.
-   */
-  @WorkerThread
-  private CallDetailsEntries checkRttTranscriptAvailabilityInBackground(
-      @Nullable CallDetailsEntries input) {
-    RttTranscriptUtil rttTranscriptUtil = new RttTranscriptUtil(this);
-
+  private void updateCallDetailsEntriesWithRttTranscriptAvailability(
+      ImmutableSet<String> availableTranscripIds) {
     CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder();
-    for (CallDetailsEntry entry : input.getEntriesList()) {
+    for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) {
       CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry);
-      newEntry.setHasRttTranscript(
-          rttTranscriptUtil.checkRttTranscriptAvailability(String.valueOf(entry.getDate())));
+      newEntry.setHasRttTranscript(availableTranscripIds.contains(entry.getCallMappingId()));
       mutableCallDetailsEntries.addEntries(newEntry.build());
     }
-    return mutableCallDetailsEntries.build();
+    setCallDetailsEntries(mutableCallDetailsEntries.build());
   }
 
   @Override
diff --git a/java/com/android/dialer/rtt/RttTranscriptActivity.java b/java/com/android/dialer/rtt/RttTranscriptActivity.java
index a878520..18ae20c 100644
--- a/java/com/android/dialer/rtt/RttTranscriptActivity.java
+++ b/java/com/android/dialer/rtt/RttTranscriptActivity.java
@@ -30,7 +30,6 @@
 import com.android.dialer.common.concurrent.UiListener;
 import com.android.dialer.glidephotomanager.PhotoInfo;
 import com.android.dialer.protos.ProtoParsers;
-import com.google.common.util.concurrent.ListenableFuture;
 
 /** Activity holds RTT transcript. */
 public class RttTranscriptActivity extends AppCompatActivity {
@@ -80,7 +79,7 @@
     String id = intent.getStringExtra(EXTRA_TRANSCRIPT_ID);
     rttTranscriptUiListener.listen(
         this,
-        getRttTranscript(id),
+        RttTranscriptUtil.loadRttTranscript(this, id),
         adapter::setRttTranscript,
         throwable -> {
           throw new RuntimeException(throwable);
@@ -112,10 +111,4 @@
     }
     return super.onOptionsItemSelected(item);
   }
-
-  private ListenableFuture<RttTranscript> getRttTranscript(String id) {
-    return DialerExecutorComponent.get(this)
-        .backgroundExecutor()
-        .submit(() -> new RttTranscriptUtil(this).getRttTranscript(id));
-  }
 }
diff --git a/java/com/android/dialer/rtt/RttTranscriptUtil.java b/java/com/android/dialer/rtt/RttTranscriptUtil.java
index 19b46b0..967a123 100644
--- a/java/com/android/dialer/rtt/RttTranscriptUtil.java
+++ b/java/com/android/dialer/rtt/RttTranscriptUtil.java
@@ -21,50 +21,65 @@
 import android.database.Cursor;
 import android.support.annotation.WorkerThread;
 import com.android.dialer.common.Assert;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.common.database.Selection;
 import com.android.dialer.rtt.RttTranscriptContract.RttTranscriptColumn;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 /** Util class to save and load RTT transcript. */
 public final class RttTranscriptUtil {
 
-  private final RttTranscriptDatabaseHelper databaseHelper;
-
-  public RttTranscriptUtil(Context context) {
-    databaseHelper = new RttTranscriptDatabaseHelper(context.getApplicationContext());
+  public static ListenableFuture<ImmutableSet<String>> getAvailableRttTranscriptIds(
+      Context context, ImmutableSet<String> transcriptIds) {
+    return DialerExecutorComponent.get(context)
+        .backgroundExecutor()
+        .submit(() -> checkRttTranscriptAvailability(context, transcriptIds));
   }
 
-  @Override
-  protected void finalize() throws Throwable {
-    databaseHelper.close();
-    super.finalize();
-  }
-
-  /** @return true if there is RTT transcript available. */
   @WorkerThread
-  public boolean checkRttTranscriptAvailability(String transcriptId) {
+  private static ImmutableSet<String> checkRttTranscriptAvailability(
+      Context context, ImmutableSet<String> transcriptIds) {
     Assert.isWorkerThread();
+    RttTranscriptDatabaseHelper databaseHelper = new RttTranscriptDatabaseHelper(context);
+    ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+    Selection selection =
+        Selection.builder()
+            .and(Selection.column(RttTranscriptColumn.TRANSCRIPT_ID).in(transcriptIds))
+            .build();
+
     try (Cursor cursor =
         databaseHelper
             .getReadableDatabase()
             .query(
                 RttTranscriptDatabaseHelper.TABLE,
                 new String[] {RttTranscriptColumn.TRANSCRIPT_ID},
-                RttTranscriptColumn.TRANSCRIPT_ID + " = ?",
-                new String[] {transcriptId},
+                selection.getSelection(),
+                selection.getSelectionArgs(),
                 null,
                 null,
                 null)) {
-      if (cursor != null && cursor.moveToFirst()) {
-        return true;
-      } else {
-        return false;
+      if (cursor != null) {
+        while (cursor.moveToNext()) {
+          builder.add(cursor.getString(0));
+        }
       }
     }
+    databaseHelper.close();
+    return builder.build();
+  }
+
+  static ListenableFuture<RttTranscript> loadRttTranscript(Context context, String transcriptId) {
+    return DialerExecutorComponent.get(context)
+        .lightweightExecutor()
+        .submit(() -> getRttTranscript(context, transcriptId));
   }
 
   @WorkerThread
-  public RttTranscript getRttTranscript(String transcriptId) {
+  private static RttTranscript getRttTranscript(Context context, String transcriptId) {
     Assert.isWorkerThread();
+    RttTranscriptDatabaseHelper databaseHelper = new RttTranscriptDatabaseHelper(context);
     try (Cursor cursor =
         databaseHelper
             .getReadableDatabase()
@@ -85,17 +100,32 @@
       } else {
         return null;
       }
+    } finally {
+      databaseHelper.close();
     }
   }
 
+  public static ListenableFuture<Void> saveRttTranscript(
+      Context context, RttTranscript rttTranscript) {
+    return DialerExecutorComponent.get(context)
+        .backgroundExecutor()
+        .submit(
+            () -> {
+              save(context, rttTranscript);
+              return null;
+            });
+  }
+
   @WorkerThread
-  public void saveRttTranscript(RttTranscript rttTranscript) {
+  private static void save(Context context, RttTranscript rttTranscript) {
     Assert.isWorkerThread();
+    RttTranscriptDatabaseHelper databaseHelper = new RttTranscriptDatabaseHelper(context);
     ContentValues value = new ContentValues();
     value.put(RttTranscriptColumn.TRANSCRIPT_ID, rttTranscript.getId());
     value.put(RttTranscriptColumn.TRANSCRIPT_DATA, rttTranscript.toByteArray());
     long id =
         databaseHelper.getWritableDatabase().insert(RttTranscriptDatabaseHelper.TABLE, null, value);
+    databaseHelper.close();
     if (id < 0) {
       throw new RuntimeException("Failed to save RTT transcript");
     }
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index 2c1b5e7..4966110 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -58,7 +58,6 @@
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.DefaultFutureCallback;
-import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
 import com.android.dialer.configprovider.ConfigProviderBindings;
 import com.android.dialer.duo.DuoComponent;
@@ -91,7 +90,6 @@
 import com.android.incallui.videotech.ims.ImsVideoTech;
 import com.android.incallui.videotech.utils.VideoUtils;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -550,16 +548,10 @@
   public void onDestroy() {
     LogUtil.enterBlock("DialerCall.onDestroy");
     if (rttTranscript != null) {
-      RttTranscript rttTranscriptToSave = rttTranscript;
-      ListenableFuture<Void> future =
-          DialerExecutorComponent.get(context)
-              .backgroundExecutor()
-              .submit(
-                  () -> {
-                    new RttTranscriptUtil(context).saveRttTranscript(rttTranscriptToSave);
-                    return null;
-                  });
-      Futures.addCallback(future, new DefaultFutureCallback<>(), MoreExecutors.directExecutor());
+      Futures.addCallback(
+          RttTranscriptUtil.saveRttTranscript(context, rttTranscript),
+          new DefaultFutureCallback<>(),
+          MoreExecutors.directExecutor());
       // Sets to null so it won't be saved again when called multiple times.
       rttTranscript = null;
     }