Reworked attachment grid to use a dp-based layout.

Instead of having a resource selection for the
number of columns (which requires
a different value for portrait and landscape as well
as every screen size), utilize a minimum dip tile size
that figures out, while measuring, the appropriate number
of columns to use and expands the tile size to fit the
entire width with that number of columns.
As a result we have only two different values
for the minimum tile size. 120dip is the default and
180dip for sw600dp. Verified that the appropriate
number of columns occurs on all Google experience
devices.

Additionally, added a header and bar for the attachments
so that the user can clearly see where attachments
begin.

Change-Id: I48b1645e1004596747c1585e44a387b1231bd661
diff --git a/res/layout/conversation_message_attachment.xml b/res/layout/conversation_message_attachment.xml
index b8304ea..e6a8ad6 100644
--- a/res/layout/conversation_message_attachment.xml
+++ b/res/layout/conversation_message_attachment.xml
@@ -24,7 +24,7 @@
     android:orientation="vertical"
     android:divider="?android:attr/dividerHorizontal"
     android:showDividers="middle"
-    android:background="@android:color/black">
+    android:background="@drawable/attachment_bg_holo">
 
     <RelativeLayout
         android:id="@+id/attachment_tile_layout"
@@ -35,71 +35,16 @@
             android:id="@+id/attachment_tile_image"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:scaleType="fitCenter" />
+            android:scaleType="centerCrop" />
 
-        <RelativeLayout
+        <ProgressBar
+            android:id="@+id/attachment_progress"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/attachment_tile_shadowbox_height"
-            android:background="@color/attachment_tile_shadow_box_color"
+            android:layout_height="wrap_content"
             android:layout_alignParentBottom="true"
-            android:gravity="center_vertical"
-            android:paddingLeft="8dip">
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_alignParentLeft="true"
-                android:layout_toLeftOf="@+id/attachment_tile_secondary_button"
-                android:orientation="vertical"
-                android:gravity="center_vertical"
-                android:paddingRight="4dip">
-
-                <TextView
-                    android:id="@+id/attachment_tile_title"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:textColor="@android:color/white"
-                    android:textSize="16sp"
-                    android:singleLine="true"
-                    android:fadingEdge="horizontal"
-                    android:fadingEdgeLength="3dip"
-                    android:ellipsize="marquee" />
-
-                <TextView
-                    android:id="@+id/attachment_tile_subtitle"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:textAppearance="?android:attr/textAppearanceSmall"
-                    android:textColor="@color/attachment_tile_subtitle_color"
-                    android:singleLine="true"
-                    android:paddingBottom="4dip"
-                    android:fadingEdge="horizontal"
-                    android:fadingEdgeLength="3dip"
-                    android:layout_marginTop="-3dip"
-                    android:ellipsize="marquee" />
-
-            </LinearLayout>
-
-            <Button
-                android:id="@id/attachment_tile_secondary_button"
-                android:background="?android:attr/selectableItemBackground"
-                android:layout_height="@dimen/attachment_tile_shadowbox_height"
-                android:layout_width="wrap_content"
-                android:drawableLeft="@drawable/ic_divider_dashed_holo_dark"
-                android:layout_alignParentRight="true"
-                android:text="@string/save_attachment"
-                android:textColor="@android:color/white" />
-
-            <ProgressBar
-                android:id="@+id/attachment_progress"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_alignParentBottom="true"
-                style="?android:attr/progressBarStyleHorizontal"
-                android:indeterminate="false"
-                android:visibility="gone" />
-
-        </RelativeLayout>
+            style="?android:attr/progressBarStyleHorizontal"
+            android:indeterminate="false"
+            android:visibility="gone" />
 
         <View
             android:id="@+id/attachment_tile_push_state"
