1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.cts; 18 19 import android.content.res.TypedArray; 20 import android.graphics.Bitmap; 21 import android.graphics.BitmapFactory; 22 import android.media.ExifInterface; 23 import android.os.Environment; 24 import android.os.FileUtils; 25 import android.platform.test.annotations.AppModeFull; 26 import android.test.AndroidTestCase; 27 import android.util.Log; 28 import android.system.ErrnoException; 29 import android.system.Os; 30 import android.system.OsConstants; 31 32 import java.io.BufferedInputStream; 33 import java.io.ByteArrayInputStream; 34 import java.io.File; 35 import java.io.FileDescriptor; 36 import java.io.FileInputStream; 37 import java.io.FileOutputStream; 38 import java.io.FileNotFoundException; 39 import java.io.InputStream; 40 import java.io.IOException; 41 import java.lang.reflect.Type; 42 43 import libcore.io.IoUtils; 44 import libcore.io.Streams; 45 46 @AppModeFull(reason = "Instant apps cannot access the SD card") 47 public class ExifInterfaceTest extends AndroidTestCase { 48 private static final String TAG = ExifInterface.class.getSimpleName(); 49 private static final boolean VERBOSE = false; // lots of logging 50 51 private static final double DIFFERENCE_TOLERANCE = .001; 52 53 private static final String EXTERNAL_BASE_DIRECTORY = "/test/images/"; 54 55 // This base directory is needed for the files listed below. 56 // These files will be available for download in Android O release. 57 // Link: https://source.android.com/compatibility/cts/downloads.html#cts-media-files 58 private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg"; 59 private static final String EXIF_BYTE_ORDER_MM_JPEG = "image_exif_byte_order_mm.jpg"; 60 private static final String LG_G4_ISO_800_DNG = "lg_g4_iso_800.dng"; 61 private static final String SONY_RX_100_ARW = "sony_rx_100.arw"; 62 private static final String CANON_G7X_CR2 = "canon_g7x.cr2"; 63 private static final String FUJI_X20_RAF = "fuji_x20.raf"; 64 private static final String NIKON_1AW1_NEF = "nikon_1aw1.nef"; 65 private static final String NIKON_P330_NRW = "nikon_p330.nrw"; 66 private static final String OLYMPUS_E_PL3_ORF = "olympus_e_pl3.orf"; 67 private static final String PANASONIC_GM5_RW2 = "panasonic_gm5.rw2"; 68 private static final String PENTAX_K5_PEF = "pentax_k5.pef"; 69 private static final String SAMSUNG_NX3000_SRW = "samsung_nx3000.srw"; 70 private static final String VOLANTIS_JPEG = "volantis.jpg"; 71 72 private static final String[] EXIF_TAGS = { 73 ExifInterface.TAG_MAKE, 74 ExifInterface.TAG_MODEL, 75 ExifInterface.TAG_F_NUMBER, 76 ExifInterface.TAG_DATETIME_ORIGINAL, 77 ExifInterface.TAG_EXPOSURE_TIME, 78 ExifInterface.TAG_FLASH, 79 ExifInterface.TAG_FOCAL_LENGTH, 80 ExifInterface.TAG_GPS_ALTITUDE, 81 ExifInterface.TAG_GPS_ALTITUDE_REF, 82 ExifInterface.TAG_GPS_DATESTAMP, 83 ExifInterface.TAG_GPS_LATITUDE, 84 ExifInterface.TAG_GPS_LATITUDE_REF, 85 ExifInterface.TAG_GPS_LONGITUDE, 86 ExifInterface.TAG_GPS_LONGITUDE_REF, 87 ExifInterface.TAG_GPS_PROCESSING_METHOD, 88 ExifInterface.TAG_GPS_TIMESTAMP, 89 ExifInterface.TAG_IMAGE_LENGTH, 90 ExifInterface.TAG_IMAGE_WIDTH, 91 ExifInterface.TAG_ISO_SPEED_RATINGS, 92 ExifInterface.TAG_ORIENTATION, 93 ExifInterface.TAG_WHITE_BALANCE 94 }; 95 96 private static class ExpectedValue { 97 // Thumbnail information. 98 public final boolean hasThumbnail; 99 public final int thumbnailWidth; 100 public final int thumbnailHeight; 101 public final boolean isThumbnailCompressed; 102 103 // GPS information. 104 public final boolean hasLatLong; 105 public final float latitude; 106 public final float longitude; 107 public final float altitude; 108 109 // Values. 110 public final String make; 111 public final String model; 112 public final float aperture; 113 public final String dateTimeOriginal; 114 public final float exposureTime; 115 public final float flash; 116 public final String focalLength; 117 public final String gpsAltitude; 118 public final String gpsAltitudeRef; 119 public final String gpsDatestamp; 120 public final String gpsLatitude; 121 public final String gpsLatitudeRef; 122 public final String gpsLongitude; 123 public final String gpsLongitudeRef; 124 public final String gpsProcessingMethod; 125 public final String gpsTimestamp; 126 public final int imageLength; 127 public final int imageWidth; 128 public final String iso; 129 public final int orientation; 130 public final int whiteBalance; 131 getString(TypedArray typedArray, int index)132 private static String getString(TypedArray typedArray, int index) { 133 String stringValue = typedArray.getString(index); 134 if (stringValue == null || stringValue.equals("")) { 135 return null; 136 } 137 return stringValue.trim(); 138 } 139 ExpectedValue(TypedArray typedArray)140 public ExpectedValue(TypedArray typedArray) { 141 int index = 0; 142 143 // Reads thumbnail information. 144 hasThumbnail = typedArray.getBoolean(index++, false); 145 thumbnailWidth = typedArray.getInt(index++, 0); 146 thumbnailHeight = typedArray.getInt(index++, 0); 147 isThumbnailCompressed = typedArray.getBoolean(index++, false); 148 149 // Reads GPS information. 150 hasLatLong = typedArray.getBoolean(index++, false); 151 latitude = typedArray.getFloat(index++, 0f); 152 longitude = typedArray.getFloat(index++, 0f); 153 altitude = typedArray.getFloat(index++, 0f); 154 155 // Reads values. 156 make = getString(typedArray, index++); 157 model = getString(typedArray, index++); 158 aperture = typedArray.getFloat(index++, 0f); 159 dateTimeOriginal = getString(typedArray, index++); 160 exposureTime = typedArray.getFloat(index++, 0f); 161 flash = typedArray.getFloat(index++, 0f); 162 focalLength = getString(typedArray, index++); 163 gpsAltitude = getString(typedArray, index++); 164 gpsAltitudeRef = getString(typedArray, index++); 165 gpsDatestamp = getString(typedArray, index++); 166 gpsLatitude = getString(typedArray, index++); 167 gpsLatitudeRef = getString(typedArray, index++); 168 gpsLongitude = getString(typedArray, index++); 169 gpsLongitudeRef = getString(typedArray, index++); 170 gpsProcessingMethod = getString(typedArray, index++); 171 gpsTimestamp = getString(typedArray, index++); 172 imageLength = typedArray.getInt(index++, 0); 173 imageWidth = typedArray.getInt(index++, 0); 174 iso = getString(typedArray, index++); 175 orientation = typedArray.getInt(index++, 0); 176 whiteBalance = typedArray.getInt(index++, 0); 177 178 typedArray.recycle(); 179 } 180 } 181 printExifTagsAndValues(String fileName, ExifInterface exifInterface)182 private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) { 183 // Prints thumbnail information. 184 if (exifInterface.hasThumbnail()) { 185 byte[] thumbnailBytes = exifInterface.getThumbnailBytes(); 186 if (thumbnailBytes != null) { 187 Log.v(TAG, fileName + " Thumbnail size = " + thumbnailBytes.length); 188 Bitmap bitmap = exifInterface.getThumbnailBitmap(); 189 if (bitmap == null) { 190 Log.e(TAG, fileName + " Corrupted thumbnail!"); 191 } else { 192 Log.v(TAG, fileName + " Thumbnail size: " + bitmap.getWidth() + ", " 193 + bitmap.getHeight()); 194 } 195 } else { 196 Log.e(TAG, fileName + " Unexpected result: No thumbnails were found. " 197 + "A thumbnail is expected."); 198 } 199 } else { 200 if (exifInterface.getThumbnailBytes() != null) { 201 Log.e(TAG, fileName + " Unexpected result: A thumbnail was found. " 202 + "No thumbnail is expected."); 203 } else { 204 Log.v(TAG, fileName + " No thumbnail"); 205 } 206 } 207 208 // Prints GPS information. 209 Log.v(TAG, fileName + " Altitude = " + exifInterface.getAltitude(.0)); 210 211 float[] latLong = new float[2]; 212 if (exifInterface.getLatLong(latLong)) { 213 Log.v(TAG, fileName + " Latitude = " + latLong[0]); 214 Log.v(TAG, fileName + " Longitude = " + latLong[1]); 215 } else { 216 Log.v(TAG, fileName + " No latlong data"); 217 } 218 219 // Prints values. 220 for (String tagKey : EXIF_TAGS) { 221 String tagValue = exifInterface.getAttribute(tagKey); 222 Log.v(TAG, fileName + " Key{" + tagKey + "} = '" + tagValue + "'"); 223 } 224 } 225 assertIntTag(ExifInterface exifInterface, String tag, int expectedValue)226 private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) { 227 int intValue = exifInterface.getAttributeInt(tag, 0); 228 assertEquals(expectedValue, intValue); 229 } 230 assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue)231 private void assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue) { 232 double doubleValue = exifInterface.getAttributeDouble(tag, 0.0); 233 assertEquals(expectedValue, doubleValue, DIFFERENCE_TOLERANCE); 234 } 235 assertStringTag(ExifInterface exifInterface, String tag, String expectedValue)236 private void assertStringTag(ExifInterface exifInterface, String tag, String expectedValue) { 237 String stringValue = exifInterface.getAttribute(tag); 238 if (stringValue != null) { 239 stringValue = stringValue.trim(); 240 } 241 stringValue = (stringValue == "") ? null : stringValue; 242 243 assertEquals(expectedValue, stringValue); 244 } 245 compareWithExpectedValue(ExifInterface exifInterface, ExpectedValue expectedValue, String verboseTag)246 private void compareWithExpectedValue(ExifInterface exifInterface, 247 ExpectedValue expectedValue, String verboseTag) { 248 if (VERBOSE) { 249 printExifTagsAndValues(verboseTag, exifInterface); 250 } 251 // Checks a thumbnail image. 252 assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail()); 253 if (expectedValue.hasThumbnail) { 254 byte[] thumbnailBytes = exifInterface.getThumbnailBytes(); 255 assertNotNull(thumbnailBytes); 256 Bitmap thumbnailBitmap = exifInterface.getThumbnailBitmap(); 257 assertNotNull(thumbnailBitmap); 258 assertEquals(expectedValue.thumbnailWidth, thumbnailBitmap.getWidth()); 259 assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight()); 260 assertEquals(expectedValue.isThumbnailCompressed, 261 exifInterface.isThumbnailCompressed()); 262 } else { 263 assertNull(exifInterface.getThumbnail()); 264 } 265 266 // Checks GPS information. 267 float[] latLong = new float[2]; 268 assertEquals(expectedValue.hasLatLong, exifInterface.getLatLong(latLong)); 269 if (expectedValue.hasLatLong) { 270 assertEquals(expectedValue.latitude, latLong[0], DIFFERENCE_TOLERANCE); 271 assertEquals(expectedValue.longitude, latLong[1], DIFFERENCE_TOLERANCE); 272 } 273 assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE); 274 275 // Checks values. 276 assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make); 277 assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model); 278 assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture); 279 assertStringTag(exifInterface, ExifInterface.TAG_DATETIME_ORIGINAL, 280 expectedValue.dateTimeOriginal); 281 assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime); 282 assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash); 283 assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength); 284 assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude); 285 assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF, 286 expectedValue.gpsAltitudeRef); 287 assertStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, expectedValue.gpsDatestamp); 288 assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude); 289 assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF, 290 expectedValue.gpsLatitudeRef); 291 assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, expectedValue.gpsLongitude); 292 assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF, 293 expectedValue.gpsLongitudeRef); 294 assertStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD, 295 expectedValue.gpsProcessingMethod); 296 assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp); 297 assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength); 298 assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth); 299 assertStringTag(exifInterface, ExifInterface.TAG_ISO_SPEED_RATINGS, expectedValue.iso); 300 assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation); 301 assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance); 302 } 303 testExifInterfaceCommon(String fileName, ExpectedValue expectedValue)304 private void testExifInterfaceCommon(String fileName, ExpectedValue expectedValue) 305 throws IOException { 306 File imageFile = new File(Environment.getExternalStorageDirectory(), fileName); 307 String verboseTag = imageFile.getName(); 308 309 // Creates via path. 310 ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath()); 311 assertNotNull(exifInterface); 312 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 313 314 InputStream in = null; 315 // Creates via InputStream. 316 try { 317 in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath())); 318 exifInterface = new ExifInterface(in); 319 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 320 } finally { 321 IoUtils.closeQuietly(in); 322 } 323 324 // Creates via FileDescriptor. 325 FileDescriptor fd = null; 326 try { 327 fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600); 328 exifInterface = new ExifInterface(fd); 329 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 330 } catch (ErrnoException e) { 331 throw e.rethrowAsIOException(); 332 } finally { 333 IoUtils.closeQuietly(fd); 334 } 335 } 336 testSaveAttributes_withFileName(String fileName, ExpectedValue expectedValue)337 private void testSaveAttributes_withFileName(String fileName, ExpectedValue expectedValue) 338 throws IOException { 339 File imageFile = new File(Environment.getExternalStorageDirectory(), fileName); 340 String verboseTag = imageFile.getName(); 341 342 ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath()); 343 exifInterface.saveAttributes(); 344 exifInterface = new ExifInterface(imageFile.getAbsolutePath()); 345 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 346 347 // Test for modifying one attribute. 348 String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE); 349 exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc"); 350 exifInterface.saveAttributes(); 351 exifInterface = new ExifInterface(imageFile.getAbsolutePath()); 352 assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE)); 353 // Restore the backup value. 354 exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue); 355 exifInterface.saveAttributes(); 356 exifInterface = new ExifInterface(imageFile.getAbsolutePath()); 357 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 358 } 359 testSaveAttributes_withFileDescriptor(String fileName, ExpectedValue expectedValue)360 private void testSaveAttributes_withFileDescriptor(String fileName, ExpectedValue expectedValue) 361 throws IOException { 362 File imageFile = new File(Environment.getExternalStorageDirectory(), fileName); 363 String verboseTag = imageFile.getName(); 364 365 FileDescriptor fd = null; 366 try { 367 fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600); 368 ExifInterface exifInterface = new ExifInterface(fd); 369 exifInterface.saveAttributes(); 370 Os.lseek(fd, 0, OsConstants.SEEK_SET); 371 exifInterface = new ExifInterface(fd); 372 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 373 374 // Test for modifying one attribute. 375 String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE); 376 exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc"); 377 exifInterface.saveAttributes(); 378 Os.lseek(fd, 0, OsConstants.SEEK_SET); 379 exifInterface = new ExifInterface(fd); 380 assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE)); 381 // Restore the backup value. 382 exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue); 383 exifInterface.saveAttributes(); 384 Os.lseek(fd, 0, OsConstants.SEEK_SET); 385 exifInterface = new ExifInterface(fd); 386 compareWithExpectedValue(exifInterface, expectedValue, verboseTag); 387 } catch (ErrnoException e) { 388 throw e.rethrowAsIOException(); 389 } finally { 390 IoUtils.closeQuietly(fd); 391 } 392 } 393 testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)394 private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId) 395 throws IOException { 396 ExpectedValue expectedValue = new ExpectedValue( 397 getContext().getResources().obtainTypedArray(typedArrayResourceId)); 398 399 // Test for reading from external data storage. 400 fileName = EXTERNAL_BASE_DIRECTORY + fileName; 401 testExifInterfaceCommon(fileName, expectedValue); 402 403 // Test for saving attributes. 404 testSaveAttributes_withFileName(fileName, expectedValue); 405 testSaveAttributes_withFileDescriptor(fileName, expectedValue); 406 } 407 testExifInterfaceForRaw(String fileName, int typedArrayResourceId)408 private void testExifInterfaceForRaw(String fileName, int typedArrayResourceId) 409 throws IOException { 410 ExpectedValue expectedValue = new ExpectedValue( 411 getContext().getResources().obtainTypedArray(typedArrayResourceId)); 412 413 // Test for reading from external data storage. 414 fileName = EXTERNAL_BASE_DIRECTORY + fileName; 415 testExifInterfaceCommon(fileName, expectedValue); 416 417 // Since ExifInterface does not support for saving attributes for RAW files, do not test 418 // about writing back in here. 419 } 420 testReadExifDataFromExifByteOrderIIJpeg()421 public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable { 422 testExifInterfaceForJpeg(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg); 423 } 424 testReadExifDataFromExifByteOrderMMJpeg()425 public void testReadExifDataFromExifByteOrderMMJpeg() throws Throwable { 426 testExifInterfaceForJpeg(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_jpg); 427 } 428 testReadExifDataFromLgG4Iso800Dng()429 public void testReadExifDataFromLgG4Iso800Dng() throws Throwable { 430 testExifInterfaceForRaw(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng); 431 } 432 testDoNotFailOnCorruptedImage()433 public void testDoNotFailOnCorruptedImage() throws Throwable { 434 // To keep the compatibility with old versions of ExifInterface, even on a corrupted image, 435 // it shouldn't raise any exceptions except an IOException when unable to open a file. 436 byte[] bytes = new byte[1024]; 437 try { 438 new ExifInterface(new ByteArrayInputStream(bytes)); 439 // Always success 440 } catch (IOException e) { 441 fail("Should not reach here!"); 442 } 443 } 444 testReadExifDataFromVolantisJpg()445 public void testReadExifDataFromVolantisJpg() throws Throwable { 446 // Test if it is possible to parse the volantis generated JPEG smoothly. 447 testExifInterfaceForJpeg(VOLANTIS_JPEG, R.array.volantis_jpg); 448 } 449 testReadExifDataFromSonyRX100Arw()450 public void testReadExifDataFromSonyRX100Arw() throws Throwable { 451 testExifInterfaceForRaw(SONY_RX_100_ARW, R.array.sony_rx_100_arw); 452 } 453 testReadExifDataFromCanonG7XCr2()454 public void testReadExifDataFromCanonG7XCr2() throws Throwable { 455 testExifInterfaceForRaw(CANON_G7X_CR2, R.array.canon_g7x_cr2); 456 } 457 testReadExifDataFromFujiX20Raf()458 public void testReadExifDataFromFujiX20Raf() throws Throwable { 459 testExifInterfaceForRaw(FUJI_X20_RAF, R.array.fuji_x20_raf); 460 } 461 testReadExifDataFromNikon1AW1Nef()462 public void testReadExifDataFromNikon1AW1Nef() throws Throwable { 463 testExifInterfaceForRaw(NIKON_1AW1_NEF, R.array.nikon_1aw1_nef); 464 } 465 testReadExifDataFromNikonP330Nrw()466 public void testReadExifDataFromNikonP330Nrw() throws Throwable { 467 testExifInterfaceForRaw(NIKON_P330_NRW, R.array.nikon_p330_nrw); 468 } 469 testReadExifDataFromOlympusEPL3Orf()470 public void testReadExifDataFromOlympusEPL3Orf() throws Throwable { 471 testExifInterfaceForRaw(OLYMPUS_E_PL3_ORF, R.array.olympus_e_pl3_orf); 472 } 473 testReadExifDataFromPanasonicGM5Rw2()474 public void testReadExifDataFromPanasonicGM5Rw2() throws Throwable { 475 testExifInterfaceForRaw(PANASONIC_GM5_RW2, R.array.panasonic_gm5_rw2); 476 } 477 testReadExifDataFromPentaxK5Pef()478 public void testReadExifDataFromPentaxK5Pef() throws Throwable { 479 testExifInterfaceForRaw(PENTAX_K5_PEF, R.array.pentax_k5_pef); 480 } 481 testReadExifDataFromSamsungNX3000Srw()482 public void testReadExifDataFromSamsungNX3000Srw() throws Throwable { 483 testExifInterfaceForRaw(SAMSUNG_NX3000_SRW, R.array.samsung_nx3000_srw); 484 } 485 testSetDateTime()486 public void testSetDateTime() throws IOException { 487 final String dateTimeValue = "2017:02:02 22:22:22"; 488 final String dateTimeOriginalValue = "2017:01:01 11:11:11"; 489 490 File srcFile = new File(Environment.getExternalStorageDirectory(), 491 EXTERNAL_BASE_DIRECTORY + EXIF_BYTE_ORDER_II_JPEG); 492 File imageFile = new File(Environment.getExternalStorageDirectory(), 493 EXTERNAL_BASE_DIRECTORY + EXIF_BYTE_ORDER_II_JPEG + "_copied"); 494 495 FileUtils.copyFileOrThrow(srcFile, imageFile); 496 ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath()); 497 exif.setAttribute(ExifInterface.TAG_DATETIME, dateTimeValue); 498 exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTimeOriginalValue); 499 exif.saveAttributes(); 500 501 // Check that the DATETIME value is not overwritten by DATETIME_ORIGINAL's value. 502 exif = new ExifInterface(imageFile.getAbsolutePath()); 503 assertEquals(dateTimeValue, exif.getAttribute(ExifInterface.TAG_DATETIME)); 504 assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)); 505 506 // Now remove the DATETIME value. 507 exif.setAttribute(ExifInterface.TAG_DATETIME, null); 508 exif.saveAttributes(); 509 510 // When the DATETIME has no value, then it should be set to DATETIME_ORIGINAL's value. 511 exif = new ExifInterface(imageFile.getAbsolutePath()); 512 assertEquals(dateTimeOriginalValue, exif.getAttribute(ExifInterface.TAG_DATETIME)); 513 imageFile.delete(); 514 } 515 } 516