Merge "Create messenger preview screen for magnification gesture settings screen" into nyc-dev
diff --git a/res/drawable-hdpi/msg_bubble_incoming.9.png b/res/drawable-hdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..7fed0a8
--- /dev/null
+++ b/res/drawable-hdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-hdpi/msg_bubble_outgoing.9.png b/res/drawable-hdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..8e7ccc0
--- /dev/null
+++ b/res/drawable-hdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-hdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..8e7ccc0
--- /dev/null
+++ b/res/drawable-ldrtl-hdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-hdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..7fed0a8
--- /dev/null
+++ b/res/drawable-ldrtl-hdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-mdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..5ca9b14
--- /dev/null
+++ b/res/drawable-ldrtl-mdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-mdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..4e7e464
--- /dev/null
+++ b/res/drawable-ldrtl-mdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-xhdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..52cb936
--- /dev/null
+++ b/res/drawable-ldrtl-xhdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-xhdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..0661fdb
--- /dev/null
+++ b/res/drawable-ldrtl-xhdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-xxhdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..5d8fc74
--- /dev/null
+++ b/res/drawable-ldrtl-xxhdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-xxhdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..009c721
--- /dev/null
+++ b/res/drawable-ldrtl-xxhdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/msg_bubble_incoming.9.png b/res/drawable-ldrtl-xxxhdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..8378db5
--- /dev/null
+++ b/res/drawable-ldrtl-xxxhdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/msg_bubble_outgoing.9.png b/res/drawable-ldrtl-xxxhdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..f0d0d9e
--- /dev/null
+++ b/res/drawable-ldrtl-xxxhdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-mdpi/msg_bubble_incoming.9.png b/res/drawable-mdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..4e7e464
--- /dev/null
+++ b/res/drawable-mdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-mdpi/msg_bubble_outgoing.9.png b/res/drawable-mdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..5ca9b14
--- /dev/null
+++ b/res/drawable-mdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/msg_bubble_incoming.9.png b/res/drawable-xhdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..0661fdb
--- /dev/null
+++ b/res/drawable-xhdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/msg_bubble_outgoing.9.png b/res/drawable-xhdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..52cb936
--- /dev/null
+++ b/res/drawable-xhdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/msg_bubble_incoming.9.png b/res/drawable-xxhdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..ab693b3
--- /dev/null
+++ b/res/drawable-xxhdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/msg_bubble_outgoing.9.png b/res/drawable-xxhdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..5b2a2ff
--- /dev/null
+++ b/res/drawable-xxhdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/msg_bubble_incoming.9.png b/res/drawable-xxxhdpi/msg_bubble_incoming.9.png
new file mode 100644
index 0000000..34130fc
--- /dev/null
+++ b/res/drawable-xxxhdpi/msg_bubble_incoming.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/msg_bubble_outgoing.9.png b/res/drawable-xxxhdpi/msg_bubble_outgoing.9.png
new file mode 100644
index 0000000..cfd5734
--- /dev/null
+++ b/res/drawable-xxxhdpi/msg_bubble_outgoing.9.png
Binary files differ
diff --git a/res/drawable/conversation_message_icon.xml b/res/drawable/conversation_message_icon.xml
new file mode 100644
index 0000000..a1192ec
--- /dev/null
+++ b/res/drawable/conversation_message_icon.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2015 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="oval">
+
+   <solid
+       android:color="#FF0000"/>
+
+   <size
+       android:width="42dp"
+        android:height="42dp"/>
+</shape>
diff --git a/res/layout/conversation_message_content.xml b/res/layout/conversation_message_content.xml
new file mode 100644
index 0000000..7bc53e5
--- /dev/null
+++ b/res/layout/conversation_message_content.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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:id="@+id/message_content"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" >
+
+    <com.android.settings.display.MessageBubbleBackground
+        android:id="@+id/message_text_and_info"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:minHeight="@dimen/conversation_message_contact_icon_size"
+        android:layout_marginTop="0dp" >
+
+        <TextView
+            android:id="@+id/message_text"
+            android:textSize="@dimen/conversation_message_text_size"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/message_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/conversation_status_text_size" />
+
+    </com.android.settings.display.MessageBubbleBackground>
+
+</LinearLayout>
diff --git a/res/layout/conversation_message_icon.xml b/res/layout/conversation_message_icon.xml
new file mode 100644
index 0000000..4ffd285
--- /dev/null
+++ b/res/layout/conversation_message_icon.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/conversation_icon"
+    android:layout_width="@dimen/conversation_message_contact_icon_size"
+    android:layout_height="@dimen/conversation_message_contact_icon_size"
+    android:fontFamily="sans-serif"
+    android:textStyle="bold"
+    android:textSize="@dimen/conversation_message_contact_icon_text_size"
+    android:gravity="center" />
diff --git a/res/layout/screen_zoom_preview_1.xml b/res/layout/screen_zoom_preview_1.xml
index ca2ecca..62cd093 100644
--- a/res/layout/screen_zoom_preview_1.xml
+++ b/res/layout/screen_zoom_preview_1.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 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.
@@ -14,10 +14,52 @@
      limitations under the License.
 -->
 
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:scrollbars="none"
-    android:background="?android:attr/colorBackgroundFloating">
+    android:background="@color/conversation_background"
+    android:padding="@dimen/conversation_message_list_padding"
+    android:orientation="vertical" >
 
