Add RTT transcript to call details.

1. Save RTT transcript when call is destroyed
2. Show RTT transcript link when it's available

Bug: 67596257,77717594
Test: manual
PiperOrigin-RevId: 192673172
Change-Id: If541ad9137c965166548d2fb449e835b95566727
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java
index 4dc294e..f0f9711 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivity.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java
@@ -134,7 +134,6 @@
 
     private void updateCallDetailsEntries(CallDetailsEntries newEntries) {
       activity.setCallDetailsEntries(newEntries);
-      activity.getAdapter().updateCallDetailsEntries(newEntries);
       EnrichedCallComponent.get(activity)
           .getEnrichedCallManager()
           .requestAllHistoricalData(activity.getNumber(), newEntries);
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
index dea1b8c..46705eb 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
@@ -26,9 +26,11 @@
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.support.annotation.CallSuper;
+import android.support.annotation.MainThread;
 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;
@@ -45,6 +47,7 @@
 import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
 import com.android.dialer.common.concurrent.DialerExecutor.Worker;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.common.concurrent.UiListener;
 import com.android.dialer.common.database.Selection;
 import com.android.dialer.constants.ActivityRequestCodes;
 import com.android.dialer.duo.Duo;
@@ -58,7 +61,9 @@
 import com.android.dialer.performancereport.PerformanceReport;
 import com.android.dialer.postcall.PostCall;
 import com.android.dialer.precall.PreCall;
+import com.android.dialer.rtt.RttTranscriptUtil;
 import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
 import com.google.i18n.phonenumbers.NumberParseException;
 import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
@@ -91,6 +96,7 @@
 
   private CallDetailsAdapterCommon adapter;
   private CallDetailsEntries callDetailsEntries;
+  private UiListener<CallDetailsEntries> checkRttTranscriptAvailabilityListener;
 
   /**
    * Handles the intent that launches {@link OldCallDetailsActivity} or {@link CallDetailsActivity},
@@ -121,6 +127,9 @@
         });
     handleIntent(getIntent());
     setupRecyclerViewForEntries();
+    checkRttTranscriptAvailabilityListener =
+        DialerExecutorComponent.get(this)
+            .createUiListener(getFragmentManager(), "Query RTT transcript availability");
   }
 
   @Override
@@ -143,6 +152,40 @@
     EnrichedCallComponent.get(this)
         .getEnrichedCallManager()
         .requestAllHistoricalData(getNumber(), callDetailsEntries);
+    checkRttTranscriptAvailabilityListener.listen(
+        this,
+        checkRttTranscriptAvailability(),
+        this::setCallDetailsEntries,
+        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);
+
+    CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder();
+    for (CallDetailsEntry entry : input.getEntriesList()) {
+      CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry);
+      newEntry.setHasRttTranscript(
+          rttTranscriptUtil.checkRttTranscriptAvailability(String.valueOf(entry.getDate())));
+      mutableCallDetailsEntries.addEntries(newEntry.build());
+    }
+    return mutableCallDetailsEntries.build();
   }
 
   @Override
@@ -185,8 +228,13 @@
     super.onBackPressed();
   }
 
+  @MainThread
   protected final void setCallDetailsEntries(CallDetailsEntries entries) {
+    Assert.isMainThread();
     this.callDetailsEntries = entries;
+    if (adapter != null) {
+      adapter.updateCallDetailsEntries(entries);
+    }
   }
 
   protected final CallDetailsEntries getCallDetailsEntries() {
@@ -415,7 +463,7 @@
       Map<CallDetailsEntry, List<HistoryResult>> mappedResults =
           getAllHistoricalData(activity.getNumber(), activity.callDetailsEntries);
 
-      activity.adapter.updateCallDetailsEntries(
+      activity.setCallDetailsEntries(
           generateAndMapNewCallDetailsEntriesHistoryResults(
               activity.getNumber(), activity.callDetailsEntries, mappedResults));
     }
diff --git a/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java b/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java
index 27feff8..a79642b 100644
--- a/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java
+++ b/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.support.annotation.CallSuper;
+import android.support.annotation.MainThread;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.LayoutInflater;
@@ -137,7 +138,9 @@
     return callDetailsEntries;
   }
 
+  @MainThread
   final void updateCallDetailsEntries(CallDetailsEntries entries) {
+    Assert.isMainThread();
     callDetailsEntries = entries;
     notifyDataSetChanged();
   }
diff --git a/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
index edf25df..5c4a196 100644
--- a/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
+++ b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
@@ -55,6 +55,7 @@
 
   private final TextView multimediaDetails;
   private final TextView postCallNote;
+  private final TextView rttTranscript;
 
   private final ImageView multimediaImage;
 
@@ -81,6 +82,7 @@
     multimediaImage = (ImageView) container.findViewById(R.id.multimedia_image);
     multimediaAttachmentsNumber =
         (TextView) container.findViewById(R.id.multimedia_attachments_number);
+    rttTranscript = container.findViewById(R.id.rtt_transcript);
   }
 
   void setCallDetails(
@@ -94,6 +96,9 @@
         (entry.getFeatures() & Calls.FEATURES_PULLED_EXTERNALLY)
             == Calls.FEATURES_PULLED_EXTERNALLY;
     boolean isDuoCall = entry.getIsDuoCall();
+    boolean isRttCall =
+        BuildCompat.isAtLeastP()
+            && (entry.getFeatures() & Calls.FEATURES_RTT) == Calls.FEATURES_RTT;
 
     callTime.setTextColor(getColorForCallType(context, callType));
     callTypeIcon.clear();
@@ -123,6 +128,18 @@
               context, entry.getDuration(), entry.getDataUsage()));
     }
     setMultimediaDetails(number, entry, showMultimediaDivider);
+    if (isRttCall) {
+      if (entry.getHasRttTranscript()) {
+        rttTranscript.setText(R.string.rtt_transcript_link);
+        rttTranscript.setTextAppearance(R.style.RttTranscriptLink);
+        rttTranscript.setClickable(true);
+      } else {
+        rttTranscript.setText(R.string.rtt_transcript_not_available);
+        rttTranscript.setTextAppearance(R.style.RttTranscriptMessage);
+        rttTranscript.setClickable(false);
+      }
+      rttTranscript.setVisibility(View.VISIBLE);
+    }
   }
 
   private void setMultimediaDetails(String number, CallDetailsEntry entry, boolean showDivider) {
diff --git a/java/com/android/dialer/calldetails/proto/call_details_entries.proto b/java/com/android/dialer/calldetails/proto/call_details_entries.proto
index 7a5700b..e0202cd 100644
--- a/java/com/android/dialer/calldetails/proto/call_details_entries.proto
+++ b/java/com/android/dialer/calldetails/proto/call_details_entries.proto
@@ -19,6 +19,7 @@
     optional int64 data_usage = 6;
     repeated enrichedcall.historyquery.proto.HistoryResult history_results = 7;
     optional bool is_duo_call = 8;
+    optional bool has_rtt_transcript = 9;
   }
 
   repeated CallDetailsEntry entries = 1;
diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml b/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml
index 3d4750d..1e98996 100644
--- a/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml
+++ b/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml
@@ -30,60 +30,71 @@
 
   <TextView
       android:id="@+id/call_type"
+      style="@style/PrimaryText"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginStart="@dimen/call_entry_text_left_margin"
       android:layout_marginEnd="16dp"
       android:layout_toStartOf="@+id/call_duration"
       android:gravity="start"
-      android:maxLines="100"
-      style="@style/PrimaryText"/>
+      android:maxLines="100"/>
 
   <TextView
       android:id="@+id/call_time"
+      style="@style/SecondaryText"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      android:layout_marginStart="@dimen/call_entry_text_left_margin"
-      android:layout_below="@+id/call_type"
       android:layout_marginBottom="@dimen/call_entry_bottom_padding"
-      style="@style/SecondaryText"/>
+      android:layout_marginStart="@dimen/call_entry_text_left_margin"
+      android:layout_below="@+id/call_type"/>
 
   <TextView
       android:id="@+id/call_duration"
+      style="@style/PrimaryText"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      android:layout_alignParentEnd="true"
       android:layout_marginEnd="@dimen/call_entry_padding"
-      style="@style/PrimaryText"/>
+      android:layout_alignParentEnd="true"/>
 
   <include
-      layout="@layout/ec_data_container"
       android:id="@+id/ec_container"
-      android:layout_height="@dimen/ec_container_height"
+      layout="@layout/ec_data_container"
       android:layout_width="match_parent"
+      android:layout_height="@dimen/ec_container_height"
       android:layout_below="@+id/call_time"
       android:visibility="gone"/>
 
   <TextView
       android:id="@+id/post_call_note"
+      style="@style/SecondaryText"
       android:layout_width="match_parent"
       android:layout_height="@dimen/ec_container_height"
       android:layout_below="@+id/ec_container"
       android:paddingStart="@dimen/call_entry_text_left_margin"
+      android:background="?attr/selectableItemBackground"
       android:gravity="center_vertical"
       android:maxLines="2"
-      android:visibility="gone"
-      android:background="?attr/selectableItemBackground"
-      style="@style/SecondaryText"/>
+      android:visibility="gone"/>
+  <TextView
+      android:id="@+id/rtt_transcript"
+      style="@style/SecondaryText"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="@dimen/call_entry_bottom_padding"
+      android:layout_marginStart="@dimen/call_entry_text_left_margin"
+      android:layout_marginEnd="@dimen/call_entry_padding"
+      android:layout_below="@id/post_call_note"
+      android:maxLines="2"
+      android:visibility="gone"/>
 
   <View
       android:id="@+id/divider"
       android:layout_width="match_parent"
       android:layout_height="1dp"
-      android:layout_below="@id/post_call_note"
       android:layout_marginTop="@dimen/ec_divider_top_bottom_margin"
       android:layout_marginBottom="@dimen/ec_divider_top_bottom_margin"
       android:layout_marginStart="@dimen/call_entry_text_left_margin"
+      android:layout_below="@id/rtt_transcript"
       android:background="#12000000"
       android:visibility="gone"/>
 </RelativeLayout>
\ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/values/strings.xml b/java/com/android/dialer/calldetails/res/values/strings.xml
index f816960..c3fff20 100644
--- a/java/com/android/dialer/calldetails/res/values/strings.xml
+++ b/java/com/android/dialer/calldetails/res/values/strings.xml
@@ -55,4 +55,10 @@
 
   <!-- A fallback string for the assisted dialing header incase parsing failes.. [CHAR LIMIT=NONE] -->
   <string name="assisted_dialing_country_code_entry_failure">Assisted dialing was used</string>
+
+  <!-- String shown when RTT transcript is not available for any reason. [CHAR LIMIT=NONE] -->
+  <string name="rtt_transcript_not_available">Transcript available only for calls with messaging</string>
+
+  <!-- String shown when RTT transcript is available. [CHAR LIMIT=NONE] -->
+  <string name="rtt_transcript_link">See transcript</string>
 </resources>
diff --git a/java/com/android/dialer/calldetails/res/values/styles.xml b/java/com/android/dialer/calldetails/res/values/styles.xml
index 1a2b529..93567ef 100644
--- a/java/com/android/dialer/calldetails/res/values/styles.xml
+++ b/java/com/android/dialer/calldetails/res/values/styles.xml
@@ -27,4 +27,16 @@
     <item name="android:textColor">#8A000000</item>
     <item name="android:textSize">14sp</item>
   </style>
+
+  <style name="RttTranscriptLink">
+    <item name="android:fontFamily">sans-serif-medium</item>
+    <item name="android:textColor">@color/dialer_link_color</item>
+    <item name="android:textAllCaps">true</item>
+    <item name="textAllCaps">true</item>
+  </style>
+  <style name="RttTranscriptMessage" parent="SecondaryText">
+    <item name="android:fontFamily">sans-serif</item>
+    <item name="android:textAllCaps">false</item>
+    <item name="textAllCaps">false</item>
+  </style>
 </resources>
\ No newline at end of file
diff --git a/java/com/android/dialer/rtt/RttTranscriptContract.java b/java/com/android/dialer/rtt/RttTranscriptContract.java
new file mode 100644
index 0000000..ddc9c53
--- /dev/null
+++ b/java/com/android/dialer/rtt/RttTranscriptContract.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.rtt;
+
+/** Contract for the RTT transcript database. */
+public final class RttTranscriptContract {
+
+  /** Columns for RTT transcript. */
+  static final class RttTranscriptColumn {
+
+    /**
+     * Unique key that should match {@link android.provider.CallLog.Calls#DATE} of the data row it
+     * is associated with.
+     *
+     * <p>TYPE: TEXT
+     */
+    static final String TRANSCRIPT_ID = "rtt_transcript_id";
+
+    /**
+     * Transcript data, encoded as {@link RttTranscript} proto.
+     *
+     * <p>TYPE: BLOB
+     */
+    static final String TRANSCRIPT_DATA = "transcript_data";
+  }
+}
diff --git a/java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java b/java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java
new file mode 100644
index 0000000..934eedb
--- /dev/null
+++ b/java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.rtt;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.SystemClock;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.rtt.RttTranscriptContract.RttTranscriptColumn;
+
+/** Database helper class for RTT transcript. */
+final class RttTranscriptDatabaseHelper extends SQLiteOpenHelper {
+
+  static final String TABLE = "rtt_transcript";
+
+  private static final String CREATE_TABLE_SQL =
+      "create table if not exists "
+          + TABLE
+          + " ("
+          + (RttTranscriptColumn.TRANSCRIPT_ID + " integer primary key, ")
+          + (RttTranscriptColumn.TRANSCRIPT_DATA + " blob not null")
+          + ");";
+
+  RttTranscriptDatabaseHelper(Context context) {
+    super(context, "rtt_transcript.db", null, 1);
+  }
+
+  @Override
+  public void onCreate(SQLiteDatabase db) {
+    LogUtil.enterBlock("RttTranscriptDatabaseHelper.onCreate");
+    long startTime = SystemClock.elapsedRealtime();
+    db.execSQL(CREATE_TABLE_SQL);
+    LogUtil.i(
+        "RttTranscriptDatabaseHelper.onCreate",
+        "took: %dms",
+        SystemClock.elapsedRealtime() - startTime);
+  }
+
+  @Override
+  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
+}
diff --git a/java/com/android/dialer/rtt/RttTranscriptUtil.java b/java/com/android/dialer/rtt/RttTranscriptUtil.java
new file mode 100644
index 0000000..e55d2ea
--- /dev/null
+++ b/java/com/android/dialer/rtt/RttTranscriptUtil.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.rtt;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.common.Assert;
+import com.android.dialer.rtt.RttTranscriptContract.RttTranscriptColumn;
+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);
+  }
+
+  /** @return true if there is RTT transcript available. */
+  @WorkerThread
+  public boolean checkRttTranscriptAvailability(String transcriptId) {
+    Assert.isWorkerThread();
+    try (Cursor cursor =
+        databaseHelper
+            .getReadableDatabase()
+            .query(
+                RttTranscriptDatabaseHelper.TABLE,
+                new String[] {RttTranscriptColumn.TRANSCRIPT_ID},
+                RttTranscriptColumn.TRANSCRIPT_ID + " = ?",
+                new String[] {transcriptId},
+                null,
+                null,
+                null)) {
+      if (cursor != null && cursor.moveToFirst()) {
+        return true;
+      } else {
+        return false;
+      }
+    }
+  }
+
+  @WorkerThread
+  public RttTranscript getRttTranscript(String transcriptId) {
+    Assert.isWorkerThread();
+    try (Cursor cursor =
+        databaseHelper
+            .getReadableDatabase()
+            .query(
+                RttTranscriptDatabaseHelper.TABLE,
+                new String[] {RttTranscriptColumn.TRANSCRIPT_DATA},
+                RttTranscriptColumn.TRANSCRIPT_ID + " = ?",
+                new String[] {transcriptId},
+                null,
+                null,
+                null)) {
+      if (cursor != null && cursor.moveToFirst()) {
+        try {
+          return RttTranscript.parseFrom(cursor.getBlob(0));
+        } catch (InvalidProtocolBufferException e) {
+          throw new RuntimeException("Parse failed for RTT transcript", e);
+        }
+      } else {
+        return null;
+      }
+    }
+  }
+
+  @WorkerThread
+  public void saveRttTranscript(RttTranscript rttTranscript) {
+    Assert.isWorkerThread();
+    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);
+    if (id < 0) {
+      throw new RuntimeException("Failed to save RTT transcript");
+    }
+  }
+}
diff --git a/java/com/android/dialer/theme/res/values/colors.xml b/java/com/android/dialer/theme/res/values/colors.xml
index 6b98e20..e80fc4b 100644
--- a/java/com/android/dialer/theme/res/values/colors.xml
+++ b/java/com/android/dialer/theme/res/values/colors.xml
@@ -46,6 +46,8 @@
   <!-- 38% opacity -->
   <color name="dialer_secondary_text_color_hiden">#61000000</color>
 
