Merge "Attachments now show in a grid-like view." into jb-ub-mail
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 */