Loading api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -23461,6 +23461,7 @@ package android.media { } public class ExifInterface { ctor public ExifInterface(java.io.File) throws java.io.IOException; ctor public ExifInterface(java.lang.String) throws java.io.IOException; ctor public ExifInterface(java.io.FileDescriptor) throws java.io.IOException; ctor public ExifInterface(java.io.InputStream) throws java.io.IOException; Loading @@ -23468,11 +23469,13 @@ package android.media { method public java.lang.String getAttribute(java.lang.String); method public double getAttributeDouble(java.lang.String, double); method public int getAttributeInt(java.lang.String, int); method public long[] getAttributeRange(java.lang.String); method public boolean getLatLong(float[]); method public byte[] getThumbnail(); method public android.graphics.Bitmap getThumbnailBitmap(); method public byte[] getThumbnailBytes(); method public long[] getThumbnailRange(); method public boolean hasAttribute(java.lang.String); method public boolean hasThumbnail(); method public boolean isThumbnailCompressed(); method public void saveAttributes() throws java.io.IOException; core/java/com/android/internal/util/XmlUtils.java +8 −6 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ public class XmlUtils { public static final int convertValueToList(CharSequence value, String[] options, int defaultValue) { if (null != value) { if (!TextUtils.isEmpty(value)) { for (int i = 0; i < options.length; i++) { if (value.equals(options[i])) return i; Loading @@ -79,8 +79,9 @@ public class XmlUtils { { boolean result = false; if (null == value) if (TextUtils.isEmpty(value)) { return defaultValue; } if (value.equals("1") || value.equals("true") Loading @@ -94,8 +95,9 @@ public class XmlUtils { public static final int convertValueToInt(CharSequence charSeq, int defaultValue) { if (null == charSeq) if (TextUtils.isEmpty(charSeq)) { return defaultValue; } String nm = charSeq.toString(); Loading Loading @@ -138,7 +140,7 @@ public class XmlUtils { } public static int convertValueToUnsignedInt(String value, int defaultValue) { if (null == value) { if (TextUtils.isEmpty(value)) { return defaultValue; } Loading Loading @@ -1674,7 +1676,7 @@ public class XmlUtils { public static boolean readBooleanAttribute(XmlPullParser in, String name, boolean defaultValue) { final String value = in.getAttributeValue(null, name); if (value == null) { if (TextUtils.isEmpty(value)) { return defaultValue; } else { return Boolean.parseBoolean(value); Loading Loading @@ -1711,7 +1713,7 @@ public class XmlUtils { public static byte[] readByteArrayAttribute(XmlPullParser in, String name) { final String value = in.getAttributeValue(null, name); if (value != null) { if (!TextUtils.isEmpty(value)) { return Base64.decode(value, Base64.DEFAULT); } else { return null; Loading media/java/android/media/ExifInterface.java +126 −34 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package android.media; import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.graphics.Bitmap; Loading @@ -26,12 +29,14 @@ import android.system.Os; import android.system.OsConstants; import android.util.Log; import android.util.Pair; import android.annotation.IntDef; import libcore.io.IoUtils; import libcore.io.Streams; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; Loading @@ -42,14 +47,14 @@ import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.LinkedList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; Loading @@ -58,11 +63,6 @@ import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import libcore.io.IoUtils; import libcore.io.Streams; /** * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file. Loading Loading @@ -583,11 +583,19 @@ public class ExifInterface { private static class ExifAttribute { public final int format; public final int numberOfComponents; public final long bytesOffset; public final byte[] bytes; public static final long BYTES_OFFSET_UNKNOWN = -1; private ExifAttribute(int format, int numberOfComponents, byte[] bytes) { this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes); } private ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) { this.format = format; this.numberOfComponents = numberOfComponents; this.bytesOffset = bytesOffset; this.bytes = bytes; } Loading Loading @@ -1318,6 +1326,7 @@ public class ExifInterface { private int mOrfThumbnailLength; private int mRw2JpgFromRawOffset; private boolean mIsSupportedFile; private boolean mModified; // Pattern to check non zero timestamp private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*"); Loading @@ -1328,7 +1337,14 @@ public class ExifInterface { /** * Reads Exif tags from the specified image file. */ public ExifInterface(String filename) throws IOException { public ExifInterface(@NonNull File file) throws IOException { this(file.getAbsolutePath()); } /** * Reads Exif tags from the specified image file. */ public ExifInterface(@NonNull String filename) throws IOException { if (filename == null) { throw new IllegalArgumentException("filename cannot be null"); } Loading @@ -1354,7 +1370,7 @@ public class ExifInterface { * for writable and seekable file descriptors only. This constructor will not rewind the offset * of the given file descriptor. Developers should close the file descriptor after use. */ public ExifInterface(FileDescriptor fileDescriptor) throws IOException { public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException { if (fileDescriptor == null) { throw new IllegalArgumentException("fileDescriptor cannot be null"); } Loading Loading @@ -1388,7 +1404,7 @@ public class ExifInterface { * for input streams. The given input stream will proceed its current position. Developers * should close the input stream after use. */ public ExifInterface(InputStream inputStream) throws IOException { public ExifInterface(@NonNull InputStream inputStream) throws IOException { if (inputStream == null) { throw new IllegalArgumentException("inputStream cannot be null"); } Loading @@ -1414,7 +1430,7 @@ public class ExifInterface { * * @param tag the name of the tag. */ private ExifAttribute getExifAttribute(String tag) { private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) { // Retrieves all tag groups. The value from primary image tag group has a higher priority // than the value from the thumbnail tag group if there are more than one candidates. for (int i = 0; i < EXIF_TAGS.length; ++i) { Loading @@ -1432,7 +1448,7 @@ public class ExifInterface { * * @param tag the name of the tag. */ public String getAttribute(String tag) { public @Nullable String getAttribute(@NonNull String tag) { ExifAttribute attribute = getExifAttribute(tag); if (attribute != null) { if (!sTagSetForCompatibility.contains(tag)) { Loading Loading @@ -1470,7 +1486,7 @@ public class ExifInterface { * @param tag the name of the tag. * @param defaultValue the value to return if the tag is not available. */ public int getAttributeInt(String tag, int defaultValue) { public int getAttributeInt(@NonNull String tag, int defaultValue) { ExifAttribute exifAttribute = getExifAttribute(tag); if (exifAttribute == null) { return defaultValue; Loading @@ -1491,7 +1507,7 @@ public class ExifInterface { * @param tag the name of the tag. * @param defaultValue the value to return if the tag is not available. */ public double getAttributeDouble(String tag, double defaultValue) { public double getAttributeDouble(@NonNull String tag, double defaultValue) { ExifAttribute exifAttribute = getExifAttribute(tag); if (exifAttribute == null) { return defaultValue; Loading @@ -1510,7 +1526,7 @@ public class ExifInterface { * @param tag the name of the tag. * @param value the value of the tag. */ public void setAttribute(String tag, String value) { public void setAttribute(@NonNull String tag, @Nullable String value) { // Convert the given value to rational values for backwards compatibility. if (value != null && sTagSetForCompatibility.contains(tag)) { if (tag.equals(TAG_GPS_TIMESTAMP)) { Loading Loading @@ -1772,12 +1788,18 @@ public class ExifInterface { } /** * Save the tag data into the original image file. This is expensive because it involves * copying all the data from one file to another and deleting the old file and renaming the * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write * and make a single call rather than multiple calls for each attribute. * Save the tag data into the original image file. This is expensive because * it involves copying all the data from one file to another and deleting * the old file and renaming the other. It's best to use * {@link #setAttribute(String,String)} to set all attributes to write and * make a single call rather than multiple calls for each attribute. * <p> * This method is only supported for JPEG files. * <p class="note"> * Note: after calling this method, any attempts to obtain range information * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()} * will throw {@link IllegalStateException}, since the offsets may have * changed in the newly written file. * </p> */ public void saveAttributes() throws IOException { Loading @@ -1789,6 +1811,10 @@ public class ExifInterface { "ExifInterface does not support saving attributes for the current input."); } // Remember the fact that we've changed the file on disk from what was // originally parsed, meaning we can't answer range questions mModified = true; // Keep the thumbnail in memory mThumbnailBytes = getThumbnail(); Loading Loading @@ -1848,6 +1874,15 @@ public class ExifInterface { return mHasThumbnail; } /** * Returns true if the image file has the given attribute defined. * * @param tag the name of the tag. */ public boolean hasAttribute(String tag) { return (getExifAttribute(tag) != null); } /** * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no * JPEG compressed thumbnail. Loading Loading @@ -1968,17 +2003,45 @@ public class ExifInterface { * * @return two-element array, the offset in the first value, and length in * the second, or {@code null} if no thumbnail was found. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. */ public long[] getThumbnailRange() { if (!mHasThumbnail) { public @Nullable long[] getThumbnailRange() { if (mModified) { throw new IllegalStateException( "The underlying file has been modified since being parsed"); } if (mHasThumbnail) { return new long[] { mThumbnailOffset, mThumbnailLength }; } else { return null; } } long[] range = new long[2]; range[0] = mThumbnailOffset; range[1] = mThumbnailLength; /** * Returns the offset and length of the requested tag inside the image file, * or {@code null} if the tag is not contained. * * @return two-element array, the offset in the first value, and length in * the second, or {@code null} if no tag was found. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. */ public @Nullable long[] getAttributeRange(@NonNull String tag) { if (mModified) { throw new IllegalStateException( "The underlying file has been modified since being parsed"); } return range; final ExifAttribute attribute = getExifAttribute(tag); if (attribute != null) { return new long[] { attribute.bytesOffset, attribute.bytes.length }; } else { return null; } } /** Loading Loading @@ -2023,13 +2086,41 @@ public class ExifInterface { } /** * Returns number of milliseconds since Jan. 1, 1970, midnight local time. * Returns -1 if the date time information if not available. * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid. * * @hide */ @UnsupportedAppUsage public long getDateTime() { String dateTimeString = getAttribute(TAG_DATETIME); public @CurrentTimeMillisLong long getDateTime() { return parseDateTime(getAttribute(TAG_DATETIME), getAttribute(TAG_SUBSEC_TIME)); } /** * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or * invalid. * * @hide */ public @CurrentTimeMillisLong long getDateTimeDigitized() { return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED), getAttribute(TAG_SUBSEC_TIME_DIGITIZED)); } /** * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or * invalid. * * @hide */ @UnsupportedAppUsage public @CurrentTimeMillisLong long getDateTimeOriginal() { return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL), getAttribute(TAG_SUBSEC_TIME_ORIGINAL)); } private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString, @Nullable String subSecs) { if (dateTimeString == null || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1; Loading @@ -2041,7 +2132,6 @@ public class ExifInterface { if (datetime == null) return -1; long msecs = datetime.getTime(); String subSecs = getAttribute(TAG_SUBSEC_TIME); if (subSecs != null) { try { long sub = Long.parseLong(subSecs); Loading Loading @@ -3125,9 +3215,11 @@ public class ExifInterface { continue; } byte[] bytes = new byte[(int) byteCount]; final int bytesOffset = dataInputStream.peek(); final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytesOffset, bytes); mAttributes[ifdType].put(tag.name, attribute); // DNG files have a DNG Version tag specifying the version of specifications that the Loading Loading
api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -23461,6 +23461,7 @@ package android.media { } public class ExifInterface { ctor public ExifInterface(java.io.File) throws java.io.IOException; ctor public ExifInterface(java.lang.String) throws java.io.IOException; ctor public ExifInterface(java.io.FileDescriptor) throws java.io.IOException; ctor public ExifInterface(java.io.InputStream) throws java.io.IOException; Loading @@ -23468,11 +23469,13 @@ package android.media { method public java.lang.String getAttribute(java.lang.String); method public double getAttributeDouble(java.lang.String, double); method public int getAttributeInt(java.lang.String, int); method public long[] getAttributeRange(java.lang.String); method public boolean getLatLong(float[]); method public byte[] getThumbnail(); method public android.graphics.Bitmap getThumbnailBitmap(); method public byte[] getThumbnailBytes(); method public long[] getThumbnailRange(); method public boolean hasAttribute(java.lang.String); method public boolean hasThumbnail(); method public boolean isThumbnailCompressed(); method public void saveAttributes() throws java.io.IOException;
core/java/com/android/internal/util/XmlUtils.java +8 −6 Original line number Diff line number Diff line Loading @@ -63,7 +63,7 @@ public class XmlUtils { public static final int convertValueToList(CharSequence value, String[] options, int defaultValue) { if (null != value) { if (!TextUtils.isEmpty(value)) { for (int i = 0; i < options.length; i++) { if (value.equals(options[i])) return i; Loading @@ -79,8 +79,9 @@ public class XmlUtils { { boolean result = false; if (null == value) if (TextUtils.isEmpty(value)) { return defaultValue; } if (value.equals("1") || value.equals("true") Loading @@ -94,8 +95,9 @@ public class XmlUtils { public static final int convertValueToInt(CharSequence charSeq, int defaultValue) { if (null == charSeq) if (TextUtils.isEmpty(charSeq)) { return defaultValue; } String nm = charSeq.toString(); Loading Loading @@ -138,7 +140,7 @@ public class XmlUtils { } public static int convertValueToUnsignedInt(String value, int defaultValue) { if (null == value) { if (TextUtils.isEmpty(value)) { return defaultValue; } Loading Loading @@ -1674,7 +1676,7 @@ public class XmlUtils { public static boolean readBooleanAttribute(XmlPullParser in, String name, boolean defaultValue) { final String value = in.getAttributeValue(null, name); if (value == null) { if (TextUtils.isEmpty(value)) { return defaultValue; } else { return Boolean.parseBoolean(value); Loading Loading @@ -1711,7 +1713,7 @@ public class XmlUtils { public static byte[] readByteArrayAttribute(XmlPullParser in, String name) { final String value = in.getAttributeValue(null, name); if (value != null) { if (!TextUtils.isEmpty(value)) { return Base64.decode(value, Base64.DEFAULT); } else { return null; Loading
media/java/android/media/ExifInterface.java +126 −34 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package android.media; import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.graphics.Bitmap; Loading @@ -26,12 +29,14 @@ import android.system.Os; import android.system.OsConstants; import android.util.Log; import android.util.Pair; import android.annotation.IntDef; import libcore.io.IoUtils; import libcore.io.Streams; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; Loading @@ -42,14 +47,14 @@ import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.LinkedList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; Loading @@ -58,11 +63,6 @@ import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import libcore.io.IoUtils; import libcore.io.Streams; /** * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file. Loading Loading @@ -583,11 +583,19 @@ public class ExifInterface { private static class ExifAttribute { public final int format; public final int numberOfComponents; public final long bytesOffset; public final byte[] bytes; public static final long BYTES_OFFSET_UNKNOWN = -1; private ExifAttribute(int format, int numberOfComponents, byte[] bytes) { this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes); } private ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) { this.format = format; this.numberOfComponents = numberOfComponents; this.bytesOffset = bytesOffset; this.bytes = bytes; } Loading Loading @@ -1318,6 +1326,7 @@ public class ExifInterface { private int mOrfThumbnailLength; private int mRw2JpgFromRawOffset; private boolean mIsSupportedFile; private boolean mModified; // Pattern to check non zero timestamp private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*"); Loading @@ -1328,7 +1337,14 @@ public class ExifInterface { /** * Reads Exif tags from the specified image file. */ public ExifInterface(String filename) throws IOException { public ExifInterface(@NonNull File file) throws IOException { this(file.getAbsolutePath()); } /** * Reads Exif tags from the specified image file. */ public ExifInterface(@NonNull String filename) throws IOException { if (filename == null) { throw new IllegalArgumentException("filename cannot be null"); } Loading @@ -1354,7 +1370,7 @@ public class ExifInterface { * for writable and seekable file descriptors only. This constructor will not rewind the offset * of the given file descriptor. Developers should close the file descriptor after use. */ public ExifInterface(FileDescriptor fileDescriptor) throws IOException { public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException { if (fileDescriptor == null) { throw new IllegalArgumentException("fileDescriptor cannot be null"); } Loading Loading @@ -1388,7 +1404,7 @@ public class ExifInterface { * for input streams. The given input stream will proceed its current position. Developers * should close the input stream after use. */ public ExifInterface(InputStream inputStream) throws IOException { public ExifInterface(@NonNull InputStream inputStream) throws IOException { if (inputStream == null) { throw new IllegalArgumentException("inputStream cannot be null"); } Loading @@ -1414,7 +1430,7 @@ public class ExifInterface { * * @param tag the name of the tag. */ private ExifAttribute getExifAttribute(String tag) { private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) { // Retrieves all tag groups. The value from primary image tag group has a higher priority // than the value from the thumbnail tag group if there are more than one candidates. for (int i = 0; i < EXIF_TAGS.length; ++i) { Loading @@ -1432,7 +1448,7 @@ public class ExifInterface { * * @param tag the name of the tag. */ public String getAttribute(String tag) { public @Nullable String getAttribute(@NonNull String tag) { ExifAttribute attribute = getExifAttribute(tag); if (attribute != null) { if (!sTagSetForCompatibility.contains(tag)) { Loading Loading @@ -1470,7 +1486,7 @@ public class ExifInterface { * @param tag the name of the tag. * @param defaultValue the value to return if the tag is not available. */ public int getAttributeInt(String tag, int defaultValue) { public int getAttributeInt(@NonNull String tag, int defaultValue) { ExifAttribute exifAttribute = getExifAttribute(tag); if (exifAttribute == null) { return defaultValue; Loading @@ -1491,7 +1507,7 @@ public class ExifInterface { * @param tag the name of the tag. * @param defaultValue the value to return if the tag is not available. */ public double getAttributeDouble(String tag, double defaultValue) { public double getAttributeDouble(@NonNull String tag, double defaultValue) { ExifAttribute exifAttribute = getExifAttribute(tag); if (exifAttribute == null) { return defaultValue; Loading @@ -1510,7 +1526,7 @@ public class ExifInterface { * @param tag the name of the tag. * @param value the value of the tag. */ public void setAttribute(String tag, String value) { public void setAttribute(@NonNull String tag, @Nullable String value) { // Convert the given value to rational values for backwards compatibility. if (value != null && sTagSetForCompatibility.contains(tag)) { if (tag.equals(TAG_GPS_TIMESTAMP)) { Loading Loading @@ -1772,12 +1788,18 @@ public class ExifInterface { } /** * Save the tag data into the original image file. This is expensive because it involves * copying all the data from one file to another and deleting the old file and renaming the * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write * and make a single call rather than multiple calls for each attribute. * Save the tag data into the original image file. This is expensive because * it involves copying all the data from one file to another and deleting * the old file and renaming the other. It's best to use * {@link #setAttribute(String,String)} to set all attributes to write and * make a single call rather than multiple calls for each attribute. * <p> * This method is only supported for JPEG files. * <p class="note"> * Note: after calling this method, any attempts to obtain range information * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()} * will throw {@link IllegalStateException}, since the offsets may have * changed in the newly written file. * </p> */ public void saveAttributes() throws IOException { Loading @@ -1789,6 +1811,10 @@ public class ExifInterface { "ExifInterface does not support saving attributes for the current input."); } // Remember the fact that we've changed the file on disk from what was // originally parsed, meaning we can't answer range questions mModified = true; // Keep the thumbnail in memory mThumbnailBytes = getThumbnail(); Loading Loading @@ -1848,6 +1874,15 @@ public class ExifInterface { return mHasThumbnail; } /** * Returns true if the image file has the given attribute defined. * * @param tag the name of the tag. */ public boolean hasAttribute(String tag) { return (getExifAttribute(tag) != null); } /** * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no * JPEG compressed thumbnail. Loading Loading @@ -1968,17 +2003,45 @@ public class ExifInterface { * * @return two-element array, the offset in the first value, and length in * the second, or {@code null} if no thumbnail was found. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. */ public long[] getThumbnailRange() { if (!mHasThumbnail) { public @Nullable long[] getThumbnailRange() { if (mModified) { throw new IllegalStateException( "The underlying file has been modified since being parsed"); } if (mHasThumbnail) { return new long[] { mThumbnailOffset, mThumbnailLength }; } else { return null; } } long[] range = new long[2]; range[0] = mThumbnailOffset; range[1] = mThumbnailLength; /** * Returns the offset and length of the requested tag inside the image file, * or {@code null} if the tag is not contained. * * @return two-element array, the offset in the first value, and length in * the second, or {@code null} if no tag was found. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. */ public @Nullable long[] getAttributeRange(@NonNull String tag) { if (mModified) { throw new IllegalStateException( "The underlying file has been modified since being parsed"); } return range; final ExifAttribute attribute = getExifAttribute(tag); if (attribute != null) { return new long[] { attribute.bytesOffset, attribute.bytes.length }; } else { return null; } } /** Loading Loading @@ -2023,13 +2086,41 @@ public class ExifInterface { } /** * Returns number of milliseconds since Jan. 1, 1970, midnight local time. * Returns -1 if the date time information if not available. * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid. * * @hide */ @UnsupportedAppUsage public long getDateTime() { String dateTimeString = getAttribute(TAG_DATETIME); public @CurrentTimeMillisLong long getDateTime() { return parseDateTime(getAttribute(TAG_DATETIME), getAttribute(TAG_SUBSEC_TIME)); } /** * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or * invalid. * * @hide */ public @CurrentTimeMillisLong long getDateTimeDigitized() { return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED), getAttribute(TAG_SUBSEC_TIME_DIGITIZED)); } /** * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or * invalid. * * @hide */ @UnsupportedAppUsage public @CurrentTimeMillisLong long getDateTimeOriginal() { return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL), getAttribute(TAG_SUBSEC_TIME_ORIGINAL)); } private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString, @Nullable String subSecs) { if (dateTimeString == null || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1; Loading @@ -2041,7 +2132,6 @@ public class ExifInterface { if (datetime == null) return -1; long msecs = datetime.getTime(); String subSecs = getAttribute(TAG_SUBSEC_TIME); if (subSecs != null) { try { long sub = Long.parseLong(subSecs); Loading Loading @@ -3125,9 +3215,11 @@ public class ExifInterface { continue; } byte[] bytes = new byte[(int) byteCount]; final int bytesOffset = dataInputStream.peek(); final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytesOffset, bytes); mAttributes[ifdType].put(tag.name, attribute); // DNG files have a DNG Version tag specifying the version of specifications that the Loading