Attachments now show in a grid-like view.

Currently, two per row are displayed in portrait
and three per row in landscape. Need UX decision
on how many to show for larger screen sizes.

Much of this code is taken from Contacts which
used a similar layout for favorite contacts.
That layout was taken and tweaked to suit the
attachment handling purposes.

Still need to add the appropriate interactions
for the various attachment types per UX's guidelines.
Will add that in the next CL.

Change-Id: Iad6ea716a680b1fd48f16d34de2a5bc2e87b7db5
diff --git a/res/drawable-hdpi/ic_divider_dashed_holo_dark.png b/res/drawable-hdpi/ic_divider_dashed_holo_dark.png
new file mode 100644
index 0000000..663a2f8
--- /dev/null
+++ b/res/drawable-hdpi/ic_divider_dashed_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_divider_dashed_holo_dark.png b/res/drawable-mdpi/ic_divider_dashed_holo_dark.png
new file mode 100644
index 0000000..6652541
--- /dev/null
+++ b/res/drawable-mdpi/ic_divider_dashed_holo_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_divider_dashed_holo_dark.png b/res/drawable-xhdpi/ic_divider_dashed_holo_dark.png
new file mode 100644
index 0000000..34e8fd2
--- /dev/null
+++ b/res/drawable-xhdpi/ic_divider_dashed_holo_dark.png
Binary files differ
diff --git a/res/layout/conversation_message_attachment.xml b/res/layout/conversation_message_attachment.xml
index df0a466..e60ea5a 100644
--- a/res/layout/conversation_message_attachment.xml
+++ b/res/layout/conversation_message_attachment.xml
@@ -15,8 +15,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.mail.browse.MessageHeaderAttachment xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+<com.android.mail.browse.MessageAttachmentTile
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/attachment_tile"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginBottom="8dp"
     android:orientation="vertical"
@@ -25,59 +27,87 @@
     android:background="@drawable/attachment_bg_holo">
 
     <RelativeLayout
+        android:id="@+id/attachment_tile_layout"
         android:layout_width="match_parent"
-        android:layout_height="48dp">
+        android:layout_height="match_parent" >
 
         <ImageView
-            android:id="@+id/attachment_icon"
-            android:layout_width="48dp"
+            android:id="@+id/attachment_tile_image"
+            android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:scaleType="centerCrop"
-            android:background="#e5e5e5" />
+            android:scaleType="centerCrop" />
 
         <RelativeLayout
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_toRightOf="@id/attachment_icon"
+            android:layout_height="@dimen/attachment_tile_shadowbox_height"
+            android:background="@color/attachment_tile_shadow_box_color"
+            android:layout_alignParentBottom="true"
             android:gravity="center_vertical"
-            android:layout_marginLeft="16dp"
-            android:layout_marginRight="16dp">
+            android:paddingLeft="8dip">
 
-            <TextView
-                android:id="@+id/attachment_title"
+            <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="true" />
+                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_subtitle"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/attachment_title"
-                android:singleLine="true" />
+                <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_below="@id/attachment_title"
+                android:layout_alignParentBottom="true"
                 style="?android:attr/progressBarStyleHorizontal"
                 android:indeterminate="false"
                 android:visibility="gone" />
 
         </RelativeLayout>
 
+        <View
+            android:id="@+id/attachment_tile_push_state"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:focusable="true"
+            android:background="?android:attr/selectableItemBackground" />
+
     </RelativeLayout>
 
-    <LinearLayout
-            android:id="@+id/attachment_buttons"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:divider="?android:attr/dividerVertical"
-            android:showDividers="middle"
-            android:dividerPadding="8dp"
-            android:orientation="horizontal">
-        <include layout="@layout/conversation_message_attachment_buttons" />
-    </LinearLayout>
-
-</com.android.mail.browse.MessageHeaderAttachment>
+</com.android.mail.browse.MessageAttachmentTile>
diff --git a/res/values-land/constants.xml b/res/values-land/constants.xml
new file mode 100644
index 0000000..fc5925f
--- /dev/null
+++ b/res/values-land/constants.xml
@@ -0,0 +1,21 @@
+<?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/colors.xml b/res/values/colors.xml
index e4c0366..3f71f9a 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -70,4 +70,9 @@
     <color name="photo_background_color">#ff000000</color>
     <color name="photo_crop_dim_color">#cc000000</color>
     <color name="photo_crop_highlight_color">#fff</color>
