Update dependency org.matrix.rustcomponents:sdk-android to v25.8.18 (#5182)

* Update dependency org.matrix.rustcomponents:sdk-android to v25.8.18

* Fix broken API changes:
- The send queue usage is now mandatory.
- The media upload progress now comes back in the send queue state (this still hasn't been applied to the UI in the timeline).

* Update screenshots
---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
renovate[bot]
2025-08-18 16:41:14 +02:00
committed by GitHub
parent fcb63c02e1
commit aac9642159
27 changed files with 85 additions and 147 deletions

View File

@@ -203,6 +203,6 @@ class ConfigureRoomPresenter @Inject constructor(
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(), mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
).getOrThrow() ).getOrThrow()
val byteArray = preprocessed.file.readBytes() val byteArray = preprocessed.file.readBytes()
return matrixClient.uploadMedia(MimeTypes.Jpeg, byteArray, null).getOrThrow() return matrixClient.uploadMedia(MimeTypes.Jpeg, byteArray).getOrThrow()
} }
} }

View File

@@ -32,7 +32,6 @@ import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage
import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig
import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.api.MediaSender
@@ -47,7 +46,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.coroutineContext
class AttachmentsPreviewPresenter @AssistedInject constructor( class AttachmentsPreviewPresenter @AssistedInject constructor(
@Assisted private val attachment: Attachment, @Assisted private val attachment: Attachment,
@@ -304,20 +302,11 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
dismissAfterSend: Boolean, dismissAfterSend: Boolean,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
) = runCatchingExceptions { ) = runCatchingExceptions {
val context = coroutineContext sendActionState.value = SendActionState.Sending.Uploading(mediaUploadInfo)
val progressCallback = object : ProgressCallback {
override fun onProgress(current: Long, total: Long) {
// Note will not happen if useSendQueue is true
if (context.isActive) {
sendActionState.value = SendActionState.Sending.Uploading(current.toFloat() / total.toFloat(), mediaUploadInfo)
}
}
}
mediaSender.sendPreProcessedMedia( mediaSender.sendPreProcessedMedia(
mediaUploadInfo = mediaUploadInfo, mediaUploadInfo = mediaUploadInfo,
caption = caption, caption = caption,
formattedCaption = null, formattedCaption = null,
progressCallback = progressCallback,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
).getOrThrow() ).getOrThrow()
}.fold( }.fold(

View File

@@ -30,7 +30,7 @@ sealed interface SendActionState {
sealed interface Sending : SendActionState { sealed interface Sending : SendActionState {
data class Processing(val displayProgress: Boolean) : Sending data class Processing(val displayProgress: Boolean) : Sending
data class ReadyToUpload(val mediaInfo: MediaUploadInfo) : Sending data class ReadyToUpload(val mediaInfo: MediaUploadInfo) : Sending
data class Uploading(val progress: Float, val mediaUploadInfo: MediaUploadInfo) : Sending data class Uploading(val mediaUploadInfo: MediaUploadInfo) : Sending
} }
data class Failure(val error: Throwable, val mediaUploadInfo: MediaUploadInfo?) : SendActionState data class Failure(val error: Throwable, val mediaUploadInfo: MediaUploadInfo?) : SendActionState

View File

@@ -39,7 +39,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider<Attachment
), ),
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Processing(displayProgress = true)), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Processing(displayProgress = true)),
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.ReadyToUpload(aMediaUploadInfo())), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.ReadyToUpload(aMediaUploadInfo())),
anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f, aMediaUploadInfo())), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(aMediaUploadInfo())),
anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"), aMediaUploadInfo())), anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"), aMediaUploadInfo())),
anAttachmentsPreviewState(displayFileTooLargeError = true), anAttachmentsPreviewState(displayFileTooLargeError = true),
anAttachmentsPreviewState( anAttachmentsPreviewState(

View File

@@ -143,7 +143,7 @@ private fun AttachmentSendStateView(
} }
is SendActionState.Sending.Uploading -> { is SendActionState.Sending.Uploading -> {
ProgressDialog( ProgressDialog(
type = ProgressDialogType.Determinate(sendActionState.progress), type = ProgressDialogType.Indeterminate,
text = stringResource(id = CommonStrings.common_sending), text = stringResource(id = CommonStrings.common_sending),
showCancelButton = true, showCancelButton = true,
onDismissRequest = onDismissClick, onDismissRequest = onDismissClick,

View File

@@ -510,7 +510,6 @@ class MessageComposerPresenter @AssistedInject constructor(
mediaSender.sendMedia( mediaSender.sendMedia(
uri = uri, uri = uri,
mimeType = mimeType, mimeType = mimeType,
progressCallback = null,
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(), mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
).getOrThrow() ).getOrThrow()
} }

View File

@@ -19,7 +19,7 @@ class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider<Timel
override val values: Sequence<TimelineItem.Event> override val values: Sequence<TimelineItem.Event>
get() = sequenceOf( get() = sequenceOf(
aTimelineItemEvent(), aTimelineItemEvent(),
aTimelineItemEvent().copy(localSendState = LocalEventSendState.Sending), aTimelineItemEvent().copy(localSendState = LocalEventSendState.Sending.Event),
aTimelineItemEvent().copy(localSendState = LocalEventSendState.Failed.Unknown("AN_ERROR")), aTimelineItemEvent().copy(localSendState = LocalEventSendState.Failed.Unknown("AN_ERROR")),
// Edited // Edited
aTimelineItemEvent().copy(content = aTimelineItemTextContent().copy(isEdited = true)), aTimelineItemEvent().copy(content = aTimelineItemTextContent().copy(isEdited = true)),

View File

@@ -17,7 +17,7 @@ class ReadReceiptViewStateForTimelineItemEventRowProvider :
override val values: Sequence<ReadReceiptViewState> override val values: Sequence<ReadReceiptViewState>
get() = sequenceOf( get() = sequenceOf(
aReadReceiptViewState( aReadReceiptViewState(
sendState = LocalEventSendState.Sending, sendState = LocalEventSendState.Sending.Event,
), ),
aReadReceiptViewState( aReadReceiptViewState(
sendState = LocalEventSendState.Sent(EventId("\$eventId")), sendState = LocalEventSendState.Sent(EventId("\$eventId")),

View File

@@ -20,7 +20,7 @@ class ReadReceiptViewStateProvider : PreviewParameterProvider<ReadReceiptViewSta
override val values: Sequence<ReadReceiptViewState> override val values: Sequence<ReadReceiptViewState>
get() = sequenceOf( get() = sequenceOf(
aReadReceiptViewState(), aReadReceiptViewState(),
aReadReceiptViewState(sendState = LocalEventSendState.Sending), aReadReceiptViewState(sendState = LocalEventSendState.Sending.Event),
aReadReceiptViewState(sendState = LocalEventSendState.Sent(EventId("\$eventId"))), aReadReceiptViewState(sendState = LocalEventSendState.Sent(EventId("\$eventId"))),
aReadReceiptViewState( aReadReceiptViewState(
sendState = LocalEventSendState.Sent(EventId("\$eventId")), sendState = LocalEventSendState.Sent(EventId("\$eventId")),

View File

@@ -76,7 +76,7 @@ fun TimelineItemReadReceiptView(
} }
} else { } else {
when (state.sendState) { when (state.sendState) {
LocalEventSendState.Sending -> { is LocalEventSendState.Sending -> {
ReadReceiptsRow(modifier) { ReadReceiptsRow(modifier) {
Icon( Icon(
modifier = Modifier.padding(2.dp), modifier = Modifier.padding(2.dp),

View File

@@ -26,7 +26,6 @@ import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.core.mimetype.MimeTypes 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.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.ImageInfo
@@ -90,17 +89,11 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send media success scenario`() = runTest { fun `present - send media success scenario`() = runTest {
val sendFileResult = val sendFileResult =
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, FileInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val room = FakeJoinedRoom( val room = FakeJoinedRoom(
liveTimeline = FakeTimeline( liveTimeline = FakeTimeline().apply {
progressCallbackValues = listOf(
Pair(0, 10),
Pair(5, 10),
Pair(10, 10)
),
).apply {
sendFileLambda = sendFileResult sendFileLambda = sendFileResult
}, },
) )
@@ -120,9 +113,7 @@ class AttachmentsPreviewPresenterTest {
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0f, mediaUploadInfo)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0.5f, mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f, mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
sendFileResult.assertions().isCalledOnce() sendFileResult.assertions().isCalledOnce()
onDoneListener.assertions().isCalledOnce() onDoneListener.assertions().isCalledOnce()
@@ -133,7 +124,7 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send media after pre-processing success scenario`() = runTest { fun `present - send media after pre-processing success scenario`() = runTest {
val sendFileResult = val sendFileResult =
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, FileInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val room = FakeJoinedRoom( val room = FakeJoinedRoom(
@@ -160,6 +151,7 @@ class AttachmentsPreviewPresenterTest {
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
sendFileResult.assertions().isCalledOnce() sendFileResult.assertions().isCalledOnce()
onDoneListener.assertions().isCalledOnce() onDoneListener.assertions().isCalledOnce()
@@ -170,7 +162,7 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send media before pre-processing success scenario`() = runTest { fun `present - send media before pre-processing success scenario`() = runTest {
val sendFileResult = val sendFileResult =
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, FileInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val room = FakeJoinedRoom( val room = FakeJoinedRoom(
@@ -197,6 +189,7 @@ class AttachmentsPreviewPresenterTest {
processLatch.complete(Unit) processLatch.complete(Unit)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = true))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
sendFileResult.assertions().isCalledOnce() sendFileResult.assertions().isCalledOnce()
onDoneListener.assertions().isCalledOnce() onDoneListener.assertions().isCalledOnce()
@@ -281,7 +274,7 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send image with caption success scenario`() = runTest { fun `present - send image with caption success scenario`() = runTest {
val sendImageResult = val sendImageResult =
lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: EventId? ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val mediaPreProcessor = FakeMediaPreProcessor().apply { val mediaPreProcessor = FakeMediaPreProcessor().apply {
@@ -307,6 +300,7 @@ class AttachmentsPreviewPresenterTest {
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java) assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.Uploading::class.java)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
sendImageResult.assertions().isCalledOnce().with( sendImageResult.assertions().isCalledOnce().with(
any(), any(),
@@ -315,7 +309,6 @@ class AttachmentsPreviewPresenterTest {
value(A_CAPTION), value(A_CAPTION),
any(), any(),
any(), any(),
any(),
) )
onDoneListener.assertions().isCalledOnce() onDoneListener.assertions().isCalledOnce()
} }
@@ -324,7 +317,7 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send video with caption success scenario`() = runTest { fun `present - send video with caption success scenario`() = runTest {
val sendVideoResult = val sendVideoResult =
lambdaRecorder { _: File, _: File?, _: VideoInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> lambdaRecorder { _: File, _: File?, _: VideoInfo, _: String?, _: String?, _: EventId? ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val mediaPreProcessor = FakeMediaPreProcessor().apply { val mediaPreProcessor = FakeMediaPreProcessor().apply {
@@ -350,6 +343,7 @@ class AttachmentsPreviewPresenterTest {
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java) assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.Uploading::class.java)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
sendVideoResult.assertions().isCalledOnce().with( sendVideoResult.assertions().isCalledOnce().with(
any(), any(),
@@ -358,7 +352,6 @@ class AttachmentsPreviewPresenterTest {
value(A_CAPTION), value(A_CAPTION),
any(), any(),
any(), any(),
any(),
) )
onDoneListener.assertions().isCalledOnce() onDoneListener.assertions().isCalledOnce()
} }
@@ -367,7 +360,7 @@ class AttachmentsPreviewPresenterTest {
@Test @Test
fun `present - send audio with caption success scenario`() = runTest { fun `present - send audio with caption success scenario`() = runTest {
val sendAudioResult = val sendAudioResult =
lambdaRecorder<File, AudioInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, AudioInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val mediaPreProcessor = FakeMediaPreProcessor().apply { val mediaPreProcessor = FakeMediaPreProcessor().apply {
@@ -391,6 +384,7 @@ class AttachmentsPreviewPresenterTest {
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java) assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.ReadyToUpload::class.java)
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Sending.Uploading::class.java)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
sendAudioResult.assertions().isCalledOnce().with( sendAudioResult.assertions().isCalledOnce().with(
any(), any(),
@@ -398,7 +392,6 @@ class AttachmentsPreviewPresenterTest {
value(A_CAPTION), value(A_CAPTION),
any(), any(),
any(), any(),
any(),
) )
onDoneListener.assertions().isCalledOnce() onDoneListener.assertions().isCalledOnce()
} }
@@ -408,7 +401,7 @@ class AttachmentsPreviewPresenterTest {
fun `present - send media failure scenario`() = runTest { fun `present - send media failure scenario`() = runTest {
val failure = MediaPreProcessor.Failure(null) val failure = MediaPreProcessor.Failure(null)
val sendFileResult = val sendFileResult =
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, FileInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.failure(failure) Result.failure(failure)
} }
val onDoneListenerResult = lambdaRecorder<Unit> {} val onDoneListenerResult = lambdaRecorder<Unit> {}
@@ -426,6 +419,7 @@ class AttachmentsPreviewPresenterTest {
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
// Check that the onDoneListener is called so the screen would be dismissed // Check that the onDoneListener is called so the screen would be dismissed
onDoneListenerResult.assertions().isCalledOnce() onDoneListenerResult.assertions().isCalledOnce()
@@ -445,7 +439,7 @@ class AttachmentsPreviewPresenterTest {
val presenter = createAttachmentsPreviewPresenter( val presenter = createAttachmentsPreviewPresenter(
room = FakeJoinedRoom( room = FakeJoinedRoom(
liveTimeline = FakeTimeline().apply { liveTimeline = FakeTimeline().apply {
sendFileLambda = { _, _, _, _, _, _ -> sendFileLambda = { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
} }
@@ -460,7 +454,9 @@ class AttachmentsPreviewPresenterTest {
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing(displayProgress = false))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo)) assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(mediaUploadInfo))
initialState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState) initialState.eventSink(AttachmentsPreviewEvents.CancelAndClearSendState)
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.ReadyToUpload(mediaUploadInfo))
// The sending is cancelled and the state is kept at ReadyToUpload // The sending is cancelled and the state is kept at ReadyToUpload
ensureAllEventsConsumed() ensureAllEventsConsumed()
@@ -480,7 +476,7 @@ class AttachmentsPreviewPresenterTest {
localMedia = localMedia, localMedia = localMedia,
room = FakeJoinedRoom( room = FakeJoinedRoom(
liveTimeline = FakeTimeline().apply { liveTimeline = FakeTimeline().apply {
sendFileLambda = { _, _, _, _, _, _ -> sendFileLambda = { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
} }
@@ -521,7 +517,7 @@ class AttachmentsPreviewPresenterTest {
localMedia = localMedia, localMedia = localMedia,
room = FakeJoinedRoom( room = FakeJoinedRoom(
liveTimeline = FakeTimeline().apply { liveTimeline = FakeTimeline().apply {
sendFileLambda = { _, _, _, _, _, _ -> sendFileLambda = { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
} }

View File

@@ -17,10 +17,21 @@ class SendActionStateTest {
@Test @Test
fun `mediaUploadInfo() should return the value from Uploading class`() { fun `mediaUploadInfo() should return the value from Uploading class`() {
val mediaUploadInfo: MediaUploadInfo = aMediaUploadInfo() val mediaUploadInfo: MediaUploadInfo = aMediaUploadInfo()
val state: SendActionState = SendActionState.Sending.Uploading( val state: SendActionState = SendActionState.Sending.Uploading(mediaUploadInfo = aMediaUploadInfo())
progress = 0.5f, assertThat(state.mediaUploadInfo()).isEqualTo(mediaUploadInfo)
mediaUploadInfo = aMediaUploadInfo() }
)
@Test
fun `mediaUploadInfo() should return the value from ReadyToUpload class`() {
val mediaUploadInfo: MediaUploadInfo = aMediaUploadInfo()
val state: SendActionState = SendActionState.Sending.ReadyToUpload(mediaInfo = aMediaUploadInfo())
assertThat(state.mediaUploadInfo()).isEqualTo(mediaUploadInfo)
}
@Test
fun `mediaUploadInfo() should return the value from Failure class`() {
val mediaUploadInfo: MediaUploadInfo = aMediaUploadInfo()
val state: SendActionState = SendActionState.Failure(error = IllegalStateException("An error"), mediaUploadInfo = aMediaUploadInfo())
assertThat(state.mediaUploadInfo()).isEqualTo(mediaUploadInfo) assertThat(state.mediaUploadInfo()).isEqualTo(mediaUploadInfo)
} }
} }

View File

@@ -1181,7 +1181,7 @@ class MessageComposerPresenterTest {
room = FakeJoinedRoom( room = FakeJoinedRoom(
typingNoticeResult = { Result.success(Unit) }, typingNoticeResult = { Result.success(Unit) },
liveTimeline = FakeTimeline().apply { liveTimeline = FakeTimeline().apply {
sendFileLambda = { _, _, _, _, _, _ -> sendFileLambda = { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
} }

View File

@@ -20,7 +20,6 @@ import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.messages.impl.messagecomposer.aReplyMode import io.element.android.features.messages.impl.messagecomposer.aReplyMode
import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
@@ -64,7 +63,7 @@ class VoiceMessageComposerPresenterTest {
) )
private val analyticsService = FakeAnalyticsService() private val analyticsService = FakeAnalyticsService()
private val sendVoiceMessageResult = private val sendVoiceMessageResult =
lambdaRecorder<File, AudioInfo, List<Float>, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ -> lambdaRecorder<File, AudioInfo, List<Float>, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
private val joinedRoom = FakeJoinedRoom( private val joinedRoom = FakeJoinedRoom(

View File

@@ -17,7 +17,6 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
@@ -122,7 +121,7 @@ class SharePresenterTest {
@Test @Test
fun `present - send media ok`() = runTest { fun `present - send media ok`() = runTest {
val sendFileResult = val sendFileResult =
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, FileInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val joinedRoom = FakeJoinedRoom( val joinedRoom = FakeJoinedRoom(

View File

@@ -176,7 +176,7 @@ jsoup = "org.jsoup:jsoup:1.21.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:2.1.0" molecule-runtime = "app.cash.molecule:molecule-runtime:2.1.0"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.8.5" matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.8.18"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }

View File

@@ -10,7 +10,6 @@ package io.element.android.libraries.matrix.api
import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.DeviceId
import io.element.android.libraries.matrix.api.core.MatrixPatterns import io.element.android.libraries.matrix.api.core.MatrixPatterns
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
@@ -94,7 +93,7 @@ interface MatrixClient {
*/ */
suspend fun getUserProfile(): Result<MatrixUser> suspend fun getUserProfile(): Result<MatrixUser>
suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?> suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?>
suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String> suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String>
fun roomMembershipObserver(): RoomMembershipObserver fun roomMembershipObserver(): RoomMembershipObserver
/** /**

View File

@@ -8,7 +8,6 @@
package io.element.android.libraries.matrix.api.timeline package io.element.android.libraries.matrix.api.timeline
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
@@ -88,7 +87,6 @@ interface Timeline : AutoCloseable {
imageInfo: ImageInfo, imageInfo: ImageInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>
@@ -98,7 +96,6 @@ interface Timeline : AutoCloseable {
videoInfo: VideoInfo, videoInfo: VideoInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>
@@ -107,7 +104,6 @@ interface Timeline : AutoCloseable {
audioInfo: AudioInfo, audioInfo: AudioInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>
@@ -116,7 +112,6 @@ interface Timeline : AutoCloseable {
fileInfo: FileInfo, fileInfo: FileInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>
@@ -145,7 +140,6 @@ interface Timeline : AutoCloseable {
file: File, file: File,
audioInfo: AudioInfo, audioInfo: AudioInfo,
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>

View File

@@ -14,7 +14,14 @@ import io.element.android.libraries.matrix.api.core.UserId
@Immutable @Immutable
sealed interface LocalEventSendState { sealed interface LocalEventSendState {
data object Sending : LocalEventSendState sealed interface Sending : LocalEventSendState {
data object Event : Sending
data class MediaWithProgress(
val index: Long,
val progress: Long,
val total: Long
) : Sending
}
sealed interface Failed : LocalEventSendState { sealed interface Failed : LocalEventSendState {
data class Unknown(val error: String) : Failed data class Unknown(val error: String) : Failed
data object SendingFromUnverifiedDevice : Failed data object SendingFromUnverifiedDevice : Failed

View File

@@ -16,7 +16,6 @@ import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.DeviceId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
@@ -48,7 +47,6 @@ import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
import io.element.android.libraries.matrix.impl.encryption.RustEncryptionService import io.element.android.libraries.matrix.impl.encryption.RustEncryptionService
import io.element.android.libraries.matrix.impl.exception.mapClientException import io.element.android.libraries.matrix.impl.exception.mapClientException
import io.element.android.libraries.matrix.impl.media.RustMediaLoader import io.element.android.libraries.matrix.impl.media.RustMediaLoader
@@ -627,9 +625,9 @@ class RustMatrixClient(
} }
} }
override suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String> = withContext(sessionDispatcher) { override suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String> = withContext(sessionDispatcher) {
runCatchingExceptions { runCatchingExceptions {
innerClient.uploadMedia(mimeType, data, progressCallback?.toProgressWatcher()) innerClient.uploadMedia(mimeType, data, progressWatcher = null)
} }
} }

View File

@@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl.timeline
import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.FileInfo
@@ -27,7 +26,6 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.TimelineException import io.element.android.libraries.matrix.api.timeline.TimelineException
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.media.map
import io.element.android.libraries.matrix.impl.media.toMSC3246range import io.element.android.libraries.matrix.impl.media.toMSC3246range
@@ -336,7 +334,6 @@ class RustTimeline(
imageInfo: ImageInfo, imageInfo: ImageInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> { ): Result<MediaUploadHandler> {
return sendAttachment(listOfNotNull(file, thumbnailFile)) { return sendAttachment(listOfNotNull(file, thumbnailFile)) {
@@ -347,13 +344,11 @@ class RustTimeline(
formattedCaption = formattedCaption?.let { formattedCaption = formattedCaption?.let {
FormattedBody(body = it, format = MessageFormat.Html) FormattedBody(body = it, format = MessageFormat.Html)
}, },
useSendQueue = true,
mentions = null, mentions = null,
inReplyTo = inReplyToEventId?.value, inReplyTo = inReplyToEventId?.value,
), ),
thumbnailPath = thumbnailFile?.path, thumbnailSource = thumbnailFile?.path?.let(UploadSource::File),
imageInfo = imageInfo.map(), imageInfo = imageInfo.map(),
progressWatcher = progressCallback?.toProgressWatcher()
) )
} }
} }
@@ -364,7 +359,6 @@ class RustTimeline(
videoInfo: VideoInfo, videoInfo: VideoInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> { ): Result<MediaUploadHandler> {
return sendAttachment(listOfNotNull(file, thumbnailFile)) { return sendAttachment(listOfNotNull(file, thumbnailFile)) {
@@ -375,13 +369,11 @@ class RustTimeline(
formattedCaption = formattedCaption?.let { formattedCaption = formattedCaption?.let {
FormattedBody(body = it, format = MessageFormat.Html) FormattedBody(body = it, format = MessageFormat.Html)
}, },
useSendQueue = true,
mentions = null, mentions = null,
inReplyTo = inReplyToEventId?.value, inReplyTo = inReplyToEventId?.value,
), ),
thumbnailPath = thumbnailFile?.path, thumbnailSource = thumbnailFile?.path?.let(UploadSource::File),
videoInfo = videoInfo.map(), videoInfo = videoInfo.map(),
progressWatcher = progressCallback?.toProgressWatcher()
) )
} }
} }
@@ -391,7 +383,6 @@ class RustTimeline(
audioInfo: AudioInfo, audioInfo: AudioInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> { ): Result<MediaUploadHandler> {
return sendAttachment(listOf(file)) { return sendAttachment(listOf(file)) {
@@ -402,12 +393,10 @@ class RustTimeline(
formattedCaption = formattedCaption?.let { formattedCaption = formattedCaption?.let {
FormattedBody(body = it, format = MessageFormat.Html) FormattedBody(body = it, format = MessageFormat.Html)
}, },
useSendQueue = true,
mentions = null, mentions = null,
inReplyTo = inReplyToEventId?.value, inReplyTo = inReplyToEventId?.value,
), ),
audioInfo = audioInfo.map(), audioInfo = audioInfo.map(),
progressWatcher = progressCallback?.toProgressWatcher()
) )
} }
} }
@@ -417,7 +406,6 @@ class RustTimeline(
fileInfo: FileInfo, fileInfo: FileInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> { ): Result<MediaUploadHandler> {
return sendAttachment(listOf(file)) { return sendAttachment(listOf(file)) {
@@ -428,12 +416,10 @@ class RustTimeline(
formattedCaption = formattedCaption?.let { formattedCaption = formattedCaption?.let {
FormattedBody(body = it, format = MessageFormat.Html) FormattedBody(body = it, format = MessageFormat.Html)
}, },
useSendQueue = true,
mentions = null, mentions = null,
inReplyTo = inReplyToEventId?.value, inReplyTo = inReplyToEventId?.value,
), ),
fileInfo = fileInfo.map(), fileInfo = fileInfo.map(),
progressWatcher = progressCallback?.toProgressWatcher(),
) )
} }
} }
@@ -479,7 +465,6 @@ class RustTimeline(
file: File, file: File,
audioInfo: AudioInfo, audioInfo: AudioInfo,
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<MediaUploadHandler> { ): Result<MediaUploadHandler> {
return sendAttachment(listOf(file)) { return sendAttachment(listOf(file)) {
@@ -489,13 +474,11 @@ class RustTimeline(
// Maybe allow a caption in the future? // Maybe allow a caption in the future?
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
useSendQueue = true,
mentions = null, mentions = null,
inReplyTo = inReplyToEventId?.value, inReplyTo = inReplyToEventId?.value,
), ),
audioInfo = audioInfo.map(), audioInfo = audioInfo.map(),
waveform = waveform.toMSC3246range(), waveform = waveform.toMSC3246range(),
progressWatcher = progressCallback?.toProgressWatcher(),
) )
} }
} }

View File

@@ -79,7 +79,18 @@ fun RustProfileDetails.map(): ProfileTimelineDetails {
fun RustEventSendState?.map(): LocalEventSendState? { fun RustEventSendState?.map(): LocalEventSendState? {
return when (this) { return when (this) {
null -> null null -> null
RustEventSendState.NotSentYet -> LocalEventSendState.Sending is RustEventSendState.NotSentYet -> {
val mediaUploadProgress = this.progress
if (mediaUploadProgress != null) {
LocalEventSendState.Sending.MediaWithProgress(
index = mediaUploadProgress.index.toLong(),
progress = mediaUploadProgress.progress.current.toLong(),
total = mediaUploadProgress.progress.total.toLong(),
)
} else {
LocalEventSendState.Sending.Event
}
}
is RustEventSendState.SendingFailed -> { is RustEventSendState.SendingFailed -> {
when (val queueWedgeError = error) { when (val queueWedgeError = error) {
QueueWedgeError.CrossVerificationRequired -> { QueueWedgeError.CrossVerificationRequired -> {
@@ -98,7 +109,7 @@ fun RustEventSendState?.map(): LocalEventSendState? {
} }
is QueueWedgeError.GenericApiError -> { is QueueWedgeError.GenericApiError -> {
if (isRecoverable) { if (isRecoverable) {
LocalEventSendState.Sending LocalEventSendState.Sending.Event
} else { } else {
LocalEventSendState.Failed.Unknown(queueWedgeError.msg) LocalEventSendState.Failed.Unknown(queueWedgeError.msg)
} }

View File

@@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.test
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.DeviceId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
@@ -201,7 +200,6 @@ class FakeMatrixClient(
override suspend fun uploadMedia( override suspend fun uploadMedia(
mimeType: String, mimeType: String,
data: ByteArray, data: ByteArray,
progressCallback: ProgressCallback?
): Result<String> { ): Result<String> {
return uploadMediaResult return uploadMediaResult
} }

View File

@@ -8,7 +8,6 @@
package io.element.android.libraries.matrix.test.timeline package io.element.android.libraries.matrix.test.timeline
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.TransactionId
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
@@ -27,7 +26,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -49,7 +47,6 @@ class FakeTimeline(
) )
), ),
override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(), override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(),
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() }, private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() },
) : Timeline { ) : Timeline {
var sendMessageLambda: ( var sendMessageLambda: (
@@ -150,9 +147,8 @@ class FakeTimeline(
imageInfo: ImageInfo, imageInfo: ImageInfo,
body: String?, body: String?,
formattedBody: String?, formattedBody: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _, _ -> ) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
@@ -162,17 +158,14 @@ class FakeTimeline(
imageInfo: ImageInfo, imageInfo: ImageInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
): Result<MediaUploadHandler> = simulateLongTask { ): Result<MediaUploadHandler> = simulateLongTask {
simulateSendMediaProgress(progressCallback)
sendImageLambda( sendImageLambda(
file, file,
thumbnailFile, thumbnailFile,
imageInfo, imageInfo,
caption, caption,
formattedCaption, formattedCaption,
progressCallback,
inReplyToEventId, inReplyToEventId,
) )
} }
@@ -183,9 +176,8 @@ class FakeTimeline(
videoInfo: VideoInfo, videoInfo: VideoInfo,
body: String?, body: String?,
formattedBody: String?, formattedBody: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _, _ -> ) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
@@ -195,17 +187,14 @@ class FakeTimeline(
videoInfo: VideoInfo, videoInfo: VideoInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
): Result<MediaUploadHandler> = simulateLongTask { ): Result<MediaUploadHandler> = simulateLongTask {
simulateSendMediaProgress(progressCallback)
sendVideoLambda( sendVideoLambda(
file, file,
thumbnailFile, thumbnailFile,
videoInfo, videoInfo,
caption, caption,
formattedCaption, formattedCaption,
progressCallback,
inReplyToEventId, inReplyToEventId,
) )
} }
@@ -215,9 +204,8 @@ class FakeTimeline(
audioInfo: AudioInfo, audioInfo: AudioInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ -> ) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
@@ -226,16 +214,13 @@ class FakeTimeline(
audioInfo: AudioInfo, audioInfo: AudioInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
): Result<MediaUploadHandler> = simulateLongTask { ): Result<MediaUploadHandler> = simulateLongTask {
simulateSendMediaProgress(progressCallback)
sendAudioLambda( sendAudioLambda(
file, file,
audioInfo, audioInfo,
caption, caption,
formattedCaption, formattedCaption,
progressCallback,
inReplyToEventId, inReplyToEventId,
) )
} }
@@ -245,9 +230,8 @@ class FakeTimeline(
fileInfo: FileInfo, fileInfo: FileInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ -> ) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
@@ -256,16 +240,13 @@ class FakeTimeline(
fileInfo: FileInfo, fileInfo: FileInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
): Result<MediaUploadHandler> = simulateLongTask { ): Result<MediaUploadHandler> = simulateLongTask {
simulateSendMediaProgress(progressCallback)
sendFileLambda( sendFileLambda(
file, file,
fileInfo, fileInfo,
caption, caption,
formattedCaption, formattedCaption,
progressCallback,
inReplyToEventId, inReplyToEventId,
) )
} }
@@ -274,9 +255,8 @@ class FakeTimeline(
file: File, file: File,
audioInfo: AudioInfo, audioInfo: AudioInfo,
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
) -> Result<MediaUploadHandler> = { _, _, _, _, _ -> ) -> Result<MediaUploadHandler> = { _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
@@ -284,15 +264,12 @@ class FakeTimeline(
file: File, file: File,
audioInfo: AudioInfo, audioInfo: AudioInfo,
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId??, inReplyToEventId: EventId??,
): Result<MediaUploadHandler> = simulateLongTask { ): Result<MediaUploadHandler> = simulateLongTask {
simulateSendMediaProgress(progressCallback)
sendVoiceMessageLambda( sendVoiceMessageLambda(
file, file,
audioInfo, audioInfo,
waveform, waveform,
progressCallback,
inReplyToEventId, inReplyToEventId,
) )
} }
@@ -460,12 +437,5 @@ class FakeTimeline(
closeCounter++ closeCounter++
} }
private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) {
progressCallbackValues.forEach { (current, total) ->
progressCallback?.onProgress(current, total)
delay(1)
}
}
override fun toString() = "FakeTimeline: $name" override fun toString() = "FakeTimeline: $name"
} }

View File

@@ -10,7 +10,6 @@ package io.element.android.libraries.mediaupload.api
import android.net.Uri import android.net.Uri
import io.element.android.libraries.core.extensions.flatMapCatching import io.element.android.libraries.core.extensions.flatMapCatching
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.media.MediaUploadHandler import io.element.android.libraries.matrix.api.media.MediaUploadHandler
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
@@ -45,12 +44,10 @@ class MediaSender @Inject constructor(
mediaUploadInfo: MediaUploadInfo, mediaUploadInfo: MediaUploadInfo,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
progressCallback: ProgressCallback?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
): Result<Unit> { ): Result<Unit> {
return room.liveTimeline.sendMedia( return room.liveTimeline.sendMedia(
uploadInfo = mediaUploadInfo, uploadInfo = mediaUploadInfo,
progressCallback = progressCallback,
caption = caption, caption = caption,
formattedCaption = formattedCaption, formattedCaption = formattedCaption,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
@@ -63,7 +60,6 @@ class MediaSender @Inject constructor(
mimeType: String, mimeType: String,
caption: String? = null, caption: String? = null,
formattedCaption: String? = null, formattedCaption: String? = null,
progressCallback: ProgressCallback? = null,
inReplyToEventId: EventId? = null, inReplyToEventId: EventId? = null,
mediaOptimizationConfig: MediaOptimizationConfig, mediaOptimizationConfig: MediaOptimizationConfig,
): Result<Unit> { ): Result<Unit> {
@@ -77,7 +73,6 @@ class MediaSender @Inject constructor(
.flatMapCatching { info -> .flatMapCatching { info ->
room.liveTimeline.sendMedia( room.liveTimeline.sendMedia(
uploadInfo = info, uploadInfo = info,
progressCallback = progressCallback,
caption = caption, caption = caption,
formattedCaption = formattedCaption, formattedCaption = formattedCaption,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
@@ -90,7 +85,6 @@ class MediaSender @Inject constructor(
uri: Uri, uri: Uri,
mimeType: String, mimeType: String,
waveForm: List<Float>, waveForm: List<Float>,
progressCallback: ProgressCallback? = null,
inReplyToEventId: EventId? = null, inReplyToEventId: EventId? = null,
): Result<Unit> { ): Result<Unit> {
return preProcessor return preProcessor
@@ -109,7 +103,6 @@ class MediaSender @Inject constructor(
) )
room.liveTimeline.sendMedia( room.liveTimeline.sendMedia(
uploadInfo = newInfo, uploadInfo = newInfo,
progressCallback = progressCallback,
caption = null, caption = null,
formattedCaption = null, formattedCaption = null,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
@@ -131,7 +124,6 @@ class MediaSender @Inject constructor(
private suspend fun Timeline.sendMedia( private suspend fun Timeline.sendMedia(
uploadInfo: MediaUploadInfo, uploadInfo: MediaUploadInfo,
progressCallback: ProgressCallback?,
caption: String?, caption: String?,
formattedCaption: String?, formattedCaption: String?,
inReplyToEventId: EventId?, inReplyToEventId: EventId?,
@@ -144,7 +136,6 @@ class MediaSender @Inject constructor(
imageInfo = uploadInfo.imageInfo, imageInfo = uploadInfo.imageInfo,
caption = caption, caption = caption,
formattedCaption = formattedCaption, formattedCaption = formattedCaption,
progressCallback = progressCallback,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
) )
} }
@@ -155,7 +146,6 @@ class MediaSender @Inject constructor(
videoInfo = uploadInfo.videoInfo, videoInfo = uploadInfo.videoInfo,
caption = caption, caption = caption,
formattedCaption = formattedCaption, formattedCaption = formattedCaption,
progressCallback = progressCallback,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
) )
} }
@@ -165,7 +155,6 @@ class MediaSender @Inject constructor(
audioInfo = uploadInfo.audioInfo, audioInfo = uploadInfo.audioInfo,
caption = caption, caption = caption,
formattedCaption = formattedCaption, formattedCaption = formattedCaption,
progressCallback = progressCallback,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
) )
} }
@@ -174,7 +163,6 @@ class MediaSender @Inject constructor(
file = uploadInfo.file, file = uploadInfo.file,
audioInfo = uploadInfo.audioInfo, audioInfo = uploadInfo.audioInfo,
waveform = uploadInfo.waveform, waveform = uploadInfo.waveform,
progressCallback = progressCallback,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
) )
} }
@@ -184,7 +172,6 @@ class MediaSender @Inject constructor(
fileInfo = uploadInfo.fileInfo, fileInfo = uploadInfo.fileInfo,
caption = caption, caption = caption,
formattedCaption = formattedCaption, formattedCaption = formattedCaption,
progressCallback = progressCallback,
inReplyToEventId = inReplyToEventId, inReplyToEventId = inReplyToEventId,
) )
} }

View File

@@ -11,7 +11,6 @@ import android.net.Uri
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.core.mimetype.MimeTypes 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.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -50,10 +49,9 @@ class MediaSenderTest {
FileInfo, FileInfo,
String?, String?,
String?, String?,
ProgressCallback?,
EventId?, EventId?,
Result<FakeMediaUploadHandler>, Result<FakeMediaUploadHandler>,
> { _, _, _, _, _, _ -> > { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
}, },
@@ -69,7 +67,7 @@ class MediaSenderTest {
@Test @Test
fun `given an attachment when sending it the Room will call sendMedia`() = runTest { fun `given an attachment when sending it the Room will call sendMedia`() = runTest {
val sendImageResult = val sendImageResult =
lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: EventId? ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val room = FakeJoinedRoom( val room = FakeJoinedRoom(
@@ -102,7 +100,7 @@ class MediaSenderTest {
givenImageResult() givenImageResult()
} }
val sendImageResult = val sendImageResult =
lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: EventId? ->
Result.failure<FakeMediaUploadHandler>(Exception()) Result.failure<FakeMediaUploadHandler>(Exception())
} }
val room = FakeJoinedRoom( val room = FakeJoinedRoom(
@@ -125,7 +123,7 @@ class MediaSenderTest {
@Test @Test
fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) { fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) {
val sendFileResult = val sendFileResult =
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ -> lambdaRecorder<File, FileInfo, String?, String?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler()) Result.success(FakeMediaUploadHandler())
} }
val room = FakeJoinedRoom( val room = FakeJoinedRoom(