-</ScrollView>
+    <com.android.settings.display.ConversationMessageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:incoming="false"
+        app:messageText="@string/screen_zoom_conversation_message_1"
+        app:timestampText="@string/screen_zoom_conversation_timestamp_1"
+        app:iconText="@string/screen_zoom_conversation_icon_alex"
+        app:iconTextColor="@color/message_icon_text_outgoing"
+        app:iconBackgroundColor="@color/message_icon_background_outgoing" />
+
+    <com.android.settings.display.ConversationMessageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:incoming="true"
+        app:messageText="@string/screen_zoom_conversation_message_2"
+        app:timestampText="@string/screen_zoom_conversation_timestamp_2"
+        app:iconText="@string/screen_zoom_conversation_icon_pete"
+        app:iconTextColor="@color/message_icon_text_incoming"
+        app:iconBackgroundColor="@color/message_icon_background_incoming" />
+
+    <com.android.settings.display.ConversationMessageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:incoming="false"
+        app:messageText="@string/screen_zoom_conversation_message_3"
+        app:timestampText="@string/screen_zoom_conversation_timestamp_3"
+        app:iconText="@string/screen_zoom_conversation_icon_alex"
+        app:iconTextColor="@color/message_icon_text_outgoing"
+        app:iconBackgroundColor="@color/message_icon_background_outgoing" />
+
+    <com.android.settings.display.ConversationMessageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:incoming="true"
+        app:messageText="@string/screen_zoom_conversation_message_4"
+        app:timestampText="@string/screen_zoom_conversation_timestamp_4"
+        app:iconText="@string/screen_zoom_conversation_icon_pete"
+        app:iconTextColor="@color/message_icon_text_incoming"
+        app:iconBackgroundColor="@color/message_icon_background_incoming" />
+
+</LinearLayout>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index d94ff13..c6dd246 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -106,6 +106,16 @@
         <attr name="currentPageIndicatorColor" format="color" />
     </declare-styleable>
 
+    <!-- For ConversationMessageView -->
+    <declare-styleable name="ConversationMessageView">
+        <attr name="incoming" format="boolean" />
+        <attr name="messageText" format="reference" />
+        <attr name="timestampText" format="reference" />
+        <attr name="iconText" format="reference" />
+        <attr name="iconTextColor" format="reference|color" />
+        <attr name="iconBackgroundColor" format="reference|color" />
+    </declare-styleable>
+
     <attr name="switchBarTheme" format="reference" />
     <attr name="switchBarMarginStart" format="dimension" />
     <attr name="switchBarMarginEnd" format="dimension" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 90884c9..f27b693 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -111,7 +111,20 @@
     <color name="material_blue_500">#4285F4</color>
     <color name="material_blue_700">#3367D6</color>
 
