1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.mail.ui; 18 19 import android.app.FragmentManager; 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.net.Uri; 23 import android.util.AttributeSet; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.widget.FrameLayout; 27 28 import com.android.mail.R; 29 import com.android.mail.browse.MessageAttachmentTile; 30 import com.android.mail.compose.ComposeAttachmentTile; 31 import com.android.mail.providers.Attachment; 32 import com.android.mail.ui.AttachmentTile.AttachmentPreview; 33 import com.android.mail.ui.AttachmentTile.AttachmentPreviewCache; 34 35 import com.google.common.collect.Lists; 36 import com.google.common.collect.Maps; 37 38 import java.util.ArrayList; 39 import java.util.HashMap; 40 import java.util.List; 41 42 /** 43 * Acts as a grid composed of {@link AttachmentTile}s. 44 */ 45 public class AttachmentTileGrid extends FrameLayout implements AttachmentPreviewCache { 46 private final LayoutInflater mInflater; 47 private Uri mAttachmentsListUri; 48 private final int mTileMinSize; 49 private int mColumnCount; 50 private List<Attachment> mAttachments; 51 private final HashMap<String, AttachmentPreview> mAttachmentPreviews; 52 private FragmentManager mFragmentManager; 53 54 public AttachmentTileGrid(Context context, AttributeSet attrs) { 55 super(context, attrs); 56 mInflater = LayoutInflater.from(context); 57 mTileMinSize = context.getResources() 58 .getDimensionPixelSize(R.dimen.attachment_tile_min_size); 59 mAttachmentPreviews = Maps.newHashMap(); 60 } 61 62 /** 63 * Configures the grid to add {@link Attachment}s information to the views. 64 */ 65 public void configureGrid(FragmentManager fragmentManager, Uri attachmentsListUri, 66 List<Attachment> list, boolean loaderResult) { 67 mFragmentManager = fragmentManager; 68 mAttachmentsListUri = attachmentsListUri; 69 mAttachments = list; 70 // Adding tiles to grid and filling in attachment information 71 int index = 0; 72 for (Attachment attachment : list) { 73 addMessageTileFromAttachment(attachment, index++, loaderResult); 74 } 75 } 76 77 private void addMessageTileFromAttachment(Attachment attachment, int index, 78 boolean loaderResult) { 79 final MessageAttachmentTile attachmentTile; 80 81 if (getChildCount() <= index) { 82 attachmentTile = MessageAttachmentTile.inflate(mInflater, this); 83 attachmentTile.initialize(mFragmentManager); 84 addView(attachmentTile); 85 } else { 86 attachmentTile = (MessageAttachmentTile) getChildAt(index); 87 } 88 89 attachmentTile.render(attachment, mAttachmentsListUri, index, this, loaderResult); 90 } 91 92 public ComposeAttachmentTile addComposeTileFromAttachment(Attachment attachment) { 93 final ComposeAttachmentTile attachmentTile = 94 ComposeAttachmentTile.inflate(mInflater, this); 95 96 addView(attachmentTile); 97 attachmentTile.render(attachment, null, -1, this, false); 98 99 return attachmentTile; 100 } 101 102 @Override 103 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 104 onMeasureForTiles(widthMeasureSpec); 105 } 106 107 private void onMeasureForTiles(int widthMeasureSpec) { 108 final int width = MeasureSpec.getSize(widthMeasureSpec); 109 110 final int childCount = getChildCount(); 111 if (childCount == 0) { 112 // Just in case... 113 setMeasuredDimension(width, 0); 114 return; 115 } 116 117 // Divide width by minimum tile size to get the number of columns. 118 // Truncation will ensure that the minimum will always be the minimum 119 // but that the tiles can (and likely will) grow larger. 120 mColumnCount = width / mTileMinSize; 121 122 // Just in case... 123 if (mColumnCount == 0) { 124 mColumnCount = 1; 125 } 126 127 // 1. Calculate image size. 128 // = [total width] / [child count] 129 // 130 // 2. Set it to width/height of each children. 131 // If we have a remainder, some tiles will have 132 // 1 pixel larger width than its height. 133 // 134 // 3. Set the dimensions of itself. 135 // Let width = given width. 136 // Let height = image size + bottom padding. 137 138 final int imageSize = (width) / mColumnCount; 139 final int remainder = width - (imageSize * mColumnCount); 140 141 for (int i = 0; i < childCount; i++) { 142 final View child = getChildAt(i); 143 // Compensate for the remainder 144 final int childWidth = imageSize + (i < remainder ? 1 : 0); 145 child.measure( 146 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 147 MeasureSpec.makeMeasureSpec(imageSize, MeasureSpec.EXACTLY) 148 ); 149 } 150 151 // Calculate the number of rows so we can get the proper height. 152 // Then multiply by the height of one tile to get the grid height. 153 final int numRows = ((childCount - 1) / mColumnCount) + 1; 154 setMeasuredDimension(width, 155 numRows*(imageSize + getChildAt(0).getPaddingBottom())); 156 } 157 158 @Override 159 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 160 onLayoutForTiles(); 161 } 162 163 private void onLayoutForTiles() { 164 final int count = getChildCount(); 165 int childLeft = 0; 166 int childTop = 0; 167 boolean skipBeginningOfRowFirstTime = true; 168 169 // Layout the grid. 170 for (int i = 0; i < count; i++) { 171 final View child = getChildAt(i); 172 173 // Note MeasuredWidth and MeasuredHeight include the padding. 174 final int childWidth = child.getMeasuredWidth(); 175 final int childHeight = child.getMeasuredHeight(); 176 177 // If we're at the beginning of a row and it is not the first row 178 // in the grid, reset childLeft to 0 and update childTop 179 // to reflect the top of the new row. 180 if (!skipBeginningOfRowFirstTime && i % mColumnCount == 0) { 181 childLeft = 0; 182 childTop += childHeight; 183 } else { 184 skipBeginningOfRowFirstTime = false; 185 } 186 187 child.layout(childLeft, childTop, 188 childLeft + childWidth, childTop + childHeight); 189 childLeft += childWidth; 190 } 191 } 192 193 @Override 194 public void sendAccessibilityEvent(int eventType) { 195 // This method is called when the child tile is INVISIBLE (meaning "empty"), and the 196 // Accessibility Manager needs to find alternative content description to speak. 197 // Here, we ignore the default behavior, since we don't want to let the manager speak 198 // a contact name for the tile next to the INVISIBLE tile. 199 } 200 201 public List<Attachment> getAttachments() { 202 return mAttachments; 203 } 204 205 public ArrayList<AttachmentPreview> getAttachmentPreviews() { 206 return Lists.newArrayList(mAttachmentPreviews.values()); 207 } 208 209 public void setAttachmentPreviews(ArrayList<AttachmentPreview> previews) { 210 if (previews != null) { 211 for (AttachmentPreview preview : previews) { 212 mAttachmentPreviews.put(preview.attachmentIdentifier, preview); 213 } 214 } 215 } 216 217 /* 218 * Save the preview for an attachment 219 */ 220 @Override 221 public void set(Attachment attachment, Bitmap preview) { 222 final String attachmentIdentifier = attachment.getIdentifierUri().toString(); 223 if (attachmentIdentifier != null) { 224 mAttachmentPreviews.put( 225 attachmentIdentifier, new AttachmentPreview(attachment, preview)); 226 } 227 } 228 229 /* 230 * Returns a saved preview that was previously set 231 */ 232 @Override 233 public Bitmap get(Attachment attachment) { 234 final String attachmentIdentifier = attachment.getIdentifierUri().toString(); 235 if (attachmentIdentifier != null) { 236 final AttachmentPreview attachmentPreview = mAttachmentPreviews.get( 237 attachmentIdentifier); 238 if (attachmentPreview != null) { 239 return attachmentPreview.preview; 240 } 241 } 242 return null; 243 } 244 } 245