Merge "Make the contact picture exactly half the height as it is wide"
diff --git a/res/layout/stream_item_text.xml b/res/layout/stream_item_text.xml
index 601dfb9..4c44100 100644
--- a/res/layout/stream_item_text.xml
+++ b/res/layout/stream_item_text.xml
@@ -25,9 +25,22 @@
         android:textSize="16sp"
         android:textColor="@color/social_update_text_color" />
 
-    <TextView android:id="@+id/stream_item_attribution"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="@color/social_update_attribution_color"/>
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView android:id="@+id/stream_item_attribution"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/social_update_attribution_color" />
+
+        <TextView android:id="@+id/stream_item_comments"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="4dip"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/social_update_comments_color" />
+    </LinearLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 325d2e7..b6b3f31 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -35,6 +35,8 @@
 
     <color name="social_update_attribution_color">#ff777777</color>
 
+    <color name="social_update_comments_color">#ff777777</color>
+
     <!-- Color used for the letter in the A-Z section header -->
     <color name="section_header_text_color">#ff999999</color>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 69f7428..17b0836 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -58,6 +58,7 @@
     </style>
 
     <style name="CallDetailActivityTheme" parent="android:Theme.Holo.SplitActionBarWhenNarrow">
+        <item name="android:windowBackground">@android:color/black</item>
         <item name="android:gravity">top</item>
         <item name="call_detail_transparent_background">#CC000000</item>
         <item name="call_detail_contact_background_overlay_alpha">0.25</item>
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index e9d75ef..d8ba995 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -25,6 +25,7 @@
 import com.android.contacts.util.ContactBadgeUtil;
 import com.android.contacts.util.StreamItemEntry;
 import com.android.contacts.util.StreamItemPhotoEntry;
+import com.google.common.annotations.VisibleForTesting;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -327,14 +328,22 @@
         streamContainer.addView(oneColumnView);
     }
 
-    private static View addStreamItemText(LayoutInflater inflater, Context context,
+    @VisibleForTesting
+    static View addStreamItemText(LayoutInflater inflater, Context context,
             StreamItemEntry streamItem, ViewGroup parent) {
         View textUpdate = inflater.inflate(R.layout.stream_item_text, parent, false);
         TextView htmlView = (TextView) textUpdate.findViewById(R.id.stream_item_html);
         TextView attributionView = (TextView) textUpdate.findViewById(
                 R.id.stream_item_attribution);
+        TextView commentsView = (TextView) textUpdate.findViewById(R.id.stream_item_comments);
         htmlView.setText(Html.fromHtml(streamItem.getText()));
         attributionView.setText(ContactBadgeUtil.getSocialDate(streamItem, context));
+        if (streamItem.getComments() != null) {
+            commentsView.setText(Html.fromHtml(streamItem.getComments()));
+            commentsView.setVisibility(View.VISIBLE);
+        } else {
+            commentsView.setVisibility(View.GONE);
+        }
         parent.addView(textUpdate);
         return textUpdate;
     }
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index e013665..653cc25 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -355,5 +355,10 @@
         public void setPhotoByteArray(byte[] photo) {
             mPhoto = photo;
         }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
     }
 }
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 528b129..194b6ca 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -107,4 +107,9 @@
         <item>Two short sections with headers</item>
         <item>Five short sections with headers</item>
     </string-array>
+
+    <string name="attribution_google_plus">Google+</string>
+    <string name="attribution_google_talk">Google Talk</string>
+    <string name="attribution_flicker">Flicker</string>
+    <string name="attribution_twitter">Twitter</string>
 </resources>
