mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 08:15:02 +00:00
Introduce ShareDataSender so that SharePresenter is not session scoped
Instead of creating a SharePresenter from the graph of another session, the SharePresenter now gets the client of the selected session from the MatrixClientProvider and uses an AppScope ShareDataSender to send the data. To be able to send media from AppScope, MediaSenderRoomFactory is now bound in AppScope and takes the MatrixClient as a parameter, and a new MediaOptimizationConfigProviderFactory provides the media optimization config of any session. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.share.impl
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.share.api.OnSharedData
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProviderFactory
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderRoomFactory
|
||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultShareDataSender(
|
||||
private val mediaSenderRoomFactory: MediaSenderRoomFactory,
|
||||
private val mediaOptimizationConfigProviderFactory: MediaOptimizationConfigProviderFactory,
|
||||
private val activeRoomsHolder: ActiveRoomsHolder,
|
||||
private val onSharedData: OnSharedData,
|
||||
) : ShareDataSender {
|
||||
override suspend fun send(
|
||||
client: MatrixClient,
|
||||
shareIntentData: ShareIntentData,
|
||||
roomIds: List<RoomId>,
|
||||
): Result<Unit> = runCatchingExceptions {
|
||||
val result = when (shareIntentData) {
|
||||
is ShareIntentData.PlainText -> {
|
||||
roomIds
|
||||
.map { roomId ->
|
||||
client.findJoinedRoom(roomId)?.liveTimeline?.sendMessage(
|
||||
body = shareIntentData.content,
|
||||
htmlBody = null,
|
||||
intentionalMentions = emptyList(),
|
||||
)?.isSuccess.orFalse()
|
||||
}
|
||||
.all { it }
|
||||
}
|
||||
is ShareIntentData.Uris -> {
|
||||
val filesToShare = shareIntentData.uris
|
||||
if (filesToShare.isEmpty()) {
|
||||
false
|
||||
} else {
|
||||
val mediaOptimizationConfigProvider = mediaOptimizationConfigProviderFactory.create(client)
|
||||
roomIds
|
||||
.map { roomId ->
|
||||
val room = client.findJoinedRoom(roomId) ?: return@map false
|
||||
val mediaSender = mediaSenderRoomFactory.create(client = client, room = room)
|
||||
filesToShare
|
||||
.map { fileToShare ->
|
||||
val result = mediaSender.sendMedia(
|
||||
caption = shareIntentData.text,
|
||||
uri = fileToShare.uri,
|
||||
mimeType = fileToShare.mimeType,
|
||||
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
|
||||
)
|
||||
// If the coroutine was cancelled, destroy the room and rethrow the exception
|
||||
val cancellationException = result.exceptionOrNull() as? CancellationException
|
||||
if (cancellationException != null) {
|
||||
if (activeRoomsHolder.getActiveRoomMatching(client.sessionId, roomId) == null) {
|
||||
room.destroy()
|
||||
}
|
||||
throw cancellationException
|
||||
}
|
||||
result.isSuccess
|
||||
}
|
||||
.all { isSuccess -> isSuccess }
|
||||
.also {
|
||||
if (activeRoomsHolder.getActiveRoomMatching(client.sessionId, roomId) == null) {
|
||||
room.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
.all { it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle post-processing of shared data
|
||||
onSharedData(shareIntentData)
|
||||
|
||||
if (!result) {
|
||||
error("Failed to handle incoming share intent")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MatrixClient.findJoinedRoom(roomId: RoomId): JoinedRoom? {
|
||||
return activeRoomsHolder.getActiveRoom(sessionId)
|
||||
?.takeIf { it.roomId == roomId }
|
||||
?: getJoinedRoom(roomId)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.share.impl
|
||||
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
/**
|
||||
* Sends the shared data to some rooms of a session.
|
||||
*/
|
||||
interface ShareDataSender {
|
||||
/**
|
||||
* Send [shareIntentData] to the rooms with [roomIds], using the session of [client].
|
||||
*/
|
||||
suspend fun send(client: MatrixClient, shareIntentData: ShareIntentData, roomIds: List<RoomId>): Result<Unit>
|
||||
}
|
||||
+7
-61
@@ -11,12 +11,7 @@ package io.element.android.features.share.impl
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
|
||||
@@ -28,20 +23,15 @@ import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.features.share.api.ShareEntryPoint
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.share.impl.di.ShareBindings
|
||||
import io.element.android.libraries.architecture.NodeInputs
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.SessionGraphFactory
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint
|
||||
import io.element.android.libraries.roomselect.api.RoomSelectMode
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import timber.log.Timber
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@AssistedInject
|
||||
@@ -51,8 +41,6 @@ class ShareNode(
|
||||
presenterFactory: SharePresenter.Factory,
|
||||
private val roomSelectEntryPoint: RoomSelectEntryPoint,
|
||||
private val sessionId: SessionId,
|
||||
private val matrixClientProvider: MatrixClientProvider,
|
||||
private val sessionGraphFactory: SessionGraphFactory,
|
||||
) : ParentNode<ShareNode.NavTarget>(
|
||||
navModel = PermanentNavModel(
|
||||
navTargets = setOf(NavTarget),
|
||||
@@ -66,53 +54,14 @@ class ShareNode(
|
||||
|
||||
data class Inputs(val shareIntentData: ShareIntentData) : NodeInputs
|
||||
|
||||
/**
|
||||
* The presenter used to share the data, and the session it belongs to.
|
||||
*/
|
||||
private data class ActiveShare(
|
||||
val sessionId: SessionId,
|
||||
val presenter: SharePresenter,
|
||||
)
|
||||
|
||||
private val inputs = inputs<Inputs>()
|
||||
private val presenter = presenterFactory.create(inputs.shareIntentData, sessionId)
|
||||
private val callback: ShareEntryPoint.Callback = callback()
|
||||
private val currentSessionShare = ActiveShare(
|
||||
sessionId = sessionId,
|
||||
presenter = presenterFactory.create(inputs.shareIntentData),
|
||||
)
|
||||
private var activeShare by mutableStateOf(currentSessionShare)
|
||||
|
||||
private fun onRoomSelected(sessionId: SessionId, roomIds: List<RoomId>) {
|
||||
if (sessionId == currentSessionShare.sessionId) {
|
||||
activeShare = currentSessionShare
|
||||
currentSessionShare.presenter.onRoomSelected(roomIds)
|
||||
} else if (sessionId == activeShare.sessionId) {
|
||||
// Retry with the same other session
|
||||
activeShare.presenter.onRoomSelected(roomIds)
|
||||
} else {
|
||||
lifecycleScope.launch {
|
||||
// Share using the graph of the selected session, the current session will be changed once the share is done
|
||||
matrixClientProvider.getOrRestore(sessionId)
|
||||
.onSuccess { client ->
|
||||
val presenterFactory = (sessionGraphFactory.create(client) as ShareBindings).sharePresenterFactory()
|
||||
activeShare = ActiveShare(
|
||||
sessionId = sessionId,
|
||||
presenter = presenterFactory.create(inputs.shareIntentData),
|
||||
).also {
|
||||
it.presenter.onRoomSelected(roomIds)
|
||||
}
|
||||
}
|
||||
.onFailure {
|
||||
Timber.e(it, "Failed to get the client for session $sessionId")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
val callback = object : RoomSelectEntryPoint.Callback {
|
||||
override fun onRoomSelected(sessionId: SessionId, roomIds: List<RoomId>) {
|
||||
this@ShareNode.onRoomSelected(sessionId, roomIds)
|
||||
presenter.onRoomSelected(sessionId, roomIds)
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
@@ -140,14 +89,11 @@ class ShareNode(
|
||||
navModel = navModel,
|
||||
)
|
||||
|
||||
val currentShare = activeShare
|
||||
key(currentShare) {
|
||||
val state = currentShare.presenter.present()
|
||||
ShareView(
|
||||
state = state,
|
||||
onShareSuccess = { roomIds -> callback.onDone(currentShare.sessionId, roomIds) },
|
||||
)
|
||||
}
|
||||
val state = presenter.present()
|
||||
ShareView(
|
||||
state = state,
|
||||
onShareSuccess = { roomIds -> callback.onDone(state.sessionId, roomIds) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-91
@@ -10,47 +10,54 @@ package io.element.android.features.share.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedFactory
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.features.share.api.OnSharedData
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderRoomFactory
|
||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@AssistedInject
|
||||
class SharePresenter(
|
||||
@Assisted private val shareIntentData: ShareIntentData,
|
||||
@SessionCoroutineScope
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val mediaSenderRoomFactory: MediaSenderRoomFactory,
|
||||
private val activeRoomsHolder: ActiveRoomsHolder,
|
||||
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
|
||||
private val onSharedData: OnSharedData,
|
||||
@Assisted initialSessionId: SessionId,
|
||||
@AppCoroutineScope
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val matrixClientProvider: MatrixClientProvider,
|
||||
private val shareDataSender: ShareDataSender,
|
||||
) : Presenter<ShareState> {
|
||||
@AssistedFactory
|
||||
fun interface Factory {
|
||||
fun create(shareIntentData: ShareIntentData): SharePresenter
|
||||
fun create(shareIntentData: ShareIntentData, initialSessionId: SessionId): SharePresenter
|
||||
}
|
||||
|
||||
private val shareActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized)
|
||||
|
||||
fun onRoomSelected(roomIds: List<RoomId>) {
|
||||
sessionCoroutineScope.share(shareIntentData, roomIds)
|
||||
/**
|
||||
* The session the data is shared with, it can be updated if the user selects rooms from another session.
|
||||
*/
|
||||
private var targetSessionId by mutableStateOf(initialSessionId)
|
||||
|
||||
fun onRoomSelected(sessionId: SessionId, roomIds: List<RoomId>) {
|
||||
if (shareActionState.value.isLoading()) return
|
||||
targetSessionId = sessionId
|
||||
appCoroutineScope.launch {
|
||||
suspend {
|
||||
val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow()
|
||||
shareDataSender.send(client, shareIntentData, roomIds).getOrThrow()
|
||||
roomIds
|
||||
}.runCatchingUpdatingState(shareActionState)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -62,80 +69,9 @@ class SharePresenter(
|
||||
}
|
||||
|
||||
return ShareState(
|
||||
sessionId = targetSessionId,
|
||||
shareAction = shareActionState.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom? {
|
||||
return activeRoomsHolder.getActiveRoom(matrixClient.sessionId)
|
||||
?.takeIf { it.roomId == roomId }
|
||||
?: matrixClient.getJoinedRoom(roomId)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.share(
|
||||
shareIntentData: ShareIntentData,
|
||||
roomIds: List<RoomId>,
|
||||
) = launch {
|
||||
suspend {
|
||||
val result = when (shareIntentData) {
|
||||
is ShareIntentData.PlainText -> {
|
||||
roomIds
|
||||
.map { roomId ->
|
||||
getJoinedRoom(roomId)?.liveTimeline?.sendMessage(
|
||||
body = shareIntentData.content,
|
||||
htmlBody = null,
|
||||
intentionalMentions = emptyList(),
|
||||
)?.isSuccess.orFalse()
|
||||
}
|
||||
.all { it }
|
||||
}
|
||||
is ShareIntentData.Uris -> {
|
||||
val filesToShare = shareIntentData.uris
|
||||
if (filesToShare.isEmpty()) {
|
||||
false
|
||||
} else {
|
||||
roomIds
|
||||
.map { roomId ->
|
||||
val room = getJoinedRoom(roomId) ?: return@map false
|
||||
val mediaSender = mediaSenderRoomFactory.create(room = room)
|
||||
filesToShare
|
||||
.map { fileToShare ->
|
||||
val result = mediaSender.sendMedia(
|
||||
caption = shareIntentData.text,
|
||||
uri = fileToShare.uri,
|
||||
mimeType = fileToShare.mimeType,
|
||||
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
|
||||
)
|
||||
// If the coroutine was cancelled, destroy the room and rethrow the exception
|
||||
val cancellationException = result.exceptionOrNull() as? CancellationException
|
||||
if (cancellationException != null) {
|
||||
if (activeRoomsHolder.getActiveRoomMatching(matrixClient.sessionId, roomId) == null) {
|
||||
room.destroy()
|
||||
}
|
||||
throw cancellationException
|
||||
}
|
||||
result.isSuccess
|
||||
}
|
||||
.all { isSuccess -> isSuccess }
|
||||
.also {
|
||||
if (activeRoomsHolder.getActiveRoomMatching(matrixClient.sessionId, roomId) == null) {
|
||||
room.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
.all { it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle post-processing of shared data
|
||||
onSharedData(shareIntentData)
|
||||
|
||||
if (!result) {
|
||||
error("Failed to handle incoming share intent")
|
||||
}
|
||||
roomIds
|
||||
}.runCatchingUpdatingState(shareActionState)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -10,8 +10,10 @@ package io.element.android.features.share.impl
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
data class ShareState(
|
||||
val sessionId: SessionId,
|
||||
val shareAction: AsyncAction<List<RoomId>>,
|
||||
val eventSink: (ShareEvent) -> Unit
|
||||
)
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ package io.element.android.features.share.impl
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
open class ShareStatePreviewParam : PreviewParameterProvider<ShareState> {
|
||||
override val values: Sequence<ShareState>
|
||||
@@ -31,9 +32,11 @@ open class ShareStatePreviewParam : PreviewParameterProvider<ShareState> {
|
||||
}
|
||||
|
||||
fun aShareState(
|
||||
sessionId: SessionId = SessionId("@alice:server.org"),
|
||||
shareAction: AsyncAction<List<RoomId>> = AsyncAction.Uninitialized,
|
||||
eventSink: (ShareEvent) -> Unit = {}
|
||||
) = ShareState(
|
||||
sessionId = sessionId,
|
||||
shareAction = shareAction,
|
||||
eventSink = eventSink
|
||||
)
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.share.impl.di
|
||||
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import io.element.android.features.share.impl.SharePresenter
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
/**
|
||||
* Used to get a [SharePresenter.Factory] from the graph of a session which is not the current one.
|
||||
*/
|
||||
@ContributesTo(SessionScope::class)
|
||||
interface ShareBindings {
|
||||
fun sharePresenterFactory(): SharePresenter.Factory
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.share.impl
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.share.api.OnSharedData
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.share.api.UriToShare
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
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.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProviderFactory
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderRoomFactory
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaSender
|
||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
||||
import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultShareDataSenderTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `send text ok`() = runTest {
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
liveTimeline = FakeTimeline().apply {
|
||||
sendMessageLambda = { _, _, _, _, _ -> Result.success(Unit) }
|
||||
},
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, joinedRoom)
|
||||
}
|
||||
val onSharedData = lambdaRecorder<ShareIntentData, Unit> { }
|
||||
val sender = createDefaultShareDataSender(
|
||||
onSharedData = { onSharedData(it) },
|
||||
)
|
||||
val result = sender.send(matrixClient, ShareIntentData.PlainText(A_MESSAGE), listOf(A_ROOM_ID))
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
onSharedData.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send text, room not found`() = runTest {
|
||||
val onSharedData = lambdaRecorder<ShareIntentData, Unit> { }
|
||||
val sender = createDefaultShareDataSender(
|
||||
onSharedData = { onSharedData(it) },
|
||||
)
|
||||
val result = sender.send(FakeMatrixClient(), ShareIntentData.PlainText(A_MESSAGE), listOf(A_ROOM_ID))
|
||||
assertThat(result.isFailure).isTrue()
|
||||
onSharedData.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send media ok`() = runTest {
|
||||
val sendMediaResult = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
liveTimeline = FakeTimeline(),
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, joinedRoom)
|
||||
}
|
||||
val mediaSender = FakeMediaSender(
|
||||
sendMediaResult = sendMediaResult,
|
||||
)
|
||||
val sender = createDefaultShareDataSender(
|
||||
mediaSenderRoomFactory = { _, _ -> mediaSender },
|
||||
)
|
||||
val result = sender.send(
|
||||
client = matrixClient,
|
||||
shareIntentData = ShareIntentData.Uris(
|
||||
text = A_MESSAGE,
|
||||
listOf(
|
||||
UriToShare(
|
||||
uri = Uri.parse("content://image.jpg"),
|
||||
mimeType = MimeTypes.Jpeg,
|
||||
)
|
||||
)
|
||||
),
|
||||
roomIds = listOf(A_ROOM_ID),
|
||||
)
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `send media with no uris is a failure`() = runTest {
|
||||
val sender = createDefaultShareDataSender()
|
||||
val result = sender.send(
|
||||
client = FakeMatrixClient(),
|
||||
shareIntentData = ShareIntentData.Uris(text = A_MESSAGE, emptyList()),
|
||||
roomIds = listOf(A_ROOM_ID),
|
||||
)
|
||||
assertThat(result.isFailure).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDefaultShareDataSender(
|
||||
mediaSenderRoomFactory: MediaSenderRoomFactory = MediaSenderRoomFactory { _, _ -> FakeMediaSender() },
|
||||
mediaOptimizationConfigProviderFactory: MediaOptimizationConfigProviderFactory = MediaOptimizationConfigProviderFactory {
|
||||
FakeMediaOptimizationConfigProvider()
|
||||
},
|
||||
activeRoomsHolder: ActiveRoomsHolder = DefaultActiveRoomsHolder(),
|
||||
onSharedData: OnSharedData = OnSharedData {},
|
||||
) = DefaultShareDataSender(
|
||||
mediaSenderRoomFactory = mediaSenderRoomFactory,
|
||||
mediaOptimizationConfigProviderFactory = mediaOptimizationConfigProviderFactory,
|
||||
activeRoomsHolder = activeRoomsHolder,
|
||||
onSharedData = onSharedData,
|
||||
)
|
||||
+1
-8
@@ -14,13 +14,10 @@ import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.share.api.ShareEntryPoint
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.SessionGraphFactory
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||
import io.element.android.libraries.roomselect.test.FakeRoomSelectEntryPoint
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.node.TestParentNode
|
||||
@@ -42,13 +39,9 @@ class DefaultShareEntryPointTest {
|
||||
ShareNode(
|
||||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
presenterFactory = { createSharePresenter() },
|
||||
presenterFactory = { _, _ -> createSharePresenter() },
|
||||
roomSelectEntryPoint = FakeRoomSelectEntryPoint(),
|
||||
sessionId = A_SESSION_ID,
|
||||
matrixClientProvider = FakeMatrixClientProvider(),
|
||||
sessionGraphFactory = object : SessionGraphFactory {
|
||||
override fun create(client: MatrixClient): Any = lambdaError()
|
||||
},
|
||||
)
|
||||
}
|
||||
val callback = object : ShareEntryPoint.Callback {
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.share.impl
|
||||
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeShareDataSender(
|
||||
private val sendResult: (MatrixClient, ShareIntentData, List<RoomId>) -> Result<Unit> = { _, _, _ -> lambdaError() },
|
||||
) : ShareDataSender {
|
||||
override suspend fun send(client: MatrixClient, shareIntentData: ShareIntentData, roomIds: List<RoomId>): Result<Unit> {
|
||||
return sendResult(client, shareIntentData, roomIds)
|
||||
}
|
||||
}
|
||||
+80
-80
@@ -8,37 +8,32 @@
|
||||
|
||||
package io.element.android.features.share.impl
|
||||
|
||||
import android.net.Uri
|
||||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.share.api.OnSharedData
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.share.api.UriToShare
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
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_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderRoomFactory
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaSender
|
||||
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
|
||||
import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class SharePresenterTest : RobolectricTest() {
|
||||
class SharePresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@@ -49,22 +44,25 @@ class SharePresenterTest : RobolectricTest() {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.sessionId).isEqualTo(A_SESSION_ID)
|
||||
assertThat(initialState.shareAction.isUninitialized()).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - on room selected error then clear error`() = runTest {
|
||||
val presenter = createSharePresenter()
|
||||
val presenter = createSharePresenter(
|
||||
shareDataSender = FakeShareDataSender { _, _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.shareAction.isUninitialized()).isTrue()
|
||||
presenter.onRoomSelected(listOf(A_ROOM_ID))
|
||||
presenter.onRoomSelected(A_SESSION_ID, listOf(A_ROOM_ID))
|
||||
assertThat(awaitItem().shareAction.isLoading()).isTrue()
|
||||
val failure = awaitItem()
|
||||
assertThat(failure.shareAction.isFailure()).isTrue()
|
||||
assertThat(failure.shareAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
failure.eventSink.invoke(ShareEvent.ClearError)
|
||||
assertThat(awaitItem().shareAction.isUninitialized()).isTrue()
|
||||
}
|
||||
@@ -72,113 +70,115 @@ class SharePresenterTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `present - on room selected ok`() = runTest {
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
liveTimeline = FakeTimeline().apply {
|
||||
sendMessageLambda = { _, _, _, _, _ -> Result.success(Unit) }
|
||||
},
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, joinedRoom)
|
||||
}
|
||||
val client = FakeMatrixClient(sessionId = A_SESSION_ID)
|
||||
val shareIntentData = ShareIntentData.PlainText(A_MESSAGE)
|
||||
val sendResult = lambdaRecorder<MatrixClient, ShareIntentData, List<RoomId>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val presenter = createSharePresenter(
|
||||
matrixClient = matrixClient,
|
||||
shareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||
shareIntentData = shareIntentData,
|
||||
matrixClientProvider = FakeMatrixClientProvider { Result.success(client) },
|
||||
shareDataSender = FakeShareDataSender(sendResult),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.shareAction.isUninitialized()).isTrue()
|
||||
presenter.onRoomSelected(listOf(A_ROOM_ID))
|
||||
presenter.onRoomSelected(A_SESSION_ID, listOf(A_ROOM_ID))
|
||||
assertThat(awaitItem().shareAction.isLoading()).isTrue()
|
||||
val success = awaitItem()
|
||||
assertThat(success.shareAction.isSuccess()).isTrue()
|
||||
assertThat(success.sessionId).isEqualTo(A_SESSION_ID)
|
||||
assertThat(success.shareAction).isEqualTo(AsyncAction.Success(listOf(A_ROOM_ID)))
|
||||
sendResult.assertions().isCalledOnce().with(value(client), value(shareIntentData), value(listOf(A_ROOM_ID)))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send text ok`() = runTest {
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
liveTimeline = FakeTimeline().apply {
|
||||
sendMessageLambda = { _, _, _, _, _ -> Result.success(Unit) }
|
||||
},
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, joinedRoom)
|
||||
fun `present - on room selected from another session ok`() = runTest {
|
||||
val getClient = lambdaRecorder<SessionId, Result<MatrixClient>> { sessionId -> Result.success(FakeMatrixClient(sessionId = sessionId)) }
|
||||
val sendResult = lambdaRecorder<MatrixClient, ShareIntentData, List<RoomId>, Result<Unit>> { client, _, _ ->
|
||||
assertThat(client.sessionId).isEqualTo(A_SESSION_ID_2)
|
||||
Result.success(Unit)
|
||||
}
|
||||
val presenter = createSharePresenter(
|
||||
matrixClient = matrixClient,
|
||||
shareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||
matrixClientProvider = FakeMatrixClientProvider { getClient(it) },
|
||||
shareDataSender = FakeShareDataSender(sendResult),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.shareAction.isUninitialized()).isTrue()
|
||||
presenter.onRoomSelected(listOf(A_ROOM_ID))
|
||||
assertThat(initialState.sessionId).isEqualTo(A_SESSION_ID)
|
||||
presenter.onRoomSelected(A_SESSION_ID_2, listOf(A_ROOM_ID))
|
||||
val sessionChanged = awaitItem()
|
||||
assertThat(sessionChanged.sessionId).isEqualTo(A_SESSION_ID_2)
|
||||
assertThat(sessionChanged.shareAction.isUninitialized()).isTrue()
|
||||
assertThat(awaitItem().shareAction.isLoading()).isTrue()
|
||||
val success = awaitItem()
|
||||
assertThat(success.shareAction.isSuccess()).isTrue()
|
||||
assertThat(success.sessionId).isEqualTo(A_SESSION_ID_2)
|
||||
assertThat(success.shareAction).isEqualTo(AsyncAction.Success(listOf(A_ROOM_ID)))
|
||||
getClient.assertions().isCalledOnce().with(value(A_SESSION_ID_2))
|
||||
sendResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - send media ok`() = runTest {
|
||||
val sendMediaResult = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
liveTimeline = FakeTimeline(),
|
||||
)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, joinedRoom)
|
||||
}
|
||||
val mediaSender = FakeMediaSender(
|
||||
sendMediaResult = sendMediaResult,
|
||||
)
|
||||
fun `present - on room selected, failure to get the client`() = runTest {
|
||||
val sendResult = lambdaRecorder<MatrixClient, ShareIntentData, List<RoomId>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val presenter = createSharePresenter(
|
||||
matrixClient = matrixClient,
|
||||
shareIntentData = ShareIntentData.Uris(
|
||||
text = A_MESSAGE,
|
||||
listOf(
|
||||
UriToShare(
|
||||
uri = Uri.parse("content://image.jpg"),
|
||||
mimeType = MimeTypes.Jpeg,
|
||||
)
|
||||
)
|
||||
),
|
||||
mediaSenderRoomFactory = MediaSenderRoomFactory { mediaSender },
|
||||
matrixClientProvider = FakeMatrixClientProvider { Result.failure(AN_EXCEPTION) },
|
||||
shareDataSender = FakeShareDataSender(sendResult),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.shareAction.isUninitialized()).isTrue()
|
||||
presenter.onRoomSelected(listOf(A_ROOM_ID))
|
||||
skipItems(1)
|
||||
presenter.onRoomSelected(A_SESSION_ID_2, listOf(A_ROOM_ID))
|
||||
assertThat(awaitItem().sessionId).isEqualTo(A_SESSION_ID_2)
|
||||
assertThat(awaitItem().shareAction.isLoading()).isTrue()
|
||||
assertThat(awaitItem().shareAction).isEqualTo(AsyncAction.Failure(AN_EXCEPTION))
|
||||
sendResult.assertions().isNeverCalled()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - on room selected while sharing is ignored`() = runTest {
|
||||
val completable = CompletableDeferred<Result<Unit>>()
|
||||
val sendResult = lambdaRecorder<MatrixClient, ShareIntentData, List<RoomId>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val presenter = createSharePresenter(
|
||||
shareDataSender = object : ShareDataSender {
|
||||
override suspend fun send(client: MatrixClient, shareIntentData: ShareIntentData, roomIds: List<RoomId>): Result<Unit> {
|
||||
sendResult(client, shareIntentData, roomIds)
|
||||
return completable.await()
|
||||
}
|
||||
},
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
presenter.onRoomSelected(A_SESSION_ID, listOf(A_ROOM_ID))
|
||||
assertThat(awaitItem().shareAction.isLoading()).isTrue()
|
||||
presenter.onRoomSelected(A_SESSION_ID_2, listOf(A_ROOM_ID))
|
||||
completable.complete(Result.success(Unit))
|
||||
val success = awaitItem()
|
||||
assertThat(success.shareAction.isSuccess()).isTrue()
|
||||
assertThat(success.sessionId).isEqualTo(A_SESSION_ID)
|
||||
assertThat(success.shareAction).isEqualTo(AsyncAction.Success(listOf(A_ROOM_ID)))
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
sendResult.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun TestScope.createSharePresenter(
|
||||
shareIntentData: ShareIntentData = ShareIntentData.PlainText(A_MESSAGE),
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
activeRoomsHolder: ActiveRoomsHolder = DefaultActiveRoomsHolder(),
|
||||
mediaSenderRoomFactory: MediaSenderRoomFactory = MediaSenderRoomFactory { FakeMediaSender() },
|
||||
mediaOptimizationConfigProvider: MediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||
onSharedData: OnSharedData = OnSharedData {},
|
||||
initialSessionId: SessionId = A_SESSION_ID,
|
||||
matrixClientProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
|
||||
shareDataSender: ShareDataSender = FakeShareDataSender(),
|
||||
): SharePresenter {
|
||||
return SharePresenter(
|
||||
shareIntentData = shareIntentData,
|
||||
sessionCoroutineScope = this,
|
||||
matrixClient = matrixClient,
|
||||
activeRoomsHolder = activeRoomsHolder,
|
||||
mediaSenderRoomFactory = mediaSenderRoomFactory,
|
||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
||||
onSharedData = onSharedData,
|
||||
initialSessionId = initialSessionId,
|
||||
appCoroutineScope = this,
|
||||
matrixClientProvider = matrixClientProvider,
|
||||
shareDataSender = shareDataSender,
|
||||
)
|
||||
}
|
||||
|
||||
+14
@@ -8,6 +8,8 @@
|
||||
|
||||
package io.element.android.libraries.mediaupload.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
||||
/**
|
||||
* Resolves how aggressively media should be compressed, combining the user's own preferences with the app defaults.
|
||||
*/
|
||||
@@ -15,3 +17,15 @@ fun interface MediaOptimizationConfigProvider {
|
||||
/** Returns the configuration to pass to the pre-processing and sending calls. */
|
||||
suspend fun get(): MediaOptimizationConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [MediaOptimizationConfigProvider] for a given session, used outside the session scope, for instance when sharing to another account.
|
||||
*/
|
||||
fun interface MediaOptimizationConfigProviderFactory {
|
||||
/**
|
||||
* Create a [MediaOptimizationConfigProvider] using the preferences of the session of [client].
|
||||
*
|
||||
* @param client the client of the session whose preferences should be used.
|
||||
*/
|
||||
fun create(client: MatrixClient): MediaOptimizationConfigProvider
|
||||
}
|
||||
|
||||
+3
@@ -9,6 +9,7 @@
|
||||
package io.element.android.libraries.mediaupload.api
|
||||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
@@ -34,9 +35,11 @@ fun interface MediaSenderRoomFactory {
|
||||
/**
|
||||
* Create a [MediaSender] for the given [JoinedRoom], with timeline mode Live.
|
||||
*
|
||||
* @param client the client of the session the room belongs to, used to read the media preferences of that session.
|
||||
* @param room the room the media should be sent to.
|
||||
*/
|
||||
fun create(
|
||||
client: MatrixClient,
|
||||
room: JoinedRoom,
|
||||
): MediaSender
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.mediaupload.impl
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProviderFactory
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultMediaOptimizationConfigProviderFactory(
|
||||
private val sessionPreferencesStoreFactory: SessionPreferencesStoreFactory,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
) : MediaOptimizationConfigProviderFactory {
|
||||
override fun create(client: MatrixClient): MediaOptimizationConfigProvider {
|
||||
return DefaultMediaOptimizationConfigProvider(
|
||||
sessionPreferencesStore = sessionPreferencesStoreFactory.get(client.sessionId, client.sessionCoroutineScope),
|
||||
featureFlagsService = featureFlagService,
|
||||
)
|
||||
}
|
||||
}
|
||||
+7
-4
@@ -9,12 +9,13 @@
|
||||
package io.element.android.libraries.mediaupload.impl
|
||||
|
||||
import android.net.Uri
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.hash.hash
|
||||
import io.element.android.libraries.core.extensions.flatMap
|
||||
import io.element.android.libraries.core.extensions.flatMapCatching
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
@@ -22,6 +23,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProviderFactory
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderFactory
|
||||
@@ -52,19 +54,20 @@ class DefaultMediaSenderFactory(
|
||||
}
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultMediaSenderRoomFactory(
|
||||
private val preProcessor: MediaPreProcessor,
|
||||
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
|
||||
private val mediaOptimizationConfigProviderFactory: MediaOptimizationConfigProviderFactory,
|
||||
) : MediaSenderRoomFactory {
|
||||
override fun create(
|
||||
client: MatrixClient,
|
||||
room: JoinedRoom,
|
||||
): MediaSender {
|
||||
return DefaultMediaSender(
|
||||
preProcessor = preProcessor,
|
||||
room = room,
|
||||
timelineMode = Timeline.Mode.Live,
|
||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProviderFactory.create(client),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user