Home | History | Annotate | Download | only in ui
      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