Send a concrete mime type for media shared with a wildcard one

Some apps share an image with the mime type image/* rather than a
concrete one. That value was carried straight into the uploaded event, so
the message reached the room with an unusable media type and bridges
rejected it as unsupported.

Resolve a wildcard subtype to a default for its top level type before
processing the upload, reusing the mapping the download path already
applies so the SDK can pick the right extension.
This commit is contained in:
hayaksi1
2026-08-17 04:01:05 +03:00
parent 23b75c3423
commit 4c5baf8d60
5 changed files with 76 additions and 18 deletions
@@ -29,6 +29,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
import io.element.android.libraries.core.mimetype.MimeTypes.withDefaultSubtype
import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
@@ -82,15 +83,16 @@ class AndroidMediaPreProcessor(
mediaOptimizationConfig: MediaOptimizationConfig,
): Result<MediaUploadInfo> = withContext(coroutineDispatchers.computation) {
runCatchingExceptions {
val resolvedMimeType = mimeType.withDefaultSubtype()
val result = when {
mimeType == MimeTypes.Svg -> processSvgImage(uri, mimeType)
mimeType.isMimeTypeImage() -> {
val shouldBeCompressed = mediaOptimizationConfig.compressImages && mimeType !in notCompressibleImageTypes
processImage(uri, mimeType, shouldBeCompressed)
resolvedMimeType == MimeTypes.Svg -> processSvgImage(uri, resolvedMimeType)
resolvedMimeType.isMimeTypeImage() -> {
val shouldBeCompressed = mediaOptimizationConfig.compressImages && resolvedMimeType !in notCompressibleImageTypes
processImage(uri, resolvedMimeType, shouldBeCompressed)
}
mimeType.isMimeTypeVideo() -> processVideo(uri, mimeType, mediaOptimizationConfig.videoCompressionPreset)
mimeType.isMimeTypeAudio() -> processAudio(uri, mimeType)
else -> processFile(uri, mimeType)
resolvedMimeType.isMimeTypeVideo() -> processVideo(uri, resolvedMimeType, mediaOptimizationConfig.videoCompressionPreset)
resolvedMimeType.isMimeTypeAudio() -> processAudio(uri, resolvedMimeType)
else -> processFile(uri, resolvedMimeType)
}
if (deleteOriginal) {
tryOrNull {
@@ -65,9 +65,24 @@ class AndroidMediaPreProcessorTest : RobolectricTest() {
assertThat(height).isEqualTo(2268)
}
@Test
fun `test processing an image shared with a wildcard mime type`() = runTest {
val mediaUploadInfo = process(
asset = assetImageJpeg,
mimeType = MimeTypes.Images,
mediaOptimizationConfig = MediaOptimizationConfig(
compressImages = true,
videoCompressionPreset = VideoCompressionPreset.STANDARD,
),
)
val info = mediaUploadInfo as MediaUploadInfo.Image
assertThat(info.imageInfo.mimetype).isEqualTo(MimeTypes.Jpeg)
}
private suspend fun TestScope.process(
asset: Asset,
mediaOptimizationConfig: MediaOptimizationConfig,
mimeType: String = asset.mimeType,
sdkIntVersion: Int = Build.VERSION_CODES.P,
deleteOriginal: Boolean = false,
): MediaUploadInfo {
@@ -81,7 +96,7 @@ class AndroidMediaPreProcessorTest : RobolectricTest() {
val file = getFileFromAssets(context, asset.filename)
val result = sut.process(
uri = file.toUri(),
mimeType = asset.mimeType,
mimeType = mimeType,
deleteOriginal = deleteOriginal,
mediaOptimizationConfig = mediaOptimizationConfig,
)