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