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  */
16 package com.android.messaging.datamodel.media;
17 
18 import android.content.Context;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.media.ExifInterface;
22 
23 import com.android.messaging.datamodel.media.PoolableImageCache.ReusableImageResourcePool;
24 import com.android.messaging.util.ImageUtils;
25 import com.android.messaging.util.LogUtil;
26 
27 import 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  */
34 public class FileImageRequest extends UriImageRequest {
35     private final String mPath;
36     private final boolean mCanUseThumbnail;
37 
FileImageRequest(final Context context, final FileImageRequestDescriptor descriptor)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
loadBitmapInternal()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