diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt index cba217c0e..27d82279f 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt @@ -2,7 +2,9 @@ package deckers.thibault.aves.metadata import android.content.Context import android.net.Uri +import android.util.Log import androidx.exifinterface.media.ExifInterface +import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.StorageUtils import java.io.File @@ -13,6 +15,8 @@ import java.util.* import java.util.regex.Pattern object Metadata { + private val LOG_TAG = LogUtils.createTag() + // Pattern to extract latitude & longitude from a video location tag (cf ISO 6709) // Examples: // "+37.5090+127.0243/" (Samsung) @@ -96,10 +100,10 @@ object Metadata { return dateMillis } - // opening large TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1), + // opening large PSD/TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1), // so we define an arbitrary threshold to avoid a crash on launch. // It is not clear whether it is because of the file itself or its metadata. - private const val tiffSizeBytesMax = 100 * (1 shl 20) // MB + private const val fileSizeBytesMax = 100 * (1 shl 20) // MB // we try and read metadata from large files by copying an arbitrary amount from its beginning // to a temporary file, and reusing that preview file for all metadata reading purposes @@ -108,25 +112,38 @@ object Metadata { private val previewFiles = HashMap() private fun getSafeUri(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): Uri { - if (mimeType != MimeTypes.TIFF) return uri - - if (sizeBytes != null && sizeBytes < tiffSizeBytesMax) return uri - - var previewFile = previewFiles[uri] - if (previewFile == null) { - previewFile = File.createTempFile("aves", null, context.cacheDir).apply { - deleteOnExit() - outputStream().use { outputStream -> - StorageUtils.openInputStream(context, uri)?.use { inputStream -> - val b = ByteArray(previewSize) - inputStream.read(b, 0, previewSize) - outputStream.write(b) + return when (mimeType) { + // formats known to yield OOM for large files + MimeTypes.PSD_VND, + MimeTypes.PSD_X, + MimeTypes.TIFF -> { + if (sizeBytes != null && sizeBytes < fileSizeBytesMax) { + // small enough to be safe as it is + uri + } else { + // make a preview from the beginning of the file, + // hoping the metadata is accessible in the copied chunk + Log.d(LOG_TAG, "use a preview for uri=$uri mimeType=$mimeType size=$sizeBytes") + var previewFile = previewFiles[uri] + if (previewFile == null) { + previewFile = File.createTempFile("aves", null, context.cacheDir).apply { + deleteOnExit() + outputStream().use { outputStream -> + StorageUtils.openInputStream(context, uri)?.use { inputStream -> + val b = ByteArray(previewSize) + inputStream.read(b, 0, previewSize) + outputStream.write(b) + } + } + } + previewFiles[uri] = previewFile } + Uri.fromFile(previewFile) } } - previewFiles[uri] = previewFile + // *probably* safe + else -> uri } - return Uri.fromFile(previewFile) } fun openSafeInputStream(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): InputStream? { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt index daef138a8..5786922ed 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt @@ -14,6 +14,8 @@ object MimeTypes { private const val ICO = "image/x-icon" const val JPEG = "image/jpeg" const val PNG = "image/png" + const val PSD_VND = "image/vnd.adobe.photoshop" + const val PSD_X = "image/x-photoshop" const val TIFF = "image/tiff" private const val WBMP = "image/vnd.wap.wbmp" const val WEBP = "image/webp"