diff options
author | Chih-Chung Chang <chihchung@google.com> | 2009-09-16 18:26:39 +0800 |
---|---|---|
committer | Chih-Chung Chang <chihchung@google.com> | 2009-09-16 20:03:48 +0800 |
commit | 700beb484624a9a34649cb6ff088468e78b758ff (patch) | |
tree | 20adb5279c5733e90f7245e6845a51a3a90a0f2d /media | |
parent | 9352b9fb9e0b4023c3bba4349eb9e32dc258b80a (diff) | |
download | frameworks_base-700beb484624a9a34649cb6ff088468e78b758ff.zip frameworks_base-700beb484624a9a34649cb6ff088468e78b758ff.tar.gz frameworks_base-700beb484624a9a34649cb6ff088468e78b758ff.tar.bz2 |
Clean ExifInterface. Prepare to make it public.
Change-Id: Ibff719d02f525c1e8583d1892737224a0cc42c37
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/ExifInterface.java | 368 | ||||
-rw-r--r-- | media/java/android/media/MediaScanner.java | 14 |
2 files changed, 161 insertions, 221 deletions
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index aba40b3..2595532 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -16,8 +16,7 @@ package android.media; -import android.util.Log; - +import java.io.IOException; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; @@ -25,171 +24,103 @@ import java.util.HashMap; import java.util.Map; /** - * Wrapper for native Exif library + * This is a class for reading and writing Exif tags in a JPEG file. * {@hide} */ public class ExifInterface { - private static final String TAG = "ExifInterface"; - private String mFilename; - - // Constants used for the Orientation Exif tag. - 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; - - // upside down mirror - public static final int ORIENTATION_FLIP_VERTICAL = 4; - - // flipped about top-left <--> bottom-right axis - public static final int ORIENTATION_TRANSPOSE = 5; - - // rotate 90 cw to right it - public static final int ORIENTATION_ROTATE_90 = 6; - - // flipped about top-right <--> bottom-left axis - public static final int ORIENTATION_TRANSVERSE = 7; - - // rotate 270 to right it - public static final int ORIENTATION_ROTATE_270 = 8; // The Exif tag names public static final String TAG_ORIENTATION = "Orientation"; - public static final String TAG_DATETIME = "DateTime"; public static final String TAG_MAKE = "Make"; public static final String TAG_MODEL = "Model"; public static final String TAG_FLASH = "Flash"; public static final String TAG_IMAGE_WIDTH = "ImageWidth"; public static final String TAG_IMAGE_LENGTH = "ImageLength"; - public static final String TAG_GPS_LATITUDE = "GPSLatitude"; public static final String TAG_GPS_LONGITUDE = "GPSLongitude"; - public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef"; public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef"; public static final String TAG_WHITE_BALANCE = "WhiteBalance"; - private boolean mSavedAttributes = false; - private boolean mHasThumbnail = false; - private HashMap<String, String> mCachedAttributes = null; + // Constants used for the Orientation Exif tag. + public static final int ORIENTATION_UNDEFINED = 0; + public static final int ORIENTATION_NORMAL = 1; + public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // left right reversed mirror + public static final int ORIENTATION_ROTATE_180 = 3; + public static final int ORIENTATION_FLIP_VERTICAL = 4; // upside down mirror + public static final int ORIENTATION_TRANSPOSE = 5; // flipped about top-left <--> bottom-right axis + public static final int ORIENTATION_ROTATE_90 = 6; // rotate 90 cw to right it + public static final int ORIENTATION_TRANSVERSE = 7; // flipped about top-right <--> bottom-left axis + public static final int ORIENTATION_ROTATE_270 = 8; // rotate 270 to right it + + // Constants used for white balance + public static final int WHITEBALANCE_AUTO = 0; + public static final int WHITEBALANCE_MANUAL = 1; static { System.loadLibrary("exif"); } - private static ExifInterface sExifObj = null; - /** - * Since the underlying jhead native code is not thread-safe, - * ExifInterface should use singleton interface instead of public - * constructor. - */ - private static synchronized ExifInterface instance() { - if (sExifObj == null) { - sExifObj = new ExifInterface(); - } + private String mFilename; + private HashMap<String, String> mAttributes; + private boolean mHasThumbnail = false; - return sExifObj; - } + // Because the underlying implementation (jhead) uses static variables, + // there can only be one user at a time for the native functions (and + // they cannot keep state in the native code across function calls). We + // use sLock the serialize the accesses. + private static Object sLock = new Object(); /** - * The following 3 static methods are handy routines for atomic operation - * of underlying jhead library. It retrieves EXIF data and then release - * ExifInterface immediately. + * Reads Exif tags from the specified JPEG file. */ - public static synchronized HashMap<String, String> loadExifData(String filename) { - ExifInterface exif = instance(); - HashMap<String, String> exifData = null; - if (exif != null) { - exif.setFilename(filename); - exifData = exif.getAttributes(); - } - return exifData; + public ExifInterface(String filename) throws IOException { + mFilename = filename; + loadAttributes(); } - public static synchronized void saveExifData(String filename, HashMap<String, String> exifData) { - ExifInterface exif = instance(); - if (exif != null) { - exif.setFilename(filename); - exif.saveAttributes(exifData); - } - } - - public static synchronized byte[] getExifThumbnail(String filename) { - ExifInterface exif = instance(); - if (exif != null) { - exif.setFilename(filename); - return exif.getThumbnail(); - } - return null; - } - - public void setFilename(String filename) { - if (mFilename == null || !mFilename.equals(filename)) { - mFilename = filename; - mCachedAttributes = null; - } + /** + * Returns the value of the specified tag or {@code null} if there + * is no such tag in the file. + * + * @param tag the name of the tag. + */ + public String getAttribute(String tag) { + return mAttributes.get(tag); } /** - * Given a HashMap of Exif tags and associated values, an Exif section in - * the JPG file is created and loaded with the tag data. saveAttributes() - * is expensive because it involves copying all the JPG data from one file - * to another and deleting the old file and renaming the other. It's best - * to collect all the attributes to write and make a single call rather - * than multiple calls for each attribute. You must call "commitChanges()" - * at some point to commit the changes. + * Set the value of the specified tag. + * + * @param tag the name of the tag. + * @param value the value of the tag. */ - public void saveAttributes(HashMap<String, String> attributes) { - // format of string passed to native C code: - // "attrCnt attr1=valueLen value1attr2=value2Len value2..." - // example: - // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO" - StringBuilder sb = new StringBuilder(); - int size = attributes.size(); - if (attributes.containsKey("hasThumbnail")) { - --size; - } - sb.append(size + " "); - for (Map.Entry<String, String> iter : attributes.entrySet()) { - String key = iter.getKey(); - if (key.equals("hasThumbnail")) { - // this is a fake attribute not saved as an exif tag - continue; - } - String val = iter.getValue(); - sb.append(key + "="); - sb.append(val.length() + " "); - sb.append(val); - } - String s = sb.toString(); - saveAttributesNative(mFilename, s); - commitChangesNative(mFilename); - mSavedAttributes = true; + public void setAttribute(String tag, String value) { + mAttributes.put(tag, value); } /** - * Returns a HashMap loaded with the Exif attributes of the file. The key - * is the standard tag name and the value is the tag's value: e.g. - * Model -> Nikon. Numeric values are returned as strings. + * Initialize mAttributes with the attributes from the file mFilename. + * + * mAttributes is a HashMap which stores the Exif attributes of the file. + * The key is the standard tag name and the value is the tag's value: e.g. + * Model -> Nikon. Numeric values are stored as strings. + * + * This function also initialize mHasThumbnail to indicate whether the + * file has a thumbnail inside. */ - public HashMap<String, String> getAttributes() { - if (mCachedAttributes != null) { - return mCachedAttributes; - } + private void loadAttributes() { // format of string passed from native C code: // "attrCnt attr1=valueLen value1attr2=value2Len value2..." // example: // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO" - mCachedAttributes = new HashMap<String, String>(); + mAttributes = new HashMap<String, String>(); - String attrStr = getAttributesNative(mFilename); + String attrStr; + synchronized (sLock) { + attrStr = getAttributesNative(mFilename); + } // get count int ptr = attrStr.indexOf(' '); @@ -215,17 +146,78 @@ public class ExifInterface { if (attrName.equals("hasThumbnail")) { mHasThumbnail = attrValue.equalsIgnoreCase("true"); } else { - mCachedAttributes.put(attrName, attrValue); + mAttributes.put(attrName, attrValue); } } - return mCachedAttributes; } /** - * Given a numerical white balance value, return a - * human-readable string describing it. + * Save the tag data into the JPEG file. This is expensive because it involves + * copying all the JPG data from one file to another and deleting the old file + * and renaming the other. It's best to use {@link setAttribute()} to set all + * attributes to write and make a single call rather than multiple calls for + * each attribute. */ - public static String whiteBalanceToString(int whitebalance) { + public void saveAttributes() throws IOException { + // format of string passed to native C code: + // "attrCnt attr1=valueLen value1attr2=value2Len value2..." + // example: + // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO" + StringBuilder sb = new StringBuilder(); + int size = mAttributes.size(); + if (mAttributes.containsKey("hasThumbnail")) { + --size; + } + sb.append(size + " "); + for (Map.Entry<String, String> iter : mAttributes.entrySet()) { + String key = iter.getKey(); + if (key.equals("hasThumbnail")) { + // this is a fake attribute not saved as an exif tag + continue; + } + String val = iter.getValue(); + sb.append(key + "="); + sb.append(val.length() + " "); + sb.append(val); + } + String s = sb.toString(); + synchronized (sLock) { + saveAttributesNative(mFilename, s); + commitChangesNative(mFilename); + } + } + + /** + * Returns true if the JPEG file has a thumbnail. + */ + public boolean hasThumbnail() { + return mHasThumbnail; + } + + /** + * Returns the thumbnail inside the JPEG file, or {@code null} if there is no thumbnail. + */ + public byte[] getThumbnail() { + synchronized (sLock) { + return getThumbnailNative(mFilename); + } + } + + /** + * Returns a human-readable string describing the white balance value. Returns empty + * string if there is no white balance value or it is not recognized. + */ + public String getWhiteBalanceString() { + String value = getAttribute(TAG_WHITE_BALANCE); + if (value == null) return ""; + + int whitebalance; + try { + whitebalance = Integer.parseInt(value); + } catch (NumberFormatException ex) { + return ""; + } + switch (whitebalance) { case WHITEBALANCE_AUTO: return "Auto"; @@ -237,12 +229,21 @@ public class ExifInterface { } /** - * Given a numerical orientation, return a human-readable string describing - * the orientation. + * Returns a human-readable string describing the orientation value. Returns empty + * string if there is no orientation value or it it not recognized. */ - public static String orientationToString(int orientation) { - // TODO: this function needs to be localized and use string resource ids - // rather than strings + public String getOrientationString() { + // TODO: this function needs to be localized. + String value = getAttribute(TAG_ORIENTATION); + if (value == null) return ""; + + int orientation; + try { + orientation = Integer.parseInt(value); + } catch (NumberFormatException ex) { + return ""; + } + String orientationString; switch (orientation) { case ORIENTATION_NORMAL: @@ -277,48 +278,21 @@ public class ExifInterface { } /** - * Copies the thumbnail data out of the filename and puts it in the Exif - * data associated with the file used to create this object. You must call - * "commitChanges()" at some point to commit the changes. + * Returns the latitude and longitude value in a float array. The first element is + * the latitude, and the second element is the longitude. */ - public boolean appendThumbnail(String thumbnailFileName) { - if (!mSavedAttributes) { - throw new RuntimeException("Must call saveAttributes " - + "before calling appendThumbnail"); - } - mHasThumbnail = appendThumbnailNative(mFilename, thumbnailFileName); - return mHasThumbnail; - } - - public boolean hasThumbnail() { - if (!mSavedAttributes) { - getAttributes(); - } - return mHasThumbnail; - } - - public byte[] getThumbnail() { - return getThumbnailNative(mFilename); - } - - public static float[] getLatLng(HashMap<String, String> exifData) { - if (exifData == null) { - return null; - } - - String latValue = exifData.get(ExifInterface.TAG_GPS_LATITUDE); - String latRef = exifData.get(ExifInterface.TAG_GPS_LATITUDE_REF); - String lngValue = exifData.get(ExifInterface.TAG_GPS_LONGITUDE); - String lngRef = exifData.get(ExifInterface.TAG_GPS_LONGITUDE_REF); + public float[] getLatLong() { + String latValue = mAttributes.get(ExifInterface.TAG_GPS_LATITUDE); + String latRef = mAttributes.get(ExifInterface.TAG_GPS_LATITUDE_REF); + String lngValue = mAttributes.get(ExifInterface.TAG_GPS_LONGITUDE); + String lngRef = mAttributes.get(ExifInterface.TAG_GPS_LONGITUDE_REF); float[] latlng = null; if (latValue != null && latRef != null && lngValue != null && lngRef != null) { latlng = new float[2]; - latlng[0] = ExifInterface.convertRationalLatLonToFloat( - latValue, latRef); - latlng[1] = ExifInterface.convertRationalLatLonToFloat( - lngValue, lngRef); + latlng[0] = convertRationalLatLonToFloat(latValue, latRef); + latlng[1] = convertRationalLatLonToFloat(lngValue, lngRef); } return latlng; @@ -327,14 +301,12 @@ public class ExifInterface { private static SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); - // Returns number of milliseconds since Jan. 1, 1970, midnight GMT. - // Returns -1 if the date time information if not available. - public static long getDateTime(HashMap<String, String> exifData) { - if (exifData == null) { - return -1; - } - - String dateTimeString = exifData.get(ExifInterface.TAG_DATETIME); + /** + * Returns number of milliseconds since Jan. 1, 1970, midnight GMT. + * Returns -1 if the date time information if not available. + */ + public long getDateTime() { + String dateTimeString = mAttributes.get(TAG_DATETIME); if (dateTimeString == null) return -1; ParsePosition pos = new ParsePosition(0); @@ -347,7 +319,7 @@ public class ExifInterface { } } - public static float convertRationalLatLonToFloat( + private static float convertRationalLatLonToFloat( String rationalString, String ref) { try { String [] parts = rationalString.split(","); @@ -377,42 +349,6 @@ public class ExifInterface { } } - public static String convertRationalLatLonToDecimalString( - String rationalString, String ref, boolean usePositiveNegative) { - float result = convertRationalLatLonToFloat(rationalString, ref); - - String preliminaryResult = String.valueOf(result); - if (usePositiveNegative) { - String neg = (ref.equals("S") || ref.equals("E")) ? "-" : ""; - return neg + preliminaryResult; - } else { - return preliminaryResult + String.valueOf((char) 186) + " " - + ref; - } - } - - public static String makeLatLongString(double d) { - d = Math.abs(d); - - int degrees = (int) d; - - double remainder = d - degrees; - int minutes = (int) (remainder * 60D); - // really seconds * 1000 - int seconds = (int) (((remainder * 60D) - minutes) * 60D * 1000D); - - String retVal = degrees + "/1," + minutes + "/1," + seconds + "/1000"; - return retVal; - } - - public static String makeLatStringRef(double lat) { - return lat >= 0D ? "N" : "S"; - } - - public static String makeLonStringRef(double lon) { - return lon >= 0D ? "W" : "E"; - } - private native boolean appendThumbnailNative(String fileName, String thumbnailFileName); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index f6d30e0..d9127e7 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -719,16 +719,20 @@ public class MediaScanner values.put(Audio.Media.IS_MUSIC, music); values.put(Audio.Media.IS_PODCAST, podcasts); } else if (mFileType == MediaFile.FILE_TYPE_JPEG) { - HashMap<String, String> exifData = - ExifInterface.loadExifData(entry.mPath); - if (exifData != null) { - float[] latlng = ExifInterface.getLatLng(exifData); + ExifInterface exif = null; + try { + exif = new ExifInterface(entry.mPath); + } catch (IOException ex) { + // exif is null + } + if (exif != null) { + float[] latlng = exif.getLatLong(); if (latlng != null) { values.put(Images.Media.LATITUDE, latlng[0]); values.put(Images.Media.LONGITUDE, latlng[1]); } - long time = ExifInterface.getDateTime(exifData); + long time = exif.getDateTime(); if (time != -1) { values.put(Images.Media.DATE_TAKEN, time); } |