Add RTT call chat window.

This change add a mock in simulator with a type bot that's simulating remote typing. The integration into incall UI will be in following changes.

Bug: 67596257
Test: RttChatMessageTest
PiperOrigin-RevId: 181211591
Change-Id: If6cdcb010afc0c25e90d3a44fe349920d5a856c6
diff --git a/assets/quantum/res/drawable/quantum_ic_done_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_done_vd_theme_24.xml
new file mode 100644
index 0000000..bd1887e
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_done_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
+</vector>
\ No newline at end of file
diff --git a/assets/quantum/res/drawable/quantum_ic_mic_off_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_mic_off_vd_theme_24.xml
new file mode 100644
index 0000000..0519c55
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_mic_off_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M19,11h-1.7c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zM14.98,11.17c0,-0.06 0.02,-0.11 0.02,-0.17L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v0.18l5.98,5.99zM4.27,3L3,4.27l6.01,6.01L9.01,11c0,1.66 1.33,3 2.99,3 0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52 -2.76,0 -5.3,-2.1 -5.3,-5.1L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9L19.73,21 21,19.73 4.27,3z"/>
+</vector>
\ No newline at end of file
diff --git a/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java b/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java
index c48e283..4ef579f 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java
@@ -31,6 +31,7 @@
 import com.android.dialer.enrichedcall.simulator.EnrichedCallSimulatorActivity;
 import com.android.dialer.persistentlog.PersistentLogger;
 import com.android.dialer.preferredsim.PreferredSimFallbackContract;
+import com.android.incallui.rtt.impl.RttChatActivity;
 
 /** Implements the top level simulator menu. */
 final class SimulatorMainMenu {
@@ -40,6 +41,7 @@
         .addItem("Voice call", SimulatorVoiceCall.getActionProvider(activity))
         .addItem(
             "IMS video", SimulatorVideoCall.getActionProvider(activity.getApplicationContext()))
+        .addItem("Rtt call mock", () -> simulateRttCallMock(activity.getApplicationContext()))
         .addItem(
             "Notifications",
             SimulatorNotifications.getActionProvider(activity.getApplicationContext()))
@@ -61,6 +63,10 @@
                     EnrichedCallSimulatorActivity.newIntent(activity.getApplicationContext())));
   }
 
