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