diff --git a/res/layout/conversation_message_footer.xml b/res/layout/conversation_message_footer.xml
index 82649e7..6b7627a 100644
--- a/res/layout/conversation_message_footer.xml
+++ b/res/layout/conversation_message_footer.xml
@@ -20,7 +20,29 @@
     android:id="@+id/attachments"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp">
+    android:orientation="vertical">
+    <TextView
+        android:id="@+id/attachments_header_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/attachments_header"
+        android:textColor="@android:color/holo_blue_light"
+        android:textAllCaps="true"
+        android:visibility="gone"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"/>
+    <View
+        android:id="@+id/attachments_header_bar"
+        android:layout_width="match_parent"
+        android:layout_height="2dip"
+        android:layout_marginBottom="2dip"
+        android:background="@android:color/holo_blue_light"
+        android:visibility="gone"/>
+    <com.android.mail.browse.AttachmentTileGrid
+        android:id="@+id/attachment_tile_grid"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"/>
 </com.android.mail.browse.MessageFooterView>
diff --git a/res/values-land/constants.xml b/res/values-land/constants.xml
deleted file mode 100644
index fc5925f..0000000
--- a/res/values-land/constants.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2012 Google Inc.
-     Licensed to 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>
-    <!-- Determines the number of columns in an AttachmentTileRow -->
-    <integer name="attachment_tile_column_count">3</integer>
-</resources>
diff --git a/res/values-sw600dp/dimen.xml b/res/values-sw600dp/dimen.xml
index ca31177..69eeb59 100644
--- a/res/values-sw600dp/dimen.xml
+++ b/res/values-sw600dp/dimen.xml
@@ -45,4 +45,5 @@
     <dimen name="compose_area_left_padding">100dip</dimen>
     <dimen name="compose_area_right_padding">100dip</dimen>
     <dimen name="search_view_width">500dip</dimen>
+    <dimen name="attachment_tile_min_size">180dp</dimen>
 </resources>
diff --git a/res/values/constants.xml b/res/values/constants.xml
index 4459642..0b255ed 100644
--- a/res/values/constants.xml
+++ b/res/values/constants.xml
@@ -69,7 +69,4 @@
 
     <!-- Whether to display folder colors in the widget -->
     <bool name="display_folder_colors_in_widget">false</bool>
-
-    <!-- Determines the number of columns in an AttachmentTileRow -->
-    <integer name="attachment_tile_column_count">2</integer>
 </resources>
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index cf54584..5d8bdb1 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -103,4 +103,5 @@
       when the font size is scaled for accessibility purposes
     -->
     <dimen name="attachment_tile_shadowbox_height">48sp</dimen>
+    <dimen name="attachment_tile_min_size">120dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cd381a4..3147c95 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -236,6 +236,9 @@
     <string name="saved">Saved,&#160;</string>
     <!-- Displayed in the conversation view. Status of a failed attachment. [CHAR LIMIT=25]-->
     <string name="download_failed">Couldn\'t download.</string>
+    <!-- Displayed in the conversation view, as a header informing
+         the user that the following is the attachments. -->
+    <string name="attachments_header">Attachments</string>
 
     <!-- Webview Context Menu Strings -->
     <!-- Title of dialog for choosing which activity to share a link with. [CHAR LIMIT=50]-->
