From 6f64b50db6565bc6454304fd20c0c4020b297bb7 Mon Sep 17 00:00:00 2001 From: Sascha Haeberling Date: Wed, 14 Aug 2013 16:23:18 -0700 Subject: Brings back the details menu with most EXIF data in it. Some are still missing and will come back later. Bug: 10330505 Change-Id: I7bc44b19fac5b47557d5feae3ed969040ac3ae9b --- src/com/android/camera/data/DataUtils.java | 49 +++++++ src/com/android/camera/data/LocalData.java | 6 +- src/com/android/camera/data/LocalMediaData.java | 58 +++++--- src/com/android/camera/data/MediaDetails.java | 171 ++++++++++++++++++++++++ src/com/android/camera/data/SimpleViewData.java | 5 + 5 files changed, 273 insertions(+), 16 deletions(-) create mode 100644 src/com/android/camera/data/DataUtils.java create mode 100644 src/com/android/camera/data/MediaDetails.java (limited to 'src/com/android/camera/data') diff --git a/src/com/android/camera/data/DataUtils.java b/src/com/android/camera/data/DataUtils.java new file mode 100644 index 000000000..0b074d824 --- /dev/null +++ b/src/com/android/camera/data/DataUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 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.camera.data; + +import android.content.ContentResolver; +import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; + +public class DataUtils { + + /** + * Get the file path from a Media storage URI. + */ + public static String getPathFromURI(ContentResolver contentResolver, Uri contentUri) { + String[] proj = { + MediaStore.Images.Media.DATA + }; + Cursor cursor = contentResolver.query(contentUri, proj, null, null, null); + if (cursor == null) { + return null; + } + try { + int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + if (!cursor.moveToFirst()) { + return null; + } else { + return cursor.getString(columnIndex); + } + } finally { + cursor.close(); + } + } + +} diff --git a/src/com/android/camera/data/LocalData.java b/src/com/android/camera/data/LocalData.java index 7d6dfefc6..61714e243 100644 --- a/src/com/android/camera/data/LocalData.java +++ b/src/com/android/camera/data/LocalData.java @@ -23,7 +23,6 @@ import android.net.Uri; import android.view.View; import com.android.camera.ui.FilmStripView; -import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; import java.util.Comparator; @@ -102,6 +101,11 @@ public interface LocalData extends FilmStripView.ImageData { */ Uri getContentUri(); + /** + * Return media data (such as EXIF) for the item. + */ + MediaDetails getMediaDetails(Context context); + /** * Returns the type of the local data defined by {@link LocalData}. * diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java index d5a929d36..e55274f06 100644 --- a/src/com/android/camera/data/LocalMediaData.java +++ b/src/com/android/camera/data/LocalMediaData.java @@ -35,12 +35,13 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; -import com.android.camera.util.CameraUtil; import com.android.camera.ui.FilmStripView; +import com.android.camera.util.CameraUtil; import com.android.camera.util.PhotoSphereHelper; import com.android.camera2.R; import java.io.File; +import java.text.DateFormat; import java.util.Date; /** @@ -53,12 +54,13 @@ public abstract class LocalMediaData implements LocalData { protected long id; protected String title; protected String mimeType; - protected long dateTaken; - protected long dateModified; + protected long dateTakenInSeconds; + protected long dateModifiedInSeconds; protected String path; // width and height should be adjusted according to orientation. protected int width; protected int height; + protected long sizeInBytes; /** The panorama metadata information of this media data. */ protected PhotoSphereHelper.PanoramaMetadata mPanoramaMetadata; @@ -74,12 +76,12 @@ public abstract class LocalMediaData implements LocalData { @Override public long getDateTaken() { - return dateTaken; + return dateTakenInSeconds; } @Override public long getDateModified() { - return dateModified; + return dateModifiedInSeconds; } @Override @@ -214,6 +216,7 @@ public abstract class LocalMediaData implements LocalData { public static final int COL_ORIENTATION = 6; public static final int COL_WIDTH = 7; public static final int COL_HEIGHT = 8; + public static final int COL_SIZE = 9; static final Uri CONTENT_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; @@ -232,6 +235,7 @@ public abstract class LocalMediaData implements LocalData { MediaStore.Images.ImageColumns.ORIENTATION, // 6, int, 0, 90, 180, 270 MediaStore.Images.ImageColumns.WIDTH, // 7, int MediaStore.Images.ImageColumns.HEIGHT, // 8, int + MediaStore.Images.ImageColumns.SIZE, // 9, long }; private static final int mSupportedUIActions = @@ -251,8 +255,8 @@ public abstract class LocalMediaData implements LocalData { d.id = c.getLong(COL_ID); d.title = c.getString(COL_TITLE); d.mimeType = c.getString(COL_MIME_TYPE); - d.dateTaken = c.getLong(COL_DATE_TAKEN); - d.dateModified = c.getLong(COL_DATE_MODIFIED); + d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN); + d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED); d.path = c.getString(COL_DATA); d.orientation = c.getInt(COL_ORIENTATION); d.width = c.getInt(COL_WIDTH); @@ -281,6 +285,7 @@ public abstract class LocalMediaData implements LocalData { d.width = d.height; d.height = b; } + d.sizeInBytes = c.getLong(COL_SIZE); return d; } @@ -288,7 +293,7 @@ public abstract class LocalMediaData implements LocalData { public String toString() { return "Photo:" + ",data=" + path + ",mimeType=" + mimeType + "," + width + "x" + height + ",orientation=" + orientation - + ",date=" + new Date(dateTaken); + + ",date=" + new Date(dateTakenInSeconds); } @Override @@ -319,6 +324,23 @@ public abstract class LocalMediaData implements LocalData { return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); } + @Override + public MediaDetails getMediaDetails(Context context) { + DateFormat formater = DateFormat.getDateTimeInstance(); + MediaDetails mediaDetails = new MediaDetails(); + mediaDetails.addDetail(MediaDetails.INDEX_TITLE, title); + mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, width); + mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, height); + mediaDetails.addDetail(MediaDetails.INDEX_PATH, path); + mediaDetails.addDetail(MediaDetails.INDEX_DATETIME, + formater.format(new Date(dateModifiedInSeconds * 1000))); + if (sizeInBytes > 0) + mediaDetails.addDetail(MediaDetails.INDEX_SIZE, sizeInBytes); + + MediaDetails.extractExifInfo(mediaDetails, path); + return mediaDetails; + } + @Override public int getLocalDataType(int dataID) { if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) { @@ -338,8 +360,8 @@ public abstract class LocalMediaData implements LocalData { id = newData.id; title = newData.title; mimeType = newData.mimeType; - dateTaken = newData.dateTaken; - dateModified = newData.dateModified; + dateTakenInSeconds = newData.dateTakenInSeconds; + dateModifiedInSeconds = newData.dateModifiedInSeconds; path = newData.path; orientation = newData.orientation; width = newData.width; @@ -436,8 +458,8 @@ public abstract class LocalMediaData implements LocalData { d.id = c.getLong(COL_ID); d.title = c.getString(COL_TITLE); d.mimeType = c.getString(COL_MIME_TYPE); - d.dateTaken = c.getLong(COL_DATE_TAKEN); - d.dateModified = c.getLong(COL_DATE_MODIFIED); + d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN); + d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED); d.path = c.getString(COL_DATA); d.width = c.getInt(COL_WIDTH); d.height = c.getInt(COL_HEIGHT); @@ -475,7 +497,7 @@ public abstract class LocalMediaData implements LocalData { @Override public String toString() { return "Video:" + ",data=" + path + ",mimeType=" + mimeType - + "," + width + "x" + height + ",date=" + new Date(dateTaken); + + "," + width + "x" + height + ",date=" + new Date(dateTakenInSeconds); } @Override @@ -506,6 +528,12 @@ public abstract class LocalMediaData implements LocalData { return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); } + @Override + public MediaDetails getMediaDetails(Context context) { + // TODO: Return valid MediaDetails for videos. + return new MediaDetails(); + } + @Override public int getLocalDataType(int dataID) { return LOCAL_VIDEO; @@ -525,8 +553,8 @@ public abstract class LocalMediaData implements LocalData { id = newData.id; title = newData.title; mimeType = newData.mimeType; - dateTaken = newData.dateTaken; - dateModified = newData.dateModified; + dateTakenInSeconds = newData.dateTakenInSeconds; + dateModifiedInSeconds = newData.dateModifiedInSeconds; path = newData.path; width = newData.width; height = newData.height; diff --git a/src/com/android/camera/data/MediaDetails.java b/src/com/android/camera/data/MediaDetails.java new file mode 100644 index 000000000..cb752733a --- /dev/null +++ b/src/com/android/camera/data/MediaDetails.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 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.camera.data; + +import android.util.Log; + +import com.android.camera2.R; +import com.android.gallery3d.exif.ExifInterface; +import com.android.gallery3d.exif.ExifTag; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.TreeMap; + +public class MediaDetails implements Iterable> { + @SuppressWarnings("unused") + private static final String TAG = "MediaDetails"; + + private TreeMap mDetails = new TreeMap(); + private HashMap mUnits = new HashMap(); + + public static final int INDEX_TITLE = 1; + public static final int INDEX_DESCRIPTION = 2; + public static final int INDEX_DATETIME = 3; + public static final int INDEX_LOCATION = 4; + public static final int INDEX_WIDTH = 5; + public static final int INDEX_HEIGHT = 6; + public static final int INDEX_ORIENTATION = 7; + public static final int INDEX_DURATION = 8; + public static final int INDEX_MIMETYPE = 9; + public static final int INDEX_SIZE = 10; + + // for EXIF + public static final int INDEX_MAKE = 100; + public static final int INDEX_MODEL = 101; + public static final int INDEX_FLASH = 102; + public static final int INDEX_FOCAL_LENGTH = 103; + public static final int INDEX_WHITE_BALANCE = 104; + public static final int INDEX_APERTURE = 105; + public static final int INDEX_SHUTTER_SPEED = 106; + public static final int INDEX_EXPOSURE_TIME = 107; + public static final int INDEX_ISO = 108; + + // Put this last because it may be long. + public static final int INDEX_PATH = 200; + + public static class FlashState { + private static int FLASH_FIRED_MASK = 1; + private static int FLASH_RETURN_MASK = 2 | 4; + private static int FLASH_MODE_MASK = 8 | 16; + private static int FLASH_FUNCTION_MASK = 32; + private static int FLASH_RED_EYE_MASK = 64; + private int mState; + + public FlashState(int state) { + mState = state; + } + + public boolean isFlashFired() { + return (mState & FLASH_FIRED_MASK) != 0; + } + } + + public void addDetail(int index, Object value) { + mDetails.put(index, value); + } + + public Object getDetail(int index) { + return mDetails.get(index); + } + + public int size() { + return mDetails.size(); + } + + @Override + public Iterator> iterator() { + return mDetails.entrySet().iterator(); + } + + public void setUnit(int index, int unit) { + mUnits.put(index, unit); + } + + public boolean hasUnit(int index) { + return mUnits.containsKey(index); + } + + public int getUnit(int index) { + return mUnits.get(index); + } + + private static void setExifData(MediaDetails details, ExifTag tag, + int key) { + if (tag != null) { + String value = null; + int type = tag.getDataType(); + if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) { + value = String.valueOf(tag.getValueAsRational(0).toDouble()); + } else if (type == ExifTag.TYPE_ASCII) { + value = tag.getValueAsString(); + } else { + value = String.valueOf(tag.forceGetValueAsLong(0)); + } + if (key == MediaDetails.INDEX_FLASH) { + MediaDetails.FlashState state = new MediaDetails.FlashState( + Integer.valueOf(value.toString())); + details.addDetail(key, state); + } else { + details.addDetail(key, value); + } + } + } + + /** + * Extracts data from the EXIF of the given file and stores it in the + * MediaDetails instance. + */ + public static void extractExifInfo(MediaDetails details, String filePath) { + ExifInterface exif = new ExifInterface(); + try { + exif.readExif(filePath); + } catch (FileNotFoundException e) { + Log.w(TAG, "Could not find file to read exif: " + filePath, e); + } catch (IOException e) { + Log.w(TAG, "Could not read exif from file: " + filePath, e); + } + + setExifData(details, exif.getTag(ExifInterface.TAG_FLASH), + MediaDetails.INDEX_FLASH); + setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_WIDTH), + MediaDetails.INDEX_WIDTH); + setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_LENGTH), + MediaDetails.INDEX_HEIGHT); + setExifData(details, exif.getTag(ExifInterface.TAG_MAKE), + MediaDetails.INDEX_MAKE); + setExifData(details, exif.getTag(ExifInterface.TAG_MODEL), + MediaDetails.INDEX_MODEL); + setExifData(details, exif.getTag(ExifInterface.TAG_APERTURE_VALUE), + MediaDetails.INDEX_APERTURE); + setExifData(details, exif.getTag(ExifInterface.TAG_ISO_SPEED_RATINGS), + MediaDetails.INDEX_ISO); + setExifData(details, exif.getTag(ExifInterface.TAG_WHITE_BALANCE), + MediaDetails.INDEX_WHITE_BALANCE); + setExifData(details, exif.getTag(ExifInterface.TAG_EXPOSURE_TIME), + MediaDetails.INDEX_EXPOSURE_TIME); + ExifTag focalTag = exif.getTag(ExifInterface.TAG_FOCAL_LENGTH); + if (focalTag != null) { + details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH, + focalTag.getValueAsRational(0).toDouble()); + details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm); + } + } +} diff --git a/src/com/android/camera/data/SimpleViewData.java b/src/com/android/camera/data/SimpleViewData.java index d6fc1c416..8801599bb 100644 --- a/src/com/android/camera/data/SimpleViewData.java +++ b/src/com/android/camera/data/SimpleViewData.java @@ -145,4 +145,9 @@ public class SimpleViewData implements LocalData { public boolean canSwipeInFullScreen() { return true; } + + @Override + public MediaDetails getMediaDetails(Context context) { + return null; + } } -- cgit v1.2.3