+  <color name="dialer_link_color">#2A56C6</color>
+
   <!-- Color of the theme of the Dialer app -->
   <color name="dialtacts_theme_color">@color/dialer_theme_color</color>
 
diff --git a/java/com/android/incallui/RttCallPresenter.java b/java/com/android/incallui/RttCallPresenter.java
index 5e83907..21e28ce 100644
--- a/java/com/android/incallui/RttCallPresenter.java
+++ b/java/com/android/incallui/RttCallPresenter.java
@@ -63,28 +63,34 @@
     LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiReady");
     InCallPresenter.getInstance().addListener(this);
     startListenOnRemoteMessage();
-    DialerCall call = CallList.getInstance().getActiveCall();
+    DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId());
     if (call != null) {
       rttCallScreen.onRestoreRttChat(call.getRttTranscript());
     }
   }
 
   @Override
+  public void onSaveRttTranscript() {
+    LogUtil.enterBlock("RttCallPresenter.onSaveRttTranscript");
+    DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId());
+    if (call != null) {
+      saveTranscript(call);
+    }
+  }
+
+  @Override
   public void onRttCallScreenUiUnready() {
     LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiUnready");
     InCallPresenter.getInstance().removeListener(this);
     stopListenOnRemoteMessage();
-    DialerCall call = CallList.getInstance().getActiveCall();
-    if (call != null) {
-      saveTranscript(call);
-    }
+    onSaveRttTranscript();
   }
 
   private void saveTranscript(DialerCall dialerCall) {
     LogUtil.enterBlock("RttCallPresenter.saveTranscript");
     RttTranscript.Builder builder = RttTranscript.newBuilder();
     builder
-        .setId(dialerCall.getNumber() + dialerCall.getCreationTimeMillis())
+        .setId(String.valueOf(dialerCall.getCreationTimeMillis()))
         .setTimestamp(dialerCall.getCreationTimeMillis())
         .setNumber(dialerCall.getNumber())
         .addAllMessages(rttCallScreen.getRttTranscriptMessageList());
@@ -100,9 +106,9 @@
   }
 
   private void startListenOnRemoteMessage() {
-    DialerCall call = CallList.getInstance().getActiveCall();
+    DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId());
     if (call == null) {
-      LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call is not active yet");
+      LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call does not exist");
       return;
     }
     rttCall = call.getRttCall();
