1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.data;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.app.GalleryApp;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.BitmapUtils;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.GalleryUtils;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool.Job;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool.JobContext;
24113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport com.android.gallery3d.util.UpdateHelper;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.ContentResolver;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.ContentValues;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.database.Cursor;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.BitmapFactory;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.BitmapRegionDecoder;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.media.ExifInterface;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.net.Uri;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.provider.MediaStore.Images;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.provider.MediaStore.Images.ImageColumns;
36113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport android.util.Log;
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.File;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.IOException;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// LocalImage represents an image in the local storage.
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class LocalImage extends LocalMediaItem {
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "LocalImage";
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    static final Path ITEM_PATH = Path.fromString("/local/image/item");
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Must preserve order between these indices and the order of the terms in
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the following PROJECTION array.
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_ID = 0;
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_CAPTION = 1;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_MIME_TYPE = 2;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_LATITUDE = 3;
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_LONGITUDE = 4;
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_DATE_TAKEN = 5;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_DATE_ADDED = 6;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_DATE_MODIFIED = 7;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_DATA = 8;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_ORIENTATION = 9;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_BUCKET_ID = 10;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_SIZE_ID = 11;
61cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang    private static final int INDEX_WIDTH = 12;
62cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang    private static final int INDEX_HEIGHT = 13;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    static final String[] PROJECTION =  {
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns._ID,           // 0
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.TITLE,         // 1
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.MIME_TYPE,     // 2
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.LATITUDE,      // 3
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.LONGITUDE,     // 4
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.DATE_TAKEN,    // 5
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.DATE_ADDED,    // 6
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.DATE_MODIFIED, // 7
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.DATA,          // 8
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.ORIENTATION,   // 9
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ImageColumns.BUCKET_ID,     // 10
76cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang            ImageColumns.SIZE,          // 11
77cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang            // These should be changed to proper names after they are made public.
78cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang            "width", // ImageColumns.WIDTH,         // 12
79cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang            "height", // ImageColumns.HEIGHT         // 13
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    };
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final GalleryApp mApplication;
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int rotation;
85cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang    public int width;
86cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang    public int height;
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public LocalImage(Path path, GalleryApp application, Cursor cursor) {
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(path, nextVersionNumber());
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mApplication = application;
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        loadFromCursor(cursor);
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public LocalImage(Path path, GalleryApp application, int id) {
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(path, nextVersionNumber());
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mApplication = application;
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ContentResolver resolver = mApplication.getContentResolver();
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Uri uri = Images.Media.EXTERNAL_CONTENT_URI;
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Cursor cursor = LocalAlbum.getItemCursor(resolver, uri, PROJECTION, id);
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (cursor == null) {
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("cannot get cursor for: " + path);
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (cursor.moveToNext()) {
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                loadFromCursor(cursor);
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                throw new RuntimeException("cannot find data for: " + path);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } finally {
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            cursor.close();
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void loadFromCursor(Cursor cursor) {
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        id = cursor.getInt(INDEX_ID);
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        caption = cursor.getString(INDEX_CAPTION);
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mimeType = cursor.getString(INDEX_MIME_TYPE);
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        latitude = cursor.getDouble(INDEX_LATITUDE);
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        longitude = cursor.getDouble(INDEX_LONGITUDE);
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        dateTakenInMs = cursor.getLong(INDEX_DATE_TAKEN);
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        filePath = cursor.getString(INDEX_DATA);
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        rotation = cursor.getInt(INDEX_ORIENTATION);
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        bucketId = cursor.getInt(INDEX_BUCKET_ID);
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        fileSize = cursor.getLong(INDEX_SIZE_ID);
125cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang        width = cursor.getInt(INDEX_WIDTH);
126cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang        height = cursor.getInt(INDEX_HEIGHT);
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected boolean updateFromCursor(Cursor cursor) {
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        UpdateHelper uh = new UpdateHelper();
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        id = uh.update(id, cursor.getInt(INDEX_ID));
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        caption = uh.update(caption, cursor.getString(INDEX_CAPTION));
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE));
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE));
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE));
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        dateTakenInMs = uh.update(
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN));
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        dateAddedInSec = uh.update(
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED));
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        dateModifiedInSec = uh.update(
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED));
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        filePath = uh.update(filePath, cursor.getString(INDEX_DATA));
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        rotation = uh.update(rotation, cursor.getInt(INDEX_ORIENTATION));
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID));
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE_ID));
147cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang        width = uh.update(width, cursor.getInt(INDEX_WIDTH));
148cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang        height = uh.update(height, cursor.getInt(INDEX_HEIGHT));
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return uh.isUpdated();
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Job<Bitmap> requestImage(int type) {
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return new LocalImageRequest(mApplication, mPath, type, filePath);
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static class LocalImageRequest extends ImageCacheRequest {
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private String mLocalFilePath;
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        LocalImageRequest(GalleryApp application, Path path, int type,
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                String localFilePath) {
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            super(application, path, type, getTargetSize(type));
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLocalFilePath = localFilePath;
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Bitmap onDecodeOriginal(JobContext jc, int type) {
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            BitmapFactory.Options options = new BitmapFactory.Options();
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
170113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
171113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            // try to decode from JPEG EXIF
172113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            if (type == MediaItem.TYPE_MICROTHUMBNAIL) {
173113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                ExifInterface exif = null;
174113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                byte [] thumbData = null;
175113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                try {
176113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    exif = new ExifInterface(mLocalFilePath);
177113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    if (exif != null) {
178113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                        thumbData = exif.getThumbnail();
179113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    }
180113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                } catch (Throwable t) {
181113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    Log.w(TAG, "fail to get exif thumb", t);
182113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                }
183113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                if (thumbData != null) {
184113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    Bitmap bitmap = DecodeUtils.requestDecodeIfBigEnough(
185113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                            jc, thumbData, options, getTargetSize(type));
186113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    if (bitmap != null) return bitmap;
187113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                }
188113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return DecodeUtils.requestDecode(
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    jc, mLocalFilePath, options, getTargetSize(type));
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    static int getTargetSize(int type) {
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (type) {
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case TYPE_THUMBNAIL:
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return THUMBNAIL_TARGET_SIZE;
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case TYPE_MICROTHUMBNAIL:
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return MICROTHUMBNAIL_TARGET_SIZE;
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default:
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                throw new RuntimeException(
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    "should only request thumb/microthumb from cache");
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Job<BitmapRegionDecoder> requestLargeImage() {
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return new LocalLargeImageRequest(filePath);
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static class LocalLargeImageRequest
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            implements Job<BitmapRegionDecoder> {
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String mLocalFilePath;
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public LocalLargeImageRequest(String localFilePath) {
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mLocalFilePath = localFilePath;
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public BitmapRegionDecoder run(JobContext jc) {
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return DecodeUtils.requestCreateBitmapRegionDecoder(
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    jc, mLocalFilePath, false);
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getSupportedOperations() {
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int operation = SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_CROP
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                | SUPPORT_SETAS | SUPPORT_EDIT | SUPPORT_INFO;
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (BitmapUtils.isSupportedByRegionDecoder(mimeType)) {
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            operation |= SUPPORT_FULL_IMAGE;
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (BitmapUtils.isRotationSupported(mimeType)) {
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            operation |= SUPPORT_ROTATE;
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (GalleryUtils.isValidLocation(latitude, longitude)) {
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            operation |= SUPPORT_SHOW_ON_MAP;
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return operation;
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void delete() {
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GalleryUtils.assertNotInRenderThread();
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mApplication.getContentResolver().delete(baseUri, "_id=?",
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                new String[]{String.valueOf(id)});
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static String getExifOrientation(int orientation) {
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (orientation) {
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 0:
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return String.valueOf(ExifInterface.ORIENTATION_NORMAL);
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 90:
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_90);
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 180:
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_180);
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case 270:
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_270);
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default:
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                throw new AssertionError("invalid: " + orientation);
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void rotate(int degrees) {
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GalleryUtils.assertNotInRenderThread();
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ContentValues values = new ContentValues();
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int rotation = (this.rotation + degrees) % 360;
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (rotation < 0) rotation += 360;
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mimeType.equalsIgnoreCase("image/jpeg")) {
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            try {
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                ExifInterface exif = new ExifInterface(filePath);
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                exif.setAttribute(ExifInterface.TAG_ORIENTATION,
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        getExifOrientation(rotation));
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                exif.saveAttributes();
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } catch (IOException e) {
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Log.w(TAG, "cannot set exif data: " + filePath);
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // We need to update the filesize as well
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            fileSize = new File(filePath).length();
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            values.put(Images.Media.SIZE, fileSize);
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        values.put(Images.Media.ORIENTATION, rotation);
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mApplication.getContentResolver().update(baseUri, values, "_id=?",
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                new String[]{String.valueOf(id)});
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Uri getContentUri() {
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getMediaType() {
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return MEDIA_TYPE_IMAGE;
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaDetails getDetails() {
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaDetails details = super.getDetails();
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        details.addDetail(MediaDetails.INDEX_ORIENTATION, Integer.valueOf(rotation));
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaDetails.extractExifInfo(details, filePath);
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return details;
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getRotation() {
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return rotation;
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
317bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang
318bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang    @Override
319bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang    public int getWidth() {
320cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang        return width;
321bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang    }
322bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang
323bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang    @Override
324bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang    public int getHeight() {
325cac63ebeb6848920b37ee883ddc22f3e5d1e1d61Chih-Chung Chang        return height;
326bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang    }
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
328