+    <color name="message_text_incoming">#ffffffff</color>
+    <color name="message_text_outgoing">#ff323232</color>
+    <color name="timestamp_text_outgoing">#99323232</color>
+    <color name="timestamp_text_incoming">#99ffffff</color>
+    <color name="message_bubble_incoming">#689f38</color>
+    <color name="message_bubble_outgoing">#ffffffff</color>
+    <color name="conversation_background">#eeeeee</color>
+    <color name="message_icon_background_incoming">#689f38</color>
+    <color name="message_icon_text_incoming">#ffffffff</color>
+    <color name="message_icon_background_outgoing">#4285f4</color>
+    <color name="message_icon_text_outgoing">#ffffffff</color>
+
+    <color name="seek_bar_preference_preview_text">#fff</color>
     <!-- Black at 80% opacity -->
-    <color name="seek_bar_preference_preview_border_tint">#CC000000</color>
+    <color name="seek_bar_preference_preview_border_tint">#cc000000</color>
 
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 14aac55..53dd515 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -258,6 +258,18 @@
     <!-- Display, Screen zoom -->
     <dimen name="screen_zoom_preview_height">240dp</dimen>
     <dimen name="screen_zoom_preview_app_icon_width">88dp</dimen>
+    <dimen name="conversation_message_list_padding">10dp</dimen>
+    <dimen name="conversation_message_contact_icon_size">42dp</dimen>
+    <dimen name="conversation_message_contact_icon_text_size">32sp</dimen>
+    <dimen name="conversation_message_text_size">16sp</dimen>
+    <dimen name="conversation_status_text_size">12sp</dimen>
+    <dimen name="conversation_bubble_width_snap">20dp</dimen>
+    <dimen name="message_bubble_arrow_width">9dp</dimen>
+    <dimen name="message_padding_default">18dp</dimen>
+    <dimen name="message_text_left_right_padding">14dp</dimen>
+    <dimen name="message_text_top_padding">10dp</dimen>
+    <dimen name="message_text_bottom_padding">12dp</dimen>
+    <dimen name="message_metadata_top_padding">4dp</dimen>
 
     <!-- Accessibility Settings -->
     <dimen name="accessibility_layout_margin_start_end">24dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0c2d51a..89dd47e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6887,6 +6887,27 @@
          density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
     <string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
 
+    <!-- Name Initial shown in the conversation message icon. [CHAR LIMIT=1] -->
+    <string name="screen_zoom_conversation_icon_alex">A</string>
+    <!-- Name Initial shown in the conversation message icon. [CHAR LIMIT=1] -->
+    <string name="screen_zoom_conversation_icon_pete">P</string>
+    <!-- Conversation message body of the messaging app preview screen. [CHAR LIMIT=NONE] -->
+    <string name="screen_zoom_conversation_message_1">Hi Pete!</string>
+    <!-- Conversation message body of the messaging app preview screen. [CHAR LIMIT=NONE] -->
+    <string name="screen_zoom_conversation_message_2">Hey, want to grab coffee and catch up today?</string>
+    <!-- Conversation message body of the messaging app preview screen. [CHAR LIMIT=NONE] -->
+    <string name="screen_zoom_conversation_message_3">Sounds great. I know of a good place not too far from here.</string>
+    <!-- Conversation message body of the messaging app preview screen. [CHAR LIMIT=NONE] -->
+    <string name="screen_zoom_conversation_message_4">Perfect!</string>
+    <!-- Conversation message timestamp of the messaging app preview screen. [CHAR LIMIT=20] -->
+    <string name="screen_zoom_conversation_timestamp_1">Tue 6:00PM</string>
+    <!-- Conversation message timestamp of the messaging app preview screen. [CHAR LIMIT=20] -->
+    <string name="screen_zoom_conversation_timestamp_2">Tue 6:01PM</string>
+    <!-- Conversation message timestamp of the messaging app preview screen. [CHAR LIMIT=20] -->
+    <string name="screen_zoom_conversation_timestamp_3">Tue 6:02PM</string>
+    <!-- Conversation message timestamp of the messaging app preview screen. [CHAR LIMIT=20] -->
+    <string name="screen_zoom_conversation_timestamp_4">Tue 6:03PM</string>
+
     <!-- Button to show all top-level settings items [CHAR LIMIT=20] -->
     <string name="see_all">See all</string>
     <!-- Button to show less top-level settings items [CHAR LIMIT=20] -->