diff --git a/src/com/android/mail/browse/AttachmentTileGrid.java b/src/com/android/mail/browse/AttachmentTileGrid.java
new file mode 100644
index 0000000..84d483b
--- /dev/null
+++ b/src/com/android/mail/browse/AttachmentTileGrid.java
@@ -0,0 +1,169 @@
+/*
+ * 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.mail.browse;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.mail.R;
+import com.android.mail.providers.Attachment;
+
+import java.util.List;
+
+/**
+ * Acts as a grid composed of {@link MessageAttachmentTile}s.
+ */
+public class AttachmentTileGrid extends FrameLayout {
+    private LayoutInflater mInflater;
+    private Uri mAttachmentsListUri;
+    private final int mTileMinSize;
+    private int mColumnCount;
+
+    public AttachmentTileGrid(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mInflater = LayoutInflater.from(context);
+        mTileMinSize = context.getResources()
+                .getDimensionPixelSize(R.dimen.attachment_tile_min_size);
+    }
+
+    /**
+     * Configures the grid to add {@link Attachment}s information to the views.
+     */
+    public void configureGrid(Uri attachmentsListUri, List<Attachment> list) {
+        mAttachmentsListUri = attachmentsListUri;
+        // Adding tiles to grid and filling in attachment information
+        int index = 0;
+        for (Attachment attachment : list) {
+            addTileFromAttachment(attachment, index++);
+        }
+    }
+
+    private void addTileFromAttachment(Attachment attachment, int index) {
+        final MessageAttachmentTile attachmentTile;
+
+        if (getChildCount() <= index) {
+            attachmentTile = MessageAttachmentTile.inflate(mInflater, this);
+            addView(attachmentTile);
+        } else {
+            attachmentTile = (MessageAttachmentTile) getChildAt(index);
+        }
+
+        attachmentTile.render(attachment, mAttachmentsListUri, index);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        onMeasureForTiles(widthMeasureSpec);
+    }
+
+    private void onMeasureForTiles(int widthMeasureSpec) {
+        final int width = MeasureSpec.getSize(widthMeasureSpec);
+
+        final int childCount = getChildCount();
+        if (childCount == 0) {
+            // Just in case...
+            setMeasuredDimension(width, 0);
+            return;
+        }
+
+        // Divide width by minimum tile size to get the number of columns.
+        // Truncation will ensure that the minimum will always be the minimum
+        // but that the tiles can (and likely will) grow larger.
+        mColumnCount = width / mTileMinSize;
+
+        // Just in case...
+        if (mColumnCount == 0) {
+            mColumnCount = 1;
+        }
+
+        // 1. Calculate image size.
+        //      = [total width] / [child count]
+        //
+        // 2. Set it to width/height of each children.
+        //    If we have a remainder, some tiles will have
+        //    1 pixel larger width than its height.
+        //
+        // 3. Set the dimensions of itself.
+        //    Let width = given width.
+        //    Let height = image size + bottom padding.
+        final int imageSize = (width) / mColumnCount;
+        final int remainder = width - (imageSize * mColumnCount);
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            // Compensate for the remainder
+            final int childWidth = imageSize + (i < remainder ? 1 : 0);
+            child.measure(
+                    MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(imageSize, MeasureSpec.EXACTLY)
+                    );
+        }
+
+        // Calculate the number of rows so we can get the proper height.
+        // Then multiply by the height of one tile to get the grid height.
+        final int numRows = ((childCount - 1) / mColumnCount) + 1;
+        setMeasuredDimension(width,
+                numRows*(imageSize + getChildAt(0).getPaddingBottom()));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        onLayoutForTiles();
+    }
+
+    private void onLayoutForTiles() {
+        final int count = getChildCount();
+        int childLeft = 0;
+        int childTop = 0;
+        boolean skipBeginningOfRowFirstTime = true;
+
+        // Layout the grid.
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+
+            // Note MeasuredWidth and MeasuredHeight include the padding.
+            final int childWidth = child.getMeasuredWidth();
+            final int childHeight = child.getMeasuredHeight();
+
+            // If we're at the beginning of a row and it is not the first row
+            // in the grid, reset childLeft to 0 and update childTop
+            // to reflect the top of the new row.
+            if (!skipBeginningOfRowFirstTime && i % mColumnCount == 0) {
+                childLeft = 0;
+                childTop += childHeight;
+            } else {
+                skipBeginningOfRowFirstTime = false;
+            }
+
+            child.layout(childLeft, childTop,
+                    childLeft + childWidth, childTop + childHeight);
+            childLeft += childWidth;
+        }
+    }
+
+    @Override
+    public void sendAccessibilityEvent(int eventType) {
+        // This method is called when the child tile is INVISIBLE (meaning "empty"), and the
+        // Accessibility Manager needs to find alternative content description to speak.
+        // Here, we ignore the default behavior, since we don't want to let the manager speak
+        // a contact name for the tile next to the INVISIBLE tile.
+    }
+}
diff --git a/src/com/android/mail/browse/AttachmentTileRow.java b/src/com/android/mail/browse/AttachmentTileRow.java
deleted file mode 100644
index 794370e..0000000
--- a/src/com/android/mail/browse/AttachmentTileRow.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.mail.browse;
-
-import android.content.Context;
-import android.net.Uri;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.mail.providers.Attachment;
-
-import java.util.List;
-
-/**
- * Acts as a row item composed of {@link MessageAttachmentTile}s.
- */
-public class AttachmentTileRow extends FrameLayout {
-    private int mColumnCount;
-    private LayoutInflater mInflater;
-    private Uri mAttachmentsListUri;
-
-    public AttachmentTileRow(Context context, Uri attachmentsListUri, int columnCount) {
-        super(context);
-        mColumnCount = columnCount;
-        mInflater = LayoutInflater.from(context);
-        mAttachmentsListUri = attachmentsListUri;
-    }
-
-    /**
-     * Configures the row to add {@link Attachment}s information to the views
-     */
-    public void configureRow(List<Attachment> list, int rowIndex) {
-        // Adding tiles to row and filling in attachment information
-        for (int columnCounter = 0; columnCounter < mColumnCount; columnCounter++) {
-            Attachment attachment =
-                    columnCounter < list.size() ? list.get(columnCounter) : null;
-            addTileFromEntry(attachment, columnCounter, rowIndex);
-        }
-    }
-
-    private void addTileFromEntry(Attachment attachment, int columnIndex, int rowIndex) {
-        final MessageAttachmentTile attachmentTile;
-
-        if (getChildCount() <= columnIndex) {
-            attachmentTile = MessageAttachmentTile.inflate(mInflater, this);
-            addView(attachmentTile);
-        } else {
-            attachmentTile = (MessageAttachmentTile) getChildAt(columnIndex);
-        }
-
-        attachmentTile.render(attachment, mAttachmentsListUri,
-                mColumnCount*rowIndex + columnIndex); // determine the attachment's index
-                                                      // using number of columns and the
-                                                      // current row and column
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        onLayoutForTiles();
-    }
-
-    private void onLayoutForTiles() {
-        final int count = getChildCount();
-
-        // Just line up children horizontally.
-        int childLeft = 0;
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-
-            // Note MeasuredWidth includes the padding.
-            final int childWidth = child.getMeasuredWidth();
-            child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
-            childLeft += childWidth;
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        onMeasureForTiles(widthMeasureSpec);
-    }
-
-    private void onMeasureForTiles(int widthMeasureSpec) {
-        final int width = MeasureSpec.getSize(widthMeasureSpec);
-
-        final int childCount = getChildCount();
-        if (childCount == 0) {
-            // Just in case...
-            setMeasuredDimension(width, 0);
-            return;
-        }
-
-        // 1. Calculate image size.
-        //      = [total width] / [child count]
-        //
-        // 2. Set it to width/height of each children.
-        //    If we have a remainder, some tiles will have 1 pixel larger width than its height.
-        //
-        // 3. Set the dimensions of itself.
-        //    Let width = given width.
-        //    Let height = image size + bottom paddding.
-        final int imageSize = (width) / mColumnCount;
-        final int remainder = width - (imageSize * mColumnCount);
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final int childWidth = imageSize + child.getPaddingRight()
-                    // Compensate for the remainder
-                    + (i < remainder ? 1 : 0);
-            final int childHeight = imageSize + child.getPaddingBottom();
-            child.measure(
-                    MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)
-                    );
-        }
-        setMeasuredDimension(width, imageSize + getChildAt(0).getPaddingBottom());
-    }
-
-    @Override
-    public void sendAccessibilityEvent(int eventType) {
-        // This method is called when the child tile is INVISIBLE (meaning "empty"), and the
-        // Accessibility Manager needs to find alternative content description to speak.
-        // Here, we ignore the default behavior, since we don't want to let the manager speak
-        // a contact name for the tile next to the INVISIBLE tile.
-    }
-}
diff --git a/src/com/android/mail/browse/MessageAttachmentTile.java b/src/com/android/mail/browse/MessageAttachmentTile.java
index 7a4b9bc..5b55b64 100644
--- a/src/com/android/mail/browse/MessageAttachmentTile.java
+++ b/src/com/android/mail/browse/MessageAttachmentTile.java
@@ -30,19 +30,16 @@
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 import android.widget.ProgressBar;
-import android.widget.TextView;
 
 import com.android.mail.R;
 import com.android.mail.photo.Intents;
