OSDN Git Service

Import translations. DO NOT MERGE
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / data / LocalImage.java
1 /*
2  * Copyright (C) 2010 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
17 package com.android.gallery3d.data;
18
19 import android.annotation.TargetApi;
20 import android.content.ContentResolver;
21 import android.content.ContentValues;
22 import android.database.Cursor;
23 import android.graphics.Bitmap;
24 import android.graphics.BitmapFactory;
25 import android.graphics.BitmapRegionDecoder;
26 import android.media.ExifInterface;
27 import android.net.Uri;
28 import android.provider.MediaStore.Images;
29 import android.provider.MediaStore.Images.ImageColumns;
30 import android.provider.MediaStore.MediaColumns;
31 import android.util.Log;
32
33 import com.android.gallery3d.app.GalleryApp;
34 import com.android.gallery3d.common.ApiHelper;
35 import com.android.gallery3d.common.BitmapUtils;
36 import com.android.gallery3d.util.GalleryUtils;
37 import com.android.gallery3d.util.ThreadPool.Job;
38 import com.android.gallery3d.util.ThreadPool.JobContext;
39 import com.android.gallery3d.util.UpdateHelper;
40
41 import java.io.File;
42 import java.io.IOException;
43
44 // LocalImage represents an image in the local storage.
45 public class LocalImage extends LocalMediaItem {
46     private static final String TAG = "LocalImage";
47
48     static final Path ITEM_PATH = Path.fromString("/local/image/item");
49
50     // Must preserve order between these indices and the order of the terms in
51     // the following PROJECTION array.
52     private static final int INDEX_ID = 0;
53     private static final int INDEX_CAPTION = 1;
54     private static final int INDEX_MIME_TYPE = 2;
55     private static final int INDEX_LATITUDE = 3;
56     private static final int INDEX_LONGITUDE = 4;
57     private static final int INDEX_DATE_TAKEN = 5;
58     private static final int INDEX_DATE_ADDED = 6;
59     private static final int INDEX_DATE_MODIFIED = 7;
60     private static final int INDEX_DATA = 8;
61     private static final int INDEX_ORIENTATION = 9;
62     private static final int INDEX_BUCKET_ID = 10;
63     private static final int INDEX_SIZE = 11;
64     private static final int INDEX_WIDTH = 12;
65     private static final int INDEX_HEIGHT = 13;
66
67     static final String[] PROJECTION =  {
68             ImageColumns._ID,           // 0
69             ImageColumns.TITLE,         // 1
70             ImageColumns.MIME_TYPE,     // 2
71             ImageColumns.LATITUDE,      // 3
72             ImageColumns.LONGITUDE,     // 4
73             ImageColumns.DATE_TAKEN,    // 5
74             ImageColumns.DATE_ADDED,    // 6
75             ImageColumns.DATE_MODIFIED, // 7
76             ImageColumns.DATA,          // 8
77             ImageColumns.ORIENTATION,   // 9
78             ImageColumns.BUCKET_ID,     // 10
79             ImageColumns.SIZE,          // 11
80             "0",                        // 12
81             "0"                         // 13
82     };
83
84     static {
85         updateWidthAndHeightProjection();
86     }
87
88     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
89     private static void updateWidthAndHeightProjection() {
90         if (ApiHelper.HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT) {
91             PROJECTION[INDEX_WIDTH] = MediaColumns.WIDTH;
92             PROJECTION[INDEX_HEIGHT] = MediaColumns.HEIGHT;
93         }
94     }
95
96     private final GalleryApp mApplication;
97
98     public int rotation;
99
100     public LocalImage(Path path, GalleryApp application, Cursor cursor) {
101         super(path, nextVersionNumber());
102         mApplication = application;
103         loadFromCursor(cursor);
104     }
105
106     public LocalImage(Path path, GalleryApp application, int id) {
107         super(path, nextVersionNumber());
108         mApplication = application;
109         ContentResolver resolver = mApplication.getContentResolver();
110         Uri uri = Images.Media.EXTERNAL_CONTENT_URI;
111         Cursor cursor = LocalAlbum.getItemCursor(resolver, uri, PROJECTION, id);
112         if (cursor == null) {
113             throw new RuntimeException("cannot get cursor for: " + path);
114         }
115         try {
116             if (cursor.moveToNext()) {
117                 loadFromCursor(cursor);
118             } else {
119                 throw new RuntimeException("cannot find data for: " + path);
120             }
121         } finally {
122             cursor.close();
123         }
124     }
125
126     private void loadFromCursor(Cursor cursor) {
127         id = cursor.getInt(INDEX_ID);
128         caption = cursor.getString(INDEX_CAPTION);
129         mimeType = cursor.getString(INDEX_MIME_TYPE);
130         latitude = cursor.getDouble(INDEX_LATITUDE);
131         longitude = cursor.getDouble(INDEX_LONGITUDE);
132         dateTakenInMs = cursor.getLong(INDEX_DATE_TAKEN);
133         filePath = cursor.getString(INDEX_DATA);
134         rotation = cursor.getInt(INDEX_ORIENTATION);
135         bucketId = cursor.getInt(INDEX_BUCKET_ID);
136         fileSize = cursor.getLong(INDEX_SIZE);
137         width = cursor.getInt(INDEX_WIDTH);
138         height = cursor.getInt(INDEX_HEIGHT);
139     }
140
141     @Override
142     protected boolean updateFromCursor(Cursor cursor) {
143         UpdateHelper uh = new UpdateHelper();
144         id = uh.update(id, cursor.getInt(INDEX_ID));
145         caption = uh.update(caption, cursor.getString(INDEX_CAPTION));
146         mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE));
147         latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE));
148         longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE));
149         dateTakenInMs = uh.update(
150                 dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN));
151         dateAddedInSec = uh.update(
152                 dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED));
153         dateModifiedInSec = uh.update(
154                 dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED));
155         filePath = uh.update(filePath, cursor.getString(INDEX_DATA));
156         rotation = uh.update(rotation, cursor.getInt(INDEX_ORIENTATION));
157         bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID));
158         fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE));
159         width = uh.update(width, cursor.getInt(INDEX_WIDTH));
160         height = uh.update(height, cursor.getInt(INDEX_HEIGHT));
161         return uh.isUpdated();
162     }
163
164     @Override
165     public Job<Bitmap> requestImage(int type) {
166         return new LocalImageRequest(mApplication, mPath, type, filePath);
167     }
168
169     public static class LocalImageRequest extends ImageCacheRequest {
170         private String mLocalFilePath;
171
172         LocalImageRequest(GalleryApp application, Path path, int type,
173                 String localFilePath) {
174             super(application, path, type, MediaItem.getTargetSize(type));
175             mLocalFilePath = localFilePath;
176         }
177
178         @Override
179         public Bitmap onDecodeOriginal(JobContext jc, final int type) {
180             BitmapFactory.Options options = new BitmapFactory.Options();
181             options.inPreferredConfig = Bitmap.Config.ARGB_8888;
182             int targetSize = MediaItem.getTargetSize(type);
183
184             // try to decode from JPEG EXIF
185             if (type == MediaItem.TYPE_MICROTHUMBNAIL) {
186                 ExifInterface exif = null;
187                 byte [] thumbData = null;
188                 try {
189                     exif = new ExifInterface(mLocalFilePath);
190                     if (exif != null) {
191                         thumbData = exif.getThumbnail();
192                     }
193                 } catch (Throwable t) {
194                     Log.w(TAG, "fail to get exif thumb", t);
195                 }
196                 if (thumbData != null) {
197                     Bitmap bitmap = DecodeUtils.decodeIfBigEnough(
198                             jc, thumbData, options, targetSize);
199                     if (bitmap != null) return bitmap;
200                 }
201             }
202
203             return DecodeUtils.decodeThumbnail(jc, mLocalFilePath, options, targetSize, type);
204         }
205     }
206
207     @Override
208     public Job<BitmapRegionDecoder> requestLargeImage() {
209         return new LocalLargeImageRequest(filePath);
210     }
211
212     public static class LocalLargeImageRequest
213             implements Job<BitmapRegionDecoder> {
214         String mLocalFilePath;
215
216         public LocalLargeImageRequest(String localFilePath) {
217             mLocalFilePath = localFilePath;
218         }
219
220         public BitmapRegionDecoder run(JobContext jc) {
221             return DecodeUtils.createBitmapRegionDecoder(jc, mLocalFilePath, false);
222         }
223     }
224
225     @Override
226     public int getSupportedOperations() {
227         int operation = SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_CROP
228                 | SUPPORT_SETAS | SUPPORT_EDIT | SUPPORT_INFO;
229         if (BitmapUtils.isSupportedByRegionDecoder(mimeType)) {
230             operation |= SUPPORT_FULL_IMAGE;
231         }
232
233         if (BitmapUtils.isRotationSupported(mimeType)) {
234             operation |= SUPPORT_ROTATE;
235         }
236
237         if (GalleryUtils.isValidLocation(latitude, longitude)) {
238             operation |= SUPPORT_SHOW_ON_MAP;
239         }
240         return operation;
241     }
242
243     @Override
244     public void delete() {
245         GalleryUtils.assertNotInRenderThread();
246         Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
247         mApplication.getContentResolver().delete(baseUri, "_id=?",
248                 new String[]{String.valueOf(id)});
249         mApplication.getDataManager().broadcastLocalDeletion();
250     }
251
252     private static String getExifOrientation(int orientation) {
253         switch (orientation) {
254             case 0:
255                 return String.valueOf(ExifInterface.ORIENTATION_NORMAL);
256             case 90:
257                 return String.valueOf(ExifInterface.ORIENTATION_ROTATE_90);
258             case 180:
259                 return String.valueOf(ExifInterface.ORIENTATION_ROTATE_180);
260             case 270:
261                 return String.valueOf(ExifInterface.ORIENTATION_ROTATE_270);
262             default:
263                 throw new AssertionError("invalid: " + orientation);
264         }
265     }
266
267     @Override
268     public void rotate(int degrees) {
269         GalleryUtils.assertNotInRenderThread();
270         Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
271         ContentValues values = new ContentValues();
272         int rotation = (this.rotation + degrees) % 360;
273         if (rotation < 0) rotation += 360;
274
275         if (mimeType.equalsIgnoreCase("image/jpeg")) {
276             try {
277                 ExifInterface exif = new ExifInterface(filePath);
278                 exif.setAttribute(ExifInterface.TAG_ORIENTATION,
279                         getExifOrientation(rotation));
280                 exif.saveAttributes();
281             } catch (IOException e) {
282                 Log.w(TAG, "cannot set exif data: " + filePath);
283             }
284
285             // We need to update the filesize as well
286             fileSize = new File(filePath).length();
287             values.put(Images.Media.SIZE, fileSize);
288         }
289
290         values.put(Images.Media.ORIENTATION, rotation);
291         mApplication.getContentResolver().update(baseUri, values, "_id=?",
292                 new String[]{String.valueOf(id)});
293     }
294
295     @Override
296     public Uri getContentUri() {
297         Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
298         return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
299     }
300
301     @Override
302     public int getMediaType() {
303         return MEDIA_TYPE_IMAGE;
304     }
305
306     @Override
307     public MediaDetails getDetails() {
308         MediaDetails details = super.getDetails();
309         details.addDetail(MediaDetails.INDEX_ORIENTATION, Integer.valueOf(rotation));
310         MediaDetails.extractExifInfo(details, filePath);
311         return details;
312     }
313
314     @Override
315     public int getRotation() {
316         return rotation;
317     }
318
319     @Override
320     public int getWidth() {
321         return width;
322     }
323
324     @Override
325     public int getHeight() {
326         return height;
327     }
328 }