ThumbnailLoadTask.java revision 4cb51dbce9635f4bf68a6de88f759e2f86d20325
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
18package com.android.mail.ui;
19
20import android.content.res.AssetFileDescriptor;
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
23import android.net.Uri;
24import android.os.AsyncTask;
25
26import com.android.mail.providers.Attachment;
27import com.android.mail.utils.LogTag;
28import com.android.mail.utils.LogUtils;
29
30import java.io.IOException;
31
32/**
33 * Performs the load of a thumbnail bitmap in a background
34 * {@link AsyncTask}. Available for use with any view that implements
35 * the {@link AttachmentBitmapHolder} interface.
36 */
37public class ThumbnailLoadTask extends AsyncTask<Uri, Void, Bitmap> {
38    private static final String LOG_TAG = LogTag.getLogTag();
39
40    private final AttachmentBitmapHolder mHolder;
41    private final int mWidth;
42    private final int mHeight;
43
44    public static void setupThumbnailPreview(
45            ThumbnailLoadTask task, final AttachmentBitmapHolder holder,
46            final Attachment attachment, final Attachment prevAttachment) {
47        final int width = holder.getThumbnailWidth();
48        final int height = holder.getThumbnailHeight();
49        if (attachment == null || width == 0 || height == 0 || !attachment.isImage()) {
50            holder.setThumbnailToDefault();
51            return;
52        }
53
54        final Uri thumbnailUri = attachment.thumbnailUri;
55        final Uri contentUri = attachment.contentUri;
56        final Uri uri = (prevAttachment == null) ? null : prevAttachment.uri;
57        final Uri prevUri = (prevAttachment == null) ? null : prevAttachment.uri;
58        // begin loading a thumbnail if this is an image and either the thumbnail or the original
59        // content is ready (and different from any existing image)
60        if ((thumbnailUri != null || contentUri != null)
61                && (holder.bitmapSetToDefault() ||
62                prevUri == null || !uri.equals(prevUri))) {
63            // cancel/dispose any existing task and start a new one
64            if (task != null) {
65                task.cancel(true);
66            }
67
68            task = new ThumbnailLoadTask(
69                    holder, width, height);
70            task.execute(thumbnailUri, contentUri);
71        } else if (thumbnailUri == null && contentUri == null) {
72            // not an image, or no thumbnail exists. fall back to default.
73            // async image load must separately ensure the default appears upon load failure.
74            holder.setThumbnailToDefault();
75        }
76    }
77
78    public ThumbnailLoadTask(AttachmentBitmapHolder holder, int width, int height) {
79        mHolder = holder;
80        mWidth = width;
81        mHeight = height;
82    }
83
84    @Override
85    protected Bitmap doInBackground(Uri... params) {
86        Bitmap result = loadBitmap(params[0]);
87        if (result == null) {
88            result = loadBitmap(params[1]);
89        }
90
91        return result;
92    }
93
94    private Bitmap loadBitmap(final Uri thumbnailUri) {
95        AssetFileDescriptor fd = null;
96        Bitmap result = null;
97
98        try {
99            fd = mHolder.getResolver().openAssetFileDescriptor(thumbnailUri, "r");
100            if (isCancelled() || fd == null) {
101                return null;
102            }
103
104            final BitmapFactory.Options opts = new BitmapFactory.Options();
105            opts.inJustDecodeBounds = true;
106
107            BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, opts);
108            if (isCancelled() || opts.outWidth == -1 || opts.outHeight == -1) {
109                return null;
110            }
111
112            opts.inJustDecodeBounds = false;
113            // Shrink both X and Y (but do not over-shrink)
114            // and pick the least affected dimension to ensure the thumbnail is fillable
115            // (i.e. ScaleType.CENTER_CROP)
116            final int wDivider = Math.max(opts.outWidth / mWidth, 1);
117            final int hDivider = Math.max(opts.outHeight / mHeight, 1);
118            opts.inSampleSize = Math.min(wDivider, hDivider);
119
120            LogUtils.d(LOG_TAG, "in background, src w/h=%d/%d dst w/h=%d/%d, divider=%d",
121                    opts.outWidth, opts.outHeight, mWidth, mHeight, opts.inSampleSize);
122
123            result = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, opts);
124
125        } catch (Throwable t) {
126            LogUtils.e(LOG_TAG, t, "Unable to decode thumbnail %s", thumbnailUri);
127        } finally {
128            if (fd != null) {
129                try {
130                    fd.close();
131                } catch (IOException e) {
132                    LogUtils.e(LOG_TAG, e, "");
133                }
134            }
135        }
136
137        return result;
138    }
139
140    @Override
141    protected void onPostExecute(Bitmap result) {
142        if (result == null) {
143            LogUtils.d(LOG_TAG, "back in UI thread, decode failed");
144            mHolder.setThumbnailToDefault();
145            return;
146        }
147
148        LogUtils.d(LOG_TAG, "back in UI thread, decode success, w/h=%d/%d", result.getWidth(),
149                result.getHeight());
150        mHolder.setThumbnail(result);
151    }
152
153}
154