@@ -52,7 +49,6 @@
 import com.android.mail.providers.UIProvider.AttachmentColumns;
 import com.android.mail.providers.UIProvider.AttachmentDestination;
 import com.android.mail.providers.UIProvider.AttachmentState;
-import com.android.mail.utils.AttachmentUtils;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.MimeType;
 import com.android.mail.utils.Utils;
@@ -72,21 +68,10 @@
     private ImageView mIcon;
     private ImageView.ScaleType mIconScaleType;
     private int mPhotoIndex;
-    private TextView mTitle;
-    private TextView mSubTitle;
-    private String mAttachmentSizeText;
-    private String mDisplayType;
     private Uri mAttachmentsListUri;
     private ProgressDialog mViewProgressDialog;
     private AttachmentCommandHandler mCommandHandler;
     private ProgressBar mProgress;
-    private Button mPreviewButton;
-    private Button mViewButton;
-    private Button mSaveButton;
-    private Button mInfoButton;
-    private Button mPlayButton;
-    private Button mInstallButton;
-    private Button mCancelButton;
 
     private ThumbnailLoadTask mThumbnailTask;
 
@@ -215,17 +200,6 @@
                 attachment.destination, attachment.downloadedSize, attachment.contentUri,
                 attachment.contentType);
 
-        if (prevAttachment == null || TextUtils.equals(attachment.name, prevAttachment.name)) {
-            mTitle.setText(attachment.name);
-        }
-
-        if (prevAttachment == null || attachment.size != prevAttachment.size) {
-            mAttachmentSizeText = AttachmentUtils.convertToHumanReadableSize(getContext(),
-                    attachment.size);
-            mDisplayType = AttachmentUtils.getDisplayType(getContext(), attachment);
-            updateSubtitleText(null);
-        }
-
         final Uri imageUri = attachment.getImageUri();
         final Uri prevImageUri = (prevAttachment == null) ? null : prevAttachment.getImageUri();
         // begin loading a thumbnail if this is an image and either the thumbnail or the original
