Small clean-up of stream items utility class.

We do not need a way to add a set of stream items to a container: we now
use a ListView.

Do not set the listener in the helper method: we can just do that in the
adapter itself.

Bug: 5119353
Change-Id: Id647ddc358f82b5fee8628b06645412e48712229
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index e88e5e8..0c59695 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -249,36 +249,15 @@
         }
     }
 
-    /**
-     * Displays the social stream items under the given layout.
-     */
-    public static void showSocialStreamItems(LayoutInflater inflater, Context context,
-            Result contactData, LinearLayout streamContainer, View.OnClickListener listener) {
-        if (streamContainer != null) {
-            streamContainer.removeAllViews();
-            List<StreamItemEntry> streamItems = contactData.getStreamItems();
-            for (StreamItemEntry streamItem : streamItems) {
-                addStreamItemToContainer(inflater, context, streamItem, streamContainer, listener);
-            }
-        }
-    }
-
-    public static View addStreamItemToContainer(LayoutInflater inflater, Context context,
-            StreamItemEntry streamItem, LinearLayout streamContainer,
-            View.OnClickListener listener) {
+    /** Creates the view that represents a stream item. */
+    public static View createStreamItemView(LayoutInflater inflater, Context context,
+            StreamItemEntry streamItem, LinearLayout parent) {
         View oneColumnView = inflater.inflate(R.layout.stream_item_one_column,
-                streamContainer, false);
+                parent, false);
         ViewGroup contentBox = (ViewGroup) oneColumnView.findViewById(R.id.stream_item_content);
         int internalPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.detail_update_section_internal_padding);
 
-        // Add the listener only if there is an action and corresponding URI.
-        if (streamItem.getAction() != null && streamItem.getActionUri() != null) {
-            contentBox.setTag(streamItem);
-            contentBox.setOnClickListener(listener);
-            contentBox.setFocusable(true);
-        }
-
         // TODO: This is not the correct layout for a stream item with photos.  Photos should be
         // displayed first, then the update text either to the right of the final image (if there
         // are an odd number of images) or below the last row of images (if there are an even
@@ -335,8 +314,8 @@
             }
         }
 
-        if (streamContainer != null) {
-            streamContainer.addView(oneColumnView);
+        if (parent != null) {
+            parent.addView(oneColumnView);
         }
 
         return oneColumnView;
@@ -358,7 +337,9 @@
         } else {
             commentsView.setVisibility(View.GONE);
         }
-        parent.addView(textUpdate);
+        if (parent != null) {
+            parent.addView(textUpdate);
+        }
         return textUpdate;
     }
 
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index 308254f..bb2bdb4 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -66,16 +66,31 @@
         @Override
         public void onClick(View view) {
             StreamItemEntry streamItemEntry = (StreamItemEntry) view.getTag();
-            Uri uri;
-            try {
-                uri = Uri.parse(streamItemEntry.getActionUri());
-            } catch (Throwable throwable) {
-                Log.e(TAG, "invalid URI for stream item #" + streamItemEntry.getId() + ": "
-                        + streamItemEntry.getActionUri());
+            if (streamItemEntry == null) {
+                // Ignore if this item does not have a stream item associated with it.
                 return;
             }
-            Intent streamItemIntent = new Intent(streamItemEntry.getAction(), uri);
-            startActivity(streamItemIntent);
+            String actionUri = streamItemEntry.getActionUri();
+            if (actionUri == null) {
+                // Ignore if this item does not have a URI.
+                return;
+            }
+            // Parse the URI.
+            Uri uri;
+            try {
+                uri = Uri.parse(actionUri);
+            } catch (Throwable throwable) {
+                // This may fail if the URI is invalid: instead of failing, just ignore it.
+                Log.e(TAG, "invalid URI for stream item #" + streamItemEntry.getId() + ": "
+                        + actionUri);
+                return;
+            }
+            String action = streamItemEntry.getAction();
+            if (action == null) {
+                // Ignore if this item does not have an action.
+                return;
+            }
+            startActivity(new Intent(action, uri));
         }
     };
 
diff --git a/src/com/android/contacts/detail/StreamItemAdapter.java b/src/com/android/contacts/detail/StreamItemAdapter.java
index d8f4a81..95880d7 100644
--- a/src/com/android/contacts/detail/StreamItemAdapter.java
+++ b/src/com/android/contacts/detail/StreamItemAdapter.java
@@ -74,8 +74,19 @@
         if (position == 0) {
             return mInflater.inflate(R.layout.updates_header_contact, null);
         }
