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; 25d5fa58cf3d71f2cf94299584050b2698fa3753ecRay Chenimport java.util.TimeZone; 2620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 2720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync/** 28700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * This is a class for reading and writing Exif tags in a JPEG file. 2920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */ 3020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo syncpublic class ExifInterface { 3120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // The Exif tag names 325d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is int. */ 3320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_ORIENTATION = "Orientation"; 345d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 35099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang public static final String TAG_DATETIME = "DateTime"; 365d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 3720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_MAKE = "Make"; 385d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 3920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_MODEL = "Model"; 405d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is int. */ 4120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_FLASH = "Flash"; 425d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is int. */ 4320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_IMAGE_WIDTH = "ImageWidth"; 445d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is int. */ 4520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_IMAGE_LENGTH = "ImageLength"; 465d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */ 4720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_GPS_LATITUDE = "GPSLatitude"; 485d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */ 4920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_GPS_LONGITUDE = "GPSLongitude"; 505d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 5120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef"; 525d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 5320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef"; 544414eb4b2955be9eb62d1ddf379d1a7ae1822887Ray Chen /** Type is String. */ 554414eb4b2955be9eb62d1ddf379d1a7ae1822887Ray Chen public static final String TAG_EXPOSURE_TIME = "ExposureTime"; 564414eb4b2955be9eb62d1ddf379d1a7ae1822887Ray Chen /** Type is String. */ 574414eb4b2955be9eb62d1ddf379d1a7ae1822887Ray Chen public static final String TAG_APERTURE = "FNumber"; 584414eb4b2955be9eb62d1ddf379d1a7ae1822887Ray Chen /** Type is String. */ 594414eb4b2955be9eb62d1ddf379d1a7ae1822887Ray Chen public static final String TAG_ISO = "ISOSpeedRatings"; 60c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li 61c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li /** 62c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF. 63c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * Type is rational. 64c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li */ 65c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li public static final String TAG_GPS_ALTITUDE = "GPSAltitude"; 66c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li 67c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li /** 68c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * 0 if the altitude is above sea level. 1 if the altitude is below sea 69c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * level. Type is int. 70c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li */ 71c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef"; 72c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li 735d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 743a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp"; 755d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is String. */ 763a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen public static final String TAG_GPS_DATESTAMP = "GPSDateStamp"; 775d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is int. */ 7820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync public static final String TAG_WHITE_BALANCE = "WhiteBalance"; 795d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** Type is rational. */ 805d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li public static final String TAG_FOCAL_LENGTH = "FocalLength"; 81e208377fbab6b90f41e68699700942a81f4caaebRay Chen /** Type is String. Name of GPS processing method used for location finding. */ 82e208377fbab6b90f41e68699700942a81f4caaebRay Chen public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod"; 8320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 84700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // Constants used for the Orientation Exif tag. 85700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_UNDEFINED = 0; 86700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_NORMAL = 1; 87700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // left right reversed mirror 88700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_ROTATE_180 = 3; 89700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_FLIP_VERTICAL = 4; // upside down mirror 90700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_TRANSPOSE = 5; // flipped about top-left <--> bottom-right axis 91700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_ROTATE_90 = 6; // rotate 90 cw to right it 92700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_TRANSVERSE = 7; // flipped about top-right <--> bottom-left axis 93700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int ORIENTATION_ROTATE_270 = 8; // rotate 270 to right it 94700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang 95700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // Constants used for white balance 96700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int WHITEBALANCE_AUTO = 0; 97700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public static final int WHITEBALANCE_MANUAL = 1; 98d5fa58cf3d71f2cf94299584050b2698fa3753ecRay Chen private static SimpleDateFormat sFormatter; 9920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 10020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync static { 101934349e8ad37c1ad251d6992e0751635f9ab3f28Zhijun He System.loadLibrary("jhead_jni"); 102d5fa58cf3d71f2cf94299584050b2698fa3753ecRay Chen sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); 1033a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen sFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); 10420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 10520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 106700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang private String mFilename; 107700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang private HashMap<String, String> mAttributes; 108872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang private boolean mHasThumbnail; 10920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 110700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // Because the underlying implementation (jhead) uses static variables, 111700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // there can only be one user at a time for the native functions (and 112700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // they cannot keep state in the native code across function calls). We 113872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang // use sLock to serialize the accesses. 11430c918ce7fbe171944b28fc91b3f22b3d631872dGlenn Kasten private static final Object sLock = new Object(); 11520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 11620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync /** 117700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Reads Exif tags from the specified JPEG file. 11820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */ 119700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public ExifInterface(String filename) throws IOException { 120c9b6be7c402bd349f8d9322730256f593053dc52Dave Burke if (filename == null) { 121c9b6be7c402bd349f8d9322730256f593053dc52Dave Burke throw new IllegalArgumentException("filename cannot be null"); 122c9b6be7c402bd349f8d9322730256f593053dc52Dave Burke } 123700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang mFilename = filename; 124700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang loadAttributes(); 12520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 12620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 127700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang /** 128700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Returns the value of the specified tag or {@code null} if there 129872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * is no such tag in the JPEG file. 130700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * 131700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * @param tag the name of the tag. 132700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang */ 133700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public String getAttribute(String tag) { 134700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang return mAttributes.get(tag); 13520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 13620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 13720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync /** 138872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * Returns the integer value of the specified tag. If there is no such tag 139872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * in the JPEG file or the value cannot be parsed as integer, return 14029e4a3c566f435c32f0b95e4ac8e8b33cac6fabaDianne Hackborn * <var>defaultValue</var>. 141872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * 142872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * @param tag the name of the tag. 143872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * @param defaultValue the value to return if the tag is not available. 144872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang */ 145872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang public int getAttributeInt(String tag, int defaultValue) { 146872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang String value = mAttributes.get(tag); 147872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang if (value == null) return defaultValue; 148872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang try { 149872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang return Integer.valueOf(value); 150872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang } catch (NumberFormatException ex) { 151872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang return defaultValue; 152872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang } 153872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang } 154872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang 155872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang /** 1565d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li * Returns the double value of the specified rational tag. If there is no 1575d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li * such tag in the JPEG file or the value cannot be parsed as double, return 1585d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li * <var>defaultValue</var>. 1595d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li * 1605d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li * @param tag the name of the tag. 1615d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li * @param defaultValue the value to return if the tag is not available. 1625d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li */ 1635d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li public double getAttributeDouble(String tag, double defaultValue) { 1645d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li String value = mAttributes.get(tag); 1655d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li if (value == null) return defaultValue; 1665d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li try { 1675d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li int index = value.indexOf("/"); 1685d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li if (index == -1) return defaultValue; 1695d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li double denom = Double.parseDouble(value.substring(index + 1)); 1705d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li if (denom == 0) return defaultValue; 1715d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li double num = Double.parseDouble(value.substring(0, index)); 1725d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li return num / denom; 1735d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li } catch (NumberFormatException ex) { 1745d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li return defaultValue; 1755d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li } 1765d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li } 1775d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li 1785d9661be8eaa2a4702a4d3c0fa0fd07a8638b503Wu-cheng Li /** 179700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Set the value of the specified tag. 180700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * 181700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * @param tag the name of the tag. 182700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * @param value the value of the tag. 18320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */ 184700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public void setAttribute(String tag, String value) { 185700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang mAttributes.put(tag, value); 18620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 18720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 18820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync /** 189700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Initialize mAttributes with the attributes from the file mFilename. 190700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * 191700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * mAttributes is a HashMap which stores the Exif attributes of the file. 192700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * The key is the standard tag name and the value is the tag's value: e.g. 193700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Model -> Nikon. Numeric values are stored as strings. 194700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * 195700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * This function also initialize mHasThumbnail to indicate whether the 196700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * file has a thumbnail inside. 19720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */ 198872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang private void loadAttributes() throws IOException { 19920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // format of string passed from native C code: 20020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // "attrCnt attr1=valueLen value1attr2=value2Len value2..." 20120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // example: 20220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO" 203700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang mAttributes = new HashMap<String, String>(); 20420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 205700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String attrStr; 206700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang synchronized (sLock) { 207700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang attrStr = getAttributesNative(mFilename); 208700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 20920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 21020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // get count 21120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync int ptr = attrStr.indexOf(' '); 21220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync int count = Integer.parseInt(attrStr.substring(0, ptr)); 21320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // skip past the space between item count and the rest of the attributes 21420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync ++ptr; 21520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 21620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync for (int i = 0; i < count; i++) { 21720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // extract the attribute name 21820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync int equalPos = attrStr.indexOf('=', ptr); 21920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String attrName = attrStr.substring(ptr, equalPos); 22020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync ptr = equalPos + 1; // skip past = 22120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 22220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // extract the attribute value length 22320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync int lenPos = attrStr.indexOf(' ', ptr); 22420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync int attrLen = Integer.parseInt(attrStr.substring(ptr, lenPos)); 22520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync ptr = lenPos + 1; // skip pas the space 22620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 22720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync // extract the attribute value 22820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String attrValue = attrStr.substring(ptr, ptr + attrLen); 22920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync ptr += attrLen; 23020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 23120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync if (attrName.equals("hasThumbnail")) { 23220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync mHasThumbnail = attrValue.equalsIgnoreCase("true"); 23320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } else { 234700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang mAttributes.put(attrName, attrValue); 23520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 23620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 23720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 23820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 23920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync /** 240700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Save the tag data into the JPEG file. This is expensive because it involves 241700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * copying all the JPG data from one file to another and deleting the old file 242872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * and renaming the other. It's best to use {@link #setAttribute(String,String)} 243872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * to set all attributes to write and make a single call rather than multiple 244872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * calls for each attribute. 24520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */ 246700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public void saveAttributes() throws IOException { 247700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // format of string passed to native C code: 248700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // "attrCnt attr1=valueLen value1attr2=value2Len value2..." 249700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // example: 250700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // "4 attrPtr ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO" 251700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang StringBuilder sb = new StringBuilder(); 252700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang int size = mAttributes.size(); 253700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang if (mAttributes.containsKey("hasThumbnail")) { 254700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang --size; 255700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 256700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang sb.append(size + " "); 257700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang for (Map.Entry<String, String> iter : mAttributes.entrySet()) { 258700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String key = iter.getKey(); 259700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang if (key.equals("hasThumbnail")) { 260700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang // this is a fake attribute not saved as an exif tag 261700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang continue; 262700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 263700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String val = iter.getValue(); 264700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang sb.append(key + "="); 265700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang sb.append(val.length() + " "); 266700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang sb.append(val); 267700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 268700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String s = sb.toString(); 269700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang synchronized (sLock) { 270700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang saveAttributesNative(mFilename, s); 271700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang commitChangesNative(mFilename); 272700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 273700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 274700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang 275700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang /** 276700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Returns true if the JPEG file has a thumbnail. 277700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang */ 278700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public boolean hasThumbnail() { 279700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang return mHasThumbnail; 280700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 281700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang 282700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang /** 283700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Returns the thumbnail inside the JPEG file, or {@code null} if there is no thumbnail. 284872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * The returned data is in JPEG format and can be decoded using 285872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)} 286700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang */ 287700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public byte[] getThumbnail() { 288700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang synchronized (sLock) { 289700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang return getThumbnailNative(mFilename); 290700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 291700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang } 292700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang 293700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang /** 2946398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey * Returns the offset and length of thumbnail inside the JPEG file, or 2956398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey * {@code null} if there is no thumbnail. 2966398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey * 2976398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey * @return two-element array, the offset in the first value, and length in 2986398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey * the second, or {@code null} if no thumbnail was found. 2996398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey * @hide 3006398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey */ 3016398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey public long[] getThumbnailRange() { 3026398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey synchronized (sLock) { 3036398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey return getThumbnailRangeNative(mFilename); 3046398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey } 3056398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey } 3066398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey 3076398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey /** 308872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * Stores the latitude and longitude value in a float array. The first element is 309872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * the latitude, and the second element is the longitude. Returns false if the 310872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * Exif tags are not available. 31120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync */ 312872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang public boolean getLatLong(float output[]) { 313700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String latValue = mAttributes.get(ExifInterface.TAG_GPS_LATITUDE); 314700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String latRef = mAttributes.get(ExifInterface.TAG_GPS_LATITUDE_REF); 315700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String lngValue = mAttributes.get(ExifInterface.TAG_GPS_LONGITUDE); 316700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String lngRef = mAttributes.get(ExifInterface.TAG_GPS_LONGITUDE_REF); 31720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 318872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang if (latValue != null && latRef != null && lngValue != null && lngRef != null) { 319a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe try { 320a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe output[0] = convertRationalLatLonToFloat(latValue, latRef); 321a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe output[1] = convertRationalLatLonToFloat(lngValue, lngRef); 322a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe return true; 323a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe } catch (IllegalArgumentException e) { 324a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe // if values are not parseable 325a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe } 32620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 327a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe 328a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe return false; 32920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 33020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 331700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang /** 332c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * Return the altitude in meters. If the exif tag does not exist, return 333c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * <var>defaultValue</var>. 334c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * 335c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li * @param defaultValue the value to return if the tag is not available. 336c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li */ 337c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li public double getAltitude(double defaultValue) { 338c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1); 339c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1); 340c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li 341c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li if (altitude >= 0 && ref >= 0) { 342c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li return (double) (altitude * ((ref == 1) ? -1 : 1)); 343c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li } else { 344c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li return defaultValue; 345c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li } 346c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li } 347c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li 348c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li /** 3493a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen * Returns number of milliseconds since Jan. 1, 1970, midnight. 350700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang * Returns -1 if the date time information if not available. 351872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang * @hide 352700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang */ 353700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang public long getDateTime() { 354700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang String dateTimeString = mAttributes.get(TAG_DATETIME); 355099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang if (dateTimeString == null) return -1; 356099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang 357099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang ParsePosition pos = new ParsePosition(0); 358099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang try { 3593a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen Date datetime = sFormatter.parse(dateTimeString, pos); 3603a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen if (datetime == null) return -1; 3613a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen return datetime.getTime(); 3623a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen } catch (IllegalArgumentException ex) { 3633a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen return -1; 3643a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen } 3653a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen } 3663a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen 3673a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen /** 3683a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen * Returns number of milliseconds since Jan. 1, 1970, midnight UTC. 3693a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen * Returns -1 if the date time information if not available. 3703a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen * @hide 3713a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen */ 3723a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen public long getGpsDateTime() { 3733a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen String date = mAttributes.get(TAG_GPS_DATESTAMP); 3743a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen String time = mAttributes.get(TAG_GPS_TIMESTAMP); 3753a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen if (date == null || time == null) return -1; 3763a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen 3773a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen String dateTimeString = date + ' ' + time; 3783a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen if (dateTimeString == null) return -1; 3793a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen 3803a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen ParsePosition pos = new ParsePosition(0); 3813a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen try { 3823a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen Date datetime = sFormatter.parse(dateTimeString, pos); 3833a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen if (datetime == null) return -1; 3843a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen return datetime.getTime(); 385099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang } catch (IllegalArgumentException ex) { 386099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang return -1; 387099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang } 388099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang } 389099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang 390700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang private static float convertRationalLatLonToFloat( 39120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String rationalString, String ref) { 39220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync try { 39320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String [] parts = rationalString.split(","); 39420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 39520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String [] pair; 39620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync pair = parts[0].split("/"); 397a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe double degrees = Double.parseDouble(pair[0].trim()) 398a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe / Double.parseDouble(pair[1].trim()); 39920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 40020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync pair = parts[1].split("/"); 401a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe double minutes = Double.parseDouble(pair[0].trim()) 402a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe / Double.parseDouble(pair[1].trim()); 40320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 40420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync pair = parts[2].split("/"); 405c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li double seconds = Double.parseDouble(pair[0].trim()) 406c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li / Double.parseDouble(pair[1].trim()); 40720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 408c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li double result = degrees + (minutes / 60.0) + (seconds / 3600.0); 40920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync if ((ref.equals("S") || ref.equals("W"))) { 410c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li return (float) -result; 41120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 412c109190b6984da6cba4cea44a0304b6da12d77e6Wu-cheng Li return (float) result; 413a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe } catch (NumberFormatException e) { 414a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe // Some of the nubmers are not valid 415a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe throw new IllegalArgumentException(); 416a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe } catch (ArrayIndexOutOfBoundsException e) { 417a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe // Some of the rational does not follow the correct format 418a113a075ca9afa14361806ea592c8f078b1636c5Oscar Rydhe throw new IllegalArgumentException(); 41920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 42020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync } 42120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 42220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync private native boolean appendThumbnailNative(String fileName, 42320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String thumbnailFileName); 42420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 42520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync private native void saveAttributesNative(String fileName, 42620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync String compressedAttributes); 42720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 42820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync private native String getAttributesNative(String fileName); 42920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 43020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync private native void commitChangesNative(String fileName); 43120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync 43220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync private native byte[] getThumbnailNative(String fileName); 4336398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey 4346398343e83b3fd11dd6536cf6f390a52c1e19d2eJeff Sharkey private native long[] getThumbnailRangeNative(String fileName); 43520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync} 436