mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:30:43 +00:00
Merge pull request #7705 from element-hq/bma/fixLeak
Close the threaded timelines created to create a poll, share a location or send media
This commit is contained in:
+36
-21
@@ -100,9 +100,9 @@ class DefaultMediaSender(
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<Unit> {
|
||||
val mediaLogId = mediaId(mediaUploadInfo.file)
|
||||
return getTimeline().flatMap {
|
||||
Timber.d("Started sending media $mediaLogId using timeline: ${it.mode}")
|
||||
it.sendMedia(
|
||||
return withTimeline { timeline ->
|
||||
Timber.d("Started sending media $mediaLogId using timeline: ${timeline.mode}")
|
||||
timeline.sendMedia(
|
||||
uploadInfo = mediaUploadInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
@@ -128,12 +128,14 @@ class DefaultMediaSender(
|
||||
mediaOptimizationConfig = mediaOptimizationConfig,
|
||||
)
|
||||
.flatMapCatching { info ->
|
||||
getTimeline().getOrThrow().sendMedia(
|
||||
uploadInfo = info,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
withTimeline { timeline ->
|
||||
timeline.sendMedia(
|
||||
uploadInfo = info,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
}
|
||||
}
|
||||
.handleSendResult(mediaId(uri))
|
||||
}
|
||||
@@ -158,12 +160,14 @@ class DefaultMediaSender(
|
||||
audioInfo = audioInfo,
|
||||
waveform = waveForm,
|
||||
)
|
||||
getTimeline().getOrThrow().sendMedia(
|
||||
uploadInfo = newInfo,
|
||||
caption = null,
|
||||
formattedCaption = null,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
withTimeline { timeline ->
|
||||
timeline.sendMedia(
|
||||
uploadInfo = newInfo,
|
||||
caption = null,
|
||||
formattedCaption = null,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
}
|
||||
}
|
||||
.handleSendResult(mediaId(uri))
|
||||
}
|
||||
@@ -176,7 +180,7 @@ class DefaultMediaSender(
|
||||
): Result<Unit> {
|
||||
val galleryLogId = "gallery[${mediaUploadInfos.size} items]"
|
||||
Timber.d("Sending $galleryLogId")
|
||||
return getTimeline().flatMap { timeline ->
|
||||
return withTimeline { timeline ->
|
||||
val galleryItems = mediaUploadInfos.map { it.toGalleryItemInfo() }
|
||||
timeline.sendGallery(
|
||||
items = galleryItems,
|
||||
@@ -184,10 +188,10 @@ class DefaultMediaSender(
|
||||
formattedCaption = formattedCaption,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
.flatMapCatching { uploadHandler ->
|
||||
uploadHandler.await()
|
||||
}
|
||||
}
|
||||
.flatMapCatching { uploadHandler ->
|
||||
uploadHandler.await()
|
||||
}
|
||||
.handleSendResult(galleryLogId)
|
||||
}
|
||||
|
||||
@@ -269,12 +273,23 @@ class DefaultMediaSender(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getTimeline(): Result<Timeline> {
|
||||
/**
|
||||
* Invokes [block] with the [Timeline] matching [timelineMode].
|
||||
*
|
||||
* For a thread, a dedicated timeline is created and closed once [block] returns, since such a
|
||||
* timeline owns SDK resources. [block] must then perform the whole sending, including waiting
|
||||
* for the media upload to complete. For the other modes, the live timeline is used, and is not
|
||||
* closed here since it is owned by its room.
|
||||
*/
|
||||
private suspend fun <T> withTimeline(block: suspend (Timeline) -> Result<T>): Result<T> {
|
||||
return when (timelineMode) {
|
||||
is Timeline.Mode.Thread -> {
|
||||
room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = timelineMode.threadRootId))
|
||||
.flatMap { threadedTimeline ->
|
||||
threadedTimeline.use { block(it) }
|
||||
}
|
||||
}
|
||||
else -> Result.success(room.liveTimeline)
|
||||
else -> block(room.liveTimeline)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+40
-1
@@ -16,6 +16,7 @@ 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.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
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.timeline.FakeTimeline
|
||||
@@ -156,14 +157,52 @@ class DefaultMediaSenderTest : RobolectricTest() {
|
||||
sendFileResult.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a thread, the created timeline is closed once the media is sent`() = runTest {
|
||||
val threadedTimeline = FakeTimeline(mode = Timeline.Mode.Thread(A_THREAD_ID)).apply {
|
||||
sendFileLambda = { _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) }
|
||||
}
|
||||
val liveTimeline = FakeTimeline()
|
||||
val room = FakeJoinedRoom(
|
||||
liveTimeline = liveTimeline,
|
||||
createTimelineResult = { Result.success(threadedTimeline) },
|
||||
)
|
||||
val sender = createDefaultMediaSender(
|
||||
room = room,
|
||||
timelineMode = Timeline.Mode.Thread(A_THREAD_ID),
|
||||
)
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, mediaOptimizationConfig = mediaOptimizationConfig)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assertThat(threadedTimeline.closeCounter).isEqualTo(1)
|
||||
assertThat(liveTimeline.closeCounter).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a live timeline, the timeline is not closed once the media is sent`() = runTest {
|
||||
val liveTimeline = FakeTimeline().apply {
|
||||
sendFileLambda = { _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) }
|
||||
}
|
||||
val sender = createDefaultMediaSender(room = FakeJoinedRoom(liveTimeline = liveTimeline))
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, mediaOptimizationConfig = mediaOptimizationConfig)
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assertThat(liveTimeline.closeCounter).isEqualTo(0)
|
||||
}
|
||||
|
||||
private fun createDefaultMediaSender(
|
||||
preProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
|
||||
room: JoinedRoom = FakeJoinedRoom(),
|
||||
timelineMode: Timeline.Mode = Timeline.Mode.Live,
|
||||
mediaOptimizationConfigProvider: MediaOptimizationConfigProvider = MediaOptimizationConfigProvider { mediaOptimizationConfig },
|
||||
) = DefaultMediaSender(
|
||||
preProcessor = preProcessor,
|
||||
room = room,
|
||||
timelineMode = Timeline.Mode.Live,
|
||||
timelineMode = timelineMode,
|
||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user