-        return ContactDetailDisplayUtils.addStreamItemToContainer(
-                mInflater, mContext, (StreamItemEntry) getItem(position), null, mListener);
+        StreamItemEntry streamItem = (StreamItemEntry) getItem(position);
+        View view = ContactDetailDisplayUtils.createStreamItemView(
+                mInflater, mContext, streamItem, null);
+        if (streamItem.getAction() != null && streamItem.getActionUri() != null) {
+            view.setTag(streamItem);
+            view.setFocusable(true);
+            view.setOnClickListener(mListener);
+        } else {
+            view.setTag(null);
+            view.setFocusable(false);
+            view.setOnClickListener(null);
+        }
+        return view;
     }
 
     @Override
diff --git a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
index 98001ae..aebb8c2 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailDisplayUtilsTest.java
@@ -18,6 +18,7 @@
 
 import com.android.contacts.R;
 import com.android.contacts.util.StreamItemEntry;
+import com.android.contacts.util.StreamItemEntryBuilder;
 
 import android.content.Context;
 import android.test.AndroidTestCase;
@@ -27,7 +28,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 /**
@@ -37,14 +37,11 @@
 public class ContactDetailDisplayUtilsTest extends AndroidTestCase {
     private static final String TEST_STREAM_ITEM_TEXT = "text";
 
-    private LinearLayout mParent;
     private LayoutInflater mLayoutInflater;
-    private FakeOnClickListener mListener = new FakeOnClickListener();
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mParent = new LinearLayout(getContext());
         mLayoutInflater =
                 (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     }
@@ -72,81 +69,6 @@
         assertGone(streamItemView, R.id.stream_item_comments);
     }
 
-    public void testAddStreamItemToContainer_NoAction() {
-        StreamItemEntry streamItem = getTestBuilder()
-                .setAction(null)
-                .setActionUri(null)
-                .build();
-        addStreamItemToContainer(streamItem, mListener);
-        assertStreamItemNotClickable();
-    }
-
-    public void testAddStreamItemToContainer_WithActionButNoActionUri() {
-        StreamItemEntry streamItem = getTestBuilder()
-                .setAction("action")
-                .setActionUri(null)
-                .build();
-        addStreamItemToContainer(streamItem, mListener);
-        assertStreamItemNotClickable();
-    }
-
-    public void testAddStreamItemToContainer_WithActionUriButNoAction() {
-        StreamItemEntry streamItem = getTestBuilder()
-                .setAction(null)
-                .setActionUri("http://www.google.com")
-                .build();
-        addStreamItemToContainer(streamItem, mListener);
-        assertStreamItemNotClickable();
-    }
-
-    public void testAddStreamItemToContainer_WithActionAndActionUri() {
-        StreamItemEntry streamItem = getTestBuilder()
-                .setAction("action")
-                .setActionUri("http://www.google.com")
-                .build();
-        addStreamItemToContainer(streamItem, mListener);
-        assertStreamItemClickable();
-        assertStreamItemHasOnClickListener();
-        assertStreamItemHasTag(streamItem);
-    }
-
-    /** Checks that the stream item view is clickable. */
-    private void assertStreamItemClickable() {
-        View streamItemView = mParent.findViewById(R.id.stream_item_content);
-        assertNotNull("should have a stream item", streamItemView);
-        assertTrue("should be clickable", streamItemView.isClickable());
-        assertTrue("should be focusable", streamItemView.isFocusable());
-    }
-
-    /** Asserts that there is a stream item but it is not clickable. */
-    private void assertStreamItemNotClickable() {
-        View streamItemView = mParent.findViewById(R.id.stream_item_content);
-        assertNotNull("should have a stream item", streamItemView);
-        assertFalse("should not be clickable", streamItemView.isClickable());
-        assertFalse("should not be focusable", streamItemView.isFocusable());
-    }
-
-    /** Checks that the stream item view has a click listener. */
-    private void assertStreamItemHasOnClickListener() {
-        // Check that the on-click listener is invoked when clicked.
-        View streamItemView = mParent.findViewById(R.id.stream_item_content);
-        assertFalse("listener should have not been invoked yet", mListener.clicked);
-        streamItemView.performClick();
-        assertTrue("listener should have been invoked", mListener.clicked);
-    }
-
-    /** Checks that the stream item view has the given stream item as its tag. */
-    private void assertStreamItemHasTag(StreamItemEntry streamItem) {
-        // The view's tag should point to the stream item entry for this view.
-        View streamItemView = mParent.findViewById(R.id.stream_item_content);
-        Object tag = streamItemView.getTag();
-        assertNotNull("should have a tag", tag);
-        assertTrue("should be a StreamItemEntry", tag instanceof StreamItemEntry);
-        StreamItemEntry streamItemTag = (StreamItemEntry) tag;
-        // The streamItem itself should be in the tag.
-        assertSame(streamItem, streamItemTag);
-    }
-
     /** 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);
@@ -187,70 +109,7 @@
      */
     private View addStreamItemText(StreamItemEntry streamItem) {
         return ContactDetailDisplayUtils.addStreamItemText(
-                mLayoutInflater, getContext(), streamItem, mParent);
-    }
-
-    /**
-     * Calls {@link ContactDetailDisplayUtils#addStreamItemToContainer(LayoutInflater,
-     * Context,StreamItemEntry, LinearLayout, android.view.View.OnClickListener)} with the default
-     * parameters and the given stream item and listener.
-     */
-    private void addStreamItemToContainer(StreamItemEntry streamItem,
-            View.OnClickListener listener) {
-        ContactDetailDisplayUtils.addStreamItemToContainer(mLayoutInflater, getContext(),
-                streamItem, mParent, listener);
-    }
-
-    /**
-     * Simple fake implementation of {@link View.OnClickListener} which sets a member variable to
-     * true when clicked.
-     */
-    private final class FakeOnClickListener implements View.OnClickListener {
-        public boolean clicked = false;
-
-        @Override
-        public void onClick(View view) {
-            clicked = true;
-        }
-    }
-
-    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 StreamItemEntryBuilder setAction(String action) {
-            mAction = action;
-            return this;
-        }
-
-        public StreamItemEntryBuilder setActionUri(String actionUri) {
-            mActionUri = actionUri;
-            return this;
-        }
-
-        public StreamItemEntry build() {
-            return new StreamItemEntry(mId, mText, mComment, mTimestamp, mAction, mActionUri,
-                    mResPackage, mIconRes, mLabelRes);
-        }
+                mLayoutInflater, getContext(), streamItem, null);
     }
 
     private StreamItemEntryBuilder getTestBuilder() {
diff --git a/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java b/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java
new file mode 100644
index 0000000..99ae834
--- /dev/null
+++ b/tests/src/com/android/contacts/detail/StreamItemAdapterTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.util.StreamItemEntry;
+import com.android.contacts.util.StreamItemEntryBuilder;
+import com.google.common.collect.Lists;
+
+import android.content.Intent;
+import android.test.AndroidTestCase;
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * Unit tests for {@link StreamItemAdapter}.
+ */
+public class StreamItemAdapterTest extends AndroidTestCase {
+    private StreamItemAdapter mAdapter;
+    private FakeOnClickListener mListener;
+    private View mView;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mListener = new FakeOnClickListener();
+        mAdapter = new StreamItemAdapter(getContext(), mListener);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mAdapter = null;
+        mListener = null;
+        super.tearDown();
+    }
+
+    public void testGetCount_Empty() {
+        mAdapter.setStreamItems(createStreamItemList(0));
+        // There is actually one view: the header.
+        assertEquals(1, mAdapter.getCount());
+    }
+
+    public void testGetCount_NonEmpty() {
+        mAdapter.setStreamItems(createStreamItemList(3));
+        // There is one extra view: the header.
+        assertEquals(4, mAdapter.getCount());
+    }
+
+    public void testGetView_WithAction() {
+        StreamItemEntry streamItem = createStreamItemWithAction();
+        mAdapter.setStreamItems(Lists.newArrayList(streamItem));
+        mView = mAdapter.getView(1, null, null);
+        assertStreamItemViewHasTag(streamItem);
+        assertStreamItemViewHasOnClickListener();
+        assertStreamItemViewFocusable();
+    }
+
+    public void testGetView_WithoutAction() {
+        mAdapter.setStreamItems(Lists.newArrayList(createStreamItemWithoutAction()));
+        mView = mAdapter.getView(1, null, null);
+        assertStreamItemViewHasNoTag();
+        assertStreamItemViewHasNoOnClickListener();
+        assertStreamItemViewNotFocusable();
+    }
+
+    public void testGetView_Header() {
+        // Just check that we can inflate it correctly.
+        mView = mAdapter.getView(0, null, null);
+    }
+
+    /** Counter used by {@link #createStreamItemEntryBuilder()} to create unique builders. */
+    private int mCreateStreamItemEntryBuilderCounter = 0;
+
+    /** Returns a stream item builder with basic information in it. */
+    private StreamItemEntryBuilder createStreamItemEntryBuilder() {
+        return new StreamItemEntryBuilder().setText(
+                "text #" + mCreateStreamItemEntryBuilderCounter++);
+    }
+
+    /** Returns a stream item with an action and action URI set. */
+    private StreamItemEntry createStreamItemWithAction() {
+        return createStreamItemEntryBuilder()
+                .setAction(Intent.ACTION_VIEW)
+                .setActionUri("http://www.google.com")
+                .build();
+    }
+
+    /** Returns a stream item without an action and action URI set. */
+    private StreamItemEntry createStreamItemWithoutAction() {
+        return createStreamItemEntryBuilder()
+                .setAction(null)
+                .setActionUri(null)
+                .build();
+    }
+
+    /** Creates a list containing the given number of {@link StreamItemEntry}s. */
+    private ArrayList<StreamItemEntry> createStreamItemList(int count) {
+        ArrayList<StreamItemEntry> list = Lists.newArrayList();
+        for (int index = 0; index < count; ++index) {
+            list.add(createStreamItemEntryBuilder().build());
+        }
+        return list;
+    }
+
+    /** Checks that the stream item view has a click listener. */
+    private void assertStreamItemViewHasOnClickListener() {
+        assertFalse("listener should have not been invoked yet", mListener.clicked);
+        mView.performClick();
+        assertTrue("listener should have been invoked", mListener.clicked);
+    }
+
+    /** Checks that the stream item view does not have a click listener. */
+    private void assertStreamItemViewHasNoOnClickListener() {
+        assertFalse("listener should have not been invoked yet", mListener.clicked);
+        mView.performClick();
+        assertFalse("listener should have not been invoked", mListener.clicked);
+    }
+
+    /** Checks that the stream item view is clickable. */
+    private void assertStreamItemViewFocusable() {
+        assertNotNull("should have a stream item", mView);
+        assertTrue("should be focusable", mView.isFocusable());
+    }
+
+    /** Asserts that there is a stream item but it is not clickable. */
+    private void assertStreamItemViewNotFocusable() {
+        assertNotNull("should have a stream item", mView);
+        assertFalse("should not be focusable", mView.isFocusable());
+    }
+
+    /** Checks that the stream item view has the given stream item as its tag. */
+    private void assertStreamItemViewHasTag(StreamItemEntry streamItem) {
+        Object tag = mView.getTag();
+        assertNotNull("should have a tag", tag);
+        assertTrue("should be a StreamItemEntry", tag instanceof StreamItemEntry);
+        StreamItemEntry streamItemTag = (StreamItemEntry) tag;
+        // The streamItem itself should be in the tag.
+        assertSame(streamItem, streamItemTag);
+    }
+
+    /** Checks that the stream item view has the given stream item as its tag. */
+    private void assertStreamItemViewHasNoTag() {
+        Object tag = mView.getTag();
+        assertNull("should not have a tag", tag);
+    }
+
+    /**
+     * Simple fake implementation of {@link View.OnClickListener} which sets a member variable to
+     * true when clicked.
+     */
+    private final class FakeOnClickListener implements View.OnClickListener {
+        public boolean clicked = false;
+
+        @Override
+        public void onClick(View view) {
+            clicked = true;
+        }
+    }
+}
diff --git a/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java b/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java
new file mode 100644
index 0000000..8a17b4a
--- /dev/null
+++ b/tests/src/com/android/contacts/util/StreamItemEntryBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util;
+
+/**
+ * Builder for {@link StreamItemEntry}s to make writing tests easier.
+ */
+public 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 StreamItemEntryBuilder setAction(String action) {
+        mAction = action;
+        return this;
+    }
+
+    public StreamItemEntryBuilder setActionUri(String actionUri) {
+        mActionUri = actionUri;
+        return this;
+    }
+
+    public StreamItemEntry build() {
+        return new StreamItemEntry(mId, mText, mComment, mTimestamp, mAction, mActionUri,
+                mResPackage, mIconRes, mLabelRes);
+    }
+}
\ No newline at end of file