+
+    <!--  Color of the semi-transparent shadow box on attachment tiles -->
+    <color name="attachment_tile_shadow_box_color">#7F000000</color>
+    <!--  Color of the subtitle message in the attachment tile -->
+    <color name="attachment_tile_subtitle_color">#CCCCCC</color>
 </resources>
diff --git a/res/values/constants.xml b/res/values/constants.xml
index 0b255ed..4459642 100644
--- a/res/values/constants.xml
+++ b/res/values/constants.xml
@@ -69,4 +69,7 @@
 
     <!-- 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 d39558b..cf54584 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -96,4 +96,11 @@
     <dimen name="photo_crop_stroke_width">1dp</dimen>
     <dimen name="photo_overlay_right_padding">4dp</dimen>
     <dimen name="photo_overlay_bottom_padding">6dp</dimen>
+
+    <!-- Attachment Tile Layouts -->
+    <!--
+      Use sp instead of dip so that the shadowbox heights can all scale uniformly
+      when the font size is scaled for accessibility purposes
+    -->
+    <dimen name="attachment_tile_shadowbox_height">48sp</dimen>
 </resources>
diff --git a/src/com/android/mail/browse/AttachmentTileRow.java b/src/com/android/mail/browse/AttachmentTileRow.java
new file mode 100644
index 0000000..794370e
--- /dev/null
+++ b/src/com/android/mail/browse/AttachmentTileRow.java
@@ -0,0 +1,140 @@
+/*
+ * 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/MessageHeaderAttachment.java b/src/com/android/mail/browse/MessageAttachmentTile.java
similarity index 90%
rename from src/com/android/mail/browse/MessageHeaderAttachment.java
rename to src/com/android/mail/browse/MessageAttachmentTile.java
index 3dc8344..be86463 100644
--- a/src/com/android/mail/browse/MessageHeaderAttachment.java
+++ b/src/com/android/mail/browse/MessageAttachmentTile.java
@@ -64,7 +64,7 @@
  * intents to act on an attachment.
  *
  */
-public class MessageHeaderAttachment extends LinearLayout implements OnClickListener,
+public class MessageAttachmentTile extends LinearLayout implements OnClickListener,
         OnMenuItemClickListener, DialogInterface.OnCancelListener,
         DialogInterface.OnDismissListener {
 
@@ -183,18 +183,18 @@
 
     }
 
-    public MessageHeaderAttachment(Context context) {
+    public MessageAttachmentTile(Context context) {
         super(context);
     }
 
-    public MessageHeaderAttachment(Context context, AttributeSet attrs) {
+    public MessageAttachmentTile(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         mCommandHandler = new AttachmentCommandHandler();
     }
 
-    public static MessageHeaderAttachment inflate(LayoutInflater inflater, ViewGroup parent) {
-        MessageHeaderAttachment view = (MessageHeaderAttachment) inflater.inflate(
+    public static MessageAttachmentTile inflate(LayoutInflater inflater, ViewGroup parent) {
+        MessageAttachmentTile view = (MessageAttachmentTile) inflater.inflate(
                 R.layout.conversation_message_attachment, parent, false);
         return view;
     }
@@ -206,6 +206,11 @@
      *
      */
     public void render(Attachment attachment, Uri attachmentsListUri, int index) {
+        if (attachment == null) {
+            setVisibility(View.INVISIBLE);
+            return;
+        }
+
         final Attachment prevAttachment = mAttachment;
         mAttachment = attachment;
         mAttachmentsListUri = attachmentsListUri;
@@ -244,7 +249,9 @@
             setThumbnailToDefault();
         }
 
-        mProgress.setMax(attachment.size);
+        if (mProgress != null) {
+            mProgress.setMax(attachment.size);
+        }
 
         updateActions();
         updateStatus();
@@ -321,27 +328,27 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mIcon = (ImageView) findViewById(R.id.attachment_icon);
-        mTitle = (TextView) findViewById(R.id.attachment_title);
-        mSubTitle = (TextView) findViewById(R.id.attachment_subtitle);
+        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.save_attachment);
-        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);
+//        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);
+//        mPreviewButton.setOnClickListener(this);
+//        mViewButton.setOnClickListener(this);
         mSaveButton.setOnClickListener(this);
-        mInfoButton.setOnClickListener(this);
-        mPlayButton.setOnClickListener(this);
-        mInstallButton.setOnClickListener(this);
-        mCancelButton.setOnClickListener(this);
+//        mInfoButton.setOnClickListener(this);
+//        mPlayButton.setOnClickListener(this);
+//        mInstallButton.setOnClickListener(this);
+//        mCancelButton.setOnClickListener(this);
 
         mIconScaleType = mIcon.getScaleType();
     }
@@ -363,9 +370,11 @@
                 break;
             case R.id.view_attachment:
             case R.id.play_attachment:
+            case R.id.attachment_tile:
                 showAttachment(AttachmentDestination.CACHE);
                 break;
             case R.id.save_attachment:
+            case R.id.attachment_tile_secondary_button:
                 if (mAttachment.canSave()) {
                     startDownloadingAttachment(AttachmentDestination.EXTERNAL);
                 }
@@ -383,8 +392,6 @@
                 cancelAttachment();
                 break;
             default:
-                // entire attachment view is clickable.
-                // TODO: this should execute a default action
                 break;
         }
         return true;