+  private static void simulateRttCallMock(@NonNull Context context) {
+    context.startActivity(new Intent(context, RttChatActivity.class));
+  }
+
   private static void populateDatabase(@NonNull Context context) {
     DialerExecutorComponent.get(context)
         .dialerExecutorFactory()
diff --git a/java/com/android/incallui/rtt/impl/AndroidManifest.xml b/java/com/android/incallui/rtt/impl/AndroidManifest.xml
new file mode 100644
index 0000000..fc0705d
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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
+  -->
+<manifest
+    package="com.android.incallui.rtt.impl"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+  <application android:theme="@style/Theme.AppCompat">
+  <activity
+      android:name=".RttChatActivity"
+      android:exported="false"
+      android:theme="@style/DialerThemeBase.NoActionBar"
+      android:windowSoftInputMode="adjustResize"/>
+  </application>
+</manifest>
diff --git a/java/com/android/incallui/rtt/impl/RttChatActivity.java b/java/com/android/incallui/rtt/impl/RttChatActivity.java
new file mode 100644
index 0000000..96056f7
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/RttChatActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.incallui.rtt.impl;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+import android.view.View;
+
+/** Activity to for RTT chat window. */
+public class RttChatActivity extends FragmentActivity {
+
+  @Override
+  protected void onCreate(@Nullable Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_rtt);
+    getSupportFragmentManager()
+        .beginTransaction()
+        .add(
+            R.id.fragment_rtt,
+            RttChatFragment.newInstance("", "Jane Williamson", SystemClock.elapsedRealtime()))
+        .commit();
+    getWindow().setStatusBarColor(getColor(R.color.rtt_status_bar_color));
+    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
new file mode 100644
index 0000000..1db4c6b
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
@@ -0,0 +1,218 @@
+/*
+ * 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.incallui.rtt.impl;
+
+import android.content.Context;
+import android.support.annotation.MainThread;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/** Adapter class for holding RTT chat data. */
+public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolder> {
+
+  interface MessageListener {
+    void newMessageAdded();
+  }
+
+  private final Context context;
+  private final List<RttChatMessage> rttMessages = new ArrayList<>();
+  private int lastIndexOfLocalMessage = -1;
+  private int lastIndexOfRemoteMessage = -1;
+  private final TypeBot typeBot;
+  private final MessageListener messageListener;
+
+  RttChatAdapter(Context context, MessageListener listener) {
+    this.context = context;
+    this.messageListener = listener;
+    typeBot = new TypeBot(text -> ThreadUtil.postOnUiThread(() -> addRemoteMessage(text)));
+  }
+
+  @Override
+  public RttChatMessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    LayoutInflater layoutInflater = LayoutInflater.from(context);
+    View view = layoutInflater.inflate(R.layout.rtt_chat_list_item, parent, false);
+    return new RttChatMessageViewHolder(view);
+  }
+
+  @Override
+  public int getItemViewType(int position) {
+    return super.getItemViewType(position);
+  }
+
+  @Override
+  public void onBindViewHolder(RttChatMessageViewHolder rttChatMessageViewHolder, int i) {
+    boolean isSameGroup = false;
+    if (i > 0) {
+      isSameGroup = rttMessages.get(i).isRemote == rttMessages.get(i - 1).isRemote;
+    }
+    rttChatMessageViewHolder.setMessage(rttMessages.get(i), isSameGroup);
+  }
+
+  @Override
+  public int getItemCount() {
+    return rttMessages.size();
+  }
+
+  private void updateCurrentRemoteMessage(String newText) {
+    RttChatMessage rttChatMessage = null;
+    if (lastIndexOfRemoteMessage >= 0) {
+      rttChatMessage = rttMessages.get(lastIndexOfRemoteMessage);
+    }
+    RttChatMessage[] newMessages = RttChatMessage.getRemoteRttChatMessage(rttChatMessage, newText);
+
+    if (rttChatMessage == null) {
+      lastIndexOfRemoteMessage = rttMessages.size();
+      rttMessages.add(lastIndexOfRemoteMessage, newMessages[0]);
+      rttMessages.addAll(Arrays.asList(newMessages).subList(1, newMessages.length));
+      notifyItemRangeInserted(lastIndexOfRemoteMessage, newMessages.length);
+      lastIndexOfRemoteMessage = rttMessages.size() - 1;
+    } else {
+      rttMessages.set(lastIndexOfRemoteMessage, newMessages[0]);
+      int lastIndex = rttMessages.size();
+      rttMessages.addAll(Arrays.asList(newMessages).subList(1, newMessages.length));
+
+      notifyItemChanged(lastIndexOfRemoteMessage);
+      notifyItemRangeInserted(lastIndex, newMessages.length);
+    }
+    if (rttMessages.get(lastIndexOfRemoteMessage).isFinished()) {
+      lastIndexOfRemoteMessage = -1;
+    }
+  }
+
+  private void updateCurrentLocalMessage(String newMessage) {
+    RttChatMessage rttChatMessage = null;
+    if (lastIndexOfLocalMessage >= 0) {
+      rttChatMessage = rttMessages.get(lastIndexOfLocalMessage);
+    }
+    if (rttChatMessage == null || rttChatMessage.isFinished()) {
+      rttChatMessage = new RttChatMessage();
+      rttChatMessage.append(newMessage);
+      rttMessages.add(rttChatMessage);
+      lastIndexOfLocalMessage = rttMessages.size() - 1;
+      notifyItemInserted(lastIndexOfLocalMessage);
+    } else {
+      rttChatMessage.append(newMessage);
+      notifyItemChanged(lastIndexOfLocalMessage);
+    }
+  }
+
+  void addLocalMessage(String message) {
+    LogUtil.enterBlock("RttChatAdapater.addLocalMessage");
+    updateCurrentLocalMessage(message);
+    if (messageListener != null) {
+      messageListener.newMessageAdded();
+    }
+  }
+
+  void submitLocalMessage() {
+    LogUtil.enterBlock("RttChatAdapater.submitLocalMessage");
+    rttMessages.get(lastIndexOfLocalMessage).finish();
+    notifyItemChanged(lastIndexOfLocalMessage);
+    lastIndexOfLocalMessage = -1;
+    startChatBot();
+  }
+
+  void addRemoteMessage(String message) {
+    LogUtil.enterBlock("RttChatAdapater.addRemoteMessage");
+    if (TextUtils.isEmpty(message)) {
+      return;
+    }
+    updateCurrentRemoteMessage(message);
+    if (messageListener != null) {
+      messageListener.newMessageAdded();
+    }
+  }
+
+  private void startChatBot() {
+    typeBot.scheduleMessage();
+  }
+
+  // TODO(wangqi): Move this out of this class once a bug is fixed.
+  private static class TypeBot {
+    interface Callback {
+      void type(String text);
+    }
+
+    private static final String[] CANDIDATE_MESSAGES =
+        new String[] {
+          "To RTT or not to RTT, that is the question...",
+          "Making TTY great again!",
+          "I would be more comfortable with real \"Thyme\" chatting."
+              + " I don't know how to end this pun",
+          "お疲れ様でした",
+          "The FCC has mandated that I respond... I will do so begrudgingly",
+          "😂😂😂💯"
+        };
+    private final Random random = new Random();
+    private final Callback callback;
+    private final List<String> messageQueue = new ArrayList<>();
+    private int currentTypingPosition = -1;
+    private String currentTypingMessage = null;
+
+    TypeBot(Callback callback) {
+      this.callback = callback;
+    }
+
+    @MainThread
+    public void scheduleMessage() {
+      Assert.isMainThread();
+      if (random.nextDouble() < 0.5) {
+        return;
+      }
+
+      String text = CANDIDATE_MESSAGES[random.nextInt(CANDIDATE_MESSAGES.length)];
+      messageQueue.add(text);
+      typeMessage();
+    }
+
+    @MainThread
+    private void typeMessage() {
+      Assert.isMainThread();
+      if (currentTypingPosition < 0 || currentTypingMessage == null) {
+        if (messageQueue.size() <= 0) {
+          return;
+        }
+        currentTypingMessage = messageQueue.remove(0);
+        currentTypingPosition = 0;
+      }
+      if (currentTypingPosition < currentTypingMessage.length()) {
+        int size = random.nextInt(currentTypingMessage.length() - currentTypingPosition + 1);
+        callback.type(
+            currentTypingMessage.substring(currentTypingPosition, currentTypingPosition + size));
+        currentTypingPosition = currentTypingPosition + size;
+        // Wait up to 2s between typing.
+        ThreadUtil.postDelayedOnUiThread(this::typeMessage, 200 * random.nextInt(10));
+      } else {
+        callback.type(RttChatMessage.BUBBLE_BREAKER);
+        currentTypingPosition = -1;
+        currentTypingMessage = null;
+        // Wait 1-11s between two messages.
+        ThreadUtil.postDelayedOnUiThread(this::typeMessage, 1000 * (1 + random.nextInt(10)));
+      }
+    }
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java
new file mode 100644
index 0000000..0b0ad2a
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java
@@ -0,0 +1,176 @@
+/*
+ * 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.incallui.rtt.impl;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Chronometer;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+import com.android.incallui.rtt.impl.RttChatAdapter.MessageListener;
+
+/** RTT chat fragment to show chat bubbles. */
+public class RttChatFragment extends Fragment
+    implements OnClickListener, OnEditorActionListener, TextWatcher, MessageListener {
+
+  private static final String ARG_CALL_ID = "call_id";
+  private static final String ARG_NAME_OR_NUMBER = "name_or_number";
+  private static final String ARG_SESSION_START_TIME = "session_start_time";
+
+  private RecyclerView recyclerView;
+  private RttChatAdapter adapter;
+  private EditText editText;
+  private ImageButton submitButton;
+  private boolean isClearingInput;
+
+  private final OnScrollListener onScrollListener =
+      new OnScrollListener() {
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+          if (dy < 0) {
+            hideKeyboard();
+          }
+        }
+      };
+
+  /**
+   * Create a new instance of RttChatFragment.
+   *
+   * @param callId call id of the RTT call.
+   * @param nameOrNumber name or number of the caller to be displayed
+   * @param sessionStartTimeMillis start time of RTT session in terms of {@link
+   *     SystemClock#elapsedRealtime}.
+   * @return new RttChatFragment
+   */
+  public static RttChatFragment newInstance(
+      String callId, String nameOrNumber, long sessionStartTimeMillis) {
+    Bundle bundle = new Bundle();
+    bundle.putString(ARG_CALL_ID, callId);
+    bundle.putString(ARG_NAME_OR_NUMBER, nameOrNumber);
+    bundle.putLong(ARG_SESSION_START_TIME, sessionStartTimeMillis);
+    RttChatFragment instance = new RttChatFragment();
+    instance.setArguments(bundle);
+    return instance;
+  }
+
+  @Nullable
+  @Override
+  public View onCreateView(
+      LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+    View view = inflater.inflate(R.layout.frag_rtt_chat, container, false);
+    editText = view.findViewById(R.id.rtt_chat_input);
+    editText.setOnEditorActionListener(this);
+    editText.addTextChangedListener(this);
+    recyclerView = view.findViewById(R.id.rtt_recycler_view);
+    LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
+    layoutManager.setStackFromEnd(true);
+    recyclerView.setLayoutManager(layoutManager);
+    recyclerView.setHasFixedSize(false);
+    adapter = new RttChatAdapter(getContext(), this);
+    recyclerView.setAdapter(adapter);
+    recyclerView.addOnScrollListener(onScrollListener);
+    submitButton = view.findViewById(R.id.rtt_chat_submit_button);
+    submitButton.setOnClickListener(this);
+    submitButton.setEnabled(false);
+
+    String nameOrNumber = null;
+    Bundle bundle = getArguments();
+    if (bundle != null) {
+      nameOrNumber = bundle.getString(ARG_NAME_OR_NUMBER, getString(R.string.unknown));
+    }
+    TextView nameTextView = view.findViewById(R.id.rtt_name_or_number);
+    nameTextView.setText(nameOrNumber);
+
+    long sessionStartTime = SystemClock.elapsedRealtime();
+    if (bundle != null) {
+      sessionStartTime = bundle.getLong(ARG_SESSION_START_TIME, sessionStartTime);
+    }
+    Chronometer chronometer = view.findViewById(R.id.rtt_timer);
+    chronometer.setBase(sessionStartTime);
+    chronometer.start();
+    return view;
+  }
+
+  @Override
+  public void onClick(View v) {
+    if (v.getId() == R.id.rtt_chat_submit_button) {
+      adapter.submitLocalMessage();
+      isClearingInput = true;
+      editText.setText("");
+      isClearingInput = false;
+    }
+  }
+
+  @Override
+  public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+    if (actionId == EditorInfo.IME_ACTION_DONE) {
+      submitButton.performClick();
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+  @Override
+  public void onTextChanged(CharSequence s, int start, int before, int count) {
+    if (isClearingInput) {
+      return;
+    }
+    adapter.addLocalMessage(RttChatMessage.getChangedString(s, start, before, count));
+  }
+
+  @Override
+  public void afterTextChanged(Editable s) {
+    if (TextUtils.isEmpty(s)) {
+      submitButton.setEnabled(false);
+    } else {
+      submitButton.setEnabled(true);
+    }
+  }
+
+  @Override
+  public void newMessageAdded() {
+    recyclerView.smoothScrollToPosition(adapter.getItemCount());
+  }
+
+  private void hideKeyboard() {
+    InputMethodManager inputMethodManager = getContext().getSystemService(InputMethodManager.class);
+    if (inputMethodManager.isAcceptingText()) {
+      inputMethodManager.hideSoftInputFromWindow(
+          getActivity().getCurrentFocus().getWindowToken(), 0);
+    }
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/RttChatMessage.java b/java/com/android/incallui/rtt/impl/RttChatMessage.java
new file mode 100644
index 0000000..85b0451
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/RttChatMessage.java
@@ -0,0 +1,132 @@
+/*
+ * 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.incallui.rtt.impl;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextWatcher;
+import com.google.common.base.Splitter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/** Message class that holds one RTT chat content. */
+final class RttChatMessage {
+
+  static final String BUBBLE_BREAKER = "\n\n";
+  private static final Splitter SPLITTER = Splitter.on(BUBBLE_BREAKER);
+
+  boolean isRemote;
+  public boolean hasAvatar;
+  private final StringBuilder content = new StringBuilder();
+  private boolean isFinished;
+
+  public boolean isFinished() {
+    return isFinished;
+  }
+
+  public void finish() {
+    isFinished = true;
+  }
+
+  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) {
+        content.deleteCharAt(content.length() - 1);
+      }
+    }
+  }
+
+  public String getContent() {
+    return content.toString();
+  }
+
+  /**
+   * Generates delta change to a text.
+   *
+   * <p>This is used to track text change of input. See more details in {@link
+   * TextWatcher#onTextChanged}
+   *
+   * <p>e.g. "hello world" -> "hello" : "\b\b\b\b\b\b"
+   *
+   * <p>"hello world" -> "hello mom!" : "\b\b\b\b\bmom!"
+   *
+   * <p>"hello world" -> "hello d" : "\b\b\b\b\bd"
+   *
+   * <p>"hello world" -> "hello new world" : "\b\b\b\b\bnew world"
+   */
+  static String getChangedString(CharSequence s, int start, int before, int count) {
+    StringBuilder modify = new StringBuilder();
+    if (before > count) {
+      int deleteStart = start + count;
+      int deleted = before - count;
+      int numberUnModifiedCharsAfterDeleted = s.length() - start - count;
+      char c = '\b';
+      for (int i = 0; i < deleted + numberUnModifiedCharsAfterDeleted; i++) {
+        modify.append(c);
+      }
+      modify.append(s, deleteStart, s.length());
+    } else {
+      int insertStart = start + before;
+      int numberUnModifiedCharsAfterInserted = s.length() - start - count;
+      char c = '\b';
+      for (int i = 0; i < numberUnModifiedCharsAfterInserted; i++) {
+        modify.append(c);
+      }
+      modify.append(s, insertStart, s.length());
+    }
+    return modify.toString();
+  }
+
+  /** Convert remote input text into an array of {@code RttChatMessage}. */
+  static RttChatMessage[] getRemoteRttChatMessage(
+      @Nullable RttChatMessage currentMessage, @NonNull String text) {
+    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(BUBBLE_BREAKER)) {
+      firstMessage.finish();
+    }
+    messageList.add(firstMessage);
+
+    while (splitText.hasNext()) {
+      String singleMessageContent = splitText.next();
+      if (singleMessageContent.isEmpty()) {
+        continue;
+      }
+      RttChatMessage message = new RttChatMessage();
+      message.append(singleMessageContent);
+      message.isRemote = true;
+      if (splitText.hasNext()) {
+        message.finish();
+      }
+      messageList.add(message);
+    }
+
+    return messageList.toArray(new RttChatMessage[0]);
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/RttChatMessageViewHolder.java b/java/com/android/incallui/rtt/impl/RttChatMessageViewHolder.java
new file mode 100644
index 0000000..c88786a
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/RttChatMessageViewHolder.java
@@ -0,0 +1,65 @@
+/*
+ * 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.incallui.rtt.impl;
+
+import android.content.res.Resources;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+/** ViewHolder class for RTT chat message bubble. */
+public class RttChatMessageViewHolder extends ViewHolder {
+
+  private final TextView messageTextView;
+  private final Resources resources;
+  private final ImageView avatarImageView;
+  private final View container;
+
+  RttChatMessageViewHolder(View view) {
+    super(view);
+    container = view.findViewById(R.id.rtt_chat_message_container);
+    messageTextView = view.findViewById(R.id.rtt_chat_message);
+    avatarImageView = view.findViewById(R.id.rtt_chat_avatar);
+    resources = view.getResources();
+  }
+
+  void setMessage(RttChatMessage message, boolean isSameGroup) {
+    messageTextView.setText(message.getContent());
+    LinearLayout.LayoutParams params = (LayoutParams) container.getLayoutParams();
+    params.gravity = message.isRemote ? Gravity.START : Gravity.END;
+    params.topMargin =
+        isSameGroup
+            ? resources.getDimensionPixelSize(R.dimen.rtt_same_group_message_margin_top)
+            : resources.getDimensionPixelSize(R.dimen.rtt_message_margin_top);
+    container.setLayoutParams(params);
+    messageTextView.setEnabled(message.isRemote);
+    if (message.isRemote) {
+      if (isSameGroup) {
+        avatarImageView.setVisibility(View.INVISIBLE);
+      } else {
+        avatarImageView.setVisibility(View.VISIBLE);
+        avatarImageView.setImageResource(R.drawable.product_logo_avatar_anonymous_white_color_120);
+      }
+    } else {
+      avatarImageView.setVisibility(View.GONE);
+    }
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/res/color/message_bubble_color.xml b/java/com/android/incallui/rtt/impl/res/color/message_bubble_color.xml
new file mode 100644
index 0000000..b3729ee
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/color/message_bubble_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+      android:color="#E0E0E0" />
+  <item android:color="@android:color/white" />
+</selector>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/color/submit_button_background_color.xml b/java/com/android/incallui/rtt/impl/res/color/submit_button_background_color.xml
new file mode 100644
index 0000000..0da2c37
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/color/submit_button_background_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+      android:color="#BDBDBD" />
+  <item android:color="#FFFFFF" />
+</selector>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/color/submit_button_color.xml b/java/com/android/incallui/rtt/impl/res/color/submit_button_color.xml
new file mode 100644
index 0000000..2fe748f
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/color/submit_button_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+      android:color="#EEEEEE" />
+  <item android:color="#2A56C6" />
+</selector>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/drawable/input_bubble_background.xml b/java/com/android/incallui/rtt/impl/res/drawable/input_bubble_background.xml
new file mode 100644
index 0000000..ae37233
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/drawable/input_bubble_background.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:id="@+id/pill_stroke">
+    <shape android:shape="rectangle">
+      <padding
+          android:bottom="0dp"
+          android:left="1dp"
+          android:right="1dp"
+          android:top="0dp"/>
+      <solid android:color="@android:color/transparent"/>
+      <corners android:radius="24dp"/>
+    </shape>
+  </item>
+  <item>
+    <shape android:shape="rectangle">
+      <padding
+          android:bottom="1dp"
+          android:left="1dp"
+          android:right="1dp"
+          android:top="1dp"/>
+      <solid android:color="#102C3558"/>
+      <corners android:radius="24dp"/>
+    </shape>
+  </item>
+  <item>
+    <shape android:shape="rectangle">
+      <padding
+          android:bottom="1dp"
+          android:left="1dp"
+          android:right="1dp"
+          android:top="0dp"/>
+      <solid android:color="#202C3558"/>
+      <corners android:radius="24dp"/>
+    </shape>
+  </item>
+  <item>
+    <shape android:shape="rectangle">
+      <padding
+          android:bottom="1dp"
+          android:left="1dp"
+          android:right="1dp"
+          android:top="1dp"/>
+      <solid android:color="#382C3558"/>
+      <corners android:radius="24dp"/>
+    </shape>
+  </item>
+  <item>
+    <shape android:shape="rectangle">
+      <padding
+          android:bottom="1dp"
+          android:left="0dp"
+          android:right="0dp"
+          android:top="0dp"/>
+      <solid android:color="#582C3558"/>
+      <corners android:radius="24dp"/>
+    </shape>
+  </item>
+  <item android:drawable="@drawable/pill_background"/>
+</layer-list>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/drawable/message_bubble.xml b/java/com/android/incallui/rtt/impl/res/drawable/message_bubble.xml
new file mode 100644
index 0000000..2b01f62
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/drawable/message_bubble.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="@android:color/white"/>
+  <corners android:radius="20dp"/>
+</shape>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/drawable/pill_background.xml b/java/com/android/incallui/rtt/impl/res/drawable/pill_background.xml
new file mode 100644
index 0000000..cfad8df
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/drawable/pill_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <!-- Color is theme dependent and will be overridden in code. -->
+  <solid android:color="@android:color/white"/>
+  <corners android:radius="24dp"/>
+</shape>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/layout/activity_rtt.xml b/java/com/android/incallui/rtt/impl/res/layout/activity_rtt.xml
new file mode 100644
index 0000000..b48e8d4
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/layout/activity_rtt.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+  <FrameLayout
+      android:id="@+id/fragment_rtt"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml
new file mode 100644
index 0000000..7ba6a09
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/dialer_theme_color">
+
+  <include layout="@layout/rtt_banner"/>
+
+  <android.support.v7.widget.RecyclerView
+      android:id="@+id/rtt_recycler_view"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:paddingBottom="70dp"
+      android:clipToPadding="false"/>
+
+  <LinearLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginTop="6dp"
+      android:layout_marginBottom="4dp"
+      android:layout_marginStart="5dp"
+      android:layout_marginEnd="4dp"
+      android:layout_gravity="bottom"
+      android:orientation="horizontal">
+    <EditText
+        android:id="@+id/rtt_chat_input"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="10dp"
+        android:paddingBottom="12dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:background="@drawable/input_bubble_background"
+        android:hint="@string/rtt_input_hint"
+        android:imeOptions="actionDone"
+        android:inputType="textMultiLine|text"
+        android:maxLines="4"
+        android:minHeight="53dp"
+        android:textSize="16sp"/>
+    <ImageButton
+        android:id="@+id/rtt_chat_submit_button"
+        android:layout_width="55dp"
+        android:layout_height="53dp"
+        android:layout_gravity="bottom"
+        android:background="@drawable/input_bubble_background"
+        android:backgroundTint="@color/submit_button_background_color"
+        android:backgroundTintMode="multiply"
+        android:contentDescription="@string/content_description_rtt_check_button"
+        android:src="@drawable/quantum_ic_done_vd_theme_24"
+        android:tint="@color/submit_button_color"/>
+  </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
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
new file mode 100644
index 0000000..7d9cd6f
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="56dp"
+    android:background="#FAFAFA"
+    android:elevation="3dp">
+  <ImageButton
+      android:id="@+id/rtt_back"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_marginStart="16dp"
+      android:layout_alignParentStart="true"
+      android:layout_centerVertical="true"
+      android:background="@android:color/transparent"
+      android:contentDescription="@string/content_description_rtt_back_button"
+      android:src="@drawable/quantum_ic_close_vd_theme_24"
+      android:tint="#DF000000"/>
+  <LinearLayout
+      android:layout_width="wrap_content"
+      android:layout_height="match_parent"
+      android:layout_marginTop="8dp"
+      android:layout_marginBottom="8dp"
+      android:layout_marginStart="32dp"
+      android:layout_toEndOf="@id/rtt_back"
+      android:orientation="vertical">
+    <TextView
+        android:id="@+id/rtt_name_or_number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:includeFontPadding="false"
+        android:textAppearance="@style/Dialer.Incall.TextAppearance.RttTitle"
+        tools:text="Bruce Graham"/>
+    <Chronometer
+        android:id="@+id/rtt_timer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:includeFontPadding="false"
+        android:textAppearance="@style/Dialer.Incall.TextAppearance.RttTimer"
+        tools:text="00:09"/>
+  </LinearLayout>
+  <ImageButton
+      android:id="@+id/rtt_hang_up_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_marginEnd="16dp"
+      android:layout_alignParentEnd="true"
+      android:layout_centerVertical="true"
+      android:background="@android:color/transparent"
+      android:contentDescription="@string/incall_content_description_end_call"
+      android:src="@drawable/quantum_ic_call_end_vd_theme_24"
+      android:tint="#FFDF0000"/>
+  <ImageButton
+      android:id="@+id/rtt_speaker_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_marginEnd="24dp"
+      android:layout_centerVertical="true"
+      android:layout_toStartOf="@id/rtt_hang_up_button"
+      android:background="@android:color/transparent"
+      android:contentDescription="@string/incall_content_description_speaker"
+      android:src="@drawable/quantum_ic_volume_up_vd_theme_24"
+      android:tint="#DD000000"/>
+  <ImageButton
+      android:id="@+id/rtt_mic_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_marginEnd="24dp"
+      android:layout_centerVertical="true"
+      android:layout_toStartOf="@id/rtt_speaker_button"
+      android:background="@android:color/transparent"
+      android:contentDescription="@string/incall_content_description_unmuted"
+      android:src="@drawable/quantum_ic_mic_off_vd_theme_24"
+      android:tint="#DD000000"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_chat_list_item.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_chat_list_item.xml
new file mode 100644
index 0000000..54b0f4f
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_chat_list_item.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginEnd="8dp"
+    android:orientation="vertical">
+  <LinearLayout
+      android:id="@+id/rtt_chat_message_container"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/rtt_chat_avatar"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:visibility="gone"
+        android:contentDescription="@string/content_description_rtt_chat_avatar"/>
+    <TextView
+        android:id="@+id/rtt_chat_message"
+        style="@style/Dialer.Incall.TextAppearance.RttMessage"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingStart="12dp"
+        android:paddingEnd="12dp"
+        android:paddingTop="9dp"
+        android:paddingBottom="9dp"
+        android:background="@drawable/message_bubble"
+        android:backgroundTint="@color/message_bubble_color"
+        android:maxWidth="292dp"/>
+  </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/values/colors.xml b/java/com/android/incallui/rtt/impl/res/values/colors.xml
new file mode 100644
index 0000000..402cac4
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<resources>
+  <color name="rtt_status_bar_color">#E0E0E0</color>
+</resources>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/values/dimens.xml b/java/com/android/incallui/rtt/impl/res/values/dimens.xml
new file mode 100644
index 0000000..a3f230c
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<resources>
+  <dimen name="rtt_message_margin_top">16dp</dimen>
+  <dimen name="rtt_same_group_message_margin_top">2dp</dimen>
+</resources>
\ 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
new file mode 100644
index 0000000..79377ac
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<resources>
+  <!-- Content description for submit chat input button. [CHAR LIMIT=NONE] -->
+  <string name="content_description_rtt_check_button">Go ahead</string>
+
+  <!-- Content description for button of closing RTT chat window. [CHAR LIMIT=NONE] -->
+  <string name="content_description_rtt_back_button">Close RTT chat</string>
+
+  <!-- Hint text for input box. [CHAR LIMIT=NONE] -->
+  <string name="rtt_input_hint">Type a message</string>
+
+  <!-- Content description for avatar of RTT chat message. [CHAR LIMIT=NONE] -->
+  <string name="content_description_rtt_chat_avatar">Avatar</string>
+</resources>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/values/styles.xml b/java/com/android/incallui/rtt/impl/res/values/styles.xml
new file mode 100644
index 0000000..b4bb914
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/values/styles.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<resources>
+  <style name="Dialer.Incall.TextAppearance.RttTitle" parent="Dialer.Incall.TextAppearance">
+    <item name="android:fontFamily">sans-serif</item>
+    <item name="android:textColor">#DD000000</item>
+    <item name="android:textSize">20sp</item>
+  </style>
+
+  <style name="Dialer.Incall.TextAppearance.RttMessage" parent="Dialer.Incall.TextAppearance">
+    <item name="android:fontFamily">sans-serif</item>
+    <item name="android:textColor">#DD000000</item>
+    <item name="android:textSize">16sp</item>
+  </style>
+
+  <style name="Dialer.Incall.TextAppearance.RttTimer" parent="Dialer.Incall.TextAppearance">
+    <item name="android:fontFamily">sans-serif</item>
+    <item name="android:textColor">#DD000000</item>
+    <item name="android:textSize">14sp</item>
+  </style>
+</resources>
\ No newline at end of file
diff --git a/packages.mk b/packages.mk
index 03268fd..d01b3f1 100644
--- a/packages.mk
+++ b/packages.mk
@@ -75,6 +75,7 @@
 	com.android.incallui.hold \
 	com.android.incallui.incall.impl \
 	com.android.incallui.maps.impl \
+	com.android.incallui.rtt.impl \
 	com.android.incallui.sessiondata \
 	com.android.incallui.spam \
 	com.android.incallui.speakerbuttonlogic \