summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRay Chen <raychen@google.com>2009-04-22 13:26:07 +0800
committerRay Chen <raychen@google.com>2009-04-24 11:51:31 +0800
commit0f0af52aa851d761521699e423f0c3249b5dc849 (patch)
treebd26d594ef2321fb8d3077267c9b9fafb8b1a978
parent8a810b8ca4377d1e6e76f965a2e24ff195537895 (diff)
downloadLegacyCamera-0f0af52aa851d761521699e423f0c3249b5dc849.zip
LegacyCamera-0f0af52aa851d761521699e423f0c3249b5dc849.tar.gz
LegacyCamera-0f0af52aa851d761521699e423f0c3249b5dc849.tar.bz2
Issue 1799299: Display picture details.
-rw-r--r--res/layout/detailsview.xml85
-rw-r--r--res/values/strings.xml12
-rw-r--r--src/com/android/camera/ExifInterface.java45
-rw-r--r--src/com/android/camera/MenuHelper.java161
-rw-r--r--src/com/android/camera/gallery/Image.java103
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());