diff --git a/src/com/android/settings/display/ConversationMessageView.java b/src/com/android/settings/display/ConversationMessageView.java
new file mode 100644
index 0000000..a0889c4
--- /dev/null
+++ b/src/com/android/settings/display/ConversationMessageView.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2016 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.settings.display;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * The view for a single entry in a conversation. This is a simplified version of
+ * com.android.messaging.ui.conversation.ConversationMessageView class.
+ */
+public class ConversationMessageView extends FrameLayout {
+    private final boolean mIncoming;
+    private final CharSequence mMessageText;
+    private final CharSequence mTimestampText;
+    private final CharSequence mIconText;
+    private final int mIconTextColor;
+    private final int mIconBackgroundColor;
+
+    private LinearLayout mMessageBubble;
+    private ViewGroup mMessageTextAndInfoView;
+    private TextView mMessageTextView;
+    private TextView mStatusTextView;
+    private TextView mContactIconView;
+
+    public ConversationMessageView(Context context) {
+        this(context, null);
+    }
+
+    public ConversationMessageView(final Context context, final AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ConversationMessageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ConversationMessageView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.ConversationMessageView);
+
+        mIncoming = a.getBoolean(R.styleable.ConversationMessageView_incoming, true);
+        mMessageText = a.getString(R.styleable.ConversationMessageView_messageText);
+        mTimestampText = a.getString(R.styleable.ConversationMessageView_timestampText);
+        mIconText = a.getString(R.styleable.ConversationMessageView_iconText);
+        mIconTextColor = a.getColor(R.styleable.ConversationMessageView_iconTextColor, 0);
+        mIconBackgroundColor = a.getColor(R.styleable.ConversationMessageView_iconBackgroundColor,
+                0);
+
+        LayoutInflater.from(context).inflate(R.layout.conversation_message_icon, this);
+        LayoutInflater.from(context).inflate(R.layout.conversation_message_content, this);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mMessageBubble = (LinearLayout) findViewById(R.id.message_content);
+        mMessageTextAndInfoView = (ViewGroup) findViewById(R.id.message_text_and_info);
+        mMessageTextView = (TextView) findViewById(R.id.message_text);
+        mStatusTextView = (TextView) findViewById(R.id.message_status);
+        mContactIconView = (TextView) findViewById(R.id.conversation_icon);
+        updateViewContent();
+    }
+
+    @Override
+    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+        updateViewAppearance();
+
+        final int horizontalSpace = MeasureSpec.getSize(widthMeasureSpec);
+        final int iconSize = getResources()
+                .getDimensionPixelSize(R.dimen.conversation_message_contact_icon_size);
+
+        final int unspecifiedMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int iconMeasureSpec = MeasureSpec.makeMeasureSpec(iconSize, MeasureSpec.EXACTLY);
+
+        mContactIconView.measure(iconMeasureSpec, iconMeasureSpec);
+
+        final int arrowWidth =
+                getResources().getDimensionPixelSize(R.dimen.message_bubble_arrow_width);
+
+        // We need to subtract contact icon width twice from the horizontal space to get
+        // the max leftover space because we want the message bubble to extend no further than the
+        // starting position of the message bubble in the opposite direction.
+        final int maxLeftoverSpace = horizontalSpace - mContactIconView.getMeasuredWidth() * 2
+                - arrowWidth - getPaddingLeft() - getPaddingRight();
+        final int messageContentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(maxLeftoverSpace,
+                MeasureSpec.AT_MOST);
+
+        mMessageBubble.measure(messageContentWidthMeasureSpec, unspecifiedMeasureSpec);
+
+        final int maxHeight = Math.max(mContactIconView.getMeasuredHeight(),
+                mMessageBubble.getMeasuredHeight());
+        setMeasuredDimension(horizontalSpace, maxHeight + getPaddingBottom() + getPaddingTop());
+    }
+
+    @Override
+    protected void onLayout(final boolean changed, final int left, final int top, final int right,
+            final int bottom) {
+        final boolean isRtl = isLayoutRtl(this);
+
+        final int iconWidth = mContactIconView.getMeasuredWidth();
+        final int iconHeight = mContactIconView.getMeasuredHeight();
+        final int iconTop = getPaddingTop();
+        final int contentWidth = (right -left) - iconWidth - getPaddingLeft() - getPaddingRight();
+        final int contentHeight = mMessageBubble.getMeasuredHeight();
+        final int contentTop = iconTop;
+
+        final int iconLeft;
+        final int contentLeft;
+
+        if (mIncoming) {
+            if (isRtl) {
+                iconLeft = (right - left) - getPaddingRight() - iconWidth;
+                contentLeft = iconLeft - contentWidth;
+            } else {
+                iconLeft = getPaddingLeft();
+                contentLeft = iconLeft + iconWidth;
+            }
+        } else {
+            if (isRtl) {
+                iconLeft = getPaddingLeft();
+                contentLeft = iconLeft + iconWidth;
+            } else {
+                iconLeft = (right - left) - getPaddingRight() - iconWidth;
+                contentLeft = iconLeft - contentWidth;
+            }
+        }
+
+        mContactIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight);
+
+        mMessageBubble.layout(contentLeft, contentTop, contentLeft + contentWidth,
+                contentTop + contentHeight);
+    }
+
+    private static boolean isLayoutRtl(final View view) {
+        return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
+    }
+
+    private void updateViewContent() {
+        mMessageTextView.setText(mMessageText);
+        mStatusTextView.setText(mTimestampText);
+        mContactIconView.setText(mIconText);
+
+        mContactIconView.setTextColor(mIconTextColor);
+        final Drawable iconBase = getContext().getDrawable(R.drawable.conversation_message_icon);
+        mContactIconView
+                .setBackground(getTintedDrawable(getContext(), iconBase, mIconBackgroundColor));
+    }
+
+    private void updateViewAppearance() {
+        final Resources res = getResources();
+
+        final int arrowWidth = res.getDimensionPixelOffset(
+                R.dimen.message_bubble_arrow_width);
+        final int messageTextLeftRightPadding = res.getDimensionPixelOffset(
+                R.dimen.message_text_left_right_padding);
+        final int textTopPadding = res.getDimensionPixelOffset(
+                R.dimen.message_text_top_padding);
+        final int textBottomPadding = res.getDimensionPixelOffset(
+                R.dimen.message_text_bottom_padding);
+
+        final int textLeftPadding, textRightPadding;
+
+        if (mIncoming) {
+            textLeftPadding = messageTextLeftRightPadding + arrowWidth;
+            textRightPadding = messageTextLeftRightPadding;
+        } else {
+            textLeftPadding = messageTextLeftRightPadding;
+            textRightPadding = messageTextLeftRightPadding + arrowWidth;
+        }
+
+        // These values do not depend on whether the message includes attachments
+        final int gravity = mIncoming ? (Gravity.START | Gravity.CENTER_VERTICAL) :
+                (Gravity.END | Gravity.CENTER_VERTICAL);
+        final int messageTopPadding = res.getDimensionPixelSize(
+                R.dimen.message_padding_default);
+        final int metadataTopPadding =  res.getDimensionPixelOffset(
+                R.dimen.message_metadata_top_padding);
+
+        // Update the message text/info views
+        final int bubbleDrawableResId = mIncoming ? R.drawable.msg_bubble_incoming
+                : R.drawable.msg_bubble_outgoing;
+        final int bubbleColorResId = mIncoming ? R.color.message_bubble_incoming
+                : R.color.message_bubble_outgoing;
+        final Context context = getContext();
+
+        final Drawable textBackgroundDrawable = getTintedDrawable(context,
+                context.getDrawable(bubbleDrawableResId),
+                context.getColor(bubbleColorResId));
+        mMessageTextAndInfoView.setBackground(textBackgroundDrawable);
+
+        if (isLayoutRtl(this)) {
+            // Need to switch right and left padding in RtL mode
+            mMessageTextAndInfoView.setPadding(textRightPadding,
+                    textTopPadding + metadataTopPadding,
+                    textLeftPadding, textBottomPadding);
+        } else {
+            mMessageTextAndInfoView.setPadding(textLeftPadding,
+                    textTopPadding + metadataTopPadding,
+                    textRightPadding, textBottomPadding);
+        }
+
+        // Update the message row and message bubble views
+        setPadding(getPaddingLeft(), messageTopPadding, getPaddingRight(), 0);
+        mMessageBubble.setGravity(gravity);
+
+        updateTextAppearance();
+    }
+
+    private void updateTextAppearance() {
+        final int messageColorResId = (mIncoming ? R.color.message_text_incoming
+                : R.color.message_text_outgoing);
+        final int timestampColorResId = mIncoming ? R.color.timestamp_text_incoming
+                : R.color.timestamp_text_outgoing;
+        final int messageColor = getContext().getColor(messageColorResId);
+
+        mMessageTextView.setTextColor(messageColor);
+        mMessageTextView.setLinkTextColor(messageColor);
+        mStatusTextView.setTextColor(timestampColorResId);
+    }
+
+    private static Drawable getTintedDrawable(final Context context, final Drawable drawable,
+            final int color) {
+        // For some reason occassionally drawables on JB has a null constant state
+        final Drawable.ConstantState constantStateDrawable = drawable.getConstantState();
+        final Drawable retDrawable = (constantStateDrawable != null)
+                ? constantStateDrawable.newDrawable(context.getResources()).mutate()
+                : drawable;
+        retDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+        return retDrawable;
+    }
+}
diff --git a/src/com/android/settings/display/MessageBubbleBackground.java b/src/com/android/settings/display/MessageBubbleBackground.java
new file mode 100644
index 0000000..b2e1e97
--- /dev/null
+++ b/src/com/android/settings/display/MessageBubbleBackground.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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.settings.display;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.LinearLayout;
+
+import com.android.settings.R;
+
+public class MessageBubbleBackground extends LinearLayout {
+    private final  int mSnapWidthPixels;
+
+    public MessageBubbleBackground(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mSnapWidthPixels = context.getResources().getDimensionPixelSize(
+                R.dimen.conversation_bubble_width_snap);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int widthPadding = getPaddingLeft() + getPaddingRight();
+        int bubbleWidth = getMeasuredWidth() - widthPadding;
+        final int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - widthPadding;
+        // Round up to next snapWidthPixels
+        bubbleWidth = Math.min(maxWidth,
+                (int) (Math.ceil(bubbleWidth / (float) mSnapWidthPixels) * mSnapWidthPixels));
+        super.onMeasure(
+                MeasureSpec.makeMeasureSpec(bubbleWidth + widthPadding, MeasureSpec.EXACTLY),
+                heightMeasureSpec);
+        Log.w(this.getClass().getSimpleName(),
+                String.format("onMeasure called; width:%d, height:%d", this.getMeasuredWidth(),
+                        this.getMeasuredHeight()));
+    }
+}