@@ -247,7 +221,6 @@
             mProgress.setMax(attachment.size);
         }
 
-        updateActions();
         updateStatus();
     }
 
@@ -286,63 +259,24 @@
             }
 
         }
-
-        if (mAttachment.state == AttachmentState.FAILED) {
-            mSubTitle.setText(getResources().getString(R.string.download_failed));
-        } else {
-            updateSubtitleText(mAttachment.isSavedToExternal() ?
-                    getResources().getString(R.string.saved) : null);
-        }
     }
 
     private void setProgressVisible(boolean visible) {
         if (visible) {
             mProgress.setVisibility(VISIBLE);
-            mSubTitle.setVisibility(INVISIBLE);
         } else {
             mProgress.setVisibility(GONE);
-            mSubTitle.setVisibility(VISIBLE);
         }
     }
 
-    private void updateSubtitleText(String prefix) {
-        // TODO: make this a formatted resource when we have a UX design.
-        // not worth translation right now.
-        StringBuilder sb = new StringBuilder();
-        if (prefix != null) {
-            sb.append(prefix);
-        }
-        sb.append(mAttachmentSizeText);
-        sb.append(' ');
-        sb.append(mDisplayType);
-        mSubTitle.setText(sb.toString());
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
         mIcon = (ImageView) findViewById(R.id.attachment_tile_image);
-        mTitle = (TextView) findViewById(R.id.attachment_tile_title);
-        mSubTitle = (TextView) findViewById(R.id.attachment_tile_subtitle);
         mProgress = (ProgressBar) findViewById(R.id.attachment_progress);
 
-//        mPreviewButton = (Button) findViewById(R.id.preview_attachment);
-//        mViewButton = (Button) findViewById(R.id.view_attachment);
-        mSaveButton = (Button) findViewById(R.id.attachment_tile_secondary_button);
-//        mInfoButton = (Button) findViewById(R.id.info_attachment);
-//        mPlayButton = (Button) findViewById(R.id.play_attachment);
-//        mInstallButton = (Button) findViewById(R.id.install_attachment);
-//        mCancelButton = (Button) findViewById(R.id.cancel_attachment);
-
         setOnClickListener(this);
-//        mPreviewButton.setOnClickListener(this);
-//        mViewButton.setOnClickListener(this);
-        mSaveButton.setOnClickListener(this);
-//        mInfoButton.setOnClickListener(this);
-//        mPlayButton.setOnClickListener(this);
-//        mInstallButton.setOnClickListener(this);
-//        mCancelButton.setOnClickListener(this);
 
         mIconScaleType = mIcon.getScaleType();
     }
