ExifInterface.java revision 872a30ec723ebdd97de764406544516545d7c9d4
120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync/*
220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * Copyright (C) 2007 The Android Open Source Project
320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *
420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * Licensed under the Apache License, Version 2.0 (the "License");
520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * you may not use this file except in compliance with the License.
620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * You may obtain a copy of the License at
720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *
820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *      http://www.apache.org/licenses/LICENSE-2.0
920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *
1020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * Unless required by applicable law or agreed to in writing, software
1120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * distributed under the License is distributed on an "AS IS" BASIS,
1220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * See the License for the specific language governing permissions and
1420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync * limitations under the License.
1520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */
1620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo syncpackage android.media;
1820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
19700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Changimport java.io.IOException;
20099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Changimport java.text.ParsePosition;
21099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Changimport java.text.SimpleDateFormat;
22099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Changimport java.util.Date;
2320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo syncimport java.util.HashMap;
2420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo syncimport java.util.Map;
2520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
2620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync/**
27700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * This is a class for reading and writing Exif tags in a JPEG file.
2820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */
2920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo syncpublic class ExifInterface {
3020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    // The Exif tag names
3220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_ORIENTATION = "Orientation";
33099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang    public static final String TAG_DATETIME = "DateTime";
3420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_MAKE = "Make";
3520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_MODEL = "Model";
3620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_FLASH = "Flash";
3720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_IMAGE_WIDTH = "ImageWidth";
3820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_IMAGE_LENGTH = "ImageLength";
3920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_GPS_LATITUDE = "GPSLatitude";
4020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
4120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
4220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
4320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    public static final String TAG_WHITE_BALANCE = "WhiteBalance";
4420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
45700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    // Constants used for the Orientation Exif tag.
46700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_UNDEFINED = 0;
47700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_NORMAL = 1;
48700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_FLIP_HORIZONTAL = 2;  // left right reversed mirror
49700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_ROTATE_180 = 3;
50700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_FLIP_VERTICAL = 4;  // upside down mirror
51700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_TRANSPOSE = 5;  // flipped about top-left <--> bottom-right axis
52700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_ROTATE_90 = 6;  // rotate 90 cw to right it
53700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_TRANSVERSE = 7;  // flipped about top-right <--> bottom-left axis
54700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int ORIENTATION_ROTATE_270 = 8;  // rotate 270 to right it
55700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang
56700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    // Constants used for white balance
57700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int WHITEBALANCE_AUTO = 0;
58700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public static final int WHITEBALANCE_MANUAL = 1;
5920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
6020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    static {
6120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        System.loadLibrary("exif");
6220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
6320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
64700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    private String mFilename;
65700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    private HashMap<String, String> mAttributes;
66872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    private boolean mHasThumbnail;
6720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
68700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    // Because the underlying implementation (jhead) uses static variables,
69700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    // there can only be one user at a time for the native functions (and
70700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    // they cannot keep state in the native code across function calls). We
71872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    // use sLock to serialize the accesses.
72700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    private static Object sLock = new Object();
7320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
7420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    /**
75700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Reads Exif tags from the specified JPEG file.
7620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync     */
77700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public ExifInterface(String filename) throws IOException {
78700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        mFilename = filename;
79700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        loadAttributes();
8020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
8120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
82700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    /**
83700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Returns the value of the specified tag or {@code null} if there
84872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * is no such tag in the JPEG file.
85700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     *
86700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * @param tag the name of the tag.
87700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     */
88700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public String getAttribute(String tag) {
89700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        return mAttributes.get(tag);
9020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
9120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    /**
93872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * Returns the integer value of the specified tag. If there is no such tag
94872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * in the JPEG file or the value cannot be parsed as integer, return
95872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * @{code defaultValue}.
96872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     *
97872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * @param tag the name of the tag.
98872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * @param defaultValue the value to return if the tag is not available.
99872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     */
100872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    public int getAttributeInt(String tag, int defaultValue) {
101872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        String value = mAttributes.get(tag);
102872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        if (value == null) return defaultValue;
103872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        try {
104872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang            return Integer.valueOf(value);
105872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        } catch (NumberFormatException ex) {
106872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang            return defaultValue;
107872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        }
108872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    }
109872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang
110872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    /**
111700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Set the value of the specified tag.
112700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     *
113700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * @param tag the name of the tag.
114700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * @param value the value of the tag.
11520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync     */
116700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public void setAttribute(String tag, String value) {
117700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        mAttributes.put(tag, value);
11820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
11920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    /**
121700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Initialize mAttributes with the attributes from the file mFilename.
122700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     *
123700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * mAttributes is a HashMap which stores the Exif attributes of the file.
124700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * The key is the standard tag name and the value is the tag's value: e.g.
125700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Model -> Nikon. Numeric values are stored as strings.
126700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     *
127700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * This function also initialize mHasThumbnail to indicate whether the
128700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * file has a thumbnail inside.
12920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync     */
130872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    private void loadAttributes() throws IOException {
13120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        // format of string passed from native C code:
13220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        // "attrCnt attr1=valueLen value1attr2=value2Len value2..."
13320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        // example:
13420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO"
135700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        mAttributes = new HashMap<String, String>();
13620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
137700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String attrStr;
138700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        synchronized (sLock) {
139700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            attrStr = getAttributesNative(mFilename);
140700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        }
14120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        // get count
14320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        int ptr = attrStr.indexOf(' ');
14420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        int count = Integer.parseInt(attrStr.substring(0, ptr));
14520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        // skip past the space between item count and the rest of the attributes
14620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        ++ptr;
14720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        for (int i = 0; i < count; i++) {
14920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            // extract the attribute name
15020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            int equalPos = attrStr.indexOf('=', ptr);
15120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String attrName = attrStr.substring(ptr, equalPos);
15220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            ptr = equalPos + 1;     // skip past =
15320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            // extract the attribute value length
15520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            int lenPos = attrStr.indexOf(' ', ptr);
15620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            int attrLen = Integer.parseInt(attrStr.substring(ptr, lenPos));
15720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            ptr = lenPos + 1;       // skip pas the space
15820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            // extract the attribute value
16020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String attrValue = attrStr.substring(ptr, ptr + attrLen);
16120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            ptr += attrLen;
16220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            if (attrName.equals("hasThumbnail")) {
16420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                mHasThumbnail = attrValue.equalsIgnoreCase("true");
16520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            } else {
166700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                mAttributes.put(attrName, attrValue);
16720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            }
16820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
16920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
17020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
17120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    /**
172700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Save the tag data into the JPEG file. This is expensive because it involves
173700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * copying all the JPG data from one file to another and deleting the old file
174872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * and renaming the other. It's best to use {@link #setAttribute(String,String)}
175872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * to set all attributes to write and make a single call rather than multiple
176872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * calls for each attribute.
17720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync     */
178700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public void saveAttributes() throws IOException {
179700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        // format of string passed to native C code:
180700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        // "attrCnt attr1=valueLen value1attr2=value2Len value2..."
181700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        // example:
182700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO"
183700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        StringBuilder sb = new StringBuilder();
184700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        int size = mAttributes.size();
185700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        if (mAttributes.containsKey("hasThumbnail")) {
186700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            --size;
187700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        }
188700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        sb.append(size + " ");
189700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        for (Map.Entry<String, String> iter : mAttributes.entrySet()) {
190700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            String key = iter.getKey();
191700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            if (key.equals("hasThumbnail")) {
192700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                // this is a fake attribute not saved as an exif tag
193700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                continue;
194700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            }
195700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            String val = iter.getValue();
196700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            sb.append(key + "=");
197700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            sb.append(val.length() + " ");
198700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            sb.append(val);
199700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        }
200700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String s = sb.toString();
201700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        synchronized (sLock) {
202700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            saveAttributesNative(mFilename, s);
203700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            commitChangesNative(mFilename);
204700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        }
205700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    }
206700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang
207700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    /**
208700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Returns true if the JPEG file has a thumbnail.
209700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     */
210700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public boolean hasThumbnail() {
211700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        return mHasThumbnail;
212700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    }
213700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang
214700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    /**
215700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Returns the thumbnail inside the JPEG file, or {@code null} if there is no thumbnail.
216872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * The returned data is in JPEG format and can be decoded using
217872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
218700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     */
219700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public byte[] getThumbnail() {
220700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        synchronized (sLock) {
221700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang            return getThumbnailNative(mFilename);
222700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        }
223700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    }
224700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang
225700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    /**
226872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * Stores the latitude and longitude value in a float array. The first element is
227872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * the latitude, and the second element is the longitude. Returns false if the
228872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * Exif tags are not available.
22920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync     */
230872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang    public boolean getLatLong(float output[]) {
231700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String latValue = mAttributes.get(ExifInterface.TAG_GPS_LATITUDE);
232700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String latRef = mAttributes.get(ExifInterface.TAG_GPS_LATITUDE_REF);
233700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String lngValue = mAttributes.get(ExifInterface.TAG_GPS_LONGITUDE);
234700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String lngRef = mAttributes.get(ExifInterface.TAG_GPS_LONGITUDE_REF);
23520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
236872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
237872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang            output[0] = convertRationalLatLonToFloat(latValue, latRef);
238872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang            output[1] = convertRationalLatLonToFloat(lngValue, lngRef);
239872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang            return true;
240872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang        } else {
241872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang            return false;
24220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
24320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
24420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
245099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang    private static SimpleDateFormat sFormatter =
246099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang            new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
247099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang
248700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    /**
249700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Returns number of milliseconds since Jan. 1, 1970, midnight GMT.
250700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     * Returns -1 if the date time information if not available.
251872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang     * @hide
252700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang     */
253700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    public long getDateTime() {
254700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang        String dateTimeString = mAttributes.get(TAG_DATETIME);
255099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang        if (dateTimeString == null) return -1;
256099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang
257099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang        ParsePosition pos = new ParsePosition(0);
258099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang        try {
259099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang            Date date = sFormatter.parse(dateTimeString, pos);
260a7bdedabf447a559e0b914e4e5623f3af5ac8ef1Chih-Chung Chang            if (date == null) return -1;
261099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang            return date.getTime();
262099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang        } catch (IllegalArgumentException ex) {
263099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang            return -1;
264099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang        }
265099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang    }
266099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang
267700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang    private static float convertRationalLatLonToFloat(
26820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String rationalString, String ref) {
26920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        try {
27020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String [] parts = rationalString.split(",");
27120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
27220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String [] pair;
27320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            pair = parts[0].split("/");
27420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            int degrees = (int) (Float.parseFloat(pair[0].trim())
27520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    / Float.parseFloat(pair[1].trim()));
27620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
27720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            pair = parts[1].split("/");
27820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            int minutes = (int) ((Float.parseFloat(pair[0].trim())
27920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    / Float.parseFloat(pair[1].trim())));
28020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
28120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            pair = parts[2].split("/");
28220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            float seconds = Float.parseFloat(pair[0].trim())
28320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    / Float.parseFloat(pair[1].trim());
28420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
28520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            float result = degrees + (minutes / 60F) + (seconds / (60F * 60F));
28620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            if ((ref.equals("S") || ref.equals("W"))) {
28720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                return -result;
28820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            }
28920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            return result;
29020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        } catch (RuntimeException ex) {
29120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            // if for whatever reason we can't parse the lat long then return
29220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            // null
29320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            return 0f;
29420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
29520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    }
29620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
29720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private native boolean appendThumbnailNative(String fileName,
29820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String thumbnailFileName);
29920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
30020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private native void saveAttributesNative(String fileName,
30120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            String compressedAttributes);
30220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
30320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private native String getAttributesNative(String fileName);
30420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
30520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private native void commitChangesNative(String fileName);
30620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
30720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private native byte[] getThumbnailNative(String fileName);
30820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync}
309