From 6cce74db88efdb2be2650c1e831ff7525efa43ad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 Jun 2026 16:37:58 +0200 Subject: [PATCH] [Gallery] Set media optimation user choice per media --- .../preview/AttachmentsPreviewPresenter.kt | 152 +++++++++++------- .../AttachmentsPreviewStateProvider.kt | 1 + .../preview/AttachmentsPreviewView.kt | 2 +- ...faultMediaOptimizationSelectorPresenter.kt | 3 + .../MediaOptimizationSelectorPresenter.kt | 1 + .../video/MediaOptimizationSelectorState.kt | 2 + .../AttachmentsPreviewPresenterTest.kt | 21 +++ ...tMediaOptimizationSelectorPresenterTest.kt | 2 + ...diaOptimizationSelectorPresenterFactory.kt | 3 +- 9 files changed, 126 insertions(+), 61 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index ef85141df1..d60de004d9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -28,6 +28,8 @@ import io.element.android.features.messages.impl.attachments.preview.imageeditor import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEditorState import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEdits import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorPresenter +import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState +import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.androidutils.hash.hash @@ -69,6 +71,7 @@ class AttachmentsPreviewPresenter( private val temporaryUriDeleter: TemporaryUriDeleter, private val attachmentImageEditor: AttachmentImageEditor, private val mediaOptimizationSelectorPresenterFactory: MediaOptimizationSelectorPresenter.Factory, + private val videoCompressionPresetSelector: VideoCompressionPresetSelector, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val dispatchers: CoroutineDispatchers, private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider, @@ -97,9 +100,7 @@ class AttachmentsPreviewPresenter( val sendActionState = remember { mutableStateOf(SendActionState.Idle) } - val originalLocalMedia = remember { (attachments.first() as Attachment.Media).localMedia } - var currentAttachment by remember { mutableStateOf(attachments.first()) } - var canEditImage by remember { mutableStateOf(originalLocalMedia.info.canEditImage()) } + var canEditImage by remember { mutableStateOf(false) } var imageEditorState by remember { mutableStateOf(null) } var isApplyingImageEdits by remember { mutableStateOf(false) } var displayImageEditError by remember { mutableStateOf(false) } @@ -130,37 +131,57 @@ class AttachmentsPreviewPresenter( var preprocessMediaJob by remember { mutableStateOf(null) } - val firstMediaAttachment = attachments.first() as Attachment.Media - val mediaOptimizationSelectorPresenter = remember { - mediaOptimizationSelectorPresenterFactory.create( - localMedia = firstMediaAttachment.localMedia, - sendAsFile = firstMediaAttachment.sendAsFile, - ) + val mediaOptimizationSelectorPresenters = remember { + attachments + .filterIsInstance() + .mapIndexed { index, attachment -> + mediaOptimizationSelectorPresenterFactory.create( + index = index, + localMedia = attachment.localMedia, + sendAsFile = attachment.sendAsFile, + ) + } } - val mediaOptimizationSelectorState by rememberUpdatedState(mediaOptimizationSelectorPresenter.present()) + val mediaOptimizationSelectorStates by rememberUpdatedState( + mediaOptimizationSelectorPresenters.map { + it.present() + }.toImmutableList() + ) val observableSendState = snapshotFlow { sendActionState.value } var displayFileTooLargeError by remember { mutableStateOf(false) } - LaunchedEffect(mediaOptimizationSelectorState.displayMediaSelectorViews, mediaOptimizationSelectorState.selectedVideoPreset) { - if (mediaOptimizationSelectorState.displayMediaSelectorViews == false && - imageEditorState == null && - !isApplyingImageEdits + LaunchedEffect( + mediaOptimizationSelectorStates, + imageEditorState, + isApplyingImageEdits, + editedAttachments, + ) { + if (mediaOptimizationSelectorStates.any { it.displayMediaSelectorViews == true } || + imageEditorState != null || + isApplyingImageEdits ) { - val config = MediaOptimizationConfig( - compressImages = mediaOptimizationSelectorState.isImageOptimizationEnabled ?: mediaOptimizationConfigProvider.get().compressImages, - videoCompressionPreset = mediaOptimizationSelectorState.selectedVideoPreset ?: mediaOptimizationConfigProvider.get().videoCompressionPreset, + // If any of the media optimization selectors are displayed, we don't want to pre-process the media yet + return@LaunchedEffect + } + // If the media optimization selector is not displayed, we can pre-process the media + // to prepare it for sending. This is done to avoid blocking the UI thread when the + // user clicks on the send button. + val configs = mediaOptimizationSelectorStates.mapIndexed { index, mediaOptimizationSelectorState -> + getAutoPreprocessMediaOptimizationConfig( + mediaAttachment = editedAttachments[index] as Attachment.Media, + mediaOptimizationSelectorState = mediaOptimizationSelectorState, + ) + } + preprocessMediaJob?.cancel() + preprocessMediaJob = coroutineScope.launch(dispatchers.io) { + preProcessAttachments( + attachments = editedAttachments, + mediaOptimizationConfigs = configs, + displayProgress = false, + sendActionState = sendActionState, ) - preprocessMediaJob?.cancel() - preprocessMediaJob = coroutineScope.launch(dispatchers.io) { - preProcessAttachments( - attachments = editedAttachments, - mediaOptimizationConfig = config, - displayProgress = false, - sendActionState = sendActionState, - ) - } } } @@ -171,7 +192,9 @@ class AttachmentsPreviewPresenter( } } - val maxUploadSize = mediaOptimizationSelectorState.maxUploadSize.dataOrNull() + val maxUploadSize = mediaOptimizationSelectorStates.firstNotNullOfOrNull { + it.maxUploadSize.dataOrNull() + } LaunchedEffect(maxUploadSize) { if (maxUploadSize != null) { // If file size is not known, we're permissive and allow sending. The SDK will cancel the upload if needed. @@ -192,11 +215,13 @@ class AttachmentsPreviewPresenter( } } - val videoSizeEstimations = mediaOptimizationSelectorState.videoSizeEstimations.dataOrNull() - LaunchedEffect(videoSizeEstimations) { - if (videoSizeEstimations != null) { - // Check if the video size estimations are too large for the max upload size - displayFileTooLargeError = videoSizeEstimations.none { it.canUpload } + mediaOptimizationSelectorStates.forEach { mediaOptimizationSelectorState -> + val videoSizeEstimations = mediaOptimizationSelectorState.videoSizeEstimations.dataOrNull() + LaunchedEffect(videoSizeEstimations) { + if (videoSizeEstimations != null) { + // Check if the video size estimations are too large for the max upload size + displayFileTooLargeError = videoSizeEstimations.none { it.canUpload } + } } } @@ -204,32 +229,19 @@ class AttachmentsPreviewPresenter( when (event) { is AttachmentsPreviewEvent.SendAttachment -> { ongoingSendAttachmentJob.value = coroutineScope.launch { - // If the media optimization selector is displayed, we need to wait for the user to select the options - // before we can pre-process the media. - if (mediaOptimizationSelectorState.displayMediaSelectorViews == true) { - val config = MediaOptimizationConfig( - compressImages = mediaOptimizationSelectorState.isImageOptimizationEnabled == true, - videoCompressionPreset = mediaOptimizationSelectorState.selectedVideoPreset ?: VideoCompressionPreset.STANDARD, - ) - preprocessMediaJob = coroutineScope.launch(dispatchers.io) { - preProcessAttachments( - attachments = editedAttachments, - mediaOptimizationConfig = config, - displayProgress = true, - sendActionState = sendActionState, + if (preprocessMediaJob?.isActive != true && sendActionState.value !is SendActionState.Sending.ReadyToUpload) { + val configs = mediaOptimizationSelectorStates.map { + MediaOptimizationConfig( + compressImages = it.isImageOptimizationEnabled + ?: mediaOptimizationConfigProvider.get().compressImages, + videoCompressionPreset = it.selectedVideoPreset + ?: mediaOptimizationConfigProvider.get().videoCompressionPreset, ) } - } else if (preprocessMediaJob?.isActive != true && sendActionState.value !is SendActionState.Sending.ReadyToUpload) { - val config = MediaOptimizationConfig( - compressImages = mediaOptimizationSelectorState.isImageOptimizationEnabled - ?: mediaOptimizationConfigProvider.get().compressImages, - videoCompressionPreset = mediaOptimizationSelectorState.selectedVideoPreset - ?: mediaOptimizationConfigProvider.get().videoCompressionPreset, - ) preprocessMediaJob = coroutineScope.launch(dispatchers.io) { preProcessAttachments( attachments = editedAttachments, - mediaOptimizationConfig = config, + mediaOptimizationConfigs = configs, displayProgress = true, sendActionState = sendActionState, ) @@ -350,7 +362,7 @@ class AttachmentsPreviewPresenter( if (!pendingState.edits.hasChanges) { editedTempFiles[currentIndex]?.safeDelete() editedTempFiles = editedTempFiles - currentIndex - currentAttachment = attachmentsAndEdits[currentIndex].attachment + val currentAttachment = attachmentsAndEdits[currentIndex].attachment attachmentsAndEdits = attachmentsAndEdits.toMutableList().also { it[currentIndex] = AttachmentAndEdits( currentAttachment, @@ -374,7 +386,7 @@ class AttachmentsPreviewPresenter( onSuccess = { editedMedia -> editedTempFiles[currentIndex]?.safeDelete() editedTempFiles = editedTempFiles + (currentIndex to editedMedia.file) - currentAttachment = Attachment.Media(editedMedia.localMedia) + val currentAttachment = Attachment.Media(editedMedia.localMedia) attachmentsAndEdits = attachmentsAndEdits.toMutableList().also { it[currentIndex] = AttachmentAndEdits( currentAttachment, @@ -409,28 +421,50 @@ class AttachmentsPreviewPresenter( displayImageEditError = displayImageEditError, sendActionState = sendActionState.value, textEditorState = textEditorState, - mediaOptimizationSelectorState = mediaOptimizationSelectorState, + mediaOptimizationSelectorState = mediaOptimizationSelectorStates[currentIndex], displayFileTooLargeError = displayFileTooLargeError, currentIndex = currentIndex, eventSink = ::handleEvent, ) } + private suspend fun getAutoPreprocessMediaOptimizationConfig( + mediaAttachment: Attachment.Media, + mediaOptimizationSelectorState: MediaOptimizationSelectorState, + ): MediaOptimizationConfig { + return if (mediaAttachment.sendAsFile) { + // If we're sending the media as a file, we can skip image compression and we should select the highest video compression preset that still fits + // the upload limit (if the estimations are available) + val videoCompressionPreset = videoCompressionPresetSelector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = mediaOptimizationSelectorState.videoSizeEstimations, + ).dataOrNull() ?: VideoCompressionPreset.HIGH + + MediaOptimizationConfig( + compressImages = false, + videoCompressionPreset = videoCompressionPreset, + ) + } else { + // Otherwise, we just rely on the user preferences for media optimization + mediaOptimizationConfigProvider.get() + } + } + private suspend fun preProcessAttachments( attachments: List, - mediaOptimizationConfig: MediaOptimizationConfig, + mediaOptimizationConfigs: List, displayProgress: Boolean, sendActionState: MutableState, ) { sendActionState.value = SendActionState.Sending.Processing(displayProgress = displayProgress) val mediaUploadInfos = mutableListOf() - for (attachment in attachments) { + attachments.forEachIndexed { index, attachment -> when (attachment) { is Attachment.Media -> { mediaSender.preProcessMedia( uri = attachment.localMedia.uri, mimeType = attachment.localMedia.info.mimeType, - mediaOptimizationConfig = mediaOptimizationConfig, + mediaOptimizationConfig = mediaOptimizationConfigs[index], ).fold( onSuccess = { mediaUploadInfo -> Timber.d("Media ${mediaUploadInfo.file.path.orEmpty().hash()} finished processing") diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index f786e9526b..a7d4e14436 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -146,6 +146,7 @@ fun aMediaOptimisationSelectorState( displayMediaSelectorViews: Boolean = true, displayVideoPresetSelectorDialog: Boolean = false, ) = MediaOptimizationSelectorState( + index = 0, maxUploadSize = AsyncData.Success(maxUploadSize), videoSizeEstimations = videoSizeEstimations, isImageOptimizationEnabled = isImageOptimizationEnabled, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 1b36c469fe..4a14c11f4b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -346,7 +346,7 @@ private fun AttachmentPreviewContent( } } } - val mediaInfo = (state.attachments.first() as? Attachment.Media)?.localMedia?.info + val mediaInfo = (state.attachments[state.currentIndex] as? Attachment.Media)?.localMedia?.info if (mediaInfo?.isImageAttachment() == true) { ImageOptimizationSelector(state.mediaOptimizationSelectorState) } else if (mediaInfo?.mimeType?.isMimeTypeVideo() == true) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt index abc0264b2f..c194bda507 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt @@ -36,6 +36,7 @@ import kotlin.math.roundToLong @AssistedInject class DefaultMediaOptimizationSelectorPresenter( + @Assisted private val index: Int, @Assisted private val localMedia: LocalMedia, @Assisted private val sendAsFile: Boolean, private val maxUploadSizeProvider: MaxUploadSizeProvider, @@ -48,6 +49,7 @@ class DefaultMediaOptimizationSelectorPresenter( @AssistedFactory interface Factory : MediaOptimizationSelectorPresenter.Factory { override fun create( + index: Int, localMedia: LocalMedia, sendAsFile: Boolean, ): DefaultMediaOptimizationSelectorPresenter @@ -183,6 +185,7 @@ class DefaultMediaOptimizationSelectorPresenter( } return MediaOptimizationSelectorState( + index = index, maxUploadSize = maxUploadSize, videoSizeEstimations = videoSizeEstimations, isImageOptimizationEnabled = selectedImageOptimization.dataOrNull(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt index f1e17ef0a6..6c97557c37 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt @@ -14,6 +14,7 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia fun interface MediaOptimizationSelectorPresenter : Presenter { interface Factory { fun create( + index: Int, localMedia: LocalMedia, sendAsFile: Boolean, ): MediaOptimizationSelectorPresenter diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorState.kt index 29e51d39af..bc8bf90d55 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorState.kt @@ -13,6 +13,8 @@ import io.element.android.libraries.preferences.api.store.VideoCompressionPreset import kotlinx.collections.immutable.ImmutableList data class MediaOptimizationSelectorState( + // Adding an index helps to fix a Compose issue where the state of the wrong item is updated + val index: Int, val maxUploadSize: AsyncData, val videoSizeEstimations: AsyncData>, val isImageOptimizationEnabled: Boolean?, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 485c76ead3..8370ec516f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.attachments.preview.imageeditor import io.element.android.features.messages.impl.attachments.preview.imageeditor.NormalizedCropRect import io.element.android.features.messages.impl.attachments.preview.imageeditor.assertIsSimilarTo import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState +import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector import io.element.android.features.messages.impl.attachments.video.VideoUploadEstimation import io.element.android.features.messages.impl.fixtures.aMediaAttachment import io.element.android.features.messages.test.attachments.video.FakeMediaOptimizationSelectorPresenterFactory @@ -93,6 +94,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { @Test fun `present - initial state`() = runTest { createAttachmentsPreviewPresenter().test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) } @@ -117,6 +119,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) @@ -151,6 +154,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) // Pre-processing finishes @@ -187,6 +191,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) @@ -216,6 +221,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.eventSink(AttachmentsPreviewEvent.SendAttachment) @@ -239,6 +245,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) // Pre-processing finishes @@ -261,6 +268,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.eventSink(AttachmentsPreviewEvent.CancelAndDismiss) @@ -292,6 +300,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.textEditorState.setMarkdown(A_CAPTION) @@ -333,6 +342,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.textEditorState.setMarkdown(A_CAPTION) @@ -374,6 +384,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = { onDoneListener() }, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.textEditorState.setMarkdown(A_CAPTION) @@ -408,6 +419,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { ) val presenter = createAttachmentsPreviewPresenter(room = room, onDoneListener = onDoneListenerResult) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.eventSink(AttachmentsPreviewEvent.SendAttachment) @@ -437,6 +449,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = onDoneListenerResult, ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.eventSink(AttachmentsPreviewEvent.SendAttachment) @@ -469,6 +482,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = onDoneListenerResult, mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory { MediaOptimizationSelectorState( + index = 0, // Set a max upload size smaller than the file size maxUploadSize = AsyncData.Success(maxUploadSize), videoSizeEstimations = AsyncData.Uninitialized, @@ -508,6 +522,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { onDoneListener = onDoneListenerResult, mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory { MediaOptimizationSelectorState( + index = 0, // Set a max upload size smaller than the file size maxUploadSize = AsyncData.Success(Long.MAX_VALUE), videoSizeEstimations = AsyncData.Success( @@ -565,6 +580,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { ) presenter.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) val editorState = awaitItem() @@ -775,6 +791,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { attachments = listOf(Attachment.Media(localMedia)), ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.canEditImage).isTrue() @@ -833,6 +850,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { displayMediaQualitySelectorViews = false, mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory { MediaOptimizationSelectorState( + index = 0, maxUploadSize = AsyncData.Success(250_000_000L), videoSizeEstimations = AsyncData.Success( persistentListOf( @@ -933,6 +951,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { mediaOptimizationSelectorPresenterFactory: FakeMediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory( fakePresenter = { MediaOptimizationSelectorState( + index = 0, maxUploadSize = AsyncData.Uninitialized, videoSizeEstimations = AsyncData.Uninitialized, isImageOptimizationEnabled = null, @@ -957,6 +976,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { Result.failure(IllegalStateException("Check test values")) } }, + videoCompressionPresetSelector: VideoCompressionPresetSelector = VideoCompressionPresetSelector(), ): AttachmentsPreviewPresenter { return AttachmentsPreviewPresenter( attachments = attachments.toImmutableList(), @@ -977,6 +997,7 @@ class AttachmentsPreviewPresenterTest : RobolectricTest() { sessionCoroutineScope = this, dispatchers = testCoroutineDispatchers(), mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory, + videoCompressionPresetSelector = videoCompressionPresetSelector, timelineMode = timelineMode, inReplyToEventId = null, mediaOptimizationConfigProvider = mediaOptimizationConfigProvider, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt index 99483bdac4..83c5c5f575 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt @@ -274,6 +274,7 @@ class DefaultMediaOptimizationSelectorPresenterTest : RobolectricTest() { } private fun createDefaultMediaOptimizationSelectorPresenter( + index: Int = 0, localMedia: LocalMedia = aLocalMedia(mockMediaUrl, aVideoMediaInfo()), maxUploadSizeProvider: MaxUploadSizeProvider = MaxUploadSizeProvider { Result.success(1_000L) }, featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SelectableMediaQuality.key to true)), @@ -283,6 +284,7 @@ class DefaultMediaOptimizationSelectorPresenterTest : RobolectricTest() { sendAsFile: Boolean = false, ): DefaultMediaOptimizationSelectorPresenter { return DefaultMediaOptimizationSelectorPresenter( + index = index, localMedia = localMedia, sendAsFile = sendAsFile, maxUploadSizeProvider = maxUploadSizeProvider, diff --git a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt index fff3ede5d3..af068a14c2 100644 --- a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt +++ b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt @@ -16,6 +16,7 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia class FakeMediaOptimizationSelectorPresenterFactory( private val fakePresenter: MediaOptimizationSelectorPresenter = MediaOptimizationSelectorPresenter { MediaOptimizationSelectorState( + index = 0, maxUploadSize = AsyncData.Uninitialized, videoSizeEstimations = AsyncData.Uninitialized, isImageOptimizationEnabled = null, @@ -26,7 +27,7 @@ class FakeMediaOptimizationSelectorPresenterFactory( ) } ) : MediaOptimizationSelectorPresenter.Factory { - override fun create(localMedia: LocalMedia, sendAsFile: Boolean): MediaOptimizationSelectorPresenter { + override fun create(index: Int, localMedia: LocalMedia, sendAsFile: Boolean): MediaOptimizationSelectorPresenter { return fakePresenter } }