fixed opening files shared via content URI with incorrect MIME type
This commit is contained in:
parent
d68ff5861a
commit
51bfb3cd04
3 changed files with 53 additions and 13 deletions
|
@ -29,7 +29,7 @@ class ImageFileHandler(private val activity: Activity) : MethodCallHandler {
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"getEntry" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::getEntry) }
|
"getEntry" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::getEntry) }
|
||||||
"getThumbnail" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::getThumbnail) }
|
"getThumbnail" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::getThumbnail) }
|
||||||
"getRegion" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::getRegion) }
|
"getRegion" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::getRegion) }
|
||||||
"rename" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::rename) }
|
"rename" -> GlobalScope.launch(Dispatchers.IO) { safesus(call, result, ::rename) }
|
||||||
|
@ -40,7 +40,7 @@ class ImageFileHandler(private val activity: Activity) : MethodCallHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getEntry(call: MethodCall, result: MethodChannel.Result) {
|
private fun getEntry(call: MethodCall, result: MethodChannel.Result) {
|
||||||
val mimeType = call.argument<String>("mimeType") // MIME type is optional
|
val mimeType = call.argument<String>("mimeType") // MIME type is optional
|
||||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
|
|
|
@ -127,16 +127,7 @@ object Metadata {
|
||||||
Log.d(LOG_TAG, "use a preview for uri=$uri mimeType=$mimeType size=$sizeBytes")
|
Log.d(LOG_TAG, "use a preview for uri=$uri mimeType=$mimeType size=$sizeBytes")
|
||||||
var previewFile = previewFiles[uri]
|
var previewFile = previewFiles[uri]
|
||||||
if (previewFile == null) {
|
if (previewFile == null) {
|
||||||
previewFile = File.createTempFile("aves", null, context.cacheDir).apply {
|
previewFile = createPreviewFile(context, uri)
|
||||||
deleteOnExit()
|
|
||||||
outputStream().use { output ->
|
|
||||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
|
||||||
val b = ByteArray(previewSize)
|
|
||||||
input.read(b, 0, previewSize)
|
|
||||||
output.write(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previewFiles[uri] = previewFile
|
previewFiles[uri] = previewFile
|
||||||
}
|
}
|
||||||
Uri.fromFile(previewFile)
|
Uri.fromFile(previewFile)
|
||||||
|
@ -147,6 +138,19 @@ object Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createPreviewFile(context: Context, uri: Uri): File {
|
||||||
|
return File.createTempFile("aves", null, context.cacheDir).apply {
|
||||||
|
deleteOnExit()
|
||||||
|
outputStream().use { output ->
|
||||||
|
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||||
|
val b = ByteArray(previewSize)
|
||||||
|
input.read(b, 0, previewSize)
|
||||||
|
output.write(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun openSafeInputStream(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): InputStream? {
|
fun openSafeInputStream(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): InputStream? {
|
||||||
val safeUri = getSafeUri(context, uri, mimeType, sizeBytes)
|
val safeUri = getSafeUri(context, uri, mimeType, sizeBytes)
|
||||||
return StorageUtils.openInputStream(context, safeUri)
|
return StorageUtils.openInputStream(context, safeUri)
|
||||||
|
|
|
@ -4,10 +4,44 @@ import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
|
import android.util.Log
|
||||||
|
import com.drew.imaging.ImageMetadataReader
|
||||||
|
import com.drew.metadata.file.FileTypeDirectory
|
||||||
|
import deckers.thibault.aves.metadata.Metadata
|
||||||
|
import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeString
|
||||||
import deckers.thibault.aves.model.SourceEntry
|
import deckers.thibault.aves.model.SourceEntry
|
||||||
|
import deckers.thibault.aves.utils.LogUtils
|
||||||
|
import deckers.thibault.aves.utils.MimeTypes
|
||||||
|
import deckers.thibault.aves.utils.StorageUtils
|
||||||
|
|
||||||
internal class ContentImageProvider : ImageProvider() {
|
internal class ContentImageProvider : ImageProvider() {
|
||||||
override fun fetchSingle(context: Context, uri: Uri, mimeType: String?, callback: ImageOpCallback) {
|
override fun fetchSingle(context: Context, uri: Uri, sourceMimeType: String?, callback: ImageOpCallback) {
|
||||||
|
// source MIME type may be incorrect, so we get a second opinion if possible
|
||||||
|
var extractorMimeType: String? = null
|
||||||
|
try {
|
||||||
|
val safeUri = Uri.fromFile(Metadata.createPreviewFile(context, uri))
|
||||||
|
StorageUtils.openInputStream(context, safeUri)?.use { input ->
|
||||||
|
val metadata = ImageMetadataReader.readMetadata(input)
|
||||||
|
for (dir in metadata.getDirectoriesOfType(FileTypeDirectory::class.java)) {
|
||||||
|
// `metadata-extractor` is the most reliable, except for `tiff` (false positives, false negatives)
|
||||||
|
// cf https://github.com/drewnoakes/metadata-extractor/issues/296
|
||||||
|
dir.getSafeString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE) {
|
||||||
|
if (it != MimeTypes.TIFF) {
|
||||||
|
extractorMimeType = it
|
||||||
|
if (extractorMimeType != sourceMimeType) {
|
||||||
|
Log.d(LOG_TAG, "source MIME type is $sourceMimeType but extracted MIME type is $extractorMimeType for uri=$uri")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||||
|
} catch (e: NoClassDefFoundError) {
|
||||||
|
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mimeType = extractorMimeType ?: sourceMimeType
|
||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
callback.onFailure(Exception("MIME type is null for uri=$uri"))
|
callback.onFailure(Exception("MIME type is null for uri=$uri"))
|
||||||
return
|
return
|
||||||
|
@ -39,6 +73,8 @@ internal class ContentImageProvider : ImageProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val LOG_TAG = LogUtils.createTag<ContentImageProvider>()
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
const val PATH = MediaStore.MediaColumns.DATA
|
const val PATH = MediaStore.MediaColumns.DATA
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue