diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt index fa97f7599..0b016d473 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataHandler.kt @@ -43,6 +43,7 @@ import deckers.thibault.aves.metadata.MetadataExtractorHelper.isGeoTiff import deckers.thibault.aves.metadata.XMP import deckers.thibault.aves.metadata.XMP.getSafeDateMillis import deckers.thibault.aves.metadata.XMP.getSafeLocalizedText +import deckers.thibault.aves.metadata.XMP.isPanorama import deckers.thibault.aves.model.provider.FieldMap import deckers.thibault.aves.model.provider.FileImageProvider import deckers.thibault.aves.model.provider.ImageProvider @@ -298,9 +299,9 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { val values = (1 until count + 1).map { xmpMeta.getArrayItem(XMP.DC_SCHEMA_NS, XMP.SUBJECT_PROP_NAME, it).value } metadataMap[KEY_XMP_SUBJECTS] = values.joinToString(separator = XMP_SUBJECTS_SEPARATOR) } - xmpMeta.getSafeLocalizedText(XMP.DC_SCHEMA_NS, XMP.TITLE_PROP_NAME) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it } + xmpMeta.getSafeLocalizedText(XMP.DC_SCHEMA_NS, XMP.TITLE_PROP_NAME, acceptBlank = false) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it } if (!metadataMap.containsKey(KEY_XMP_TITLE_DESCRIPTION)) { - xmpMeta.getSafeLocalizedText(XMP.DC_SCHEMA_NS, XMP.DESCRIPTION_PROP_NAME) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it } + xmpMeta.getSafeLocalizedText(XMP.DC_SCHEMA_NS, XMP.DESCRIPTION_PROP_NAME, acceptBlank = false) { metadataMap[KEY_XMP_TITLE_DESCRIPTION] = it } } if (!metadataMap.containsKey(KEY_DATE_MILLIS)) { xmpMeta.getSafeDateMillis(XMP.XMP_SCHEMA_NS, XMP.CREATE_DATE_PROP_NAME) { metadataMap[KEY_DATE_MILLIS] = it } @@ -310,7 +311,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler { } // identification of panorama (aka photo sphere) - if (XMP.panoramaRequiredProps.all { xmpMeta.doesPropertyExist(XMP.GPANO_SCHEMA_NS, it) }) { + if (xmpMeta.isPanorama()) { flags = flags or MASK_IS_360 } } catch (e: XMPException) { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt index 9e71c08a5..48c027ba6 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt @@ -42,7 +42,8 @@ object XMP { // panorama // cf https://developers.google.com/streetview/spherical-metadata - const val GPANO_SCHEMA_NS = "http://ns.google.com/photos/1.0/panorama/" + private const val GPANO_SCHEMA_NS = "http://ns.google.com/photos/1.0/panorama/" + private const val PMTM_SCHEMA_NS = "http://www.hdrsoft.com/photomatix_settings01" private const val GPANO_CROPPED_AREA_HEIGHT_PROP_NAME = "GPano:CroppedAreaImageHeightPixels" private const val GPANO_CROPPED_AREA_WIDTH_PROP_NAME = "GPano:CroppedAreaImageWidthPixels" @@ -52,7 +53,9 @@ object XMP { private const val GPANO_FULL_PANO_WIDTH_PIXELS_PROP_NAME = "GPano:FullPanoWidthPixels" private const val GPANO_PROJECTION_TYPE_PROP_NAME = "GPano:ProjectionType" - val panoramaRequiredProps = listOf( + private const val PMTM_IS_PANO360 = "pmtm:IsPano360" + + private val gpanoRequiredProps = listOf( GPANO_CROPPED_AREA_HEIGHT_PROP_NAME, GPANO_CROPPED_AREA_WIDTH_PROP_NAME, GPANO_CROPPED_AREA_LEFT_PROP_NAME, @@ -64,12 +67,22 @@ object XMP { // extensions - fun XMPMeta.getSafeLocalizedText(schema: String, propName: String, save: (value: String) -> Unit) { + fun XMPMeta.isPanorama(): Boolean { + // Google + if (gpanoRequiredProps.all { doesPropertyExist(GPANO_SCHEMA_NS, it) }) return true + // Photomatix + if (getPropertyString(PMTM_SCHEMA_NS, PMTM_IS_PANO360) == "Yes") return true + return false + } + + fun XMPMeta.getSafeLocalizedText(schema: String, propName: String, acceptBlank: Boolean = true, save: (value: String) -> Unit) { try { - if (this.doesPropertyExist(schema, propName)) { - val item = this.getLocalizedText(schema, propName, GENERIC_LANG, SPECIFIC_LANG) + if (doesPropertyExist(schema, propName)) { + val item = getLocalizedText(schema, propName, GENERIC_LANG, SPECIFIC_LANG) // double check retrieved items as the property sometimes is reported to exist but it is actually null - if (item != null) save(item.value) + if (item != null && (acceptBlank || item.value.isNotBlank())) { + save(item.value) + } } } catch (e: XMPException) { Log.w(LOG_TAG, "failed to get text for XMP schema=$schema, propName=$propName", e) @@ -78,8 +91,8 @@ object XMP { fun XMPMeta.getSafeDateMillis(schema: String, propName: String, save: (value: Long) -> Unit) { try { - if (this.doesPropertyExist(schema, propName)) { - val item = this.getPropertyDate(schema, propName) + if (doesPropertyExist(schema, propName)) { + val item = getPropertyDate(schema, propName) // double check retrieved items as the property sometimes is reported to exist but it is actually null if (item != null) { // strip time zone from XMP dates so that we show date/times as local ones diff --git a/lib/ref/xmp.dart b/lib/ref/xmp.dart index 56c819de7..1ce3f86d4 100644 --- a/lib/ref/xmp.dart +++ b/lib/ref/xmp.dart @@ -25,6 +25,7 @@ class XMP { 'PanoStudioXMP': 'PanoramaStudio', 'photomechanic': 'Photo Mechanic', 'plus': 'PLUS', + 'pmtm': 'Photomatix', 'xmpBJ': 'Basic Job Ticket', 'xmpDM': 'Dynamic Media', 'xmpRights': 'Rights Management',