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