Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2012 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mail.ui;
     19 
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.graphics.Bitmap;
     23 import android.os.Parcel;
     24 import android.os.Parcelable;
     25 import android.text.TextUtils;
     26 import android.util.AttributeSet;
     27 import android.util.DisplayMetrics;
     28 import android.view.View;
     29 import android.widget.ImageView;
     30 import android.widget.ImageView.ScaleType;
     31 import android.widget.RelativeLayout;
     32 import android.widget.TextView;
     33 
     34 import com.android.ex.photo.util.ImageUtils;
     35 import com.android.mail.R;
     36 import com.android.mail.providers.Attachment;
     37 import com.android.mail.utils.AttachmentUtils;
     38 import com.android.mail.utils.LogTag;
     39 import com.android.mail.utils.LogUtils;
     40 
     41 /**
     42  * Base class for attachment tiles that handles the work of fetching and displaying the bitmaps for
     43  * the tiles.
     44  */
     45 public abstract class AttachmentTile extends RelativeLayout implements AttachmentBitmapHolder {
     46     protected Attachment mAttachment;
     47     private ImageView mIcon;
     48     private ImageView mDefaultIcon;
     49     private TextView mTitle;
     50     private TextView mSubtitle;
     51     private String mAttachmentSizeText;
     52     private String mDisplayType;
     53     private boolean mDefaultThumbnailSet;
     54     private AttachmentPreviewCache mAttachmentPreviewCache;
     55 
     56     private static final String LOG_TAG = LogTag.getLogTag();
     57     // previews with width/height or height/width less than this value will be
     58     // considered skinny
     59     private static final float skinnyThresholdRatio = 0.5f;
     60 
     61     private boolean mAlwaysShowInfoText;
     62 
     63 
     64     /**
     65      * Returns true if the attachment should be rendered as a tile. with a large image preview.
     66      * @param attachment the attachment to render
     67      * @return true if the attachment should be rendered as a tile
     68      */
     69     public static boolean isTiledAttachment(final Attachment attachment) {
     70         return ImageUtils.isImageMimeType(attachment.getContentType());
     71     }
     72 
     73     public AttachmentTile(Context context) {
     74         this(context, null);
     75     }
     76 
     77     public AttachmentTile(Context context, AttributeSet attrs) {
     78         super(context, attrs);
     79         mDefaultThumbnailSet = true;
     80         mAlwaysShowInfoText = false;
     81     }
     82 
     83     @Override
     84     protected void onFinishInflate() {
     85         super.onFinishInflate();
     86 
     87         mTitle = (TextView) findViewById(R.id.attachment_tile_title);
     88         mSubtitle = (TextView) findViewById(R.id.attachment_tile_subtitle);
     89         mIcon = (ImageView) findViewById(R.id.attachment_tile_image);
     90         mDefaultIcon = (ImageView) findViewById(R.id.attachment_default_image);
     91     }
     92 
     93     @Override
     94     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     95         super.onLayout(changed, l, t, r, b);
     96 
     97         ThumbnailLoadTask.setupThumbnailPreview(mAttachmentPreviewCache, this, mAttachment, null);
     98     }
     99 
    100     public Attachment getAttachment() {
    101         return mAttachment;
    102     }
    103 
    104     /**
    105      * Render or update an attachment's view. This happens immediately upon instantiation, and
    106      * repeatedly as status updates stream in, so only properties with new or changed values will
    107      * cause sub-views to update.
    108      */
    109     protected void render(Attachment attachment, AttachmentPreviewCache attachmentPreviewCache) {
    110         if (attachment == null) {
    111             setVisibility(View.INVISIBLE);
    112             return;
    113         }
    114 
    115         final Attachment prevAttachment = mAttachment;
    116         mAttachment = attachment;
    117         mAttachmentPreviewCache = attachmentPreviewCache;
    118 
    119         LogUtils.d(LOG_TAG, "got attachment list row: name=%s state/dest=%d/%d dled=%d" +
    120                 " contentUri=%s MIME=%s flags=%d", attachment.getName(), attachment.state,
    121                 attachment.destination, attachment.downloadedSize, attachment.contentUri,
    122                 attachment.getContentType(), attachment.flags);
    123 
    124         if ((attachment.flags & Attachment.FLAG_DUMMY_ATTACHMENT) != 0) {
    125             // TODO: This is not an ideal string, but it's too late in KLP to add new strings.
    126             mTitle.setText(R.string.load_more);
    127         } else if (prevAttachment == null
    128                 || !TextUtils.equals(attachment.getName(), prevAttachment.getName())) {
    129             mTitle.setText(attachment.getName());
    130         }
    131 
    132         if (prevAttachment == null || attachment.size != prevAttachment.size) {
    133             mAttachmentSizeText = AttachmentUtils.convertToHumanReadableSize(getContext(),
    134                     attachment.size);
    135             mDisplayType = AttachmentUtils.getDisplayType(getContext(), attachment);
    136             updateSubtitleText();
    137         }
    138 
    139         ThumbnailLoadTask.setupThumbnailPreview(mAttachmentPreviewCache, this, attachment,
    140                 prevAttachment);
    141     }
    142 
    143     private void updateSubtitleText() {
    144         // TODO: make this a formatted resource when we have a UX design.
    145         // not worth translation right now.
    146         StringBuilder sb = new StringBuilder();
    147         sb.append(mAttachmentSizeText);
    148         if (mDisplayType != null) {
    149             sb.append(' ');
    150             sb.append(mDisplayType);
    151         }
    152         mSubtitle.setText(sb.toString());
    153     }
    154 
    155     @Override
    156     public void setThumbnailToDefault() {
    157         final Bitmap cachedPreview = mAttachmentPreviewCache.get(mAttachment);
    158         if (cachedPreview != null) {
    159             setThumbnail(cachedPreview);
    160             return;
    161         }
    162         mDefaultIcon.setVisibility(View.VISIBLE);
    163         mTitle.setVisibility(View.VISIBLE);
    164         mSubtitle.setVisibility(View.VISIBLE);
    165         mDefaultThumbnailSet = true;
    166     }
    167 
    168     @Override
    169     public void setThumbnail(Bitmap result) {
    170         if (result == null) {
    171             return;
    172         }
    173 
    174         // We got a real thumbnail; hide the default thumbnail.
    175         mDefaultIcon.setVisibility(View.GONE);
    176         if (!mAlwaysShowInfoText) {
    177             mTitle.setVisibility(View.GONE);
    178             mSubtitle.setVisibility(View.GONE);
    179         }
    180 
    181         final int maxSize = getResources().getInteger(R.integer.attachment_preview_max_size);
    182         final int width = result.getWidth();
    183         final int height = result.getHeight();
    184         final int scaledWidth = width * getResources().getDisplayMetrics().densityDpi
    185                 / DisplayMetrics.DENSITY_DEFAULT;
    186         final int scaledHeight = height * getResources().getDisplayMetrics().densityDpi
    187                 / DisplayMetrics.DENSITY_DEFAULT;
    188         // ratio of the image
    189         final float ratio = Math.min((float) width / height, (float) height / width);
    190 
    191         final boolean large = width >= maxSize || scaledWidth >= mIcon.getWidth()
    192                 || height >= maxSize || scaledHeight >= mIcon.getHeight();
    193         final boolean skinny =
    194                 // the image is loooong
    195                 ratio < skinnyThresholdRatio &&
    196                 // AND if the image was centered and cropped, the resulting
    197                 // image would still be loooong
    198                 !(scaledWidth >= mIcon.getHeight() * skinnyThresholdRatio
    199                         && scaledHeight >= mIcon.getWidth() * skinnyThresholdRatio);
    200         LogUtils.d(LOG_TAG, "scaledWidth: %d, scaledHeight: %d, large: %b, skinny: %b", scaledWidth,
    201                 scaledHeight, large, skinny);
    202 
    203         if (large) {
    204             // preview fills up at least 1 dimension
    205             if (skinny) {
    206                 // just center. The shorter dimension stays the same while the
    207                 // longer dimension is cropped
    208                 mIcon.setScaleType(ScaleType.CENTER);
    209             } else {
    210                 // fill. Both dimensions are scaled to fill the box, the longer
    211                 // dimension is cropped
    212                 mIcon.setScaleType(ScaleType.CENTER_CROP);
    213             }
    214         } else {
    215             // preview is small. just center
    216             mIcon.setScaleType(ScaleType.CENTER);
    217         }
    218 
    219         mIcon.setImageBitmap(result);
    220         mAttachmentPreviewCache.set(mAttachment, result);
    221         mDefaultThumbnailSet = false;
    222     }
    223 
    224     @Override
    225     public int getThumbnailWidth() {
    226         return mIcon.getWidth();
    227     }
    228 
    229     @Override
    230     public int getThumbnailHeight() {
    231         return mIcon.getHeight();
    232     }
    233 
    234     @Override
    235     public ContentResolver getResolver() {
    236         return getContext().getContentResolver();
    237     }
    238 
    239     @Override
    240     public boolean bitmapSetToDefault() {
    241         return mDefaultThumbnailSet;
    242     }
    243 
    244     public static final class AttachmentPreview implements Parcelable {
    245         public String attachmentIdentifier;
    246         public Bitmap preview;
    247 
    248         @Override
    249         public int describeContents() {
    250             return 0;
    251         }
    252 
    253         @Override
    254         public void writeToParcel(Parcel dest, int flags) {
    255             dest.writeString(attachmentIdentifier);
    256             dest.writeParcelable(preview, 0);
    257         }
    258 
    259         public static final Parcelable.Creator<AttachmentPreview> CREATOR
    260                 = new Parcelable.Creator<AttachmentPreview>() {
    261                         @Override
    262                     public AttachmentPreview createFromParcel(Parcel in) {
    263                         return new AttachmentPreview(in);
    264                     }
    265 
    266                         @Override
    267                     public AttachmentPreview[] newArray(int size) {
    268                         return new AttachmentPreview[size];
    269                     }
    270                 };
    271 
    272         private AttachmentPreview(Parcel in) {
    273             attachmentIdentifier = in.readString();
    274             preview = in.readParcelable(null);
    275         }
    276 
    277         public AttachmentPreview(Attachment attachment, Bitmap preview) {
    278             this.attachmentIdentifier = attachment.getIdentifierUri().toString();
    279             this.preview = preview;
    280         }
    281     }
    282 
    283     public interface AttachmentPreviewCache {
    284         void set(Attachment attachment, Bitmap preview);
    285         Bitmap get(Attachment attachment);
    286     }
    287 
    288     @Override
    289     public void thumbnailLoadFailed() {
    290         setThumbnailToDefault();
    291     }
    292 
    293     protected void setAlwaysShowInfoText(boolean alwaysShowInfoText) {
    294         mAlwaysShowInfoText = alwaysShowInfoText;
    295     }
    296 }
    297