diff --git a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
new file mode 100644
index 0000000..f9b33e0
--- /dev/null
+++ b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011 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.contacts.detail;
+
+import com.android.contacts.R;
+import com.android.contacts.util.StreamItemEntry;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Html;
+import android.text.Spanned;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Unit tests for {@link ContactDetailDisplayUtils}.
+ */
+@SmallTest
+public class ContactDetailDisplayUtilsTest extends AndroidTestCase {
+    private static final String TEST_STREAM_ITEM_TEXT = "text";
+
+    private ViewGroup mParent;
+    private LayoutInflater mLayoutInflater;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mParent = new LinearLayout(getContext());
+        mLayoutInflater =
+                (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testAddStreamItemText_IncludesComments() {
+        StreamItemEntry streamItem = getTestBuilder().setComment("1 comment").build();
+        View streamItemView = addStreamItemText(streamItem);
+        assertHasText(streamItemView, R.id.stream_item_comments, "1 comment");
+    }
+
+    public void testAddStreamItemText_IncludesHtmlComments() {
+        StreamItemEntry streamItem = getTestBuilder().setComment("1 <b>comment</b>").build();
+        View streamItemView = addStreamItemText(streamItem);
+        assertHasHtmlText(streamItemView, R.id.stream_item_comments, "1 <b>comment<b>");
+    }
+
+    public void testAddStreamItemText_NoComments() {
+        StreamItemEntry streamItem = getTestBuilder().setComment(null).build();
+        View streamItemView = addStreamItemText(streamItem);
+        assertGone(streamItemView, R.id.stream_item_comments);
+    }
+
+    /**
+     * Calls {@link ContactDetailDisplayUtils#addStreamItemText(LayoutInflater, Context,
+     * StreamItemEntry, ViewGroup)} with the default parameters and the given stream item.
+     */
+    private View addStreamItemText(StreamItemEntry streamItem) {
+        return ContactDetailDisplayUtils.addStreamItemText(
+                mLayoutInflater, getContext(), streamItem, mParent);
+    }
+
+    /** Checks that the given id corresponds to a visible text view with the expected text. */
+    private void assertHasText(View parent, int textViewId, String expectedText) {
+        TextView textView = (TextView) parent.findViewById(textViewId);
+        assertNotNull(textView);
+        assertEquals(View.VISIBLE, textView.getVisibility());
+        assertEquals(expectedText, textView.getText().toString());
+    }
+
+    /** Checks that the given id corresponds to a visible text view with the expected HTML. */
+    private void assertHasHtmlText(View parent, int textViewId, String expectedHtml) {
+        TextView textView = (TextView) parent.findViewById(textViewId);
+        assertNotNull(textView);
+        assertEquals(View.VISIBLE, textView.getVisibility());
+        assertSpannableEquals(Html.fromHtml(expectedHtml), textView.getText());
+    }
+
+    /**
+     * Asserts that a char sequence is actually a {@link Spanned} matching the one expected.
+     */
+    private void assertSpannableEquals(Spanned expected, CharSequence actualCharSequence) {
+        assertEquals(expected.toString(), actualCharSequence.toString());
+        assertTrue(actualCharSequence instanceof Spanned);
+        Spanned actual = (Spanned) actualCharSequence;
+        assertEquals(Html.toHtml(expected), Html.toHtml(actual));
+    }
+
+    /** Checks that the given id corresponds to a gone view. */
+    private void assertGone(View parent, int textId) {
+        View view = parent.findViewById(textId);
+        assertNotNull(view);
+        assertEquals(View.GONE, view.getVisibility());
+    }
+
+    private static class StreamItemEntryBuilder {
+        private long mId;
+        private String mText;
+        private String mComment;
+        private long mTimestamp;
+        private String mAction;
+        private String mActionUri;
+        private String mResPackage;
+        private int mIconRes;
+        private int mLabelRes;
+
+        public StreamItemEntryBuilder() {}
+
+        public StreamItemEntryBuilder setText(String text) {
+            mText = text;
+            return this;
+        }
+
+        public StreamItemEntryBuilder setComment(String comment) {
+            mComment = comment;
+            return this;
+        }
+
+        public StreamItemEntry build() {
+            return new StreamItemEntry(mId, mText, mComment, mTimestamp, mAction, mActionUri,
+                    mResPackage, mIconRes, mLabelRes);
+        }
+    }
+
+    private StreamItemEntryBuilder getTestBuilder() {
+        return new StreamItemEntryBuilder().setText(TEST_STREAM_ITEM_TEXT);
+    }
+}
diff --git a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
index 67bf725..a6222db 100644
--- a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
@@ -105,26 +105,26 @@
     }
 
     public void testSingleWritableRawContact() {
-        expectQuery().returnRow(1, WRITABLE_ACCOUNT_TYPE, 13, "foo");
+        expectQuery().returnRow(1, WRITABLE_ACCOUNT_TYPE, null, 13, "foo");
         assertWithMessageId(R.string.deleteConfirmation);
     }
 
     public void testReadOnlyRawContacts() {
-        expectQuery().returnRow(1, READONLY_ACCOUNT_TYPE, 13, "foo");
+        expectQuery().returnRow(1, READONLY_ACCOUNT_TYPE, null, 13, "foo");
         assertWithMessageId(R.string.readOnlyContactWarning);
     }
 
     public void testMixOfWritableAndReadOnlyRawContacts() {
         expectQuery()
-                .returnRow(1, WRITABLE_ACCOUNT_TYPE, 13, "foo")
-                .returnRow(2, READONLY_ACCOUNT_TYPE, 13, "foo");
+                .returnRow(1, WRITABLE_ACCOUNT_TYPE, null, 13, "foo")
+                .returnRow(2, READONLY_ACCOUNT_TYPE, null, 13, "foo");
         assertWithMessageId(R.string.readOnlyContactDeleteConfirmation);
     }
 
     public void testMultipleWritableRawContacts() {
         expectQuery()
-                .returnRow(1, WRITABLE_ACCOUNT_TYPE, 13, "foo")
-                .returnRow(2, WRITABLE_ACCOUNT_TYPE, 13, "foo");
+                .returnRow(1, WRITABLE_ACCOUNT_TYPE, null, 13, "foo")
+                .returnRow(2, WRITABLE_ACCOUNT_TYPE, null, 13, "foo");
         assertWithMessageId(R.string.multipleContactDeleteConfirmation);
     }
 
