1/*
2 * Copyright (C) 2015 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 */
16package com.android.messaging.datamodel.media;
17
18import android.content.Context;
19import android.graphics.Bitmap;
20import android.graphics.BitmapFactory;
21import android.media.ExifInterface;
22
23import com.android.messaging.datamodel.media.PoolableImageCache.ReusableImageResourcePool;
24import com.android.messaging.util.ImageUtils;
25import com.android.messaging.util.LogUtil;
26
27import java.io.IOException;
28
29/**
30 * Serves file system based image requests. Since file paths can be expressed in Uri form, this
31 * extends regular UriImageRequest but performs additional optimizations such as loading thumbnails
32 * directly from Exif information.
33 */
34public class FileImageRequest extends UriImageRequest {
35    private final String mPath;
36    private final boolean mCanUseThumbnail;
37
38    public FileImageRequest(final Context context,
39            final FileImageRequestDescriptor descriptor) {
40        super(context, descriptor);
41        mPath = descriptor.path;
42        mCanUseThumbnail = descriptor.canUseThumbnail;
43    }
44
45    @Override
46    protected Bitmap loadBitmapInternal()
47            throws IOException {
48        // Before using the FileInputStream, check if the Exif has a thumbnail that we can use.
49        if (mCanUseThumbnail) {
50            byte[] thumbnail = null;
51            try {
52                final ExifInterface exif = new ExifInterface(mPath);
53                if (exif.hasThumbnail()) {
54                    thumbnail = exif.getThumbnail();
55                }
56            } catch (final IOException e) {
57                // Nothing to do
58            }
59
60            if (thumbnail != null) {
61                final BitmapFactory.Options options = PoolableImageCache.getBitmapOptionsForPool(
62                        false /* scaled */, 0 /* inputDensity */, 0 /* targetDensity */);
63                // First, check dimensions of the bitmap.
64                options.inJustDecodeBounds = true;
65                BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length, options);
66
67                // Calculate inSampleSize
68                options.inSampleSize = ImageUtils.get().calculateInSampleSize(options,
69                        mDescriptor.desiredWidth, mDescriptor.desiredHeight);
70
71                options.inJustDecodeBounds = false;
72
73                // Actually decode the bitmap, optionally using the bitmap pool.
74                try {
75                    // Get the orientation. We should be able to get the orientation from
76                    // the thumbnail itself but at least on some phones, the thumbnail
77                    // doesn't have an orientation tag. So use the outer image's orientation
78                    // tag and hope for the best.
79                    mOrientation = ImageUtils.getOrientation(getInputStreamForResource());
80                    if (com.android.messaging.util.exif.ExifInterface.
81                            getOrientationParams(mOrientation).invertDimensions) {
82                        mDescriptor.updateSourceDimensions(options.outHeight, options.outWidth);
83                    } else {
84                        mDescriptor.updateSourceDimensions(options.outWidth, options.outHeight);
85                    }
86                    final ReusableImageResourcePool bitmapPool = getBitmapPool();
87                    if (bitmapPool == null) {
88                        return BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length,
89                                options);
90                    } else {
91                        final int sampledWidth = options.outWidth / options.inSampleSize;
92                        final int sampledHeight = options.outHeight / options.inSampleSize;
93                        return bitmapPool.decodeByteArray(thumbnail, options, sampledWidth,
94                                sampledHeight);
95                    }
96                } catch (IOException ex) {
97                    // If the thumbnail is broken due to IOException, this will
98                    // fall back to default bitmap loading.
99                    LogUtil.e(LogUtil.BUGLE_IMAGE_TAG, "FileImageRequest: failed to load " +
100                            "thumbnail from Exif", ex);
101                }
102            }
103        }
104
105        // Fall back to default InputStream-based loading if no thumbnails could be retrieved.
106        return super.loadBitmapInternal();
107    }
108}
109