AttachmentTileGrid.java revision 4cb51dbce9635f4bf68a6de88f759e2f86d20325
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.content.Context;
20import android.net.Uri;
21import android.util.AttributeSet;
22import android.view.LayoutInflater;
23import android.view.View;
24import android.widget.FrameLayout;
25
26import com.android.mail.R;
27import com.android.mail.browse.MessageAttachmentTile;
28import com.android.mail.compose.ComposeAttachmentTile;
29import com.android.mail.providers.Attachment;
30
31import java.util.List;
32
33/**
34 * Acts as a grid composed of {@link AttachmentTile}s.
35 */
36public class AttachmentTileGrid extends FrameLayout {
37    private LayoutInflater mInflater;
38    private Uri mAttachmentsListUri;
39    private final int mTileMinSize;
40    private int mColumnCount;
41    private List<Attachment> mAttachments;
42
43    public AttachmentTileGrid(Context context, AttributeSet attrs) {
44        super(context, attrs);
45        mInflater = LayoutInflater.from(context);
46        mTileMinSize = context.getResources()
47                .getDimensionPixelSize(R.dimen.attachment_tile_min_size);
48    }
49
50    /**
51     * Configures the grid to add {@link Attachment}s information to the views.
52     */
53    public void configureGrid(Uri attachmentsListUri, List<Attachment> list) {
54        mAttachmentsListUri = attachmentsListUri;
55        mAttachments = list;
56        // Adding tiles to grid and filling in attachment information
57        int index = 0;
58        for (Attachment attachment : list) {
59            addMessageTileFromAttachment(attachment, index++);
60        }
61    }
62
63    private void addMessageTileFromAttachment(Attachment attachment, int index) {
64        final AttachmentTile attachmentTile;
65
66        if (getChildCount() <= index) {
67            attachmentTile = MessageAttachmentTile.inflate(mInflater, this);
68            addView(attachmentTile);
69        } else {
70            attachmentTile = (AttachmentTile) getChildAt(index);
71        }
72
73        attachmentTile.render(attachment, mAttachmentsListUri, index);
74    }
75
76    public ComposeAttachmentTile addComposeTileFromAttachment(Attachment attachment) {
77        final ComposeAttachmentTile attachmentTile =
78                ComposeAttachmentTile.inflate(mInflater, this);
79
80        addView(attachmentTile);
81        attachmentTile.render(attachment, null, -1);
82
83        return attachmentTile;
84    }
85
86    @Override
87    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
88        onMeasureForTiles(widthMeasureSpec);
89    }
90
91    private void onMeasureForTiles(int widthMeasureSpec) {
92        final int width = MeasureSpec.getSize(widthMeasureSpec);
93
94        final int childCount = getChildCount();
95        if (childCount == 0) {
96            // Just in case...
97            setMeasuredDimension(width, 0);
98            return;
99        }
100
101        // Divide width by minimum tile size to get the number of columns.
102        // Truncation will ensure that the minimum will always be the minimum
103        // but that the tiles can (and likely will) grow larger.
104        mColumnCount = width / mTileMinSize;
105
106        // Just in case...
107        if (mColumnCount == 0) {
108            mColumnCount = 1;
109        }
110
111        // 1. Calculate image size.
112        //      = [total width] / [child count]
113        //
114        // 2. Set it to width/height of each children.
115        //    If we have a remainder, some tiles will have
116        //    1 pixel larger width than its height.
117        //
118        // 3. Set the dimensions of itself.
119        //    Let width = given width.
120        //    Let height = image size + bottom padding.
121        final int imageSize = (width) / mColumnCount;
122        final int remainder = width - (imageSize * mColumnCount);
123
124        for (int i = 0; i < childCount; i++) {
125            final View child = getChildAt(i);
126            // Compensate for the remainder
127            final int childWidth = imageSize + (i < remainder ? 1 : 0);
128            child.measure(
129                    MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
130                    MeasureSpec.makeMeasureSpec(imageSize, MeasureSpec.EXACTLY)
131                    );
132        }
133
134        // Calculate the number of rows so we can get the proper height.
135        // Then multiply by the height of one tile to get the grid height.
136        final int numRows = ((childCount - 1) / mColumnCount) + 1;
137        setMeasuredDimension(width,
138                numRows*(imageSize + getChildAt(0).getPaddingBottom()));
139    }
140
141    @Override
142    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
143        onLayoutForTiles();
144    }
145
146    private void onLayoutForTiles() {
147        final int count = getChildCount();
148        int childLeft = 0;
149        int childTop = 0;
150        boolean skipBeginningOfRowFirstTime = true;
151
152        // Layout the grid.
153        for (int i = 0; i < count; i++) {
154            final View child = getChildAt(i);
155
156            // Note MeasuredWidth and MeasuredHeight include the padding.
157            final int childWidth = child.getMeasuredWidth();
158            final int childHeight = child.getMeasuredHeight();
159
160            // If we're at the beginning of a row and it is not the first row
161            // in the grid, reset childLeft to 0 and update childTop
162            // to reflect the top of the new row.
163            if (!skipBeginningOfRowFirstTime && i % mColumnCount == 0) {
164                childLeft = 0;
165                childTop += childHeight;
166            } else {
167                skipBeginningOfRowFirstTime = false;
168            }
169
170            child.layout(childLeft, childTop,
171                    childLeft + childWidth, childTop + childHeight);
172            childLeft += childWidth;
173        }
174    }
175
176    @Override
177    public void sendAccessibilityEvent(int eventType) {
178        // This method is called when the child tile is INVISIBLE (meaning "empty"), and the
179        // Accessibility Manager needs to find alternative content description to speak.
180        // Here, we ignore the default behavior, since we don't want to let the manager speak
181        // a contact name for the tile next to the INVISIBLE tile.
182    }
183
184    public List<Attachment> getAttachments() {
185        return mAttachments;
186    }
187}
188