@@ -368,7 +302,6 @@
                 showAttachment(AttachmentDestination.CACHE);
                 break;
             case R.id.save_attachment:
-            case R.id.attachment_tile_secondary_button:
                 if (mAttachment.canSave()) {
                     startDownloadingAttachment(AttachmentDestination.EXTERNAL);
                 }
@@ -415,49 +348,6 @@
         mCommandHandler.sendCommand(params);
     }
 
-    private void setButtonVisible(View button, boolean visible) {
-        if (button != null) {
-            button.setVisibility(visible ? VISIBLE : GONE);
-        }
-    }
-
-    /**
-     * Update all action buttons based on current downloading state.
-     */
-    private void updateActions() {
-        // To avoid visibility state transition bugs, every button's visibility should be touched
-        // once by this routine.
-
-        final boolean isDownloading = mAttachment.isDownloading();
-
-        setButtonVisible(mCancelButton, isDownloading);
-
-        final boolean canInstall = MimeType.isInstallable(mAttachment.contentType);
-        setButtonVisible(mInstallButton, canInstall && !isDownloading);
-
-        if (!canInstall) {
-
-            final boolean canPreview = (mAttachment.previewIntent != null);
-            final boolean canView = MimeType.isViewable(getContext(), mAttachment.contentType);
-            final boolean canPlay = MimeType.isPlayable(mAttachment.contentType);
-
-            setButtonVisible(mPreviewButton, canPreview);
-            setButtonVisible(mPlayButton, canView && canPlay && !isDownloading);
-            setButtonVisible(mViewButton, canView && !canPlay && !isDownloading);
-            setButtonVisible(mSaveButton, canView && mAttachment.canSave() && !isDownloading);
-            setButtonVisible(mInfoButton, !(canPreview || canView));
-
-        } else {
-
-            setButtonVisible(mPreviewButton, false);
-            setButtonVisible(mPlayButton, false);
-            setButtonVisible(mViewButton, false);
-            setButtonVisible(mSaveButton, false);
-            setButtonVisible(mInfoButton, false);
-
-        }
-    }
-
     /**
      * View an attachment by an application on device.
      */
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
index 1dc90a7..de9af60 100644
--- a/src/com/android/mail/browse/MessageFooterView.java
+++ b/src/com/android/mail/browse/MessageFooterView.java
@@ -23,7 +23,9 @@
 import android.database.Cursor;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.mail.R;
 import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
