diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/media/MediaStoreUtils.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/media/MediaStoreUtils.kt index 3af520bb77..36f207bd44 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/media/MediaStoreUtils.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/media/MediaStoreUtils.kt @@ -5,6 +5,10 @@ import android.net.Uri import android.os.Build import android.provider.MediaStore import android.provider.OpenableColumns +import android.util.Log +import android.webkit.MimeTypeMap + +private const val TAG = "MediaStoreUtils" object MediaStoreUtils { private fun externalFilesUri(): Uri = @@ -31,6 +35,14 @@ object MediaStoreUtils { else -> externalFilesUri() } + fun resolveMimeType(context: Context, uri: Uri, fallbackMimeType: String? = null): String? { + return context.contentResolver.getType(uri) + ?: fallbackMimeType + ?: resolveMimeTypeFromDisplayName(context, uri) + ?: resolveMimeTypeFromPath(uri.path) + ?: resolveMimeTypeFromPath(uri.toString()) + } + fun resolveLocalIdByRelativePath(context: Context, path: String, mimeType: String): String? { val fileName = path.substringAfterLast('/', missingDelimiterValue = path) val parent = path.substringBeforeLast('/', "").let { if (it.isEmpty()) "" else "$it/" } @@ -76,6 +88,39 @@ object MediaStoreUtils { ) } + private fun resolveMimeTypeFromDisplayName(context: Context, uri: Uri): String? { + return try { + context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor -> + if (!cursor.moveToFirst()) { + return null + } + + val displayNameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + if (displayNameIndex < 0) { + return null + } + + resolveMimeTypeFromPath(cursor.getString(displayNameIndex)) + } + } catch (e: Exception) { + Log.w(TAG, "Failed to resolve MIME type from display name: $uri", e) + null + } + } + + private fun resolveMimeTypeFromPath(path: String?): String? { + if (path.isNullOrBlank()) { + return null + } + + val extension = path.substringAfterLast('.', missingDelimiterValue = "").substringBefore('?').substringBefore('#') + if (extension.isBlank()) { + return null + } + + return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.lowercase()) + } + private fun queryLatestId( context: Context, tableUri: Uri, diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/viewintent/ViewIntentPlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/viewintent/ViewIntentPlugin.kt index 8f26aaa8c1..9773906682 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/viewintent/ViewIntentPlugin.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/viewintent/ViewIntentPlugin.kt @@ -83,7 +83,7 @@ class ViewIntentPlugin : FlutterPlugin, ActivityAware, PluginRegistry.NewIntentL ioScope.launch { try { - val mimeType = context.contentResolver.getType(uri) + val mimeType = MediaStoreUtils.resolveMimeType(context, uri, intent.type) if (mimeType == null || (!mimeType.startsWith("image/") && !mimeType.startsWith("video/"))) { callback(Result.success(null)) return@launch