summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/data/LocalImage.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/data/LocalImage.java')
-rw-r--r--src/com/android/gallery3d/data/LocalImage.java289
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;
+ }
+}