@@ -39,11 +41,12 @@
 public class MessageFooterView extends LinearLayout implements DetachListener,
         LoaderManager.LoaderCallbacks<Cursor> {
 
-    private int mColumnCount;
-
     private MessageHeaderItem mMessageHeaderItem;
     private LoaderManager mLoaderManager;
     private AttachmentCursor mAttachmentsCursor;
+    private TextView mTitleText;
+    private View mTitleBar;
+    private AttachmentTileGrid mAttachmentGrid;
 
     /**
      * An easy way for the conversation view to disable immediately kicking off attachment loaders
@@ -59,9 +62,6 @@
 
     public MessageFooterView(Context context, AttributeSet attrs) {
         super(context, attrs);
-
-        mColumnCount = context.getResources().getInteger(
-                R.integer.attachment_tile_column_count);
     }
 
     /**
@@ -77,6 +77,10 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+
+        mTitleText = (TextView) findViewById(R.id.attachments_header_text);
+        mTitleBar = findViewById(R.id.attachments_header_bar);
+        mAttachmentGrid = (AttachmentTileGrid) findViewById(R.id.attachment_tile_grid);
     }
 
     public void initialize(LoaderManager loaderManager) {
@@ -91,7 +95,9 @@
          * we should just always render even if the matching header is collapsed.
          */
 
-        removeAllViewsInLayout();
+        mAttachmentGrid.removeAllViewsInLayout();
+        mTitleText.setVisibility(View.GONE);
+        mTitleBar.setVisibility(View.GONE);
 
         // kick off load of Attachment objects in background thread
         final Integer attachmentLoaderId = getAttachmentLoaderId();
@@ -102,7 +108,7 @@
         }
 
         // Do an initial render if initLoader didn't already do one
-        if (getChildCount() == 0) {
+        if (mAttachmentGrid.getChildCount() == 0) {
             renderAttachments();
         }
         setVisibility(mMessageHeaderItem.isExpanded() ? VISIBLE : GONE);
@@ -138,34 +144,12 @@
             return;
         }
 
-        int rowStartIndex = 0; // the index for the first attachment in the current row
-        final Context context = getContext();
-        final int attachmentListSize = attachments.size();
-        final int numRows = (attachmentListSize - 1)/mColumnCount + 1;
+        mTitleText.setVisibility(View.VISIBLE);
+        mTitleBar.setVisibility(View.VISIBLE);
+        mAttachmentGrid.setVisibility(View.VISIBLE);
 
-        for (int i = 0; i < numRows; i++) {
-            // Get the row view if it already exists.
-            AttachmentTileRow rowView = (AttachmentTileRow) getChildAt(i);
-
-            // If the row view does not exist, create it and add it to its parent.
-            if (rowView == null) {
-                rowView = new AttachmentTileRow(context,
-                        mMessageHeaderItem.message.attachmentListUri, mColumnCount);
-                addView(rowView);
-            }
-
-            // Get the sub-list of attachments for that row.
-            int rowEnd = rowStartIndex + mColumnCount;
-            if (rowEnd > attachmentListSize) {
-                rowEnd = attachmentListSize;
-            }
-            List<Attachment> sublist = attachments.subList(rowStartIndex, rowEnd);
-
-            // Setup the tiles in this row.
-            rowView.configureRow(sublist, i);
-
-            rowStartIndex += mColumnCount;
-        }
+        // Setup the tiles.
+        mAttachmentGrid.configureGrid(mMessageHeaderItem.message.attachmentListUri, attachments);
     }
 
     private Integer getAttachmentLoaderId() {