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