diff --git a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
index d70cb44..e0b443a 100644
--- a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
@@ -89,7 +89,7 @@
     public void testSendSmsWhenOnlyOneNumberAvailable() {
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
-                .returnRow(1, "123", 0, null, Phone.TYPE_HOME, null);
+                .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, InteractionType.SMS, null);
@@ -107,7 +107,7 @@
     public void testSendSmsWhenDataIdIsProvided() {
         Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, 1);
         expectQuery(dataUri, true /* isDataUri */ )
-                .returnRow(1, "987", 0, null, Phone.TYPE_HOME, null);
+                .returnRow(1, "987", 0, null, null, Phone.TYPE_HOME, null);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, InteractionType.SMS, null);
@@ -125,8 +125,8 @@
     public void testSendSmsWhenThereIsPrimaryNumber() {
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
-                .returnRow(1, "123", 0, null, Phone.TYPE_HOME, null)
-                .returnRow(2, "456", 1, null, Phone.TYPE_HOME, null);
+                .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null)
+                .returnRow(2, "456", 1, null, null, Phone.TYPE_HOME, null);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, InteractionType.SMS, null);
@@ -164,8 +164,8 @@
     public void testCallNumberWhenThereAreDuplicates() {
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
-                .returnRow(1, "123", 0, null, Phone.TYPE_HOME, null)
-                .returnRow(2, "123", 0, null, Phone.TYPE_WORK, null);
+                .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null)
+                .returnRow(2, "123", 0, null, null, Phone.TYPE_WORK, null);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, InteractionType.PHONE_CALL, null);
@@ -183,8 +183,8 @@
     public void testShowDisambigDialogForCalling() {
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
-                .returnRow(1, "123", 0, "account", Phone.TYPE_HOME, "label")
-                .returnRow(2, "456", 0, null, Phone.TYPE_WORK, null);
+                .returnRow(1, "123", 0, "account", null, Phone.TYPE_HOME, "label")
+                .returnRow(2, "456", 0, null, null, Phone.TYPE_WORK, null);
 
         TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
                 mContext, InteractionType.PHONE_CALL, null);
diff --git a/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java b/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java
index c984418..e27c767 100644
--- a/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java
+++ b/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java
@@ -83,6 +83,17 @@
             "<i>24567</i> <font color='blue' size='+1'><b>likes</b></font>"
     };
 
+    private Integer[] labelIds = new Integer[] {
+            R.string.attribution_google_plus,
+            R.string.attribution_google_talk,
+            R.string.attribution_flicker,
+            R.string.attribution_twitter
+    };
+
+    public Integer[] iconIds = new Integer[] {
+            R.drawable.default_icon,
+    };
+
     // Photos to randomly select from.
     private Integer[] imageIds = new Integer[]{
             R.drawable.android,
@@ -242,6 +253,7 @@
     }
 
     private ContentValues buildStreamItemValues(String accountType, String accountName) {
+        boolean includeAttribution = randInt(100) < 70;
         boolean includeComments = randInt(100) < 30;
         boolean includeAction = randInt(100) < 30;
         ContentValues values = new ContentValues();
@@ -250,6 +262,14 @@
                 String.format(pickRandom(snippetStrings) , place)
                 + (includeComments ? " [c]" : "")
                 + (includeAction ? " [a]" : ""));
+        if (includeAttribution) {
+            values.put(StreamItems.RES_PACKAGE, "com.android.contacts.tests");
+            int sourceIndex = randInt(labelIds.length);
+            values.put(StreamItems.RES_LABEL, labelIds[sourceIndex]);
+            if (sourceIndex < iconIds.length) {
+                values.put(StreamItems.RES_ICON, iconIds[sourceIndex]);
+            }
+        }
         if (includeComments) {
             values.put(StreamItems.COMMENTS, pickRandom(commentStrings));
         } else {