diff options
Diffstat (limited to 'src/com/android/gallery3d/data/LocalImage.java')
-rw-r--r-- | src/com/android/gallery3d/data/LocalImage.java | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java new file mode 100644 index 000000000..f3dedf037 --- /dev/null +++ b/src/com/android/gallery3d/data/LocalImage.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.data; + +import com.android.gallery3d.app.GalleryApp; +import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.util.UpdateHelper; +import com.android.gallery3d.util.GalleryUtils; +import com.android.gallery3d.util.ThreadPool.Job; +import com.android.gallery3d.util.ThreadPool.JobContext; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapRegionDecoder; +import android.media.ExifInterface; +import android.net.Uri; +import android.provider.MediaStore.Images; +import android.provider.MediaStore.Images.ImageColumns; + +import java.io.File; +import java.io.IOException; + +// LocalImage represents an image in the local storage. +public class LocalImage extends LocalMediaItem { + private static final int THUMBNAIL_TARGET_SIZE = 640; + private static final int MICROTHUMBNAIL_TARGET_SIZE = 200; + + private static final String TAG = "LocalImage"; + + static final Path ITEM_PATH = Path.fromString("/local/image/item"); + + // Must preserve order between these indices and the order of the terms in + // the following PROJECTION array. + private static final int INDEX_ID = 0; + private static final int INDEX_CAPTION = 1; + private static final int INDEX_MIME_TYPE = 2; + private static final int INDEX_LATITUDE = 3; + private static final int INDEX_LONGITUDE = 4; + private static final int INDEX_DATE_TAKEN = 5; + private static final int INDEX_DATE_ADDED = 6; + private static final int INDEX_DATE_MODIFIED = 7; + private static final int INDEX_DATA = 8; + private static final int INDEX_ORIENTATION = 9; + private static final int INDEX_BUCKET_ID = 10; + private static final int INDEX_SIZE_ID = 11; + + static final String[] PROJECTION = { + ImageColumns._ID, // 0 + ImageColumns.TITLE, // 1 + ImageColumns.MIME_TYPE, // 2 + ImageColumns.LATITUDE, // 3 + ImageColumns.LONGITUDE, // 4 + ImageColumns.DATE_TAKEN, // 5 + ImageColumns.DATE_ADDED, // 6 + ImageColumns.DATE_MODIFIED, // 7 + ImageColumns.DATA, // 8 + ImageColumns.ORIENTATION, // 9 + ImageColumns.BUCKET_ID, // 10 + ImageColumns.SIZE // 11 + }; + + private final GalleryApp mApplication; + + public int rotation; + + public LocalImage(Path path, GalleryApp application, Cursor cursor) { + super(path, nextVersionNumber()); + mApplication = application; + loadFromCursor(cursor); + } + + public LocalImage(Path path, GalleryApp application, int id) { + super(path, nextVersionNumber()); + mApplication = application; + ContentResolver resolver = mApplication.getContentResolver(); + Uri uri = Images.Media.EXTERNAL_CONTENT_URI; + Cursor cursor = LocalAlbum.getItemCursor(resolver, uri, PROJECTION, id); + if (cursor == null) { + throw new RuntimeException("cannot get cursor for: " + path); + } + try { + if (cursor.moveToNext()) { + loadFromCursor(cursor); + } else { + throw new RuntimeException("cannot find data for: " + path); + } + } finally { + cursor.close(); + } + } + + private void loadFromCursor(Cursor cursor) { + id = cursor.getInt(INDEX_ID); + caption = cursor.getString(INDEX_CAPTION); + mimeType = cursor.getString(INDEX_MIME_TYPE); + latitude = cursor.getDouble(INDEX_LATITUDE); + longitude = cursor.getDouble(INDEX_LONGITUDE); + dateTakenInMs = cursor.getLong(INDEX_DATE_TAKEN); + filePath = cursor.getString(INDEX_DATA); + rotation = cursor.getInt(INDEX_ORIENTATION); + bucketId = cursor.getInt(INDEX_BUCKET_ID); + fileSize = cursor.getLong(INDEX_SIZE_ID); + } + + @Override + protected boolean updateFromCursor(Cursor cursor) { + UpdateHelper uh = new UpdateHelper(); + id = uh.update(id, cursor.getInt(INDEX_ID)); + caption = uh.update(caption, cursor.getString(INDEX_CAPTION)); + mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE)); + latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE)); + longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE)); + dateTakenInMs = uh.update( + dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN)); + dateAddedInSec = uh.update( + dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED)); + dateModifiedInSec = uh.update( + dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED)); + filePath = uh.update(filePath, cursor.getString(INDEX_DATA)); + rotation = uh.update(rotation, cursor.getInt(INDEX_ORIENTATION)); + bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID)); + fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE_ID)); + return uh.isUpdated(); + } + + @Override + public Job<Bitmap> requestImage(int type) { + return new LocalImageRequest(mApplication, mPath, type, filePath); + } + + public static class LocalImageRequest extends ImageCacheRequest { + private String mLocalFilePath; + + LocalImageRequest(GalleryApp application, Path path, int type, + String localFilePath) { + super(application, path, type, getTargetSize(type)); + mLocalFilePath = localFilePath; + } + + @Override + public Bitmap onDecodeOriginal(JobContext jc, int type) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + return DecodeUtils.requestDecode( + jc, mLocalFilePath, options, getTargetSize(type)); + } + } + + static int getTargetSize(int type) { + switch (type) { + case TYPE_THUMBNAIL: + return THUMBNAIL_TARGET_SIZE; + case TYPE_MICROTHUMBNAIL: + return MICROTHUMBNAIL_TARGET_SIZE; + default: + throw new RuntimeException( + "should only request thumb/microthumb from cache"); + } + } + + @Override + public Job<BitmapRegionDecoder> requestLargeImage() { + return new LocalLargeImageRequest(filePath); + } + + public static class LocalLargeImageRequest + implements Job<BitmapRegionDecoder> { + String mLocalFilePath; + + public LocalLargeImageRequest(String localFilePath) { + mLocalFilePath = localFilePath; + } + + public BitmapRegionDecoder run(JobContext jc) { + return DecodeUtils.requestCreateBitmapRegionDecoder( + jc, mLocalFilePath, false); + } + } + + @Override + public int getSupportedOperations() { + int operation = SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_CROP + | SUPPORT_SETAS | SUPPORT_EDIT | SUPPORT_INFO; + if (BitmapUtils.isSupportedByRegionDecoder(mimeType)) { + operation |= SUPPORT_FULL_IMAGE; + } + + if (BitmapUtils.isRotationSupported(mimeType)) { + operation |= SUPPORT_ROTATE; + } + + if (GalleryUtils.isValidLocation(latitude, longitude)) { + operation |= SUPPORT_SHOW_ON_MAP; + } + return operation; + } + + @Override + public void delete() { + GalleryUtils.assertNotInRenderThread(); + Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; + mApplication.getContentResolver().delete(baseUri, "_id=?", + new String[]{String.valueOf(id)}); + } + + private static String getExifOrientation(int orientation) { + switch (orientation) { + case 0: + return String.valueOf(ExifInterface.ORIENTATION_NORMAL); + case 90: + return String.valueOf(ExifInterface.ORIENTATION_ROTATE_90); + case 180: + return String.valueOf(ExifInterface.ORIENTATION_ROTATE_180); + case 270: + return String.valueOf(ExifInterface.ORIENTATION_ROTATE_270); + default: + throw new AssertionError("invalid: " + orientation); + } + } + + @Override + public void rotate(int degrees) { + GalleryUtils.assertNotInRenderThread(); + Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; + ContentValues values = new ContentValues(); + int rotation = (this.rotation + degrees) % 360; + if (rotation < 0) rotation += 360; + + if (mimeType.equalsIgnoreCase("image/jpeg")) { + try { + ExifInterface exif = new ExifInterface(filePath); + exif.setAttribute(ExifInterface.TAG_ORIENTATION, + getExifOrientation(rotation)); + exif.saveAttributes(); + } catch (IOException e) { + Log.w(TAG, "cannot set exif data: " + filePath); + } + + // We need to update the filesize as well + fileSize = new File(filePath).length(); + values.put(Images.Media.SIZE, fileSize); + } + + values.put(Images.Media.ORIENTATION, rotation); + mApplication.getContentResolver().update(baseUri, values, "_id=?", + new String[]{String.valueOf(id)}); + } + + @Override + public Uri getContentUri() { + Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; + return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); + } + + @Override + public int getMediaType() { + return MEDIA_TYPE_IMAGE; + } + + @Override + public MediaDetails getDetails() { + MediaDetails details = super.getDetails(); + details.addDetail(MediaDetails.INDEX_ORIENTATION, Integer.valueOf(rotation)); + MediaDetails.extractExifInfo(details, filePath); + return details; + } + + @Override + public int getRotation() { + return rotation; + } +} |