diff --git a/java/com/android/incallui/call/CallList.java b/java/com/android/incallui/call/CallList.java
index eccdcee..04e02c4 100644
--- a/java/com/android/incallui/call/CallList.java
+++ b/java/com/android/incallui/call/CallList.java
@@ -745,6 +745,7 @@
     call.setState(DialerCall.State.IDLE);
     updateCallInMap(call);
     notifyGenericListeners();
+    call.onDestroy();
   }
 
   /**
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index 3372c03..5d32f46 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -57,6 +57,8 @@
 import com.android.dialer.callintent.CallSpecificAppData;
 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;
@@ -74,6 +76,7 @@
 import com.android.dialer.logging.Logger;
 import com.android.dialer.preferredsim.PreferredAccountRecorder;
 import com.android.dialer.rtt.RttTranscript;
+import com.android.dialer.rtt.RttTranscriptUtil;
 import com.android.dialer.telecom.TelecomCallUtil;
 import com.android.dialer.telecom.TelecomUtil;
 import com.android.dialer.theme.R;
@@ -86,6 +89,9 @@
 import com.android.incallui.videotech.empty.EmptyVideoTech;
 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;
 import java.util.ArrayList;
@@ -536,6 +542,28 @@
     return countryIso;
   }
 
+  /**
+   * Called when call is disconnected and removed from {@link CallList}, UI may already be destroyed
+   * at this point. This is last chance to do something for the call.
+   */
+  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());
+      // Sets to null so it won't be saved again when called multiple times.
+      rttTranscript = null;
+    }
+  }
+
   private void updateIsVoiceMailNumber() {
     if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) {
       isVoicemailNumber = true;
diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java
index 15baa8c..2d70b6b 100644
--- a/java/com/android/incallui/rtt/impl/RttChatFragment.java
+++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java
@@ -462,6 +462,9 @@
     } else {
       hideWaitingForJoinBanner();
     }
+    if (primaryCallState.state() == State.DISCONNECTED) {
+      rttCallScreenDelegate.onSaveRttTranscript();
+    }
   }
 
   private void showWaitingForJoinBanner() {
diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java
index 8c484a8..990c075 100644
--- a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java
+++ b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java
@@ -26,4 +26,6 @@
   void onRttCallScreenUiUnready();
 
   void onLocalMessage(String message);
+
+  void onSaveRttTranscript();
 }