prevent editing item when Exif editing changes mime type
This commit is contained in:
parent
806f57785c
commit
7f9229a227
4 changed files with 44 additions and 2 deletions
|
@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
|
|||
### Fixed
|
||||
|
||||
- Viewer: multi-page context update when removing burst entries
|
||||
- prevent editing item when Exif editing changes mime type
|
||||
|
||||
## <a id="v1.8.5"></a>[v1.8.5] - 2023-04-18
|
||||
|
||||
|
|
|
@ -336,7 +336,7 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
|
||||
private fun submitPickedItems(call: MethodCall) {
|
||||
val pickedUris = call.argument<List<String>>("uris")
|
||||
if (pickedUris != null && pickedUris.isNotEmpty()) {
|
||||
if (!pickedUris.isNullOrEmpty()) {
|
||||
val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this, Uri.parse(uriString)) }
|
||||
val intent = Intent().apply {
|
||||
val firstUri = toUri(pickedUris.first())
|
||||
|
|
|
@ -31,6 +31,7 @@ import deckers.thibault.aves.metadata.Mp4ParserHelper.updateRotation
|
|||
import deckers.thibault.aves.metadata.Mp4ParserHelper.updateXmp
|
||||
import deckers.thibault.aves.metadata.PixyMetaHelper.extendedXmpDocString
|
||||
import deckers.thibault.aves.metadata.PixyMetaHelper.xmpDocString
|
||||
import deckers.thibault.aves.metadata.metadataextractor.Helper
|
||||
import deckers.thibault.aves.model.*
|
||||
import deckers.thibault.aves.utils.*
|
||||
import deckers.thibault.aves.utils.FileUtils.transferFrom
|
||||
|
@ -330,6 +331,7 @@ abstract class ImageProvider {
|
|||
@Suppress("deprecation")
|
||||
Bitmap.CompressFormat.WEBP
|
||||
}
|
||||
|
||||
else -> throw Exception("unsupported export MIME type=$exportMimeType")
|
||||
}
|
||||
bitmap.compress(format, quality, output)
|
||||
|
@ -592,12 +594,14 @@ abstract class ImageProvider {
|
|||
}
|
||||
nameWithoutExtension
|
||||
}
|
||||
|
||||
NameConflictStrategy.REPLACE -> {
|
||||
if (targetFile.exists()) {
|
||||
deletePath(contextWrapper, targetFile.path, mimeType)
|
||||
}
|
||||
desiredNameWithoutExtension
|
||||
}
|
||||
|
||||
NameConflictStrategy.SKIP -> {
|
||||
if (targetFile.exists()) {
|
||||
null
|
||||
|
@ -608,6 +612,25 @@ abstract class ImageProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// cf `MetadataFetchHandler.getCatalogMetadataByMetadataExtractor()` for a more thorough check
|
||||
private fun detectMimeType(context: Context, uri: Uri, mimeType: String): String? {
|
||||
var detectedMimeType: String? = null
|
||||
if (MimeTypes.canReadWithMetadataExtractor(mimeType)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
detectedMimeType = Helper.readMimeType(input)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
|
||||
} catch (e: AssertionError) {
|
||||
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
|
||||
}
|
||||
}
|
||||
return detectedMimeType
|
||||
}
|
||||
|
||||
private fun editExif(
|
||||
context: Context,
|
||||
path: String,
|
||||
|
@ -657,6 +680,11 @@ abstract class ImageProvider {
|
|||
try {
|
||||
edit(ExifInterface(editableFile))
|
||||
|
||||
val editedMimeType = detectMimeType(context, Uri.fromFile(editableFile), mimeType)
|
||||
if (editedMimeType != mimeType) {
|
||||
throw Exception("editing Exif changes mimeType=$mimeType -> $editedMimeType for uri=$uri path=$path")
|
||||
}
|
||||
|
||||
if (videoBytes != null) {
|
||||
// append trailer video, if any
|
||||
editableFile.appendBytes(videoBytes!!)
|
||||
|
@ -730,8 +758,10 @@ abstract class ImageProvider {
|
|||
when {
|
||||
iptc != null ->
|
||||
PixyMetaHelper.setIptc(input, output, iptc)
|
||||
|
||||
canRemoveMetadata(mimeType) ->
|
||||
PixyMetaHelper.removeMetadata(input, output, setOf(TYPE_IPTC))
|
||||
|
||||
else -> {
|
||||
Log.w(LOG_TAG, "setting empty IPTC for mimeType=$mimeType")
|
||||
PixyMetaHelper.setIptc(input, output, null)
|
||||
|
@ -787,6 +817,7 @@ abstract class ImageProvider {
|
|||
newFields["rotationDegrees"] = degrees
|
||||
}
|
||||
}
|
||||
|
||||
"xmp" -> isoFile.updateXmp(value)
|
||||
}
|
||||
}
|
||||
|
@ -1039,6 +1070,7 @@ abstract class ImageProvider {
|
|||
exif.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, ExifInterfaceHelper.GPS_TIME_FORMAT.format(date))
|
||||
}
|
||||
}
|
||||
|
||||
shiftMinutes != null -> {
|
||||
// shift
|
||||
val shiftMillis = shiftMinutes * 60000
|
||||
|
@ -1067,6 +1099,7 @@ abstract class ImageProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// clear
|
||||
if (fields.contains(ExifInterface.TAG_DATETIME)) {
|
||||
|
@ -1135,6 +1168,7 @@ abstract class ImageProvider {
|
|||
ExifInterface.TAG_GPS_LONGITUDE_REF -> {
|
||||
setLocation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (value is String) {
|
||||
exif.setAttribute(tag, value)
|
||||
|
|
|
@ -25,6 +25,7 @@ import deckers.thibault.aves.utils.MimeTypes.isVideo
|
|||
import deckers.thibault.aves.utils.PermissionManager.getGrantedDirForPath
|
||||
import deckers.thibault.aves.utils.UriUtils.tryParseId
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
@ -588,6 +589,7 @@ object StorageUtils {
|
|||
// e.g. `content://media/external_primary/downloads/...`
|
||||
getMediaUriImageVideoUri(uri, mimeType)?.let { imageVideUri -> return imageVideUri }
|
||||
}
|
||||
|
||||
uriPath?.contains("/file/") == true -> {
|
||||
// e.g. `content://media/external/file/...`
|
||||
// create an ad-hoc temporary file for decoding only
|
||||
|
@ -601,6 +603,7 @@ object StorageUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
uri.userInfo != null -> return stripMediaUriUserInfo(uri)
|
||||
}
|
||||
}
|
||||
|
@ -617,6 +620,7 @@ object StorageUtils {
|
|||
// e.g. `content://media/external_primary/downloads/...`
|
||||
getMediaUriImageVideoUri(uri, mimeType)?.let { imageVideUri -> return imageVideUri }
|
||||
}
|
||||
|
||||
uri.userInfo != null -> return stripMediaUriUserInfo(uri)
|
||||
}
|
||||
}
|
||||
|
@ -643,7 +647,10 @@ object StorageUtils {
|
|||
fun openInputStream(context: Context, uri: Uri): InputStream? {
|
||||
val effectiveUri = getOriginalUri(context, uri)
|
||||
return try {
|
||||
context.contentResolver.openInputStream(effectiveUri)
|
||||
return when (uri.scheme) {
|
||||
ContentResolver.SCHEME_FILE -> FileInputStream(uri.path)
|
||||
else -> context.contentResolver.openInputStream(effectiveUri)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// among various other exceptions,
|
||||
// opening a file marked pending and owned by another package throws an `IllegalStateException`
|
||||
|
|
Loading…
Reference in a new issue