diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt
index ee54325ce1..296beaaa7a 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt
@@ -37,7 +37,7 @@ class AdvancedSettingsPresenter @Inject constructor(
.collectAsState(initial = true)
val doesCompressMedia by sessionPreferencesStore
.doesCompressMedia()
- .collectAsState(initial = false)
+ .collectAsState(initial = true)
val theme by remember {
appPreferencesStore.getThemeFlow().mapToTheme()
}
diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml
index d7b0f6dda2..d2a03f922f 100644
--- a/features/preferences/impl/src/main/res/values/localazy.xml
+++ b/features/preferences/impl/src/main/res/values/localazy.xml
@@ -8,8 +8,8 @@
"Custom Element Call base URL"
"Set a custom base URL for Element Call."
"Invalid URL, please make sure you include the protocol (http/https) and the correct address."
- "Optimize for upload"
- "Media"
+ "Upload photos and videos faster and reduce data usage"
+ "Optimise media quality"
"Push notification provider"
"Disable the rich text editor to type Markdown manually."
"Read receipts"
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt
index 1634918296..843df71afb 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt
@@ -34,7 +34,7 @@ class AdvancedSettingsPresenterTest {
assertThat(initialState.isDeveloperModeEnabled).isFalse()
assertThat(initialState.showChangeThemeDialog).isFalse()
assertThat(initialState.isSharePresenceEnabled).isTrue()
- assertThat(initialState.doesCompressMedia).isFalse()
+ assertThat(initialState.doesCompressMedia).isTrue()
assertThat(initialState.theme).isEqualTo(Theme.System)
}
}
@@ -76,11 +76,11 @@ class AdvancedSettingsPresenterTest {
presenter.present()
}.test {
val initialState = awaitLastSequentialItem()
- assertThat(initialState.doesCompressMedia).isFalse()
- initialState.eventSink.invoke(AdvancedSettingsEvents.SetCompressMedia(true))
- assertThat(awaitItem().doesCompressMedia).isTrue()
+ assertThat(initialState.doesCompressMedia).isTrue()
initialState.eventSink.invoke(AdvancedSettingsEvents.SetCompressMedia(false))
assertThat(awaitItem().doesCompressMedia).isFalse()
+ initialState.eventSink.invoke(AdvancedSettingsEvents.SetCompressMedia(true))
+ assertThat(awaitItem().doesCompressMedia).isTrue()
}
}
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
index b1352cd7b6..3b40e3bf3c 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
@@ -17,6 +17,7 @@ import io.element.android.features.roomdetails.aMatrixRoom
import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditEvents
import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditPresenter
import io.element.android.libraries.architecture.AsyncAction
+import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
@@ -513,7 +514,7 @@ class RoomDetailsEditPresenterTest {
initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto))
initialState.eventSink(RoomDetailsEditEvents.Save)
skipItems(4)
- updateAvatarResult.assertions().isCalledOnce().with(value("image/jpeg"), value(fakeFileContents))
+ updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents))
}
}
diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt
index e224955164..e0a82e7773 100644
--- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt
+++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToMetadataKtTest.kt
@@ -18,6 +18,7 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
+import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.AudioInfo
@@ -580,7 +581,7 @@ fun anImageInfo(): ImageInfo {
return ImageInfo(
height = 100,
width = 100,
- mimetype = "image/jpeg",
+ mimetype = MimeTypes.Jpeg,
size = 1000,
thumbnailInfo = null,
thumbnailSource = aMediaSource(),
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt
index 1ea1acae82..ca61aa0845 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt
@@ -125,9 +125,13 @@ class AndroidMediaPreProcessor @Inject constructor(
val compressionResult = imageCompressor.compressToTmpFile(
inputStreamProvider = { contentResolver.openInputStream(uri)!! },
resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE),
+ mimeType = mimeType,
orientation = orientation,
).getOrThrow()
- val thumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file)
+ val thumbnailResult = thumbnailFactory.createImageThumbnail(
+ file = compressionResult.file,
+ mimeType = mimeType,
+ )
val imageInfo = compressionResult.toImageInfo(
mimeType = mimeType,
thumbnailResult = thumbnailResult
@@ -142,7 +146,10 @@ class AndroidMediaPreProcessor @Inject constructor(
suspend fun processImageWithoutCompression(): MediaUploadInfo {
val file = copyToTmpFile(uri)
- val thumbnailResult = thumbnailFactory.createImageThumbnail(file)
+ val thumbnailResult = thumbnailFactory.createImageThumbnail(
+ file = file,
+ mimeType = mimeType,
+ )
val imageInfo = contentResolver.openInputStream(uri).use { input ->
val bitmap = BitmapFactory.decodeStream(input, null, null)!!
ImageInfo(
@@ -171,17 +178,13 @@ class AndroidMediaPreProcessor @Inject constructor(
}
private suspend fun processVideo(uri: Uri, mimeType: String?, shouldBeCompressed: Boolean): MediaUploadInfo {
- val resultFile = if (shouldBeCompressed) {
- videoCompressor.compress(uri)
- .onEach {
- // TODO handle progress
- }
- .filterIsInstance()
- .first()
- .file
- } else {
- copyToTmpFile(uri)
- }
+ val resultFile = videoCompressor.compress(uri, shouldBeCompressed)
+ .onEach {
+ // TODO handle progress
+ }
+ .filterIsInstance()
+ .first()
+ .file
val thumbnailInfo = thumbnailFactory.createVideoThumbnail(resultFile)
val videoInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo)
return MediaUploadInfo.Video(
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt
index 69d2e41baa..a08a9222e0 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ImageCompressor.kt
@@ -34,14 +34,16 @@ class ImageCompressor @Inject constructor(
suspend fun compressToTmpFile(
inputStreamProvider: () -> InputStream,
resizeMode: ResizeMode,
- format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
+ mimeType: String,
orientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
desiredQuality: Int = 78,
): Result = withContext(dispatchers.io) {
runCatching {
+ val format = mimeTypeToCompressFormat(mimeType)
+ val extension = mimeTypeToCompressFileExtension(mimeType)
val compressedBitmap = compressToBitmap(inputStreamProvider, resizeMode, orientation).getOrThrow()
// Encode bitmap to the destination temporary file
- val tmpFile = context.createTmpFile(extension = "jpeg")
+ val tmpFile = context.createTmpFile(extension = extension)
tmpFile.outputStream().use {
compressedBitmap.compress(format, desiredQuality, it)
}
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/MimeTypeUtil.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/MimeTypeUtil.kt
new file mode 100644
index 0000000000..6ab327e6cb
--- /dev/null
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/MimeTypeUtil.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * Please see LICENSE in the repository root for full details.
+ */
+
+package io.element.android.libraries.mediaupload.impl
+
+import android.graphics.Bitmap
+import io.element.android.libraries.core.mimetype.MimeTypes
+
+fun mimeTypeToCompressFormat(mimeType: String) = when (mimeType) {
+ MimeTypes.Png -> Bitmap.CompressFormat.PNG
+ else -> Bitmap.CompressFormat.JPEG
+}
+
+fun mimeTypeToCompressFileExtension(mimeType: String) = when (mimeType) {
+ MimeTypes.Png -> "png"
+ else -> "jpeg"
+}
+
+fun mimeTypeToThumbnailMimeType(mimeType: String) = when (mimeType) {
+ MimeTypes.Png -> MimeTypes.Png
+ else -> MimeTypes.Jpeg
+}
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt
index 7258102eb3..95d3e2c4f8 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/ThumbnailFactory.kt
@@ -53,8 +53,11 @@ class ThumbnailFactory @Inject constructor(
private val sdkIntProvider: BuildVersionSdkIntProvider
) {
@SuppressLint("NewApi")
- suspend fun createImageThumbnail(file: File): ThumbnailResult? {
- return createThumbnail { cancellationSignal ->
+ suspend fun createImageThumbnail(
+ file: File,
+ mimeType: String,
+ ): ThumbnailResult? {
+ return createThumbnail(mimeType = mimeType) { cancellationSignal ->
try {
// This API works correctly with GIF
if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.Q)) {
@@ -83,7 +86,7 @@ class ThumbnailFactory @Inject constructor(
}
suspend fun createVideoThumbnail(file: File): ThumbnailResult? {
- return createThumbnail {
+ return createThumbnail(mimeType = MimeTypes.Jpeg) {
MediaMetadataRetriever().runAndRelease {
setDataSource(context, file.toUri())
getFrameAtTime(VIDEO_THUMB_FRAME)
@@ -91,7 +94,10 @@ class ThumbnailFactory @Inject constructor(
}
}
- private suspend fun createThumbnail(bitmapFactory: (CancellationSignal) -> Bitmap?): ThumbnailResult? = suspendCancellableCoroutine { continuation ->
+ private suspend fun createThumbnail(
+ mimeType: String,
+ bitmapFactory: (CancellationSignal) -> Bitmap?,
+ ): ThumbnailResult? = suspendCancellableCoroutine { continuation ->
val cancellationSignal = CancellationSignal()
continuation.invokeOnCancellation {
cancellationSignal.cancel()
@@ -101,9 +107,11 @@ class ThumbnailFactory @Inject constructor(
continuation.resume(null)
return@suspendCancellableCoroutine
}
- val thumbnailFile = context.createTmpFile(extension = "jpeg")
+ val format = mimeTypeToCompressFormat(mimeType)
+ val extension = mimeTypeToCompressFileExtension(mimeType)
+ val thumbnailFile = context.createTmpFile(extension = extension)
thumbnailFile.outputStream().use { outputStream ->
- bitmapThumbnail.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
+ bitmapThumbnail.compress(format, 78, outputStream)
}
val blurhash = BlurHash.encode(bitmapThumbnail, 3, 3)
val thumbnailResult = ThumbnailResult(
@@ -111,7 +119,7 @@ class ThumbnailFactory @Inject constructor(
info = ThumbnailInfo(
height = bitmapThumbnail.height.toLong(),
width = bitmapThumbnail.width.toLong(),
- mimetype = MimeTypes.Jpeg,
+ mimetype = mimeTypeToThumbnailMimeType(mimeType),
size = thumbnailFile.length()
),
blurhash = blurhash
diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt
index 760679b32f..8db2fd878a 100644
--- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt
+++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/VideoCompressor.kt
@@ -24,12 +24,20 @@ import javax.inject.Inject
class VideoCompressor @Inject constructor(
@ApplicationContext private val context: Context,
) {
- fun compress(uri: Uri) = callbackFlow {
+ fun compress(uri: Uri, shouldBeCompressed: Boolean) = callbackFlow {
val tmpFile = context.createTmpFile(extension = "mp4")
val future = Transcoder.into(tmpFile.path)
.setVideoTrackStrategy(
DefaultVideoStrategy.Builder()
- .addResizer(AtMostResizer(720, 480))
+ .addResizer(
+ AtMostResizer(
+ if (shouldBeCompressed) {
+ 720
+ } else {
+ 1080
+ }
+ )
+ )
.build()
)
.addDataSource(context, uri)
diff --git a/libraries/mediaupload/impl/src/test/assets/image.jpeg b/libraries/mediaupload/impl/src/test/assets/image.jpeg
new file mode 100644
index 0000000000..3d75513889
--- /dev/null
+++ b/libraries/mediaupload/impl/src/test/assets/image.jpeg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:77276f9b174f8823eaf787ab0a659199ef5d30c0361ec8b9b4f0890adb1907a1
+size 9986336
diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt
index 832d82d210..09e316bd2f 100644
--- a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt
+++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt
@@ -35,113 +35,194 @@ import kotlin.time.Duration
@RunWith(RobolectricTestRunner::class)
class AndroidMediaPreProcessorTest {
- @Test
- fun `test processing image`() = runTest {
+ private suspend fun TestScope.process(
+ asset: Asset,
+ compressIfPossible: Boolean,
+ sdkIntVersion: Int = Build.VERSION_CODES.P,
+ deleteOriginal: Boolean = false,
+ ): MediaUploadInfo {
val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "image.png")
+ val sut = createAndroidMediaPreProcessor(context, sdkIntVersion)
+ val file = getFileFromAssets(context, asset.filename)
val result = sut.process(
uri = file.toUri(),
- mimeType = MimeTypes.Png,
- deleteOriginal = false,
- compressIfPossible = true,
+ mimeType = asset.mimeType,
+ deleteOriginal = deleteOriginal,
+ compressIfPossible = compressIfPossible,
)
val data = result.getOrThrow()
- assertThat(data.file.path).endsWith("image.png")
- val info = data as MediaUploadInfo.Image
- assertThat(info.thumbnailFile).isNotNull()
- assertThat(info.imageInfo).isEqualTo(
- ImageInfo(
- height = 1_178,
- width = 1_818,
- mimetype = MimeTypes.Png,
- size = 109_908,
- ThumbnailInfo(height = 294, width = 454, mimetype = "image/jpeg", size = 4484),
- thumbnailSource = null,
- blurhash = "K13]7q%zWC00R4of%\$baad"
- )
- )
- assertThat(file.exists()).isTrue()
+ assertThat(data.file.path).endsWith(asset.filename)
+ return data
}
@Test
- fun `test processing image api Q`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context, sdkIntVersion = Build.VERSION_CODES.Q)
- val file = getFileFromAssets(context, "image.png")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Png,
- deleteOriginal = false,
+ fun `test processing png`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImagePng,
compressIfPossible = true,
)
- val data = result.getOrThrow()
- assertThat(data.file.path).endsWith("image.png")
- val info = data as MediaUploadInfo.Image
+ val info = mediaUploadInfo as MediaUploadInfo.Image
+ assertThat(info.thumbnailFile).isNotNull()
+ assertThat(info.imageInfo).isEqualTo(
+ ImageInfo(
+ height = assetImagePng.height,
+ width = assetImagePng.width,
+ mimetype = assetImagePng.mimeType,
+ size = 2_026_433,
+ ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Png, size = 91),
+ thumbnailSource = null,
+ blurhash = "K00000fQfQfQfQfQfQfQfQ"
+ )
+ )
+ }
+
+ @Test
+ fun `test processing png api Q`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImagePng,
+ compressIfPossible = true,
+ sdkIntVersion = Build.VERSION_CODES.Q,
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
assertThat(info.thumbnailFile).isNull()
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
- height = 1_178,
- width = 1_818,
- mimetype = MimeTypes.Png,
- size = 109_908,
+ height = assetImagePng.height,
+ width = assetImagePng.width,
+ mimetype = assetImagePng.mimeType,
+ size = 2_026_433,
thumbnailInfo = null,
thumbnailSource = null,
blurhash = null,
)
)
- assertThat(file.exists()).isTrue()
}
@Test
- fun `test processing image no compression`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "image.png")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Png,
- deleteOriginal = false,
+ fun `test processing png no compression`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImagePng,
compressIfPossible = false,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("image.png")
- val info = result as MediaUploadInfo.Image
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
- height = 1_178,
- width = 1_818,
- mimetype = MimeTypes.Png,
- size = 1_856_786,
- thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643),
+ height = assetImagePng.height,
+ width = assetImagePng.width,
+ mimetype = assetImagePng.mimeType,
+ size = assetImagePng.size,
+ thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Png, size = 91),
thumbnailSource = null,
blurhash = "K00000fQfQfQfQfQfQfQfQ",
)
)
- assertThat(file.exists()).isTrue()
}
@Test
- fun `test processing image and delete`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "image.png")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Png,
- deleteOriginal = true,
+ fun `test processing png and delete`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImagePng,
compressIfPossible = false,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("image.png")
- val info = result as MediaUploadInfo.Image
+ deleteOriginal = true,
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
- height = 1_178,
- width = 1_818,
- mimetype = MimeTypes.Png,
- size = 1_856_786,
- thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643),
+ height = assetImagePng.height,
+ width = assetImagePng.width,
+ mimetype = assetImagePng.mimeType,
+ size = assetImagePng.size,
+ thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Png, size = 91),
+ thumbnailSource = null,
+ blurhash = "K00000fQfQfQfQfQfQfQfQ",
+ )
+ )
+ // Does not work
+ // assertThat(file.exists()).isFalse()
+ }
+
+ @Test
+ fun `test processing jpeg`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImageJpeg,
+ compressIfPossible = true,
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
+ assertThat(info.thumbnailFile).isNotNull()
+ assertThat(info.imageInfo).isEqualTo(
+ ImageInfo(
+ height = 979,
+ width = 3006,
+ mimetype = MimeTypes.Jpeg,
+ size = 84_845,
+ ThumbnailInfo(height = 244, width = 751, mimetype = MimeTypes.Jpeg, size = 7_178),
+ thumbnailSource = null,
+ blurhash = "K07gBzX=j_D4xZjoaSe,s:"
+ )
+ )
+ }
+
+ @Test
+ fun `test processing jpeg api Q`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImageJpeg,
+ compressIfPossible = true,
+ sdkIntVersion = Build.VERSION_CODES.Q,
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
+ assertThat(info.thumbnailFile).isNull()
+ assertThat(info.imageInfo).isEqualTo(
+ ImageInfo(
+ height = 979,
+ width = 3_006,
+ mimetype = MimeTypes.Jpeg,
+ size = 84_845,
+ thumbnailInfo = null,
+ thumbnailSource = null,
+ blurhash = null,
+ )
+ )
+ }
+
+ @Test
+ fun `test processing jpeg no compression`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImageJpeg,
+ compressIfPossible = false,
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
+ assertThat(info.thumbnailFile).isNotNull()
+ assertThat(info.imageInfo).isEqualTo(
+ ImageInfo(
+ height = assetImageJpeg.height,
+ width = assetImageJpeg.width,
+ mimetype = assetImageJpeg.mimeType,
+ size = assetImageJpeg.size,
+ thumbnailInfo = ThumbnailInfo(height = 6, width = 6, mimetype = MimeTypes.Jpeg, size = 631),
+ thumbnailSource = null,
+ blurhash = "K00000fQfQfQfQfQfQfQfQ",
+ )
+ )
+ }
+
+ @Test
+ fun `test processing jpeg and delete`() = runTest {
+ val mediaUploadInfo = process(
+ asset = assetImageJpeg,
+ compressIfPossible = false,
+ deleteOriginal = true,
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
+ assertThat(info.thumbnailFile).isNotNull()
+ assertThat(info.imageInfo).isEqualTo(
+ ImageInfo(
+ height = assetImageJpeg.height,
+ width = assetImageJpeg.width,
+ mimetype = assetImageJpeg.mimeType,
+ size = assetImageJpeg.size,
+ thumbnailInfo = ThumbnailInfo(height = 6, width = 6, mimetype = MimeTypes.Jpeg, size = 631),
thumbnailSource = null,
blurhash = "K00000fQfQfQfQfQfQfQfQ",
)
@@ -152,70 +233,50 @@ class AndroidMediaPreProcessorTest {
@Test
fun `test processing gif`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "animated_gif.gif")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Gif,
- deleteOriginal = false,
+ val mediaUploadInfo = process(
+ asset = assetAnimatedGif,
compressIfPossible = true,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("animated_gif.gif")
- val info = result as MediaUploadInfo.Image
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Image
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
- height = 600,
- width = 800,
- mimetype = MimeTypes.Gif,
- size = 687_979,
+ height = assetAnimatedGif.height,
+ width = assetAnimatedGif.width,
+ mimetype = assetAnimatedGif.mimeType,
+ size = assetAnimatedGif.size,
thumbnailInfo = ThumbnailInfo(height = 50, width = 50, mimetype = MimeTypes.Jpeg, size = 691),
thumbnailSource = null,
blurhash = "K00000fQfQfQfQfQfQfQfQ",
)
)
- assertThat(file.exists()).isTrue()
}
@Test
fun `test processing file`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "text.txt")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.PlainText,
- deleteOriginal = false,
+ val mediaUploadInfo = process(
+ asset = assetText,
compressIfPossible = true,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("text.txt")
- val info = result as MediaUploadInfo.AnyFile
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.AnyFile
assertThat(info.fileInfo).isEqualTo(
FileInfo(
- mimetype = MimeTypes.PlainText,
- size = 13,
+ mimetype = assetText.mimeType,
+ size = assetText.size,
thumbnailInfo = null,
thumbnailSource = null,
)
)
- assertThat(file.exists()).isTrue()
}
@Ignore("Compressing video is not working with Robolectric")
@Test
fun `test processing video`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "video.mp4")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Mp4,
- deleteOriginal = false,
+ val mediaUploadInfo = process(
+ asset = assetVideo,
compressIfPossible = true,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("video.mp4")
- val info = result as MediaUploadInfo.Video
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Video
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.videoInfo).isEqualTo(
VideoInfo(
@@ -230,22 +291,16 @@ class AndroidMediaPreProcessorTest {
blurhash = null,
)
)
- assertThat(file.exists()).isTrue()
}
+ @Ignore("Compressing video is not working with Robolectric")
@Test
fun `test processing video no compression`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "video.mp4")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Mp4,
- deleteOriginal = false,
+ val mediaUploadInfo = process(
+ asset = assetVideo,
compressIfPossible = false,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("video.mp4")
- val info = result as MediaUploadInfo.Video
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Video
// Computing thumbnailFile is failing with Robolectric
assertThat(info.thumbnailFile).isNull()
assertThat(info.videoInfo).isEqualTo(
@@ -263,22 +318,15 @@ class AndroidMediaPreProcessorTest {
blurhash = null,
)
)
- assertThat(file.exists()).isTrue()
}
@Test
fun `test processing audio`() = runTest {
- val context = InstrumentationRegistry.getInstrumentation().context
- val sut = createAndroidMediaPreProcessor(context)
- val file = getFileFromAssets(context, "sample3s.mp3")
- val result = sut.process(
- uri = file.toUri(),
- mimeType = MimeTypes.Mp3,
- deleteOriginal = false,
+ val mediaUploadInfo = process(
+ asset = assetAudio,
compressIfPossible = true,
- ).getOrThrow()
- assertThat(result.file.path).endsWith("sample3s.mp3")
- val info = result as MediaUploadInfo.Audio
+ )
+ val info = mediaUploadInfo as MediaUploadInfo.Audio
assertThat(info.audioInfo).isEqualTo(
AudioInfo(
// Not available with Robolectric?
@@ -287,7 +335,6 @@ class AndroidMediaPreProcessorTest {
mimetype = MimeTypes.Mp3,
)
)
- assertThat(file.exists()).isTrue()
}
@Test
diff --git a/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/Asset.kt b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/Asset.kt
new file mode 100644
index 0000000000..d923d9977a
--- /dev/null
+++ b/libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/Asset.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * Please see LICENSE in the repository root for full details.
+ */
+
+package io.element.android.libraries.mediaupload.impl
+
+import io.element.android.libraries.core.mimetype.MimeTypes
+
+data class Asset(
+ val filename: String,
+ val mimeType: String,
+ val size: Long,
+ val width: Long?,
+ val height: Long?,
+)
+
+/**
+ * "image.png" is a 1_818 x 1_178 PNG image with a size of 1_856_786 bytes.
+ */
+val assetImagePng = Asset(
+ filename = "image.png",
+ mimeType = MimeTypes.Png,
+ size = 1_856_786,
+ width = 1_818,
+ height = 1_178,
+)
+
+/**
+ * "image.jpeg" is a 12_024 x 3_916, JPEG image with a size of 9_986_336 bytes.
+ */
+val assetImageJpeg = Asset(
+ filename = "image.jpeg",
+ mimeType = MimeTypes.Jpeg,
+ size = 9_986_336,
+ width = 12_024,
+ height = 3_916,
+)
+
+/**
+ * "video.mp4" is a 1_280 x 720, MP4 video with a size of 1_673_712 bytes.
+ */
+val assetVideo = Asset(
+ filename = "video.mp4",
+ mimeType = MimeTypes.Mp4,
+ size = 1_673_712,
+ width = 1_280,
+ height = 720,
+)
+
+/**
+ * "sample3s.mp3" is a 3 seconds MP3 audio file with a size of 52_079 bytes.
+ */
+val assetAudio = Asset(
+ filename = "sample3s.mp3",
+ mimeType = MimeTypes.Mp3,
+ size = 52_079,
+ width = null,
+ height = null,
+)
+
+/**
+ * "text.txt" is a 13 bytes text file.
+ */
+val assetText = Asset(
+ filename = "text.txt",
+ mimeType = MimeTypes.PlainText,
+ size = 13,
+ width = null,
+ height = null,
+)
+
+/**
+ * "animated_gif.gif" is a 800 x 600, GIF image with a size of 687_979 bytes.
+ */
+val assetAnimatedGif = Asset(
+ filename = "animated_gif.gif",
+ mimeType = MimeTypes.Gif,
+ size = 687_979,
+ width = 800,
+ height = 600,
+)
diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt
index 3fa33eab93..32d92a4a76 100644
--- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt
+++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStore.kt
@@ -83,7 +83,7 @@ class DefaultSessionPreferencesStore(
override fun isSessionVerificationSkipped(): Flow = get(skipSessionVerification) { false }
override suspend fun setCompressMedia(compress: Boolean) = update(compressMedia, compress)
- override fun doesCompressMedia(): Flow = get(compressMedia) { false }
+ override fun doesCompressMedia(): Flow = get(compressMedia) { true }
override suspend fun clear() {
dataStoreFile.safeDelete()
diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt
index dde117adc0..7bb2258c6e 100644
--- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt
+++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemorySessionPreferencesStore.kt
@@ -18,7 +18,7 @@ class InMemorySessionPreferencesStore(
isSendTypingNotificationsEnabled: Boolean = true,
isRenderTypingNotificationsEnabled: Boolean = true,
isSessionVerificationSkipped: Boolean = false,
- doesCompressMedia: Boolean = false,
+ doesCompressMedia: Boolean = true,
) : SessionPreferencesStore {
private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled)
private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled)
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png
index 74f6d0bd9d..a38b090e83 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:13e7793d8dd6d08e182b128a9b3dac2877557e8bdd220561d36c2ce1650b94ff
-size 48107
+oid sha256:cd172b454fdaf7966e8c85481959000c28310ab2ffccd1b13dd9af3b1e392a3d
+size 54935
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png
index 889f655996..34d54ae010 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1bac5247c3a4990eb9155a21f72a49059cbaa93288380ba1ab6be2def8b3a6a9
-size 47876
+oid sha256:cff335c3b36ad364bf237b5635577da8f5c82e86bdd35bfb1c138e222e616640
+size 54698
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png
index 1fec0eae29..348c17ddf4 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:11c969c1d04150cef68da64865bade2ea3bfc1aa5f0d790262315131b57d6233
-size 31702
+oid sha256:397049b4a74bd122c09e1e16d4ee72dcf5abc6562bccfe839d394388b1b0a2b9
+size 31741
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png
index 0ab8ad267c..4b27052cd0 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dc1aa9348e470e9d7e7e1e838e18a3403159c2effb49fa359b2b2db97dd81961
-size 47901
+oid sha256:7b5cee26851a31850647b550225071bd0a5c4d16026b42771f43e395fc1f0dcc
+size 54766
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png
index a1acf97a47..4a1809c127 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:90d1184879c5a34dc27cc942ec3110e14ccd9a90c152b10a1844fde0566d54fe
-size 47841
+oid sha256:21378e572926adcafa78ecbf16042f1abab81790ead55c2da6134d84d3e88145
+size 54742
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png
index 1ca045272d..dd123faa03 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c2beb4f4f190f6aca2d4da338d980e8611283ffb9bfd2f402482f8ecc629cf22
-size 46759
+oid sha256:9dcd26f37cea379f17d4b7d35b66c8dabdef2b83afe168f261c7007149ac7044
+size 53658
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png
index 52825bc9d6..5a1be87eff 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b65acfd3126efc5cd4fe1a929a786468cc18ee371cb4462ef0cf9f6e8963fcea
-size 46456
+oid sha256:d38e06cbbabfd6fdab433008ec2249f2ca3838a9a95a5f6300e79fce5b805733
+size 53366
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png
index 528327c715..80fb2f3e56 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8e90bbde9aac7e710703e836f293a00b2a5f35447d4d63c9732de21a0f291449
-size 29336
+oid sha256:2836655a2f3ef9aca1f2ba94222bc92f9bedab82589169e34ea0a325cb1c0e52
+size 29363
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png
index 5a3f3c1ed3..53a47e0baf 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6da0cf1729162fa1745bcb4dd86be06f90d3bcf6bf034e4a64e1ee9119b6cdd5
-size 46501
+oid sha256:6823de7de81b167d27c1c9c8aadab6b027976673552590f1dbea9dad7748919f
+size 53413
diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png
index 387f0e65d3..e549902114 100644
--- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png
+++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:66109868b1e893bc569d70110e3e587d7a17d777838cfc3e5c3d3189338d924e
-size 46423
+oid sha256:fc4b71bbb0d276540c887a4c6869eca003ff500ac1d8b319d63483be21cc04d0
+size 53377