AttachmentTileGrid.java revision 4f347e811052f446c3958c76db278bcd7b39a44f
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 17package com.android.mail.ui; 18 19import android.app.FragmentManager; 20import android.content.Context; 21import android.graphics.Bitmap; 22import android.util.AttributeSet; 23import android.view.LayoutInflater; 24import android.view.View; 25import android.widget.FrameLayout; 26 27import com.android.mail.R; 28import com.android.mail.browse.ConversationMessage; 29import com.android.mail.browse.MessageAttachmentTile; 30import com.android.mail.compose.ComposeAttachmentTile; 31import com.android.mail.photo.MailPhotoViewActivity; 32import com.android.mail.providers.Account; 33import com.android.mail.providers.Attachment; 34import com.android.mail.providers.Message; 35import com.android.mail.ui.AttachmentTile.AttachmentPreview; 36import com.android.mail.ui.AttachmentTile.AttachmentPreviewCache; 37import com.android.mail.utils.ViewUtils; 38import com.google.common.collect.Lists; 39import com.google.common.collect.Maps; 40 41import java.util.ArrayList; 42import java.util.HashMap; 43import java.util.List; 44 45/** 46 * Acts as a grid composed of {@link AttachmentTile}s. 47 */ 48public class AttachmentTileGrid extends FrameLayout implements AttachmentPreviewCache, 49 MessageAttachmentTile.PhotoViewHandler { 50 private final LayoutInflater mInflater; 51 private final int mTileMinSize; 52 private int mColumnCount; 53 private List<Attachment> mAttachments; 54 private final HashMap<String, AttachmentPreview> mAttachmentPreviews; 55 private FragmentManager mFragmentManager; 56 private Account mAccount; 57 private Message mMessage; 58 59 public AttachmentTileGrid(Context context, AttributeSet attrs) { 60 super(context, attrs); 61 mInflater = LayoutInflater.from(context); 62 mTileMinSize = context.getResources() 63 .getDimensionPixelSize(R.dimen.attachment_tile_min_size); 64 mAttachmentPreviews = Maps.newHashMap(); 65 } 66 67 /** 68 * Configures the grid to add {@link Attachment}s information to the views. 69 */ 70 public void configureGrid(FragmentManager fragmentManager, Account account, 71 ConversationMessage message, List<Attachment> list, boolean loaderResult) { 72 73 mFragmentManager = fragmentManager; 74 mAccount = account; 75 mMessage = message; 76 mAttachments = list; 77 // Adding tiles to grid and filling in attachment information 78 int index = 0; 79 for (Attachment attachment : list) { 80 addMessageTileFromAttachment(attachment, index++, loaderResult); 81 } 82 } 83 84 private void addMessageTileFromAttachment(Attachment attachment, int index, 85 boolean loaderResult) { 86 final MessageAttachmentTile attachmentTile; 87 88 if (getChildCount() <= index) { 89 attachmentTile = MessageAttachmentTile.inflate(mInflater, this); 90 attachmentTile.initialize(mFragmentManager); 91 attachmentTile.setPhotoViewHandler(this); 92 addView(attachmentTile); 93 } else { 94 attachmentTile = (MessageAttachmentTile) getChildAt(index); 95 } 96 97 attachmentTile.render(attachment, index, this, loaderResult); 98 } 99 100 public ComposeAttachmentTile addComposeTileFromAttachment(Attachment attachment) { 101 final ComposeAttachmentTile attachmentTile = 102 ComposeAttachmentTile.inflate(mInflater, this); 103 104 addView(attachmentTile); 105 attachmentTile.render(attachment, this); 106 107 return attachmentTile; 108 } 109 110 @Override 111 public void viewPhoto(MessageAttachmentTile source) { 112 final int photoIndex = indexOfChild(source); 113 114 MailPhotoViewActivity.startMailPhotoViewActivity(getContext(), mAccount.getEmailAddress(), 115 mMessage, photoIndex); 116 } 117 118 @Override 119 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 120 onMeasureForTiles(widthMeasureSpec); 121 } 122 123 private void onMeasureForTiles(int widthMeasureSpec) { 124 final int width = MeasureSpec.getSize(widthMeasureSpec); 125 126 final int childCount = getChildCount(); 127 if (childCount == 0) { 128 // Just in case... 129 setMeasuredDimension(width, 0); 130 return; 131 } 132 133 // Divide width by minimum tile size to get the number of columns. 134 // Truncation will ensure that the minimum will always be the minimum 135 // but that the tiles can (and likely will) grow larger. 136 mColumnCount = width / mTileMinSize; 137 138 // Just in case... 139 if (mColumnCount == 0) { 140 mColumnCount = 1; 141 } 142 143 // 1. Calculate image size. 144 // = [total width] / [child count] 145 // 146 // 2. Set it to width/height of each children. 147 // If we have a remainder, some tiles will have 148 // 1 pixel larger width than its height. 149 // 150 // 3. Set the dimensions of itself. 151 // Let width = given width. 152 // Let height = image size + bottom padding. 153 154 final int imageSize = (width) / mColumnCount; 155 final int remainder = width - (imageSize * mColumnCount); 156 157 for (int i = 0; i < childCount; i++) { 158 final View child = getChildAt(i); 159 // Compensate for the remainder 160 final int childWidth = imageSize + (i < remainder ? 1 : 0); 161 child.measure( 162 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 163 MeasureSpec.makeMeasureSpec(imageSize, MeasureSpec.EXACTLY) 164 ); 165 } 166 167 // Calculate the number of rows so we can get the proper height. 168 // Then multiply by the height of one tile to get the grid height. 169 final int numRows = ((childCount - 1) / mColumnCount) + 1; 170 setMeasuredDimension(width, 171 numRows*(imageSize + getChildAt(0).getPaddingBottom())); 172 } 173 174 @Override 175 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 176 onLayoutForTiles(); 177 } 178 179 private void onLayoutForTiles() { 180 final int count = getChildCount(); 181 if (count == 0) { 182 return; 183 } 184 185 boolean skipBeginningOfRowFirstTime = true; 186 final boolean isRtl = ViewUtils.isViewRtl(this); 187 final int width = getMeasuredWidth(); 188 int childLeft = (isRtl) ? width - getChildAt(0).getMeasuredWidth() : 0;; 189 int childTop = 0; 190 191 // Layout the grid. 192 for (int i = 0; i < count; i++) { 193 final View child = getChildAt(i); 194 195 // Note MeasuredWidth and MeasuredHeight include the padding. 196 final int childWidth = child.getMeasuredWidth(); 197 final int childHeight = child.getMeasuredHeight(); 198 199 // If we're at the beginning of a row and it is not the first row 200 // in the grid, reset childLeft to 0 and update childTop 201 // to reflect the top of the new row. 202 if (!skipBeginningOfRowFirstTime && i % mColumnCount == 0) { 203 childLeft = (isRtl) ? width - childWidth : 0; 204 childTop += childHeight; 205 } else { 206 skipBeginningOfRowFirstTime = false; 207 } 208 209 child.layout(childLeft, childTop, 210 childLeft + childWidth, childTop + childHeight); 211 212 if (isRtl) { 213 childLeft -= childWidth; 214 } else { 215 childLeft += childWidth; 216 } 217 } 218 } 219 220 @Override 221 public void sendAccessibilityEvent(int eventType) { 222 // This method is called when the child tile is INVISIBLE (meaning "empty"), and the 223 // Accessibility Manager needs to find alternative content description to speak. 224 // Here, we ignore the default behavior, since we don't want to let the manager speak 225 // a contact name for the tile next to the INVISIBLE tile. 226 } 227 228 public List<Attachment> getAttachments() { 229 return mAttachments; 230 } 231 232 public ArrayList<AttachmentPreview> getAttachmentPreviews() { 233 return Lists.newArrayList(mAttachmentPreviews.values()); 234 } 235 236 public void setAttachmentPreviews(ArrayList<AttachmentPreview> previews) { 237 if (previews != null) { 238 for (AttachmentPreview preview : previews) { 239 mAttachmentPreviews.put(preview.attachmentIdentifier, preview); 240 } 241 } 242 } 243 244 /* 245 * Save the preview for an attachment 246 */ 247 @Override 248 public void set(Attachment attachment, Bitmap preview) { 249 final String attachmentIdentifier = attachment.getIdentifierUri().toString(); 250 if (attachmentIdentifier != null) { 251 mAttachmentPreviews.put( 252 attachmentIdentifier, new AttachmentPreview(attachment, preview)); 253 } 254 } 255 256 /* 257 * Returns a saved preview that was previously set 258 */ 259 @Override 260 public Bitmap get(Attachment attachment) { 261 final String attachmentIdentifier = attachment.getIdentifierUri().toString(); 262 if (attachmentIdentifier != null) { 263 final AttachmentPreview attachmentPreview = mAttachmentPreviews.get( 264 attachmentIdentifier); 265 if (attachmentPreview != null) { 266 return attachmentPreview.preview; 267 } 268 } 269 return null; 270 } 271} 272