@@ -415,7 +422,9 @@
     }
 
     private void setButtonVisible(View button, boolean visible) {
-        button.setVisibility(visible ? VISIBLE : GONE);
+        if (button != null) {
+            button.setVisibility(visible ? VISIBLE : GONE);
+        }
     }
 
     /**
diff --git a/src/com/android/mail/browse/MessageFooterView.java b/src/com/android/mail/browse/MessageFooterView.java
index 3b8c2be..1dc90a7 100644
--- a/src/com/android/mail/browse/MessageFooterView.java
+++ b/src/com/android/mail/browse/MessageFooterView.java
@@ -23,9 +23,9 @@
 import android.database.Cursor;
 import android.os.Bundle;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
 import android.widget.LinearLayout;
 
+import com.android.mail.R;
 import com.android.mail.browse.AttachmentLoader.AttachmentCursor;
 import com.android.mail.browse.ConversationContainer.DetachListener;
 import com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem;
@@ -39,10 +39,11 @@
 public class MessageFooterView extends LinearLayout implements DetachListener,
         LoaderManager.LoaderCallbacks<Cursor> {
 
+    private int mColumnCount;
+
     private MessageHeaderItem mMessageHeaderItem;
     private LoaderManager mLoaderManager;
     private AttachmentCursor mAttachmentsCursor;
-    private LayoutInflater mInflater;
 
     /**
      * An easy way for the conversation view to disable immediately kicking off attachment loaders
@@ -59,7 +60,8 @@
     public MessageFooterView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mInflater = LayoutInflater.from(context);
+        mColumnCount = context.getResources().getInteger(
+                R.integer.attachment_tile_column_count);
     }
 
     /**
@@ -132,19 +134,37 @@
     }
 
     private void renderAttachments(List<Attachment> attachments) {
-        int index = 0;
-        for (Attachment attachment : attachments) {
-            MessageHeaderAttachment attachView = (MessageHeaderAttachment) findViewWithTag(
-                    attachment.uri);
+        if (attachments == null || attachments.isEmpty()) {
+            return;
+        }
 
-            if (attachView == null) {
-                attachView = MessageHeaderAttachment.inflate(mInflater, this);
-                attachView.setTag(attachment.uri);
-                addView(attachView);
+        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;
+
+        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);
             }
 
-            attachView.render(attachment, mMessageHeaderItem.message.attachmentListUri, index);
-            index++;
+            // 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;
         }
     }
 
@@ -188,5 +208,4 @@
     public void onLoaderReset(Loader<Cursor> loader) {
         mAttachmentsCursor = null;
     }
-
 }
diff --git a/src/com/android/mail/photo/PhotoViewActivity.java b/src/com/android/mail/photo/PhotoViewActivity.java
index 233698d..a7116a9 100644
--- a/src/com/android/mail/photo/PhotoViewActivity.java
+++ b/src/com/android/mail/photo/PhotoViewActivity.java
@@ -150,8 +150,6 @@
 
     /** The URI of the photos we're viewing; may be {@code null} */
     private String mPhotosUri;
-    /** The resolved URI of the photo to view; may be {@code null}. */
-    private String mResolvedPhotoUri;
     /** The index of the currently viewed photo */
     private int mPhotoIndex;
     /** A hint for which cursor page the photo is located on */