Updates fragment dejunk

- Reuse stream item views.  Simplified the view layout for this.
(In this CL we still inflate views, rather than creating them in code.
Even without doing that performance now seems good enough.)

- Decode HTML into CharSequence in ContactLoader
- Removed ContactTileImageContainer and created
LayoutSuppressingImageView and LayoutSuppressingQuickContactBadge

Bug 5982899

Change-Id: I5cbd816a290a50fca9a964b921d934061915aee1
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 08e8bfe..eabd9ec 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -354,61 +354,76 @@
 
     /** Creates the view that represents a stream item. */
     public static View createStreamItemView(LayoutInflater inflater, Context context,
-            StreamItemEntry streamItem, LinearLayout parent,
-            View.OnClickListener photoClickListener) {
-        View container = inflater.inflate(R.layout.stream_item_container, parent, false);
-        ViewGroup contentTable = (ViewGroup) container.findViewById(R.id.stream_item_content);
+            View convertView, StreamItemEntry streamItem, View.OnClickListener photoClickListener) {
 
-        ContactPhotoManager contactPhotoManager = ContactPhotoManager.getInstance(context);
-        List<StreamItemPhotoEntry> photos = streamItem.getPhotos();
+        // Try to recycle existing views.
+        final View container;
+        if (convertView != null) {
+            container = convertView;
+        } else {
+            container = inflater.inflate(R.layout.stream_item_container, null, false);
+        }
+
+        final ContactPhotoManager contactPhotoManager = ContactPhotoManager.getInstance(context);
+        final List<StreamItemPhotoEntry> photos = streamItem.getPhotos();
         final int photoCount = photos.size();
 
-        // This stream item only has text.
+        // Add the text part.
+        addStreamItemText(context, streamItem, container);
+
+        // Add images.
+        final ViewGroup imageRows = (ViewGroup) container.findViewById(R.id.stream_item_image_rows);
+
         if (photoCount == 0) {
-            View textOnlyContainer = inflater.inflate(R.layout.stream_item_row_text, contentTable,
-                    false);
-            addStreamItemText(context, streamItem, textOnlyContainer);
-            contentTable.addView(textOnlyContainer);
+            // This stream item only has text.
+            imageRows.setVisibility(View.GONE);
         } else {
-            // This stream item has text and photos. Process the photos, two at a time.
-            for (int index = 0; index < photoCount; index += 2) {
-                final StreamItemPhotoEntry firstPhoto = photos.get(index);
-                if (index + 1 < photoCount) {
-                    // Put in two photos, side by side.
-                    final StreamItemPhotoEntry secondPhoto = photos.get(index + 1);
-                    View photoContainer = inflater.inflate(R.layout.stream_item_row_two_images,
-                            contentTable, false);
-                    loadPhoto(contactPhotoManager, streamItem, firstPhoto, photoContainer,
-                            R.id.stream_item_first_image, photoClickListener);
-                    loadPhoto(contactPhotoManager, streamItem, secondPhoto, photoContainer,
-                            R.id.stream_item_second_image, photoClickListener);
-                    contentTable.addView(photoContainer);
-                } else {
-                    // Put in a single photo
-                    View photoContainer = inflater.inflate(
-                            R.layout.stream_item_row_one_image, contentTable, false);
-                    loadPhoto(contactPhotoManager, streamItem, firstPhoto, photoContainer,
-                            R.id.stream_item_first_image, photoClickListener);
-                    contentTable.addView(photoContainer);
+            // This stream item has text and photos.
+            imageRows.setVisibility(View.VISIBLE);
+
+            // Number of image rows needed, which is cailing(photoCount / 2)
+            final int numImageRows = (photoCount + 1) / 2;
+
+            // Actual image rows.
+            final int numOldImageRows = imageRows.getChildCount();
+
+            // Make sure we have enough stream_item_row_images.
+            if (numOldImageRows == numImageRows) {
+                // Great, we have the just enough number of rows...
+
+            } else if (numOldImageRows < numImageRows) {
+                // Need to add more image rows.
+                for (int i = numOldImageRows; i < numImageRows; i++) {
+                    View imageRow = inflater.inflate(R.layout.stream_item_row_images, imageRows,
+                            true);
+                }
+            } else {
+                // We have exceeding image rows.  Hide them.
+                for (int i = numImageRows; i < numOldImageRows; i++) {
+                    imageRows.getChildAt(i).setVisibility(View.GONE);
                 }
             }
 
-            // Add text, comments, and attribution if applicable
-            View textContainer = inflater.inflate(R.layout.stream_item_row_text, contentTable,
-                    false);
-            // Add extra padding between the text and the images
-            int extraVerticalPadding = context.getResources().getDimensionPixelSize(
-                    R.dimen.detail_update_section_between_items_vertical_padding);
-            textContainer.setPadding(textContainer.getPaddingLeft(),
-                    textContainer.getPaddingTop() + extraVerticalPadding,
-                    textContainer.getPaddingRight(),
-                    textContainer.getPaddingBottom());
-            addStreamItemText(context, streamItem, textContainer);
-            contentTable.addView(textContainer);
-        }
+            // Put images, two by two.
+            for (int i = 0; i < photoCount; i += 2) {
+                final View imageRow = imageRows.getChildAt(i / 2);
+                // Reused image rows may not visible, so make sure they're shown.
+                imageRow.setVisibility(View.VISIBLE);
 
-        if (parent != null) {
-            parent.addView(container);
+                // Show first image.
+                loadPhoto(contactPhotoManager, streamItem, photos.get(i), imageRow,
+                        R.id.stream_item_first_image, photoClickListener);
+                final View secondContainer = imageRow.findViewById(R.id.second_image_container);
+                if (i + 1 < photoCount) {
+                    // Show the second image too.
+                    loadPhoto(contactPhotoManager, streamItem, photos.get(i + 1), imageRow,
+                            R.id.stream_item_second_image, photoClickListener);
+                    secondContainer.setVisibility(View.VISIBLE);
+                } else {
+                    // Hide the second image, but it still has to occupy the space.
+                    secondContainer.setVisibility(View.INVISIBLE);
+                }
+            }
         }
 
         return container;
@@ -447,14 +462,12 @@
         ImageGetter imageGetter = new DefaultImageGetter(context.getPackageManager());
 
         // Stream item text
-        setDataOrHideIfNone(HtmlUtils.fromHtml(context, streamItem.getText(), imageGetter, null),
-                htmlView);
+        setDataOrHideIfNone(streamItem.getDecodedText(), htmlView);
         // Attribution
         setDataOrHideIfNone(ContactBadgeUtil.getSocialDate(streamItem, context),
                 attributionView);
         // Comments
-        setDataOrHideIfNone(HtmlUtils.fromHtml(context, streamItem.getComments(), imageGetter,
-                null), commentsView);
+        setDataOrHideIfNone(streamItem.getDecodedComments(), commentsView);
         return rootView;
     }
 
@@ -516,6 +529,15 @@
         }
     }
 
+    private static Html.ImageGetter sImageGetter;
+
+    public static Html.ImageGetter getImageGetter(Context context) {
+        if (sImageGetter == null) {
+            sImageGetter = new DefaultImageGetter(context.getPackageManager());
+        }
+        return sImageGetter;
+    }
+
     /** Fetcher for images from resources to be included in HTML text. */
     private static class DefaultImageGetter implements Html.ImageGetter {
         /** The scheme used to load resources. */