Thumbnail.java revision a907c122ccec649c6cedf3a45b1c426a5fca932e
1/* 2 * Copyright (C) 2011 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 17package com.android.camera; 18 19import android.content.ContentResolver; 20import android.content.ContentUris; 21import android.database.Cursor; 22import android.graphics.Bitmap; 23import android.graphics.BitmapFactory; 24import android.graphics.Matrix; 25import android.net.Uri; 26import android.provider.MediaStore.Images; 27import android.provider.MediaStore.Images.ImageColumns; 28import android.provider.MediaStore.MediaColumns; 29import android.provider.MediaStore.Video; 30import android.provider.MediaStore.Video.VideoColumns; 31import android.util.Log; 32 33import java.io.BufferedInputStream; 34import java.io.BufferedOutputStream; 35import java.io.DataInputStream; 36import java.io.DataOutputStream; 37import java.io.File; 38import java.io.FileInputStream; 39import java.io.FileOutputStream; 40import java.io.IOException; 41import java.lang.IllegalArgumentException; 42 43public class Thumbnail { 44 private static final String TAG = "Thumbnail"; 45 46 private static final int BUFSIZE = 4096; 47 48 private Uri mUri; 49 private Bitmap mBitmap; 50 51 public Thumbnail(Uri uri, Bitmap bitmap, int orientation) { 52 mUri = uri; 53 mBitmap = rotateImage(bitmap, orientation); 54 if (mBitmap == null) throw new IllegalArgumentException("null bitmap"); 55 } 56 57 public Uri getUri() { 58 return mUri; 59 } 60 61 public Bitmap getBitmap() { 62 return mBitmap; 63 } 64 65 private static Bitmap rotateImage(Bitmap bitmap, int orientation) { 66 if (orientation != 0) { 67 // We only rotate the thumbnail once even if we get OOM. 68 Matrix m = new Matrix(); 69 m.setRotate(orientation, bitmap.getWidth() * 0.5f, 70 bitmap.getHeight() * 0.5f); 71 72 try { 73 Bitmap rotated = Bitmap.createBitmap(bitmap, 0, 0, 74 bitmap.getWidth(), bitmap.getHeight(), m, true); 75 // If the rotated bitmap is the original bitmap, then it 76 // should not be recycled. 77 if (rotated != bitmap) bitmap.recycle(); 78 return rotated; 79 } catch (Throwable t) { 80 Log.w(TAG, "Failed to rotate thumbnail", t); 81 } 82 } 83 return bitmap; 84 } 85 86 // Stores the bitmap to the specified file. 87 public void saveTo(File file) { 88 FileOutputStream f = null; 89 BufferedOutputStream b = null; 90 DataOutputStream d = null; 91 try { 92 f = new FileOutputStream(file); 93 b = new BufferedOutputStream(f, BUFSIZE); 94 d = new DataOutputStream(b); 95 d.writeUTF(mUri.toString()); 96 mBitmap.compress(Bitmap.CompressFormat.PNG, 100, d); 97 d.close(); 98 } catch (IOException e) { 99 Log.e(TAG, "Fail to store bitmap. path=" + file.getPath(), e); 100 } finally { 101 Util.closeSilently(f); 102 Util.closeSilently(b); 103 Util.closeSilently(d); 104 } 105 } 106 107 // Loads the data from the specified file. 108 // Returns null if failure. 109 public static Thumbnail loadFrom(File file) { 110 Uri uri = null; 111 Bitmap bitmap = null; 112 FileInputStream f = null; 113 BufferedInputStream b = null; 114 DataInputStream d = null; 115 try { 116 f = new FileInputStream(file); 117 b = new BufferedInputStream(f, BUFSIZE); 118 d = new DataInputStream(b); 119 uri = Uri.parse(d.readUTF()); 120 bitmap = BitmapFactory.decodeStream(d); 121 d.close(); 122 } catch (IOException e) { 123 Log.i(TAG, "Fail to load bitmap. " + e); 124 return null; 125 } finally { 126 Util.closeSilently(f); 127 Util.closeSilently(b); 128 Util.closeSilently(d); 129 } 130 return createThumbnail(uri, bitmap, 0); 131 } 132 133 public static Thumbnail getLastImageThumbnail(ContentResolver resolver) { 134 Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; 135 136 Uri query = baseUri.buildUpon().appendQueryParameter("limit", "1").build(); 137 String[] projection = new String[] {ImageColumns._ID, ImageColumns.ORIENTATION}; 138 String selection = ImageColumns.MIME_TYPE + "='image/jpeg' AND " + 139 ImageColumns.BUCKET_ID + '=' + Storage.BUCKET_ID; 140 String order = ImageColumns.DATE_TAKEN + " DESC," + ImageColumns._ID + " DESC"; 141 142 Cursor cursor = null; 143 try { 144 cursor = resolver.query(query, projection, selection, null, order); 145 if (cursor != null && cursor.moveToFirst()) { 146 long id = cursor.getLong(0); 147 int orientation = cursor.getInt(1); 148 Bitmap bitmap = Images.Thumbnails.getThumbnail(resolver, id, 149 Images.Thumbnails.MINI_KIND, null); 150 Uri uri = ContentUris.withAppendedId(baseUri, id); 151 // Ensure there's no OOM. Ensure database and storage are in sync. 152 if (Util.isUriValid(uri, resolver)) { 153 return createThumbnail(uri, bitmap, orientation); 154 } 155 } 156 } finally { 157 if (cursor != null) { 158 cursor.close(); 159 } 160 } 161 return null; 162 } 163 164 public static Thumbnail getLastVideoThumbnail(ContentResolver resolver) { 165 Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI; 166 167 Uri query = baseUri.buildUpon().appendQueryParameter("limit", "1").build(); 168 String[] projection = new String[] {VideoColumns._ID, MediaColumns.DATA}; 169 String selection = VideoColumns.BUCKET_ID + '=' + Storage.BUCKET_ID; 170 String order = VideoColumns.DATE_TAKEN + " DESC," + VideoColumns._ID + " DESC"; 171 172 Cursor cursor = null; 173 try { 174 cursor = resolver.query(query, projection, selection, null, order); 175 if (cursor != null && cursor.moveToFirst()) { 176 Log.d(TAG, "getLastVideoThumbnail: " + cursor.getString(1)); 177 long id = cursor.getLong(0); 178 Bitmap bitmap = Video.Thumbnails.getThumbnail(resolver, id, 179 Video.Thumbnails.MINI_KIND, null); 180 Uri uri = ContentUris.withAppendedId(baseUri, id); 181 // Ensure there's no OOM. Ensure database and storage are in sync. 182 if (Util.isUriValid(uri, resolver)) { 183 return createThumbnail(uri, bitmap, 0); 184 } 185 } 186 } finally { 187 if (cursor != null) { 188 cursor.close(); 189 } 190 } 191 return null; 192 } 193 194 public static Thumbnail createThumbnail(byte[] jpeg, int orientation, int inSampleSize, 195 Uri uri) { 196 // Create the thumbnail. 197 BitmapFactory.Options options = new BitmapFactory.Options(); 198 options.inSampleSize = inSampleSize; 199 Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length, options); 200 return createThumbnail(uri, bitmap, orientation); 201 } 202 203 private static Thumbnail createThumbnail(Uri uri, Bitmap bitmap, int orientation) { 204 if (bitmap == null) { 205 Log.e(TAG, "Failed to create thumbnail from null bitmap"); 206 return null; 207 } 208 try { 209 return new Thumbnail(uri, bitmap, orientation); 210 } catch (IllegalArgumentException e) { 211 Log.e(TAG, "Failed to construct thumbnail", e); 212 return null; 213 } 214 } 215} 216