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