Merge changes Ic7cb3be7,I412f8fbf,Ifebcbe87

* changes:
  Correctly display phone numbers containing whitespaces in RTL context.
  Add waiting for join banner to RTT outgoing call.
  Allow user to delete previous bubbles.
diff --git a/java/com/android/contacts/common/list/ContactTileView.java b/java/com/android/contacts/common/list/ContactTileView.java
index cfd52f3..072e07d 100644
--- a/java/com/android/contacts/common/list/ContactTileView.java
+++ b/java/com/android/contacts/common/list/ContactTileView.java
@@ -22,7 +22,6 @@
 import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.TextView;
 import com.android.contacts.common.MoreContactUtils;
 import com.android.contacts.common.R;
 import com.android.dialer.callintent.CallInitiationType;
@@ -30,6 +29,7 @@
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.contactphoto.ContactPhotoManager;
 import com.android.dialer.contactphoto.ContactPhotoManager.DefaultImageRequest;
+import com.android.dialer.widget.BidiTextView;
 
 /** A ContactTile displays a contact's picture and name */
 public abstract class ContactTileView extends FrameLayout {
@@ -38,7 +38,7 @@
   protected Listener mListener;
   private Uri mLookupUri;
   private ImageView mPhoto;
-  private TextView mName;
+  private BidiTextView mName;
   private ContactPhotoManager mPhotoManager = null;
 
   public ContactTileView(Context context, AttributeSet attrs) {
@@ -48,7 +48,7 @@
   @Override
   protected void onFinishInflate() {
     super.onFinishInflate();
-    mName = (TextView) findViewById(R.id.contact_tile_name);
+    mName = (BidiTextView) findViewById(R.id.contact_tile_name);
     mPhoto = (ImageView) findViewById(R.id.contact_tile_image);
 
     OnClickListener listener = createClickListener();
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
index 8b7a92b..71cbc8c 100644
--- a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
+++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
@@ -21,11 +21,12 @@
 import android.widget.TextView;
 import com.android.dialer.app.R;
 import com.android.dialer.calllogutils.CallTypeIconsView;
+import com.android.dialer.widget.BidiTextView;
 
 /** Encapsulates the views that are used to display the details of a phone call in the call log. */
 public final class PhoneCallDetailsViews {
 
-  public final TextView nameView;
+  public final BidiTextView nameView;
   public final View callTypeView;
   public final CallTypeIconsView callTypeIcons;
   public final TextView callLocationAndDate;
@@ -36,7 +37,7 @@
   public final TextView callAccountLabel;
 
   private PhoneCallDetailsViews(
-      TextView nameView,
+      BidiTextView nameView,
       View callTypeView,
       CallTypeIconsView callTypeIcons,
       TextView callLocationAndDate,
@@ -65,7 +66,7 @@
    */
   public static PhoneCallDetailsViews fromView(View view) {
     return new PhoneCallDetailsViews(
-        (TextView) view.findViewById(R.id.name),
+        (BidiTextView) view.findViewById(R.id.name),
         view.findViewById(R.id.call_type),
         (CallTypeIconsView) view.findViewById(R.id.call_type_icons),
         (TextView) view.findViewById(R.id.call_location_and_date),
@@ -78,7 +79,7 @@
 
   public static PhoneCallDetailsViews createForTest(Context context) {
     return new PhoneCallDetailsViews(
-        new TextView(context),
+        new BidiTextView(context),
         new View(context),
         new CallTypeIconsView(context),
         new TextView(context),
diff --git a/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java b/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java
index 330a361..6096ca8 100644
--- a/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java
+++ b/java/com/android/dialer/app/list/PhoneFavoriteSquareTileView.java
@@ -28,12 +28,11 @@
 import com.android.dialer.compat.CompatUtils;
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
+import com.android.dialer.widget.BidiTextView;
 
 /** Displays the contact's picture overlaid with their name and number type in a tile. */
 public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView {
 
-  private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
-
   private final float heightToWidthRatio;
 
   private ImageButton secondaryButton;
@@ -50,11 +49,12 @@
   @Override
   protected void onFinishInflate() {
     super.onFinishInflate();
-    final TextView nameView = (TextView) findViewById(R.id.contact_tile_name);
+    BidiTextView nameView = findViewById(R.id.contact_tile_name);
     nameView.setElegantTextHeight(false);
-    final TextView phoneTypeView = (TextView) findViewById(R.id.contact_tile_phone_type);
+
+    TextView phoneTypeView = findViewById(R.id.contact_tile_phone_type);
     phoneTypeView.setElegantTextHeight(false);
-    secondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
+    secondaryButton = findViewById(R.id.contact_tile_secondary_button);
   }
 
   @Override
diff --git a/java/com/android/dialer/app/res/layout/call_log_list_item.xml b/java/com/android/dialer/app/res/layout/call_log_list_item.xml
index acaa820..d111110 100644
--- a/java/com/android/dialer/app/res/layout/call_log_list_item.xml
+++ b/java/com/android/dialer/app/res/layout/call_log_list_item.xml
@@ -93,7 +93,7 @@
           android:gravity="center_vertical"
           android:layout_marginStart="@dimen/call_log_list_item_info_margin_start">
 
-          <TextView
+          <com.android.dialer.widget.BidiTextView
             android:id="@+id/name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml b/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml
index d2712e9..3aeba98 100644
--- a/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml
+++ b/java/com/android/dialer/app/res/layout/phone_favorite_tile_view.xml
@@ -63,7 +63,7 @@
           android:layout_height="wrap_content"
           android:gravity="center_vertical"
           android:orientation="horizontal">
-        <TextView
+        <com.android.dialer.widget.BidiTextView
             android:id="@+id/contact_tile_name"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
diff --git a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
index e4fded1..cb84a28 100644
--- a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
+++ b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
@@ -40,6 +40,7 @@
 import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent;
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
+import com.android.dialer.widget.BidiTextView;
 
 /**
  * ViewHolder for the header in {@link OldCallDetailsActivity} or {@link CallDetailsActivity}.
@@ -51,7 +52,7 @@
 
   private final CallDetailsHeaderListener callDetailsHeaderListener;
   private final ImageView callbackButton;
-  private final TextView nameView;
+  private final BidiTextView nameView;
   private final TextView numberView;
   private final TextView networkView;
   private final QuickContactBadge contactPhoto;
diff --git a/java/com/android/dialer/calldetails/res/layout/contact_container.xml b/java/com/android/dialer/calldetails/res/layout/contact_container.xml
index 5f531ab..9506183 100644
--- a/java/com/android/dialer/calldetails/res/layout/contact_container.xml
+++ b/java/com/android/dialer/calldetails/res/layout/contact_container.xml
@@ -44,7 +44,7 @@
       android:minHeight="@dimen/call_details_contact_photo_size"
       android:orientation="vertical">
 
-    <TextView
+    <com.android.dialer.widget.BidiTextView
         android:id="@+id/contact_name"
         style="@style/PrimaryText"
         android:layout_width="wrap_content"
diff --git a/java/com/android/dialer/contactsfragment/ContactViewHolder.java b/java/com/android/dialer/contactsfragment/ContactViewHolder.java
index 2730c0d..e188332 100644
--- a/java/com/android/dialer/contactsfragment/ContactViewHolder.java
+++ b/java/com/android/dialer/contactsfragment/ContactViewHolder.java
@@ -28,12 +28,13 @@
 import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener;
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
+import com.android.dialer.widget.BidiTextView;
 
 /** View holder for a contact. */
 final class ContactViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
 
   private final TextView header;
-  private final TextView name;
+  private final BidiTextView name;
   private final QuickContactBadge photo;
   private final Context context;
   private final OnContactSelectedListener onContactSelectedListener;
diff --git a/java/com/android/dialer/contactsfragment/res/layout/contact_row.xml b/java/com/android/dialer/contactsfragment/res/layout/contact_row.xml
index 9e829fe..b65a8c8 100644
--- a/java/com/android/dialer/contactsfragment/res/layout/contact_row.xml
+++ b/java/com/android/dialer/contactsfragment/res/layout/contact_row.xml
@@ -41,7 +41,7 @@
         android:layout_height="@dimen/photo_size"
         android:clickable="false"/>
 
-    <TextView
+    <com.android.dialer.widget.BidiTextView
         android:id="@+id/contact_name"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
diff --git a/java/com/android/dialer/i18n/DialerBidiFormatter.java b/java/com/android/dialer/i18n/DialerBidiFormatter.java
new file mode 100644
index 0000000..4ebaa66
--- /dev/null
+++ b/java/com/android/dialer/i18n/DialerBidiFormatter.java
@@ -0,0 +1,123 @@
+/*
+ * 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.i18n;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.text.BidiFormatter;
+import android.text.TextUtils;
+import android.util.Patterns;
+import com.android.dialer.common.Assert;
+import com.google.auto.value.AutoValue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+
+/**
+ * An enhanced version of {@link BidiFormatter} that can recognize a formatted phone number
+ * containing whitespaces.
+ *
+ * <p>Formatted phone numbers usually contain one or more whitespaces (e.g., "+1 650-253-0000",
+ * "(650) 253-0000", etc). {@link BidiFormatter} mistakes such a number for tokens separated by
+ * whitespaces. Therefore, these numbers can't be correctly shown in a RTL context (e.g., "+1
+ * 650-253-0000" would be shown as "650-253-0000 1+".)
+ */
+public final class DialerBidiFormatter {
+
+  private DialerBidiFormatter() {}
+
+  /**
+   * Divides the given text into segments, applies {@link BidiFormatter#unicodeWrap(CharSequence)}
+   * to each segment, and then reassembles the text.
+   *
+   * <p>A segment of the text is either a substring matching {@link Patterns#PHONE} or one that does
+   * not.
+   *
+   * @see BidiFormatter#unicodeWrap(CharSequence)
+   */
+  public static CharSequence unicodeWrap(@Nullable CharSequence text) {
+    if (TextUtils.isEmpty(text)) {
+      return text;
+    }
+
+    List<CharSequence> segments = segmentText(text);
+
+    StringBuilder formattedText = new StringBuilder();
+    for (CharSequence segment : segments) {
+      formattedText.append(BidiFormatter.getInstance().unicodeWrap(segment));
+    }
+
+    return formattedText.toString();
+  }
+
+  /**
+   * Segments the given text using {@link Patterns#PHONE}.
+   *
+   * <p>For example, "Mobile, +1 650-253-0000, 20 seconds" will be segmented into {"Mobile, ", "+1
+   * 650-253-0000", ", 20 seconds"}.
+   */
+  @VisibleForTesting
+  static List<CharSequence> segmentText(CharSequence text) {
+    Assert.checkArgument(!TextUtils.isEmpty(text));
+
+    List<CharSequence> segments = new ArrayList<>();
+
+    // Find the start index and the end index of each segment matching the phone number pattern.
+    Matcher matcher = Patterns.PHONE.matcher(text.toString());
+    List<Range> segmentRanges = new ArrayList<>();
+    while (matcher.find()) {
+      segmentRanges.add(Range.newBuilder().setStart(matcher.start()).setEnd(matcher.end()).build());
+    }
+
+    // Segment the text.
+    int currIndex = 0;
+    for (Range segmentRange : segmentRanges) {
+      if (currIndex < segmentRange.getStart()) {
+        segments.add(text.subSequence(currIndex, segmentRange.getStart()));
+      }
+
+      segments.add(text.subSequence(segmentRange.getStart(), segmentRange.getEnd()));
+      currIndex = segmentRange.getEnd();
+    }
+    if (currIndex < text.length()) {
+      segments.add(text.subSequence(currIndex, text.length()));
+    }
+
+    return segments;
+  }
+
+  /** Represents the start index (inclusive) and the end index (exclusive) of a text segment. */
+  @AutoValue
+  abstract static class Range {
+    static Builder newBuilder() {
+      return new AutoValue_DialerBidiFormatter_Range.Builder();
+    }
+
+    abstract int getStart();
+
+    abstract int getEnd();
+
+    @AutoValue.Builder
+    abstract static class Builder {
+      abstract Builder setStart(int start);
+
+      abstract Builder setEnd(int end);
+
+      abstract Range build();
+    }
+  }
+}
diff --git a/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml b/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
index 9be7fa0..a0d9dd2 100644
--- a/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
+++ b/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
@@ -39,7 +39,7 @@
       android:layout_centerVertical="true"
       android:layout_marginStart="8dp">
 
-    <TextView
+    <com.android.dialer.widget.BidiTextView
         android:id="@+id/primary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -47,7 +47,7 @@
         android:fontFamily="sans-serif"
         style="@style/PrimaryText"/>
 
-    <TextView
+    <com.android.dialer.widget.BidiTextView
         android:id="@+id/secondary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
index e36df4b..9d18e07 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
@@ -30,7 +30,6 @@
 import android.view.View.OnClickListener;
 import android.widget.ImageView;
 import android.widget.QuickContactBadge;
-import android.widget.TextView;
 import com.android.dialer.common.Assert;
 import com.android.dialer.contactphoto.ContactPhotoManager;
 import com.android.dialer.dialercontact.DialerContact;
@@ -44,6 +43,7 @@
 import com.android.dialer.searchfragment.common.R;
 import com.android.dialer.searchfragment.common.RowClickListener;
 import com.android.dialer.searchfragment.common.SearchCursor;
+import com.android.dialer.widget.BidiTextView;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -67,8 +67,8 @@
 
   private final RowClickListener listener;
   private final QuickContactBadge photo;
-  private final TextView nameOrNumberView;
-  private final TextView numberView;
+  private final BidiTextView nameOrNumberView;
+  private final BidiTextView numberView;
   private final ImageView callToActionView;
   private final Context context;
 
diff --git a/java/com/android/dialer/widget/BidiTextView.java b/java/com/android/dialer/widget/BidiTextView.java
new file mode 100644
index 0000000..6cf1aae
--- /dev/null
+++ b/java/com/android/dialer/widget/BidiTextView.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.widget;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.TextView;
+import com.android.dialer.i18n.DialerBidiFormatter;
+
+/** A {@link TextView} that applies bidirectional formatting to its text. */
+public final class BidiTextView extends TextView {
+
+  public BidiTextView(Context context) {
+    super(context);
+  }
+
+  public BidiTextView(Context context, @Nullable AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  @Override
+  public void setText(CharSequence text, BufferType type) {
+    super.setText(DialerBidiFormatter.unicodeWrap(text), type);
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
index 7e2b571..8d924c9 100644
--- a/java/com/android/incallui/rtt/impl/RttChatAdapter.java
+++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
@@ -37,13 +37,11 @@
   }
 
   private static final String KEY_MESSAGE_DATA = "key_message_data";
-  private static final String KEY_LAST_REMOTE_MESSAGE = "key_last_remote_message";
   private static final String KEY_LAST_LOCAL_MESSAGE = "key_last_local_message";
 
   private final Context context;
   private final List<RttChatMessage> rttMessages;
   private int lastIndexOfLocalMessage = -1;
-  private int lastIndexOfRemoteMessage = -1;
   private final MessageListener messageListener;
 
   RttChatAdapter(Context context, MessageListener listener, @Nullable Bundle savedInstanceState) {
@@ -53,7 +51,6 @@
       rttMessages = new ArrayList<>();
     } else {
       rttMessages = savedInstanceState.getParcelableArrayList(KEY_MESSAGE_DATA);
-      lastIndexOfRemoteMessage = savedInstanceState.getInt(KEY_LAST_REMOTE_MESSAGE);
       lastIndexOfLocalMessage = savedInstanceState.getInt(KEY_LAST_LOCAL_MESSAGE);
     }
   }
@@ -84,33 +81,6 @@
     return rttMessages.size();
   }
 
-  private void updateCurrentRemoteMessage(String newText) {
-    RttChatMessage rttChatMessage = null;
-    if (lastIndexOfRemoteMessage >= 0) {
-      rttChatMessage = rttMessages.get(lastIndexOfRemoteMessage);
-    }
-    List<RttChatMessage> newMessages =
-        RttChatMessage.getRemoteRttChatMessage(rttChatMessage, newText);
-
-    if (rttChatMessage == null) {
-      lastIndexOfRemoteMessage = rttMessages.size();
-      rttMessages.add(lastIndexOfRemoteMessage, newMessages.get(0));
-      rttMessages.addAll(newMessages.subList(1, newMessages.size()));
-      notifyItemRangeInserted(lastIndexOfRemoteMessage, newMessages.size());
-      lastIndexOfRemoteMessage = rttMessages.size() - 1;
-    } else {
-      rttMessages.set(lastIndexOfRemoteMessage, newMessages.get(0));
-      int lastIndex = rttMessages.size();
-      rttMessages.addAll(newMessages.subList(1, newMessages.size()));
-
-      notifyItemChanged(lastIndexOfRemoteMessage);
-      notifyItemRangeInserted(lastIndex, newMessages.size());
-    }
-    if (rttMessages.get(lastIndexOfRemoteMessage).isFinished()) {
-      lastIndexOfRemoteMessage = -1;
-    }
-  }
-
   private void updateCurrentLocalMessage(String newMessage) {
     RttChatMessage rttChatMessage = null;
     if (lastIndexOfLocalMessage >= 0) {
@@ -128,9 +98,6 @@
       if (TextUtils.isEmpty(rttChatMessage.getContent())) {
         rttMessages.remove(lastIndexOfLocalMessage);
         notifyItemRemoved(lastIndexOfLocalMessage);
-        if (lastIndexOfRemoteMessage > lastIndexOfLocalMessage) {
-          lastIndexOfRemoteMessage -= 1;
-        }
         lastIndexOfLocalMessage = -1;
       } else {
         notifyItemChanged(lastIndexOfLocalMessage);
@@ -138,8 +105,13 @@
     }
   }
 
+  private void updateCurrentRemoteMessage(String newMessage) {
+    RttChatMessage.updateRemoteRttChatMessage(rttMessages, newMessage);
+    lastIndexOfLocalMessage = RttChatMessage.getLastIndexLocalMessage(rttMessages);
+    notifyDataSetChanged();
+  }
+
   void addLocalMessage(String message) {
-    LogUtil.enterBlock("RttChatAdapater.addLocalMessage");
     updateCurrentLocalMessage(message);
     if (messageListener != null) {
       messageListener.newMessageAdded();
@@ -166,7 +138,6 @@
   }
 
   void addRemoteMessage(String message) {
-    LogUtil.enterBlock("RttChatAdapater.addRemoteMessage");
     if (TextUtils.isEmpty(message)) {
       return;
     }
@@ -176,9 +147,24 @@
     }
   }
 
+  /**
+   * Retrieve last local message and update the index. This is used when deleting to previous
+   * message bubble.
+   */
+  @Nullable
+  String retrieveLastLocalMessage() {
+    lastIndexOfLocalMessage = RttChatMessage.getLastIndexLocalMessage(rttMessages);
+    if (lastIndexOfLocalMessage >= 0) {
+      RttChatMessage rttChatMessage = rttMessages.get(lastIndexOfLocalMessage);
+      rttChatMessage.unfinish();
+      return rttChatMessage.getContent();
+    } else {
+      return null;
+    }
+  }
+
   void onSaveInstanceState(@NonNull Bundle bundle) {
     bundle.putParcelableArrayList(KEY_MESSAGE_DATA, (ArrayList<RttChatMessage>) rttMessages);
-    bundle.putInt(KEY_LAST_REMOTE_MESSAGE, lastIndexOfRemoteMessage);
     bundle.putInt(KEY_LAST_LOCAL_MESSAGE, lastIndexOfLocalMessage);
   }
 }
diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java
index 90bf199..56ac242 100644
--- a/java/com/android/incallui/rtt/impl/RttChatFragment.java
+++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java
@@ -103,6 +103,8 @@
   private boolean isTimerStarted;
   private RttOverflowMenu overflowMenu;
   private SecondaryInfo savedSecondaryInfo;
+  private TextView statusBanner;
+  private PrimaryInfo primaryInfo;
 
   /**
    * Create a new instance of RttChatFragment.
@@ -164,6 +166,26 @@
     editText = view.findViewById(R.id.rtt_chat_input);
     editText.setOnEditorActionListener(this);
     editText.addTextChangedListener(this);
+
+    editText.setOnKeyListener(
+        (v, keyCode, event) -> {
+          // This is only triggered when input method doesn't handle delete key, which means the
+          // current
+          // input box is empty.
+          if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
+            String lastMessage = adapter.retrieveLastLocalMessage();
+            if (lastMessage != null) {
+              isClearingInput = true;
+              editText.setText(lastMessage);
+              editText.setSelection(lastMessage.length());
+              isClearingInput = false;
+              rttCallScreenDelegate.onLocalMessage("\b");
+              return true;
+            }
+            return false;
+          }
+          return false;
+        });
     recyclerView = view.findViewById(R.id.rtt_recycler_view);
     LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
     layoutManager.setStackFromEnd(true);
@@ -201,13 +223,16 @@
 
     nameTextView = view.findViewById(R.id.rtt_name_or_number);
     chronometer = view.findViewById(R.id.rtt_timer);
+    statusBanner = view.findViewById(R.id.rtt_status_banner);
     return view;
   }
 
   @Override
   public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
     if (actionId == EditorInfo.IME_ACTION_SEND) {
-      submitButton.performClick();
+      if (!TextUtils.isEmpty(editText.getText())) {
+        submitButton.performClick();
+      }
       return true;
     }
     return false;
@@ -312,6 +337,7 @@
   public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
     LogUtil.i("RttChatFragment.setPrimary", primaryInfo.toString());
     nameTextView.setText(primaryInfo.name());
+    this.primaryInfo = primaryInfo;
   }
 
   @Override
@@ -359,6 +385,20 @@
       chronometer.start();
       isTimerStarted = true;
     }
+    if (primaryCallState.state() == State.DIALING) {
+      showWaitingForJoinBanner();
+    } else {
+      hideWaitingForJoinBanner();
+    }
+  }
+
+  private void showWaitingForJoinBanner() {
+    statusBanner.setText(getString(R.string.rtt_status_banner_text, primaryInfo.name()));
+    statusBanner.setVisibility(View.VISIBLE);
+  }
+
+  private void hideWaitingForJoinBanner() {
+    statusBanner.setVisibility(View.GONE);
   }
 
   @Override
diff --git a/java/com/android/incallui/rtt/impl/RttChatMessage.java b/java/com/android/incallui/rtt/impl/RttChatMessage.java
index fd83fb8..cbc53ef 100644
--- a/java/com/android/incallui/rtt/impl/RttChatMessage.java
+++ b/java/com/android/incallui/rtt/impl/RttChatMessage.java
@@ -19,10 +19,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
 import com.android.incallui.rtt.protocol.Constants;
 import com.google.common.base.Splitter;
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
@@ -44,13 +43,17 @@
     isFinished = true;
   }
 
+  void unfinish() {
+    isFinished = false;
+  }
+
   public void append(String text) {
     for (int i = 0; i < text.length(); i++) {
       char c = text.charAt(i);
-      if (c != '\b') {
-        content.append(c);
-      } else if (content.length() > 0) {
+      if (c == '\b' && content.length() > 0 && content.charAt(content.length() - 1) != '\b') {
         content.deleteCharAt(content.length() - 1);
+      } else {
+        content.append(c);
       }
     }
   }
@@ -87,39 +90,92 @@
     return modify.toString();
   }
 
-  /** Convert remote input text into an array of {@code RttChatMessage}. */
-  static List<RttChatMessage> getRemoteRttChatMessage(
-      @Nullable RttChatMessage currentMessage, @NonNull String text) {
+  /** Update list of {@code RttChatMessage} based on given remote text. */
+  static void updateRemoteRttChatMessage(List<RttChatMessage> messageList, @NonNull String text) {
+    Assert.isNotNull(messageList);
     Iterator<String> splitText = SPLITTER.split(text).iterator();
-    List<RttChatMessage> messageList = new ArrayList<>();
-
-    String firstMessageContent = splitText.next();
-    RttChatMessage firstMessage = currentMessage;
-    if (firstMessage == null) {
-      firstMessage = new RttChatMessage();
-      firstMessage.isRemote = true;
-    }
-    firstMessage.append(firstMessageContent);
-    if (splitText.hasNext() || text.endsWith(Constants.BUBBLE_BREAKER)) {
-      firstMessage.finish();
-    }
-    messageList.add(firstMessage);
 
     while (splitText.hasNext()) {
       String singleMessageContent = splitText.next();
-      if (singleMessageContent.isEmpty()) {
-        continue;
+      RttChatMessage message;
+      int index = getLastIndexUnfinishedRemoteMessage(messageList);
+      if (index < 0) {
+        message = new RttChatMessage();
+        message.append(singleMessageContent);
+        message.isRemote = true;
+        if (splitText.hasNext()) {
+          message.finish();
+        }
+        if (message.content.length() != 0) {
+          messageList.add(message);
+        }
+      } else {
+        message = messageList.get(index);
+        message.append(singleMessageContent);
+        if (splitText.hasNext()) {
+          message.finish();
+        }
+        if (message.content.length() == 0) {
+          messageList.remove(index);
+        }
       }
-      RttChatMessage message = new RttChatMessage();
-      message.append(singleMessageContent);
-      message.isRemote = true;
-      if (splitText.hasNext()) {
-        message.finish();
+      StringBuilder content = message.content;
+      // Delete previous messages.
+      while (content.length() > 0 && content.charAt(0) == '\b') {
+        messageList.remove(message);
+        content.delete(0, 1);
+        int previous = getLastIndexRemoteMessage(messageList);
+        // There are more backspaces than existing characters.
+        if (previous < 0) {
+          while (content.length() > 0 && content.charAt(0) == '\b') {
+            content.deleteCharAt(0);
+          }
+          // Add message if there are still characters after backspaces.
+          if (content.length() > 0) {
+            message = new RttChatMessage();
+            message.append(content.toString());
+            message.isRemote = true;
+            if (splitText.hasNext()) {
+              message.finish();
+            }
+            messageList.add(message);
+          }
+          break;
+        }
+        message = messageList.get(previous);
+        message.unfinish();
+        message.append(content.toString());
+        content = message.content;
       }
-      messageList.add(message);
     }
+    if (text.endsWith(Constants.BUBBLE_BREAKER)) {
+      int lastIndexRemoteMessage = getLastIndexRemoteMessage(messageList);
+      messageList.get(lastIndexRemoteMessage).finish();
+    }
+  }
 
-    return messageList;
+  private static int getLastIndexUnfinishedRemoteMessage(List<RttChatMessage> messageList) {
+    int i = messageList.size() - 1;
+    while (i >= 0 && (!messageList.get(i).isRemote || messageList.get(i).isFinished)) {
+      i--;
+    }
+    return i;
+  }
+
+  private static int getLastIndexRemoteMessage(List<RttChatMessage> messageList) {
+    int i = messageList.size() - 1;
+    while (i >= 0 && !messageList.get(i).isRemote) {
+      i--;
+    }
+    return i;
+  }
+
+  static int getLastIndexLocalMessage(List<RttChatMessage> messageList) {
+    int i = messageList.size() - 1;
+    while (i >= 0 && messageList.get(i).isRemote) {
+      i--;
+    }
+    return i;
   }
 
   @Override
diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
index c4d3a42..d62cf18 100644
--- a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
+++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
@@ -83,5 +83,15 @@
       android:id="@id/rtt_on_hold_banner"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"/>
+  <TextView
+      android:id="@+id/rtt_status_banner"
+      android:layout_width="match_parent"
+      android:layout_height="48dp"
+      android:paddingStart="16dp"
+      android:paddingEnd="16dp"
+      android:background="@android:color/white"
+      android:gravity="center_vertical"
+      android:textColor="#DD000000"
+      android:textSize="14sp"/>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/values/strings.xml b/java/com/android/incallui/rtt/impl/res/values/strings.xml
index dce16fd..b0ac205 100644
--- a/java/com/android/incallui/rtt/impl/res/values/strings.xml
+++ b/java/com/android/incallui/rtt/impl/res/values/strings.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <!-- Content description for submit chat input button. [CHAR LIMIT=NONE] -->
   <string name="content_description_rtt_check_button">Go ahead</string>
 
@@ -27,4 +27,7 @@
   <!-- Text for back button. [CHAR LIMIT=20] -->
   <string name="rtt_button_back">Back</string>
 
+  <!-- Text for status banner. [CHAT LIMIT=100] -->
+  <string name="rtt_status_banner_text">Waiting for <xliff:g id="name">%s</xliff:g> to join RTT call&#8230;</string>
+
 </resources>
\ No newline at end of file