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:
Benoit Marty
2026-10-07 10:34:20 +02:00
parent ed2a553736
commit 72e3a7d92b
15 changed files with 436 additions and 264 deletions
@@ -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
}
@@ -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
}
@@ -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,
)
}
}
@@ -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),
)
}
}