diff --git a/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java b/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java index 83741f834..9f7a7372a 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java @@ -32,6 +32,7 @@ import com.drew.metadata.Directory; import com.drew.metadata.Metadata; import com.drew.metadata.MetadataException; import com.drew.metadata.Tag; +import com.drew.metadata.exif.ExifDirectoryBase; import com.drew.metadata.exif.ExifIFD0Directory; import com.drew.metadata.exif.ExifReader; import com.drew.metadata.exif.ExifSubIFDDirectory; @@ -53,6 +54,7 @@ import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import deckers.thibault.aves.utils.ExifInterfaceHelper; import deckers.thibault.aves.utils.MetadataHelper; import deckers.thibault.aves.utils.MimeTypes; import deckers.thibault.aves.utils.StorageUtils; @@ -215,14 +217,18 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { private void getAllMetadata(MethodCall call, MethodChannel.Result result) { String mimeType = call.argument("mimeType"); - String uri = call.argument("uri"); + String uriString = call.argument("uri"); + Uri uri = Uri.parse(uriString); Map> metadataMap = new HashMap<>(); - try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uri))) { + boolean foundExif = false; + try (InputStream is = StorageUtils.openInputStream(context, uri)) { Metadata metadata = ImageMetadataReader.readMetadata(is); for (Directory dir : metadata.getDirectories()) { if (dir.getTagCount() > 0) { + foundExif |= dir instanceof ExifDirectoryBase; + // directory name String dirName = dir.getName(); Map dirMap = Objects.requireNonNull(metadataMap.getOrDefault(dirName, new HashMap<>())); @@ -247,24 +253,34 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } } } catch (XMPException e) { - Log.w(LOG_TAG, "failed to read XMP directory for uri=" + uri, e); + Log.w(LOG_TAG, "failed to read XMP directory for uri=" + uriString, e); } } } } } catch (Exception | NoClassDefFoundError e) { - Log.w(LOG_TAG, "failed to get video metadata by ImageMetadataReader for uri=" + uri, e); + Log.w(LOG_TAG, "failed to get metadata by ImageMetadataReader for uri=" + uriString, e); + } + + if (!foundExif) { + // fallback to read EXIF via ExifInterface + try (InputStream is = StorageUtils.openInputStream(context, uri)) { + ExifInterface exif = new ExifInterface(is); + metadataMap.putAll(ExifInterfaceHelper.describeAll(exif)); + } catch (IOException e) { + Log.w(LOG_TAG, "failed to get metadata by ExifInterface for uri=" + uriString, e); + } } if (isVideo(mimeType)) { - Map videoDir = getVideoAllMetadataByMediaMetadataRetriever(uri); + Map videoDir = getVideoAllMetadataByMediaMetadataRetriever(uriString); if (!videoDir.isEmpty()) { metadataMap.put("Video", videoDir); } } if (metadataMap.isEmpty()) { - result.error("getAllMetadata-failure", "failed to get metadata for uri=" + uri, null); + result.error("getAllMetadata-failure", "failed to get metadata for uri=" + uriString, null); } else { result.success(metadataMap); } @@ -555,7 +571,7 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { try (InputStream is = StorageUtils.openInputStream(context, Uri.parse(uriString))) { ExifInterface exif = new ExifInterface(is); Map metadataMap = new HashMap<>(); - for (String tag : MetadataHelper.ExifInterfaceTags) { + for (String tag : ExifInterfaceHelper.allTags.keySet()) { if (exif.hasAttribute(tag)) { metadataMap.put(tag, exif.getAttribute(tag)); } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/ExifInterfaceHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/ExifInterfaceHelper.kt new file mode 100644 index 000000000..8ce29f819 --- /dev/null +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/ExifInterfaceHelper.kt @@ -0,0 +1,237 @@ +package deckers.thibault.aves.utils + +import androidx.exifinterface.media.ExifInterface +import com.drew.metadata.Directory +import com.drew.metadata.exif.ExifDirectoryBase +import com.drew.metadata.exif.ExifThumbnailDirectory +import com.drew.metadata.exif.GpsDirectory +import com.drew.metadata.exif.PanasonicRawIFD0Directory +import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory +import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory +import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory +import java.util.* + +object ExifInterfaceHelper { + private val exifThumbnailDirectory = ExifThumbnailDirectory() + private val gpsDir = GpsDirectory() + private val olympusImageProcessingMakernoteDirectory = OlympusImageProcessingMakernoteDirectory() + private val olympusCameraSettingsMakernoteDirectory = OlympusCameraSettingsMakernoteDirectory() + private val olympusMakernoteDirectory = OlympusMakernoteDirectory() + private val panasonicRawIFD0Directory = PanasonicRawIFD0Directory() + + private val baseTags: Map?> = hashMapOf( + ExifInterface.TAG_APERTURE_VALUE to Pair(ExifDirectoryBase.TAG_APERTURE, gpsDir), + ExifInterface.TAG_ARTIST to Pair(ExifDirectoryBase.TAG_ARTIST, gpsDir), + ExifInterface.TAG_BITS_PER_SAMPLE to Pair(ExifDirectoryBase.TAG_BITS_PER_SAMPLE, gpsDir), + ExifInterface.TAG_BODY_SERIAL_NUMBER to Pair(ExifDirectoryBase.TAG_BODY_SERIAL_NUMBER, gpsDir), + ExifInterface.TAG_BRIGHTNESS_VALUE to Pair(ExifDirectoryBase.TAG_BRIGHTNESS_VALUE, gpsDir), + ExifInterface.TAG_CAMERA_OWNER_NAME to Pair(ExifDirectoryBase.TAG_CAMERA_OWNER_NAME, gpsDir), + ExifInterface.TAG_CFA_PATTERN to Pair(ExifDirectoryBase.TAG_CFA_PATTERN, gpsDir), + ExifInterface.TAG_COLOR_SPACE to Pair(ExifDirectoryBase.TAG_COLOR_SPACE, gpsDir), + ExifInterface.TAG_COMPONENTS_CONFIGURATION to Pair(ExifDirectoryBase.TAG_COMPONENTS_CONFIGURATION, gpsDir), + ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL to Pair(ExifDirectoryBase.TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL, gpsDir), + ExifInterface.TAG_COMPRESSION to Pair(ExifDirectoryBase.TAG_COMPRESSION, gpsDir), + ExifInterface.TAG_CONTRAST to Pair(ExifDirectoryBase.TAG_CONTRAST, gpsDir), + ExifInterface.TAG_COPYRIGHT to Pair(ExifDirectoryBase.TAG_COPYRIGHT, gpsDir), + ExifInterface.TAG_CUSTOM_RENDERED to Pair(ExifDirectoryBase.TAG_CUSTOM_RENDERED, gpsDir), + ExifInterface.TAG_DATETIME to Pair(ExifDirectoryBase.TAG_DATETIME, gpsDir), + ExifInterface.TAG_DATETIME_DIGITIZED to Pair(ExifDirectoryBase.TAG_DATETIME_DIGITIZED, gpsDir), + ExifInterface.TAG_DATETIME_ORIGINAL to Pair(ExifDirectoryBase.TAG_DATETIME_ORIGINAL, gpsDir), + ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION to Pair(ExifDirectoryBase.TAG_DEVICE_SETTING_DESCRIPTION, gpsDir), + ExifInterface.TAG_DIGITAL_ZOOM_RATIO to Pair(ExifDirectoryBase.TAG_DIGITAL_ZOOM_RATIO, gpsDir), + ExifInterface.TAG_EXIF_VERSION to Pair(ExifDirectoryBase.TAG_EXIF_VERSION, gpsDir), + ExifInterface.TAG_EXPOSURE_BIAS_VALUE to Pair(ExifDirectoryBase.TAG_EXPOSURE_BIAS, gpsDir), + ExifInterface.TAG_EXPOSURE_INDEX to Pair(ExifDirectoryBase.TAG_EXPOSURE_INDEX, gpsDir), + ExifInterface.TAG_EXPOSURE_MODE to Pair(ExifDirectoryBase.TAG_EXPOSURE_MODE, gpsDir), + ExifInterface.TAG_EXPOSURE_PROGRAM to Pair(ExifDirectoryBase.TAG_EXPOSURE_PROGRAM, gpsDir), + ExifInterface.TAG_EXPOSURE_TIME to Pair(ExifDirectoryBase.TAG_EXPOSURE_TIME, gpsDir), + ExifInterface.TAG_FILE_SOURCE to Pair(ExifDirectoryBase.TAG_FILE_SOURCE, gpsDir), + ExifInterface.TAG_FLASH to Pair(ExifDirectoryBase.TAG_FLASH, gpsDir), + ExifInterface.TAG_FLASHPIX_VERSION to Pair(ExifDirectoryBase.TAG_FLASHPIX_VERSION, gpsDir), + ExifInterface.TAG_FLASH_ENERGY to Pair(ExifDirectoryBase.TAG_FLASH_ENERGY, gpsDir), + ExifInterface.TAG_FOCAL_LENGTH to Pair(ExifDirectoryBase.TAG_FOCAL_LENGTH, gpsDir), + ExifInterface.TAG_FOCAL_LENGTH_IN_35MM_FILM to Pair(ExifDirectoryBase.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH, gpsDir), + ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT to Pair(ExifDirectoryBase.TAG_FOCAL_PLANE_RESOLUTION_UNIT, gpsDir), + ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION to Pair(ExifDirectoryBase.TAG_FOCAL_PLANE_X_RESOLUTION, gpsDir), + ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION to Pair(ExifDirectoryBase.TAG_FOCAL_PLANE_Y_RESOLUTION, gpsDir), + ExifInterface.TAG_F_NUMBER to Pair(ExifDirectoryBase.TAG_FNUMBER, gpsDir), + ExifInterface.TAG_GAIN_CONTROL to Pair(ExifDirectoryBase.TAG_GAIN_CONTROL, gpsDir), + ExifInterface.TAG_GAMMA to Pair(ExifDirectoryBase.TAG_GAMMA, gpsDir), + ExifInterface.TAG_IMAGE_DESCRIPTION to Pair(ExifDirectoryBase.TAG_IMAGE_DESCRIPTION, gpsDir), + ExifInterface.TAG_IMAGE_LENGTH to Pair(ExifDirectoryBase.TAG_IMAGE_HEIGHT, gpsDir), + ExifInterface.TAG_IMAGE_UNIQUE_ID to Pair(ExifDirectoryBase.TAG_IMAGE_UNIQUE_ID, gpsDir), + ExifInterface.TAG_IMAGE_WIDTH to Pair(ExifDirectoryBase.TAG_IMAGE_WIDTH, gpsDir), + ExifInterface.TAG_INTEROPERABILITY_INDEX to Pair(ExifDirectoryBase.TAG_INTEROP_INDEX, gpsDir), + ExifInterface.TAG_ISO_SPEED to Pair(ExifDirectoryBase.TAG_ISO_SPEED, gpsDir), + ExifInterface.TAG_ISO_SPEED_LATITUDE_YYY to Pair(ExifDirectoryBase.TAG_ISO_SPEED_LATITUDE_YYY, gpsDir), + ExifInterface.TAG_ISO_SPEED_LATITUDE_ZZZ to Pair(ExifDirectoryBase.TAG_ISO_SPEED_LATITUDE_ZZZ, gpsDir), + ExifInterface.TAG_LENS_MAKE to Pair(ExifDirectoryBase.TAG_LENS_MAKE, gpsDir), + ExifInterface.TAG_LENS_MODEL to Pair(ExifDirectoryBase.TAG_LENS_MODEL, gpsDir), + ExifInterface.TAG_LENS_SERIAL_NUMBER to Pair(ExifDirectoryBase.TAG_LENS_SERIAL_NUMBER, gpsDir), + ExifInterface.TAG_LENS_SPECIFICATION to Pair(ExifDirectoryBase.TAG_LENS_SPECIFICATION, gpsDir), + ExifInterface.TAG_LIGHT_SOURCE to Pair(ExifDirectoryBase.TAG_WHITE_BALANCE, gpsDir), + ExifInterface.TAG_MAKE to Pair(ExifDirectoryBase.TAG_MAKE, gpsDir), + ExifInterface.TAG_MAKER_NOTE to Pair(ExifDirectoryBase.TAG_MAKERNOTE, gpsDir), + ExifInterface.TAG_MAX_APERTURE_VALUE to Pair(ExifDirectoryBase.TAG_MAX_APERTURE, gpsDir), + ExifInterface.TAG_METERING_MODE to Pair(ExifDirectoryBase.TAG_METERING_MODE, gpsDir), + ExifInterface.TAG_MODEL to Pair(ExifDirectoryBase.TAG_MODEL, gpsDir), + ExifInterface.TAG_NEW_SUBFILE_TYPE to Pair(ExifDirectoryBase.TAG_NEW_SUBFILE_TYPE, gpsDir), + ExifInterface.TAG_OECF to Pair(ExifDirectoryBase.TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION, gpsDir), + ExifInterface.TAG_OFFSET_TIME to Pair(ExifDirectoryBase.TAG_TIME_ZONE, gpsDir), + ExifInterface.TAG_OFFSET_TIME_DIGITIZED to Pair(ExifDirectoryBase.TAG_TIME_ZONE_DIGITIZED, gpsDir), + ExifInterface.TAG_OFFSET_TIME_ORIGINAL to Pair(ExifDirectoryBase.TAG_TIME_ZONE_ORIGINAL, gpsDir), + ExifInterface.TAG_ORIENTATION to Pair(ExifDirectoryBase.TAG_ORIENTATION, gpsDir), + ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY to Pair(ExifDirectoryBase.TAG_ISO_EQUIVALENT, gpsDir), + ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION to Pair(ExifDirectoryBase.TAG_PHOTOMETRIC_INTERPRETATION, gpsDir), + ExifInterface.TAG_PIXEL_X_DIMENSION to Pair(ExifDirectoryBase.TAG_EXIF_IMAGE_WIDTH, gpsDir), + ExifInterface.TAG_PIXEL_Y_DIMENSION to Pair(ExifDirectoryBase.TAG_EXIF_IMAGE_HEIGHT, gpsDir), + ExifInterface.TAG_PLANAR_CONFIGURATION to Pair(ExifDirectoryBase.TAG_PLANAR_CONFIGURATION, gpsDir), + ExifInterface.TAG_PRIMARY_CHROMATICITIES to Pair(ExifDirectoryBase.TAG_PRIMARY_CHROMATICITIES, gpsDir), + ExifInterface.TAG_RECOMMENDED_EXPOSURE_INDEX to Pair(ExifDirectoryBase.TAG_RECOMMENDED_EXPOSURE_INDEX, gpsDir), + ExifInterface.TAG_REFERENCE_BLACK_WHITE to Pair(ExifDirectoryBase.TAG_REFERENCE_BLACK_WHITE, gpsDir), + ExifInterface.TAG_RELATED_SOUND_FILE to Pair(ExifDirectoryBase.TAG_RELATED_SOUND_FILE, gpsDir), + ExifInterface.TAG_RESOLUTION_UNIT to Pair(ExifDirectoryBase.TAG_RESOLUTION_UNIT, gpsDir), + ExifInterface.TAG_ROWS_PER_STRIP to Pair(ExifDirectoryBase.TAG_ROWS_PER_STRIP, gpsDir), + ExifInterface.TAG_SAMPLES_PER_PIXEL to Pair(ExifDirectoryBase.TAG_SAMPLES_PER_PIXEL, gpsDir), + ExifInterface.TAG_SATURATION to Pair(ExifDirectoryBase.TAG_SATURATION, gpsDir), + ExifInterface.TAG_SCENE_CAPTURE_TYPE to Pair(ExifDirectoryBase.TAG_SCENE_CAPTURE_TYPE, gpsDir), + ExifInterface.TAG_SCENE_TYPE to Pair(ExifDirectoryBase.TAG_SCENE_TYPE, gpsDir), + ExifInterface.TAG_SENSING_METHOD to Pair(ExifDirectoryBase.TAG_SENSING_METHOD, gpsDir), + ExifInterface.TAG_SENSITIVITY_TYPE to Pair(ExifDirectoryBase.TAG_SENSITIVITY_TYPE, gpsDir), + ExifInterface.TAG_SHARPNESS to Pair(ExifDirectoryBase.TAG_SHARPNESS, gpsDir), + ExifInterface.TAG_SHUTTER_SPEED_VALUE to Pair(ExifDirectoryBase.TAG_SHUTTER_SPEED, gpsDir), + ExifInterface.TAG_SOFTWARE to Pair(ExifDirectoryBase.TAG_SOFTWARE, gpsDir), + ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE to Pair(ExifDirectoryBase.TAG_SPATIAL_FREQ_RESPONSE, gpsDir), + ExifInterface.TAG_SPECTRAL_SENSITIVITY to Pair(ExifDirectoryBase.TAG_SPECTRAL_SENSITIVITY, gpsDir), + ExifInterface.TAG_STANDARD_OUTPUT_SENSITIVITY to Pair(ExifDirectoryBase.TAG_STANDARD_OUTPUT_SENSITIVITY, gpsDir), + ExifInterface.TAG_STRIP_BYTE_COUNTS to Pair(ExifDirectoryBase.TAG_STRIP_BYTE_COUNTS, gpsDir), + ExifInterface.TAG_STRIP_OFFSETS to Pair(ExifDirectoryBase.TAG_STRIP_OFFSETS, gpsDir), + ExifInterface.TAG_SUBFILE_TYPE to Pair(ExifDirectoryBase.TAG_SUBFILE_TYPE, gpsDir), + ExifInterface.TAG_SUBJECT_AREA to Pair(ExifDirectoryBase.TAG_SUBJECT_LOCATION_TIFF_EP, gpsDir), + ExifInterface.TAG_SUBJECT_DISTANCE to Pair(ExifDirectoryBase.TAG_SUBJECT_DISTANCE, gpsDir), + ExifInterface.TAG_SUBJECT_DISTANCE_RANGE to Pair(ExifDirectoryBase.TAG_SUBJECT_DISTANCE_RANGE, gpsDir), + ExifInterface.TAG_SUBJECT_LOCATION to Pair(ExifDirectoryBase.TAG_SUBJECT_LOCATION, gpsDir), + ExifInterface.TAG_SUBSEC_TIME to Pair(ExifDirectoryBase.TAG_SUBSECOND_TIME, gpsDir), + ExifInterface.TAG_SUBSEC_TIME_DIGITIZED to Pair(ExifDirectoryBase.TAG_SUBSECOND_TIME_DIGITIZED, gpsDir), + ExifInterface.TAG_SUBSEC_TIME_ORIGINAL to Pair(ExifDirectoryBase.TAG_SUBSECOND_TIME_ORIGINAL, gpsDir), + ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH to Pair(ExifDirectoryBase.TAG_IMAGE_HEIGHT, gpsDir), // IFD_THUMBNAIL_TAGS 0x0101 + ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH to Pair(ExifDirectoryBase.TAG_IMAGE_WIDTH, gpsDir), // IFD_THUMBNAIL_TAGS 0x0100 + ExifInterface.TAG_TRANSFER_FUNCTION to Pair(ExifDirectoryBase.TAG_TRANSFER_FUNCTION, gpsDir), + ExifInterface.TAG_USER_COMMENT to Pair(ExifDirectoryBase.TAG_USER_COMMENT, gpsDir), + ExifInterface.TAG_WHITE_BALANCE to Pair(ExifDirectoryBase.TAG_WHITE_BALANCE, gpsDir), + ExifInterface.TAG_WHITE_POINT to Pair(ExifDirectoryBase.TAG_WHITE_POINT, gpsDir), + ExifInterface.TAG_X_RESOLUTION to Pair(ExifDirectoryBase.TAG_X_RESOLUTION, gpsDir), + ExifInterface.TAG_Y_CB_CR_COEFFICIENTS to Pair(ExifDirectoryBase.TAG_YCBCR_COEFFICIENTS, gpsDir), + ExifInterface.TAG_Y_CB_CR_POSITIONING to Pair(ExifDirectoryBase.TAG_YCBCR_POSITIONING, gpsDir), + ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING to Pair(ExifDirectoryBase.TAG_YCBCR_SUBSAMPLING, gpsDir), + ExifInterface.TAG_Y_RESOLUTION to Pair(ExifDirectoryBase.TAG_Y_RESOLUTION, gpsDir), + ) + + private val thumbnailTags: Map?> = hashMapOf( + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT to Pair(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET, exifThumbnailDirectory), // IFD_TIFF_TAGS or IFD_THUMBNAIL_TAGS 0x0201 + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH to Pair(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH, exifThumbnailDirectory), // IFD_TIFF_TAGS or IFD_THUMBNAIL_TAGS 0x0202 + ) + + private val gpsTags: Map?> = hashMapOf( + // GPS + ExifInterface.TAG_GPS_ALTITUDE to Pair(GpsDirectory.TAG_ALTITUDE, gpsDir), + ExifInterface.TAG_GPS_ALTITUDE_REF to Pair(GpsDirectory.TAG_ALTITUDE_REF, gpsDir), + ExifInterface.TAG_GPS_AREA_INFORMATION to Pair(GpsDirectory.TAG_AREA_INFORMATION, gpsDir), + ExifInterface.TAG_GPS_DATESTAMP to Pair(GpsDirectory.TAG_DATE_STAMP, gpsDir), + ExifInterface.TAG_GPS_DEST_BEARING to Pair(GpsDirectory.TAG_DEST_BEARING, gpsDir), + ExifInterface.TAG_GPS_DEST_BEARING_REF to Pair(GpsDirectory.TAG_DEST_BEARING_REF, gpsDir), + ExifInterface.TAG_GPS_DEST_DISTANCE to Pair(GpsDirectory.TAG_DEST_DISTANCE, gpsDir), + ExifInterface.TAG_GPS_DEST_DISTANCE_REF to Pair(GpsDirectory.TAG_DEST_DISTANCE_REF, gpsDir), + ExifInterface.TAG_GPS_DEST_LATITUDE to Pair(GpsDirectory.TAG_DEST_LATITUDE, gpsDir), + ExifInterface.TAG_GPS_DEST_LATITUDE_REF to Pair(GpsDirectory.TAG_DEST_LATITUDE_REF, gpsDir), + ExifInterface.TAG_GPS_DEST_LONGITUDE to Pair(GpsDirectory.TAG_DEST_LONGITUDE, gpsDir), + ExifInterface.TAG_GPS_DEST_LONGITUDE_REF to Pair(GpsDirectory.TAG_DEST_LONGITUDE_REF, gpsDir), + ExifInterface.TAG_GPS_DIFFERENTIAL to Pair(GpsDirectory.TAG_DIFFERENTIAL, gpsDir), + ExifInterface.TAG_GPS_DOP to Pair(GpsDirectory.TAG_DOP, gpsDir), + ExifInterface.TAG_GPS_H_POSITIONING_ERROR to Pair(GpsDirectory.TAG_H_POSITIONING_ERROR, gpsDir), + ExifInterface.TAG_GPS_IMG_DIRECTION to Pair(GpsDirectory.TAG_IMG_DIRECTION, gpsDir), + ExifInterface.TAG_GPS_IMG_DIRECTION_REF to Pair(GpsDirectory.TAG_IMG_DIRECTION_REF, gpsDir), + ExifInterface.TAG_GPS_LATITUDE to Pair(GpsDirectory.TAG_LATITUDE, gpsDir), + ExifInterface.TAG_GPS_LATITUDE_REF to Pair(GpsDirectory.TAG_LATITUDE_REF, gpsDir), + ExifInterface.TAG_GPS_LONGITUDE to Pair(GpsDirectory.TAG_LONGITUDE, gpsDir), + ExifInterface.TAG_GPS_LONGITUDE_REF to Pair(GpsDirectory.TAG_LONGITUDE_REF, gpsDir), + ExifInterface.TAG_GPS_MAP_DATUM to Pair(GpsDirectory.TAG_MAP_DATUM, gpsDir), + ExifInterface.TAG_GPS_MEASURE_MODE to Pair(GpsDirectory.TAG_MEASURE_MODE, gpsDir), + ExifInterface.TAG_GPS_PROCESSING_METHOD to Pair(GpsDirectory.TAG_PROCESSING_METHOD, gpsDir), + ExifInterface.TAG_GPS_SATELLITES to Pair(GpsDirectory.TAG_SATELLITES, gpsDir), + ExifInterface.TAG_GPS_SPEED to Pair(GpsDirectory.TAG_SPEED, gpsDir), + ExifInterface.TAG_GPS_SPEED_REF to Pair(GpsDirectory.TAG_SPEED_REF, gpsDir), + ExifInterface.TAG_GPS_STATUS to Pair(GpsDirectory.TAG_STATUS, gpsDir), + ExifInterface.TAG_GPS_TIMESTAMP to Pair(GpsDirectory.TAG_TIME_STAMP, gpsDir), + ExifInterface.TAG_GPS_TRACK to Pair(GpsDirectory.TAG_TRACK, gpsDir), + ExifInterface.TAG_GPS_TRACK_REF to Pair(GpsDirectory.TAG_TRACK_REF, gpsDir), + ExifInterface.TAG_GPS_VERSION_ID to Pair(GpsDirectory.TAG_VERSION_ID, gpsDir), + ) + + private val xmpTags: Map?> = hashMapOf( + ExifInterface.TAG_XMP to null, // IFD_TIFF_TAGS 0x02BC + ) + + private val rawTags: Map?> = hashMapOf( + // DNG + ExifInterface.TAG_DEFAULT_CROP_SIZE to null, // IFD_EXIF_TAGS 0xC620 + ExifInterface.TAG_DNG_VERSION to null, // IFD_EXIF_TAGS 0xC612 + // ORF + ExifInterface.TAG_ORF_ASPECT_FRAME to Pair(OlympusImageProcessingMakernoteDirectory.TagAspectFrame, olympusImageProcessingMakernoteDirectory), // ORF_IMAGE_PROCESSING_TAGS 0x1113 + ExifInterface.TAG_ORF_PREVIEW_IMAGE_LENGTH to Pair(OlympusCameraSettingsMakernoteDirectory.TagPreviewImageLength, olympusCameraSettingsMakernoteDirectory), // ORF_CAMERA_SETTINGS_TAGS 0x0102 + ExifInterface.TAG_ORF_PREVIEW_IMAGE_START to Pair(OlympusCameraSettingsMakernoteDirectory.TagPreviewImageStart, olympusCameraSettingsMakernoteDirectory), // ORF_CAMERA_SETTINGS_TAGS 0x0101 + ExifInterface.TAG_ORF_THUMBNAIL_IMAGE to Pair(OlympusMakernoteDirectory.TAG_THUMBNAIL_IMAGE, olympusMakernoteDirectory), // ORF_MAKER_NOTE_TAGS 0x0100 + // RW2 + ExifInterface.TAG_RW2_ISO to Pair(PanasonicRawIFD0Directory.TagIso, panasonicRawIFD0Directory), // IFD_TIFF_TAGS 0x0017 + ExifInterface.TAG_RW2_JPG_FROM_RAW to Pair(PanasonicRawIFD0Directory.TagJpgFromRaw, panasonicRawIFD0Directory), // IFD_TIFF_TAGS 0x002E + ExifInterface.TAG_RW2_SENSOR_BOTTOM_BORDER to Pair(PanasonicRawIFD0Directory.TagSensorBottomBorder, panasonicRawIFD0Directory), // IFD_TIFF_TAGS 0x0006 + ExifInterface.TAG_RW2_SENSOR_LEFT_BORDER to Pair(PanasonicRawIFD0Directory.TagSensorLeftBorder, panasonicRawIFD0Directory), // IFD_TIFF_TAGS 0x0005 + ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER to Pair(PanasonicRawIFD0Directory.TagSensorRightBorder, panasonicRawIFD0Directory), // IFD_TIFF_TAGS 0x0007 + ExifInterface.TAG_RW2_SENSOR_TOP_BORDER to Pair(PanasonicRawIFD0Directory.TagSensorTopBorder, panasonicRawIFD0Directory), // IFD_TIFF_TAGS 0x0004 + ) + + // list of known ExifInterface tags (as of androidx.exifinterface:exifinterface:1.3.0) + // mapped to metadata-extractor tags (as of v2.14.0) + @JvmField + val allTags: Map?> = hashMapOf?>( + ).apply { + putAll(baseTags) + putAll(thumbnailTags) + putAll(gpsTags) + putAll(xmpTags) + putAll(rawTags) + } + + @JvmStatic + fun describeAll(exif: ExifInterface): Map> { + return HashMap>().apply { + put("Exif", describeDir(exif, baseTags)) + put("Exif Thumbnail", describeDir(exif, thumbnailTags)) + put("GPS", describeDir(exif, gpsTags)) + put("XMP", describeDir(exif, xmpTags)) + put("Exif Raw", describeDir(exif, rawTags)) + }.filterValues { it.isNotEmpty() } + } + + private fun describeDir(exif: ExifInterface, tags: Map?>): Map { + val dirMap = HashMap() + for (kv in tags) { + val exifInterfaceTag: String = kv.key + if (exif.hasAttribute(exifInterfaceTag)) { + val mapper = kv.value + val tagName = if (mapper != null) { + val extractorTagType = mapper.first + val extractorDir = mapper.second + extractorDir.getTagName(extractorTagType) + } else { + exifInterfaceTag + } + val tagValue: String? = exif.getAttribute(exifInterfaceTag) + if (tagValue != null) { + dirMap[tagName] = tagValue + } + } + } + return dirMap + } +} diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MetadataHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MetadataHelper.kt index b5562a921..5dd264b70 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MetadataHelper.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MetadataHelper.kt @@ -7,163 +7,6 @@ import java.util.* import java.util.regex.Pattern object MetadataHelper { - // list of known tags from ExifInterface as of androidx.exifinterface:exifinterface:1.3.0 - @JvmField - val ExifInterfaceTags = arrayOf( - ExifInterface.TAG_APERTURE_VALUE, - ExifInterface.TAG_ARTIST, - ExifInterface.TAG_BITS_PER_SAMPLE, - ExifInterface.TAG_BODY_SERIAL_NUMBER, - ExifInterface.TAG_BRIGHTNESS_VALUE, - ExifInterface.TAG_CAMERA_OWNER_NAME, - ExifInterface.TAG_CFA_PATTERN, - ExifInterface.TAG_COLOR_SPACE, - ExifInterface.TAG_COMPONENTS_CONFIGURATION, - ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL, - ExifInterface.TAG_COMPRESSION, - ExifInterface.TAG_CONTRAST, - ExifInterface.TAG_COPYRIGHT, - ExifInterface.TAG_CUSTOM_RENDERED, - ExifInterface.TAG_DATETIME, - ExifInterface.TAG_DATETIME_DIGITIZED, - ExifInterface.TAG_DATETIME_ORIGINAL, - ExifInterface.TAG_DEFAULT_CROP_SIZE, - ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION, - ExifInterface.TAG_DIGITAL_ZOOM_RATIO, - ExifInterface.TAG_DNG_VERSION, - ExifInterface.TAG_EXIF_VERSION, - ExifInterface.TAG_EXPOSURE_BIAS_VALUE, - ExifInterface.TAG_EXPOSURE_INDEX, - ExifInterface.TAG_EXPOSURE_MODE, - ExifInterface.TAG_EXPOSURE_PROGRAM, - ExifInterface.TAG_EXPOSURE_TIME, - ExifInterface.TAG_FILE_SOURCE, - ExifInterface.TAG_FLASH, - ExifInterface.TAG_FLASHPIX_VERSION, - ExifInterface.TAG_FLASH_ENERGY, - ExifInterface.TAG_FOCAL_LENGTH, - ExifInterface.TAG_FOCAL_LENGTH_IN_35MM_FILM, - ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT, - ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION, - ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION, - ExifInterface.TAG_F_NUMBER, - ExifInterface.TAG_GAIN_CONTROL, - ExifInterface.TAG_GAMMA, - ExifInterface.TAG_GPS_ALTITUDE, - ExifInterface.TAG_GPS_ALTITUDE_REF, - ExifInterface.TAG_GPS_AREA_INFORMATION, - ExifInterface.TAG_GPS_DATESTAMP, - ExifInterface.TAG_GPS_DEST_BEARING, - ExifInterface.TAG_GPS_DEST_BEARING_REF, - ExifInterface.TAG_GPS_DEST_DISTANCE, - ExifInterface.TAG_GPS_DEST_DISTANCE_REF, - ExifInterface.TAG_GPS_DEST_LATITUDE, - ExifInterface.TAG_GPS_DEST_LATITUDE_REF, - ExifInterface.TAG_GPS_DEST_LONGITUDE, - ExifInterface.TAG_GPS_DEST_LONGITUDE_REF, - ExifInterface.TAG_GPS_DIFFERENTIAL, - ExifInterface.TAG_GPS_DOP, - ExifInterface.TAG_GPS_H_POSITIONING_ERROR, - ExifInterface.TAG_GPS_IMG_DIRECTION, - ExifInterface.TAG_GPS_IMG_DIRECTION_REF, - ExifInterface.TAG_GPS_LATITUDE, - ExifInterface.TAG_GPS_LATITUDE_REF, - ExifInterface.TAG_GPS_LONGITUDE, - ExifInterface.TAG_GPS_LONGITUDE_REF, - ExifInterface.TAG_GPS_MAP_DATUM, - ExifInterface.TAG_GPS_MEASURE_MODE, - ExifInterface.TAG_GPS_PROCESSING_METHOD, - ExifInterface.TAG_GPS_SATELLITES, - ExifInterface.TAG_GPS_SPEED, - ExifInterface.TAG_GPS_SPEED_REF, - ExifInterface.TAG_GPS_STATUS, - ExifInterface.TAG_GPS_TIMESTAMP, - ExifInterface.TAG_GPS_TRACK, - ExifInterface.TAG_GPS_TRACK_REF, - ExifInterface.TAG_GPS_VERSION_ID, - ExifInterface.TAG_IMAGE_DESCRIPTION, - ExifInterface.TAG_IMAGE_LENGTH, - ExifInterface.TAG_IMAGE_UNIQUE_ID, - ExifInterface.TAG_IMAGE_WIDTH, - ExifInterface.TAG_INTEROPERABILITY_INDEX, - ExifInterface.TAG_ISO_SPEED, - ExifInterface.TAG_ISO_SPEED_LATITUDE_YYY, - ExifInterface.TAG_ISO_SPEED_LATITUDE_ZZZ, - ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, - ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, - ExifInterface.TAG_LENS_MAKE, - ExifInterface.TAG_LENS_MODEL, - ExifInterface.TAG_LENS_SERIAL_NUMBER, - ExifInterface.TAG_LENS_SPECIFICATION, - ExifInterface.TAG_LIGHT_SOURCE, - ExifInterface.TAG_MAKE, - ExifInterface.TAG_MAKER_NOTE, - ExifInterface.TAG_MAX_APERTURE_VALUE, - ExifInterface.TAG_METERING_MODE, - ExifInterface.TAG_MODEL, - ExifInterface.TAG_NEW_SUBFILE_TYPE, - ExifInterface.TAG_OECF, - ExifInterface.TAG_OFFSET_TIME, - ExifInterface.TAG_OFFSET_TIME_DIGITIZED, - ExifInterface.TAG_OFFSET_TIME_ORIGINAL, - ExifInterface.TAG_ORF_ASPECT_FRAME, - ExifInterface.TAG_ORF_PREVIEW_IMAGE_LENGTH, - ExifInterface.TAG_ORF_PREVIEW_IMAGE_START, - ExifInterface.TAG_ORF_THUMBNAIL_IMAGE, - ExifInterface.TAG_ORIENTATION, - ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY, - ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION, - ExifInterface.TAG_PIXEL_X_DIMENSION, - ExifInterface.TAG_PIXEL_Y_DIMENSION, - ExifInterface.TAG_PLANAR_CONFIGURATION, - ExifInterface.TAG_PRIMARY_CHROMATICITIES, - ExifInterface.TAG_RECOMMENDED_EXPOSURE_INDEX, - ExifInterface.TAG_REFERENCE_BLACK_WHITE, - ExifInterface.TAG_RELATED_SOUND_FILE, - ExifInterface.TAG_RESOLUTION_UNIT, - ExifInterface.TAG_ROWS_PER_STRIP, - ExifInterface.TAG_RW2_ISO, - ExifInterface.TAG_RW2_JPG_FROM_RAW, - ExifInterface.TAG_RW2_SENSOR_BOTTOM_BORDER, - ExifInterface.TAG_RW2_SENSOR_LEFT_BORDER, - ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER, - ExifInterface.TAG_RW2_SENSOR_TOP_BORDER, - ExifInterface.TAG_SAMPLES_PER_PIXEL, - ExifInterface.TAG_SATURATION, - ExifInterface.TAG_SCENE_CAPTURE_TYPE, - ExifInterface.TAG_SCENE_TYPE, - ExifInterface.TAG_SENSING_METHOD, - ExifInterface.TAG_SENSITIVITY_TYPE, - ExifInterface.TAG_SHARPNESS, - ExifInterface.TAG_SHUTTER_SPEED_VALUE, - ExifInterface.TAG_SOFTWARE, - ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE, - ExifInterface.TAG_SPECTRAL_SENSITIVITY, - ExifInterface.TAG_STANDARD_OUTPUT_SENSITIVITY, - ExifInterface.TAG_STRIP_BYTE_COUNTS, - ExifInterface.TAG_STRIP_OFFSETS, - ExifInterface.TAG_SUBFILE_TYPE, - ExifInterface.TAG_SUBJECT_AREA, - ExifInterface.TAG_SUBJECT_DISTANCE, - ExifInterface.TAG_SUBJECT_DISTANCE_RANGE, - ExifInterface.TAG_SUBJECT_LOCATION, - ExifInterface.TAG_SUBSEC_TIME, - ExifInterface.TAG_SUBSEC_TIME_DIGITIZED, - ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, - ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH, - ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH, - ExifInterface.TAG_TRANSFER_FUNCTION, - ExifInterface.TAG_USER_COMMENT, - ExifInterface.TAG_WHITE_BALANCE, - ExifInterface.TAG_WHITE_POINT, - ExifInterface.TAG_XMP, - ExifInterface.TAG_X_RESOLUTION, - ExifInterface.TAG_Y_CB_CR_COEFFICIENTS, - ExifInterface.TAG_Y_CB_CR_POSITIONING, - ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING, - ExifInterface.TAG_Y_RESOLUTION, - ) - // interpret EXIF code to angle (0, 90, 180 or 270 degrees) @JvmStatic fun getRotationDegreesForExifCode(exifOrientation: Int): Int = when (exifOrientation) {