diff options
author | Ray Chen <raychen@google.com> | 2009-04-22 13:26:07 +0800 |
---|---|---|
committer | Ray Chen <raychen@google.com> | 2009-04-24 11:51:31 +0800 |
commit | 0f0af52aa851d761521699e423f0c3249b5dc849 (patch) | |
tree | bd26d594ef2321fb8d3077267c9b9fafb8b1a978 | |
parent | 8a810b8ca4377d1e6e76f965a2e24ff195537895 (diff) | |
download | LegacyCamera-0f0af52aa851d761521699e423f0c3249b5dc849.zip LegacyCamera-0f0af52aa851d761521699e423f0c3249b5dc849.tar.gz LegacyCamera-0f0af52aa851d761521699e423f0c3249b5dc849.tar.bz2 |
Issue 1799299: Display picture details.
-rw-r--r-- | res/layout/detailsview.xml | 85 | ||||
-rw-r--r-- | res/values/strings.xml | 12 | ||||
-rw-r--r-- | src/com/android/camera/ExifInterface.java | 45 | ||||
-rw-r--r-- | src/com/android/camera/MenuHelper.java | 161 | ||||
-rw-r--r-- | src/com/android/camera/gallery/Image.java | 103 |
5 files changed, 330 insertions, 76 deletions
diff --git a/res/layout/detailsview.xml b/res/layout/detailsview.xml index e28f06c..72a4aa8 100644 --- a/res/layout/detailsview.xml +++ b/res/layout/detailsview.xml @@ -73,19 +73,96 @@ android:textColor="?android:attr/textColorPrimary"/> </TableRow> - <TableRow> - <TextView + <TableRow android:id="@+id/details_resolution_row"> + <TextView android:gravity="right" android:textAppearance="?android:attr/textAppearanceSmall" android:text="@string/details_image_resolution"/> <TextView android:id="@+id/details_resolution_value" - android:layout_height="wrap_content" - android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_width="fill_parent" android:textAppearance="?android:attr/textAppearanceSmall" android:paddingLeft="5dip" android:textColor="?android:attr/textColorPrimary"/> </TableRow> + <TableRow android:id="@+id/details_make_row"> + <TextView + android:gravity="right" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/details_image_make"/> + <TextView + android:id="@+id/details_make_value" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingLeft="5dip" + android:textColor="?android:attr/textColorPrimary"/> + </TableRow> + <TableRow android:id="@+id/details_model_row"> + <TextView + android:gravity="right" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/details_image_model"/> + <TextView + android:id="@+id/details_model_value" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingLeft="5dip" + android:textColor="?android:attr/textColorPrimary"/> + </TableRow> + <TableRow android:id="@+id/details_whitebalance_row"> + <TextView + android:gravity="right" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/details_image_whitebalance"/> + <TextView + android:id="@+id/details_whitebalance_value" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingLeft="5dip" + android:textColor="?android:attr/textColorPrimary"/> + </TableRow> + <TableRow android:id="@+id/details_latitude_row"> + <TextView + android:gravity="right" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/details_image_latitude"/> + <TextView + android:id="@+id/details_latitude_value" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingLeft="5dip" + android:textColor="?android:attr/textColorPrimary"/> + </TableRow> + <TableRow android:id="@+id/details_longitude_row"> <TextView + android:gravity="right" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/details_image_longitude"/> + <TextView + android:id="@+id/details_longitude_value" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingLeft="5dip" + android:textColor="?android:attr/textColorPrimary"/> + </TableRow> + <TableRow android:id="@+id/details_location_row"> + <TextView + android:gravity="right" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/details_image_location"/> + <TextView + android:id="@+id/details_location_value" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingLeft="5dip" + android:textColor="?android:attr/textColorPrimary"/> + </TableRow> <TableRow android:id="@+id/details_duration_row"> <TextView diff --git a/res/values/strings.xml b/res/values/strings.xml index 66bcfff..2676326 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -462,6 +462,18 @@ <!-- Label in message of Details dialog --> <string name="details_image_resolution">Resolution:</string> <!-- Label in message of Details dialog --> + <string name="details_image_make">Manufacturer:</string> + <!-- Label in message of Details dialog --> + <string name="details_image_model">Model:</string> + <!-- Label in message of Details dialog --> + <string name="details_image_whitebalance">WhiteBalance:</string> + <!-- Label in message of Details dialog --> + <string name="details_image_latitude">GPS Latitude:</string> + <!-- Label in message of Details dialog --> + <string name="details_image_longitude">GPS Longitude:</string> + <!-- Label in message of Details dialog --> + <string name="details_image_location">Location:</string> + <!-- Label in message of Details dialog --> <string name="details_duration">Duration:</string> <!-- Label in message of Details dialog --> <string name="details_date_taken">Date taken:</string> diff --git a/src/com/android/camera/ExifInterface.java b/src/com/android/camera/ExifInterface.java index fbbc88c..e36ab2a 100644 --- a/src/com/android/camera/ExifInterface.java +++ b/src/com/android/camera/ExifInterface.java @@ -30,6 +30,10 @@ public class ExifInterface { public static final int ORIENTATION_UNDEFINED = 0; public static final int ORIENTATION_NORMAL = 1; + // Constants used for white balance + public static final int WHITEBALANCE_AUTO = 0; + public static final int WHITEBALANCE_MANUAL = 1; + // left right reversed mirror public static final int ORIENTATION_FLIP_HORIZONTAL = 2; public static final int ORIENTATION_ROTATE_180 = 3; @@ -64,6 +68,7 @@ public class ExifInterface { static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef"; static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef"; + static final String TAG_WHITE_BALANCE = "WhiteBalance"; private boolean mSavedAttributes = false; private boolean mHasThumbnail = false; @@ -163,6 +168,23 @@ public class ExifInterface { } /** + * Given a numerical white balance value, return a + * human-readable string describing it. + * @param orientation + * @return String + */ + public static String whiteBalanceToString(int whitebalance) { + switch (whitebalance) { + case WHITEBALANCE_AUTO: + return "Auto"; + case WHITEBALANCE_MANUAL: + return "Manual"; + default: + return ""; + } + } + + /** * Given a numerical orientation, return a human-readable string describing * the orientation. */ @@ -227,8 +249,8 @@ public class ExifInterface { return getThumbnailNative(mFilename); } - public static String convertRationalLatLonToDecimalString( - String rationalString, String ref, boolean usePositiveNegative) { + public static float convertRationalLatLonToFloat( + String rationalString, String ref) { try { String [] parts = rationalString.split(","); @@ -246,6 +268,20 @@ public class ExifInterface { / Float.parseFloat(pair[1].trim()); float result = degrees + (minutes / 60F) + (seconds / (60F * 60F)); + if ((ref.equals("S") || ref.equals("W"))) { + return -result; + } + return result; + } catch (RuntimeException ex) { + // if for whatever reason we can't parse the lat long then return + // null + return 0f; + } + } + + public static String convertRationalLatLonToDecimalString( + String rationalString, String ref, boolean usePositiveNegative) { + float result = convertRationalLatLonToFloat(rationalString, ref); String preliminaryResult = String.valueOf(result); if (usePositiveNegative) { @@ -255,11 +291,6 @@ public class ExifInterface { return preliminaryResult + String.valueOf((char) 186) + " " + ref; } - } catch (RuntimeException ex) { - // if for whatever reason we can't parse the lat long then return - // null - return null; - } } public static String makeLatLongString(double d) { diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java index 01b5181..54764c3 100644 --- a/src/com/android/camera/MenuHelper.java +++ b/src/com/android/camera/MenuHelper.java @@ -17,12 +17,15 @@ package com.android.camera; import com.android.camera.gallery.IImage; +import com.android.camera.gallery.Image; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; +import android.location.Address; +import android.location.Geocoder; import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.Environment; @@ -34,17 +37,22 @@ import android.text.format.Formatter; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; import android.view.SubMenu; import android.view.View; -import android.view.MenuItem.OnMenuItemClickListener; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.io.Closeable; +import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.StringTokenizer; public class MenuHelper { private static final String TAG = "MenuHelper"; @@ -83,6 +91,7 @@ public class MenuHelper { public static final int NO_STORAGE_ERROR = -1; public static final int CANNOT_STAT_ERROR = -2; + public static final String EMPTY_STRING = ""; /** Activity result code used to report crop results. */ @@ -140,6 +149,71 @@ public class MenuHelper { } } + private static void setDetailsValue(View d, String text, int valueId) { + ((TextView) d.findViewById(valueId)).setText(text); + } + + private static void hideDetailsRow(View d, int rowId) { + d.findViewById(rowId).setVisibility(View.GONE); + } + + private static float setLatLngDetails(View d, Image img, + String tag, String refTag) { + String value = img.getExifTag(tag); + String ref = img.getExifTag(refTag); + float f = 0f; + if (value != null && ref != null) { + f = ExifInterface.convertRationalLatLonToFloat( + value, ref); + value = String.valueOf(f); + } + int valueId = R.id.details_latitude_value; + int rowId = R.id.details_latitude_row; + + if (tag == ExifInterface.TAG_GPS_LONGITUDE) { + valueId = R.id.details_longitude_value; + rowId = R.id.details_longitude_row; + } + if (value != null) { + setDetailsValue(d, value, valueId); + } else { + hideDetailsRow(d, rowId); + } + return f; + } + + private static void setReverseGeocodingDetails(View d, Activity context, + float lat, float lng) { + // Fill in reverse-geocoded address + String value = EMPTY_STRING; + try { + Geocoder geocoder = new Geocoder(context); + List<Address> address = geocoder.getFromLocation( + lat, lng, 1); + Iterator<Address> iterator = address.iterator(); + + while (iterator.hasNext()) { + Address addr = iterator.next(); + value += addr.getAddressLine( + addr.getMaxAddressLineIndex()); + Log.v(TAG, addr.toString()); + } + } catch (IOException ex) { + // Ignore this exception. + value = EMPTY_STRING; + Log.e(TAG, "Geocoder exception: ", ex); + } catch (RuntimeException ex) { + // Ignore this exception. + value = EMPTY_STRING; + Log.e(TAG, "Geocoder exception: ", ex); + } + if (value != EMPTY_STRING) { + setDetailsValue(d, value, R.id.details_location_value); + } else { + hideDetailsRow(d, R.id.details_location_row); + } + } + // Called when "Details" is clicked. // Displays detailed information about the image/video. private static boolean onDetailsClicked(MenuInvoker onInvoke, @@ -167,7 +241,7 @@ public class MenuHelper { long length = getImageFileSize(image); String lengthString = length < 0 - ? "" + ? EMPTY_STRING : Formatter.formatFileSize(activity, length); ((TextView) d .findViewById(R.id.details_file_size_value)) @@ -176,6 +250,7 @@ public class MenuHelper { int dimensionWidth = 0; int dimensionHeight = 0; if (isImage) { + // getWidth is much slower than reading from EXIF dimensionWidth = image.getWidth(); dimensionHeight = image.getHeight(); d.findViewById(R.id.details_duration_row) @@ -275,15 +350,12 @@ public class MenuHelper { String codec = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_CODEC); - - if (codec == null) { - d.findViewById(R.id.details_codec_row). - setVisibility(View.GONE); + if (codec != null) { + setDetailsValue(d, codec, R.id.details_codec_value); } else { - ((TextView) d.findViewById( - R.id.details_codec_value)) - .setText(codec); + hideDetailsRow(d, R.id.details_codec_row); } + } catch (RuntimeException ex) { // Assume this is a corrupt video file. } finally { @@ -295,26 +367,69 @@ public class MenuHelper { } } - String dimensionsString = String.format( - activity.getString(R.string.details_dimension_x), - dimensionWidth, dimensionHeight); - ((TextView) d - .findViewById(R.id.details_resolution_value)) - .setText(dimensionsString); + Image img = (Image) image; + String value = null; + if (dimensionWidth > 0 && dimensionHeight > 0) { + value = String.format( + activity.getString(R.string.details_dimension_x), + dimensionWidth, dimensionHeight); + } else { + String width = img.getExifTag(ExifInterface.TAG_IMAGE_WIDTH); + String height = img.getExifTag(ExifInterface.TAG_IMAGE_LENGTH); + if (width != null && height != null) { + value = EMPTY_STRING + width + " x " + height; + } + } + if (value != null) { + setDetailsValue(d, value, R.id.details_resolution_value); + } else { + hideDetailsRow(d, R.id.details_resolution_row); + } - String dateString = ""; + value = img.getExifTag(ExifInterface.TAG_MAKE); + if (value != null) { + setDetailsValue(d, value, R.id.details_make_value); + } else { + hideDetailsRow(d, R.id.details_make_row); + } + + value = img.getExifTag(ExifInterface.TAG_MODEL); + if (value != null) { + setDetailsValue(d, value, R.id.details_model_value); + } else { + hideDetailsRow(d, R.id.details_model_row); + } + + value = img.getExifTag(ExifInterface.TAG_WHITE_BALANCE); + if (value != null) { + value = ExifInterface.whiteBalanceToString( + Integer.parseInt(value)); + } + if (value != null) { + setDetailsValue(d, value, R.id.details_whitebalance_value); + } else { + hideDetailsRow(d, R.id.details_whitebalance_row); + } + + float lat = setLatLngDetails(d, img, + ExifInterface.TAG_GPS_LATITUDE, + ExifInterface.TAG_GPS_LATITUDE_REF); + float lng = setLatLngDetails(d, img, + ExifInterface.TAG_GPS_LONGITUDE, + ExifInterface.TAG_GPS_LONGITUDE_REF); + setReverseGeocodingDetails(d, activity, lat, lng); + + value = EMPTY_STRING; long dateTaken = image.getDateTaken(); if (dateTaken != 0) { Date date = new Date(image.getDateTaken()); SimpleDateFormat dateFormat = new SimpleDateFormat(); - dateString = dateFormat.format(date); - - ((TextView) d - .findViewById(R.id.details_date_taken_value)) - .setText(dateString); + value = dateFormat.format(date); + } + if (value != EMPTY_STRING) { + setDetailsValue(d, value, R.id.details_date_taken_value); } else { - d.findViewById(R.id.details_date_taken_row) - .setVisibility(View.GONE); + hideDetailsRow(d, R.id.details_date_taken_row); } builder.setNeutralButton(R.string.details_ok, diff --git a/src/com/android/camera/gallery/Image.java b/src/com/android/camera/gallery/Image.java index 14e238a..04ae09f 100644 --- a/src/com/android/camera/gallery/Image.java +++ b/src/com/android/camera/gallery/Image.java @@ -43,6 +43,7 @@ public class Image extends BaseImage implements IImage { private static final String TAG = "BaseImage"; private int mRotation; + private ExifInterface mExif; public Image(long id, long miniThumbMagic, ContentResolver cr, BaseImageList container, int cursorRow, int rotation) { @@ -102,7 +103,7 @@ public class Image extends BaseImage implements IImage { */ public void addExifTag(String tag, String value) { if (mExifData == null) { - mExifData = new HashMap<String, String>(); + loadExifData(); } // If the key is already there, ignore it. if (!mExifData.containsKey(tag)) { @@ -116,14 +117,28 @@ public class Image extends BaseImage implements IImage { * * @param tag */ - public int getExifTagInt(String tag) { - if (mExifData != null) { - String tagValue = mExifData.get(tag); + public int getExifTagInt(String tag, int defaultValue) { + String tagValue = getExifTag(tag); + try { if (tagValue != null) { return Integer.parseInt(tagValue); } + } catch (NumberFormatException ex) { + // Simply return defaultValue if exception is thrown. + Log.v(TAG, ex.toString()); } - return 0; + return defaultValue; + } + + /** + * Return the value of the Exif tag as a String. It's caller's + * responsibility to check nullity. + */ + public String getExifTag(String tag) { + if (mExifData == null) { + loadExifData(); + } + return mExifData.get(tag); } public boolean isReadonly() { @@ -141,7 +156,7 @@ public class Image extends BaseImage implements IImage { */ public void removeExifTag(String tag) { if (mExifData == null) { - mExifData = new HashMap<String, String>(); + loadExifData(); } mExifData.remove(tag); } @@ -153,10 +168,7 @@ public class Image extends BaseImage implements IImage { */ public void replaceExifTag(String tag, String value) { if (mExifData == null) { - mExifData = new HashMap<String, String>(); - } - if (!mExifData.containsKey(tag)) { - mExifData.remove(tag); + loadExifData(); } mExifData.put(tag, value); } @@ -254,42 +266,49 @@ public class Image extends BaseImage implements IImage { image, jpegData, orientation, filePath); } + private void loadExifData() { + Cursor c = getCursor(); + String filePath; + synchronized (c) { + filePath = c.getString(mContainer.indexData()); + } + ExifInterface mExif = new ExifInterface(filePath); + if (mExifData == null) { + mExifData = mExif.getAttributes(); + } + } + + private void saveExifData() { + if (mExif != null && mExifData != null) { + mExif.saveAttributes(mExifData); + } + } + private void setExifRotation(int degrees) { try { - Cursor c = getCursor(); - String filePath; - synchronized (c) { - filePath = c.getString(mContainer.indexData()); + if (degrees < 0) degrees += 360; + + int orientation = ExifInterface.ORIENTATION_NORMAL; + switch (degrees) { + case 0: + orientation = ExifInterface.ORIENTATION_NORMAL; + break; + case 90: + orientation = ExifInterface.ORIENTATION_ROTATE_90; + break; + case 180: + orientation = ExifInterface.ORIENTATION_ROTATE_180; + break; + case 270: + orientation = ExifInterface.ORIENTATION_ROTATE_270; + break; } - synchronized (ExifInterface.class) { - ExifInterface exif = new ExifInterface(filePath); - if (mExifData == null) { - mExifData = exif.getAttributes(); - } - if (degrees < 0) degrees += 360; - - int orientation = ExifInterface.ORIENTATION_NORMAL; - switch (degrees) { - case 0: - orientation = ExifInterface.ORIENTATION_NORMAL; - break; - case 90: - orientation = ExifInterface.ORIENTATION_ROTATE_90; - break; - case 180: - orientation = ExifInterface.ORIENTATION_ROTATE_180; - break; - case 270: - orientation = ExifInterface.ORIENTATION_ROTATE_270; - break; - } - replaceExifTag(ExifInterface.TAG_ORIENTATION, - Integer.toString(orientation)); - replaceExifTag("UserComment", - "saveRotatedImage comment orientation: " + orientation); - exif.saveAttributes(mExifData); - } + replaceExifTag(ExifInterface.TAG_ORIENTATION, + Integer.toString(orientation)); + replaceExifTag("UserComment", + "saveRotatedImage comment orientation: " + orientation); + saveExifData(); } catch (RuntimeException ex) { Log.e(TAG, "unable to save exif data with new orientation " + fullSizeImageUri()); |