diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 550f859581..1e9f9636c4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -65,6 +65,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import io.element.android.libraries.androidutils.ui.hideKeyboard import io.element.android.libraries.designsystem.components.ProgressDialog +import io.element.android.libraries.designsystem.components.ProgressDialogType import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.button.BackButton @@ -216,7 +217,15 @@ private fun AttachmentStateView( is AttachmentsState.Previewing -> LaunchedEffect(state) { onPreviewAttachments(state.attachments) } - is AttachmentsState.Sending -> ProgressDialog(text = stringResource(id = CommonStrings.common_loading)) + is AttachmentsState.Sending -> { + ProgressDialog( + type = when (state) { + is AttachmentsState.Sending.Uploading -> ProgressDialogType.Determinate(state.progress) + is AttachmentsState.Sending.Processing -> ProgressDialogType.Indeterminate + }, + text = stringResource(id = CommonStrings.common_sending) + ) + } } } 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 9761a939b3..3ee87c0bc8 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 @@ -25,9 +25,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment -import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.architecture.runUpdatingState +import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.mediaupload.api.MediaSender import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -48,13 +47,13 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( val coroutineScope = rememberCoroutineScope() val sendActionState = remember { - mutableStateOf>(Async.Uninitialized) + mutableStateOf(SendActionState.Idle) } fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) { when (attachmentsPreviewEvents) { AttachmentsPreviewEvents.SendAttachment -> coroutineScope.sendAttachment(attachment, sendActionState) - AttachmentsPreviewEvents.ClearSendState -> sendActionState.value = Async.Uninitialized + AttachmentsPreviewEvents.ClearSendState -> sendActionState.value = SendActionState.Idle } } @@ -67,7 +66,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( private fun CoroutineScope.sendAttachment( attachment: Attachment, - sendActionState: MutableState>, + sendActionState: MutableState, ) = launch { when (attachment) { is Attachment.Media -> { @@ -81,10 +80,26 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( private suspend fun sendMedia( mediaAttachment: Attachment.Media, - sendActionState: MutableState>, + sendActionState: MutableState, ) { - sendActionState.runUpdatingState { - mediaSender.sendMedia(mediaAttachment.localMedia.uri, mediaAttachment.localMedia.info.mimeType, mediaAttachment.compressIfPossible) + val progressCallback = object : ProgressCallback { + override fun onProgress(current: Long, total: Long) { + sendActionState.value = SendActionState.Sending.Uploading(current.toFloat() / total.toFloat()) + } } + sendActionState.value = SendActionState.Sending.Processing + mediaSender.sendMedia( + uri = mediaAttachment.localMedia.uri, + mimeType = mediaAttachment.localMedia.info.mimeType, + compressIfPossible = mediaAttachment.compressIfPossible, + progressCallback = progressCallback + ).fold( + onSuccess = { + sendActionState.value = SendActionState.Done + }, + onFailure = { + sendActionState.value = SendActionState.Failure(it) + } + ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt index 67350f5048..e41f43040f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt @@ -17,10 +17,21 @@ package io.element.android.features.messages.impl.attachments.preview import io.element.android.features.messages.impl.attachments.Attachment -import io.element.android.libraries.architecture.Async data class AttachmentsPreviewState( val attachment: Attachment, - val sendActionState: Async, + val sendActionState: SendActionState, val eventSink: (AttachmentsPreviewEvents) -> Unit ) + +sealed interface SendActionState { + object Idle : SendActionState + sealed interface Sending : SendActionState { + object Processing : Sending + data class Uploading(val progress: Float) : Sending + } + + data class Failure(val error: Throwable) : SendActionState + object Done : SendActionState +} + 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 58fea4a4f2..ee41ace4b0 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 @@ -22,23 +22,21 @@ import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.media.local.LocalMedia import io.element.android.features.messages.impl.media.local.MediaInfo import io.element.android.features.messages.impl.media.local.aFileInfo -import io.element.android.features.messages.impl.media.local.aVideoInfo import io.element.android.features.messages.impl.media.local.anImageInfo -import io.element.android.libraries.architecture.Async open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAttachmentsPreviewState(), anAttachmentsPreviewState(mediaInfo = aFileInfo()), - anAttachmentsPreviewState(sendActionState = Async.Loading()), - anAttachmentsPreviewState(sendActionState = Async.Failure(RuntimeException())), + anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f)), + anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException())), ) } fun anAttachmentsPreviewState( mediaInfo: MediaInfo = anImageInfo(), - sendActionState: Async = Async.Uninitialized) = AttachmentsPreviewState( + sendActionState: SendActionState = SendActionState.Idle) = AttachmentsPreviewState( attachment = Attachment.Media( localMedia = LocalMedia("file://path".toUri(), mediaInfo), compressIfPossible = true 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 7388dd665b..6f33ef5d0b 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 @@ -33,9 +33,9 @@ import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.media.local.LocalMediaView -import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule import io.element.android.libraries.designsystem.components.ProgressDialog +import io.element.android.libraries.designsystem.components.ProgressDialogType import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -58,7 +58,7 @@ fun AttachmentsPreviewView( state.eventSink(AttachmentsPreviewEvents.ClearSendState) } - if (state.sendActionState is Async.Success) { + if (state.sendActionState is SendActionState.Done) { LaunchedEffect(state.sendActionState) { onDismiss() } @@ -78,26 +78,32 @@ fun AttachmentsPreviewView( } AttachmentSendStateView( sendActionState = state.sendActionState, - onRetryClicked = ::postSendAttachment, - onRetryDismissed = ::postClearSendState + onDismissClicked = ::postClearSendState, + onRetryClicked = ::postSendAttachment ) } @Composable private fun AttachmentSendStateView( - sendActionState: Async, - onRetryDismissed: () -> Unit, + sendActionState: SendActionState, + onDismissClicked: () -> Unit, onRetryClicked: () -> Unit ) { - when (sendActionState) { - is Async.Loading -> { - ProgressDialog(text = stringResource(id = CommonStrings.common_loading)) - } - is Async.Failure -> { + when (sendActionState) { + is SendActionState.Sending -> { + ProgressDialog( + type = when (sendActionState) { + is SendActionState.Sending.Uploading -> ProgressDialogType.Determinate(sendActionState.progress) + SendActionState.Sending.Processing -> ProgressDialogType.Indeterminate + }, + text = stringResource(id = CommonStrings.common_sending) + ) + } + is SendActionState.Failure -> { RetryDialog( content = stringResource(sendAttachmentError(sendActionState.error)), - onDismiss = onRetryDismissed, + onDismiss = onDismissClicked, onRetry = onRetryClicked ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 823cd64e5c..bb7744f71f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -42,6 +42,7 @@ import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaSender @@ -110,7 +111,7 @@ class MessageComposerPresenter @Inject constructor( LaunchedEffect(attachmentsState.value) { when (val attachmentStateValue = attachmentsState.value) { - is AttachmentsState.Sending -> localCoroutineScope.sendAttachment(attachmentStateValue.attachments.first(), attachmentsState) + is AttachmentsState.Sending.Processing -> localCoroutineScope.sendAttachment(attachmentStateValue.attachments.first(), attachmentsState) else -> Unit } } @@ -245,7 +246,7 @@ class MessageComposerPresenter @Inject constructor( attachmentsState.value = if (isPreviewable) { AttachmentsState.Previewing(persistentListOf(mediaAttachment)) } else { - AttachmentsState.Sending(persistentListOf(mediaAttachment)) + AttachmentsState.Sending.Processing(persistentListOf(mediaAttachment)) } } @@ -254,7 +255,12 @@ class MessageComposerPresenter @Inject constructor( mimeType: String, attachmentState: MutableState, ) { - mediaSender.sendMedia(uri, mimeType, compressIfPossible = false) + val progressCallback = object : ProgressCallback { + override fun onProgress(current: Long, total: Long) { + attachmentState.value = AttachmentsState.Sending.Uploading(current.toFloat() / total.toFloat()) + } + } + mediaSender.sendMedia(uri, mimeType, compressIfPossible = false, progressCallback) .onSuccess { attachmentState.value = AttachmentsState.None }.onFailure { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt index 570db2faaa..2dc6042fb5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt @@ -39,5 +39,8 @@ data class MessageComposerState( sealed interface AttachmentsState { object None : AttachmentsState data class Previewing(val attachments: ImmutableList) : AttachmentsState - data class Sending(val attachments: ImmutableList) : AttachmentsState + sealed interface Sending : AttachmentsState { + data class Processing(val attachments: ImmutableList) : Sending + data class Uploading(val progress: Float) : Sending + } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt index 18f5ae8da3..db202569ee 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt @@ -27,8 +27,8 @@ import io.element.android.features.messages.fixtures.aLocalMedia import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter +import io.element.android.features.messages.impl.attachments.preview.SendActionState import io.element.android.features.messages.impl.media.local.LocalMedia -import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor @@ -47,17 +47,26 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send media success scenario`() = runTest { val room = FakeMatrixRoom() + room.givenProgressCallbackValues( + listOf( + Pair(0, 10), + Pair(5, 10), + Pair(10, 10) + ) + ) val presenter = anAttachmentsPreviewPresenter(room = room) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.sendActionState).isEqualTo(Async.Uninitialized) + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) - val loadingState = awaitItem() - assertThat(loadingState.sendActionState).isEqualTo(Async.Loading()) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0f)) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0.5f)) + assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f)) val successState = awaitItem() - assertThat(successState.sendActionState).isEqualTo(Async.Success(Unit)) + assertThat(successState.sendActionState).isEqualTo(SendActionState.Done) assertThat(room.sendMediaCount).isEqualTo(1) } } @@ -72,16 +81,16 @@ class AttachmentsPreviewPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.sendActionState).isEqualTo(Async.Uninitialized) + assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) val loadingState = awaitItem() - assertThat(loadingState.sendActionState).isEqualTo(Async.Loading()) + assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing) val failureState = awaitItem() - assertThat(failureState.sendActionState).isEqualTo(Async.Failure(failure)) + assertThat(failureState.sendActionState).isEqualTo((SendActionState.Failure(failure))) assertThat(room.sendMediaCount).isEqualTo(0) failureState.eventSink(AttachmentsPreviewEvents.ClearSendState) val clearedState = awaitItem() - assertThat(clearedState.sendActionState).isEqualTo(Async.Uninitialized) + assertThat(clearedState.sendActionState).isEqualTo(SendActionState.Idle) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index f4fc88f515..8e96820c47 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -368,6 +368,13 @@ class MessageComposerPresenterTest { @Test fun `present - Pick file from storage`() = runTest { val room = FakeMatrixRoom() + room.givenProgressCallbackValues( + listOf( + Pair(0, 10), + Pair(5, 10), + Pair(10, 10) + ) + ) val presenter = createPresenter(this, room = room) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -376,7 +383,10 @@ class MessageComposerPresenterTest { initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) val sendingState = awaitItem() assertThat(sendingState.showAttachmentSourcePicker).isFalse() - assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending::class.java) + assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Sending.Processing::class.java) + assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(0f)) + assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(0.5f)) + assertThat(awaitItem().attachmentsState).isEqualTo(AttachmentsState.Sending.Uploading(1f)) val sentState = awaitItem() assertThat(sentState.attachmentsState).isEqualTo(AttachmentsState.None) assertThat(room.sendMediaCount).isEqualTo(1) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt index 35e6743881..140ab131ef 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview @@ -41,6 +42,7 @@ import io.element.android.libraries.designsystem.theme.components.Text fun ProgressDialog( modifier: Modifier = Modifier, text: String? = null, + type: ProgressDialogType = ProgressDialogType.Indeterminate, onDismiss: () -> Unit = {}, ) { Dialog( @@ -50,14 +52,40 @@ fun ProgressDialog( ProgressDialogContent( modifier = modifier, text = text, + progressIndicator = { + when (type) { + is ProgressDialogType.Indeterminate -> { + CircularProgressIndicator( + color = MaterialTheme.colorScheme.primary + ) + } + is ProgressDialogType.Determinate -> { + CircularProgressIndicator( + progress = type.progress, + color = MaterialTheme.colorScheme.primary + ) + } + } + } ) } } +@Immutable +sealed interface ProgressDialogType { + data class Determinate(val progress: Float) : ProgressDialogType + object Indeterminate : ProgressDialogType +} + @Composable private fun ProgressDialogContent( modifier: Modifier = Modifier, text: String? = null, + progressIndicator: @Composable () -> Unit = { + CircularProgressIndicator( + color = MaterialTheme.colorScheme.primary + ) + } ) { Box( contentAlignment = Alignment.Center, @@ -71,9 +99,7 @@ private fun ProgressDialogContent( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(top = 38.dp, bottom = 32.dp, start = 40.dp, end = 40.dp) ) { - CircularProgressIndicator( - color = MaterialTheme.colorScheme.primary - ) + progressIndicator() if (!text.isNullOrBlank()) { Spacer(modifier = Modifier.height(22.dp)) Text( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 2020be19d3..e50fe5441e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline import io.element.android.tests.testutils.simulateLongTask +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import java.io.File @@ -78,6 +79,7 @@ class FakeMatrixRoom( private var forwardEventResult = Result.success(Unit) private var reportContentResult = Result.success(Unit) private var sendLocationResult = Result.success(Unit) + private var progressCallbackValues = emptyList>() var sendMediaCount = 0 private set @@ -152,7 +154,7 @@ class FakeMatrixRoom( return toggleReactionResult } - if(_myReactions.contains(emoji)) { + if (_myReactions.contains(emoji)) { _myReactions.remove(emoji) } else { _myReactions.add(emoji) @@ -229,20 +231,26 @@ class FakeMatrixRoom( thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback? - ): Result = fakeSendMedia() + ): Result = fakeSendMedia(progressCallback) - override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result = fakeSendMedia() + override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result = fakeSendMedia( + progressCallback + ) - override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result = fakeSendMedia() + override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result = fakeSendMedia(progressCallback) - override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result = fakeSendMedia() + override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result = fakeSendMedia(progressCallback) override suspend fun forwardEvent(eventId: EventId, rooms: List): Result = simulateLongTask { forwardEventResult } - private suspend fun fakeSendMedia(): Result = simulateLongTask { + private suspend fun fakeSendMedia(progressCallback: ProgressCallback?): Result = simulateLongTask { sendMediaResult.onSuccess { + progressCallbackValues.forEach { (current, total) -> + progressCallback?.onProgress(current, total) + delay(1) + } sendMediaCount++ } } @@ -381,4 +389,8 @@ class FakeMatrixRoom( fun givenSendLocationResult(result: Result) { sendLocationResult = result } + + fun givenProgressCallbackValues(values: List>) { + progressCallbackValues = values + } } diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index 9f27824858..6dab564b6e 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.mediaupload.api import android.net.Uri import io.element.android.libraries.core.extensions.flatMap +import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.room.MatrixRoom import javax.inject.Inject @@ -26,7 +27,12 @@ class MediaSender @Inject constructor( private val room: MatrixRoom, ) { - suspend fun sendMedia(uri: Uri, mimeType: String, compressIfPossible: Boolean): Result { + suspend fun sendMedia( + uri: Uri, + mimeType: String, + compressIfPossible: Boolean, + progressCallback: ProgressCallback? = null + ): Result { return preProcessor .process( uri = uri, @@ -35,12 +41,13 @@ class MediaSender @Inject constructor( compressIfPossible = compressIfPossible ) .flatMap { info -> - room.sendMedia(info) + room.sendMedia(info, progressCallback) } } private suspend fun MatrixRoom.sendMedia( info: MediaUploadInfo, + progressCallback: ProgressCallback? ): Result { return when (info) { is MediaUploadInfo.Image -> { @@ -48,7 +55,7 @@ class MediaSender @Inject constructor( file = info.file, thumbnailFile = info.thumbnailFile, imageInfo = info.info, - progressCallback = null + progressCallback = progressCallback ) } @@ -57,7 +64,7 @@ class MediaSender @Inject constructor( file = info.file, thumbnailFile = info.thumbnailFile, videoInfo = info.info, - progressCallback = null + progressCallback = progressCallback ) } @@ -65,7 +72,7 @@ class MediaSender @Inject constructor( sendFile( file = info.file, fileInfo = info.info, - progressCallback = null + progressCallback = progressCallback ) } else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $info")) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 5e42636bff..8840b9892a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99720bb9fe43eed7901c4e093da7a82000ea6721041095486719283fdec2eb52 -size 185514 +oid sha256:3a968039c2fa94e89c75e395bab808d2960c37c43576b558438ec17bcd17e507 +size 184797