Merge branch 'develop' into fix/4657-preview-param-suffix

This commit is contained in:
Benoit Marty
2026-08-24 14:08:52 +02:00
committed by GitHub
60 changed files with 282 additions and 195 deletions
@@ -22,10 +22,10 @@ import timber.log.Timber
@BindingContainer
@ContributesTo(AppScope::class)
object CacheStoreModule {
object CacheStoreBindingContainer {
@Provides
@SingleIn(AppScope::class)
fun provideCacheDatabase(
fun providesCacheDatabase(
@ApplicationContext context: Context,
): CacheDatabase {
val name = "cache_database"
@@ -26,6 +26,8 @@ import java.util.concurrent.ConcurrentHashMap
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
internal const val ANDROID_KEYSTORE = "AndroidKeyStore"
/**
* Default implementation of [SecretKeyRepository] that uses the Android Keystore to store the keys.
* The generated key uses AES algorithm, with a key size of 128 bits, and the GCM block mode.
@@ -6,19 +6,18 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.cryptography.impl
package io.element.android.libraries.cryptography.impl.di
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Provides
import io.element.android.libraries.cryptography.impl.ANDROID_KEYSTORE
import java.security.KeyStore
internal const val ANDROID_KEYSTORE = "AndroidKeyStore"
@ContributesTo(AppScope::class)
@BindingContainer
object CryptographyModule {
object CryptographyBindingContainer {
@Provides
fun providesAndroidKeyStore(): KeyStore {
return KeyStore.getInstance(ANDROID_KEYSTORE).apply {
@@ -19,7 +19,7 @@ import kotlin.time.Clock
@BindingContainer
@ContributesTo(AppScope::class)
object DateFormatterModule {
object DateFormatterBindingContainer {
@Provides
fun providesClock(): Clock = Clock.System
@@ -27,5 +27,5 @@ object DateFormatterModule {
fun providesLocale(): Locale = Locale.getDefault()
@Provides
fun providesTimezone(): TimezoneProvider = TimezoneProvider { TimeZone.currentSystemDefault() }
fun providesTimezoneProvider(): TimezoneProvider = TimezoneProvider { TimeZone.currentSystemDefault() }
}
@@ -18,7 +18,7 @@ import io.element.android.libraries.featureflag.impl.PreferencesFeatureFlagProvi
@BindingContainer
@ContributesTo(AppScope::class)
object FeatureFlagModule {
object FeatureFlagBindingContainer {
@JvmStatic
@Provides
@ElementsIntoSet
@@ -18,7 +18,7 @@ import io.element.android.libraries.fullscreenintent.impl.FullScreenIntentPermis
@ContributesTo(AppScope::class)
@BindingContainer
interface FullScreenIntentModule {
interface FullScreenIntentBindingContainer {
@Binds
fun bindFullScreenIntentPermissionsPresenter(presenter: FullScreenIntentPermissionsPresenter): Presenter<FullScreenIntentPermissionsState>
}
@@ -18,7 +18,7 @@ import kotlinx.coroutines.CoroutineScope
@BindingContainer
@ContributesTo(RoomScope::class)
object RoomModule {
object RoomBindingContainer {
@RoomCoroutineScope
@Provides
fun providesSessionCoroutineScope(room: BaseRoom): CoroutineScope {
@@ -34,7 +34,7 @@ import kotlinx.coroutines.CoroutineScope
@BindingContainer
@ContributesTo(SessionScope::class)
object SessionMatrixModule {
object SessionMatrixBindingContainer {
@Provides
fun providesSessionId(matrixClient: MatrixClient): SessionId {
return matrixClient.sessionId
@@ -56,7 +56,7 @@ object SessionMatrixModule {
}
@Provides
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
fun providesRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
return matrixClient.roomMembershipObserver
}
@@ -112,7 +112,7 @@ object SessionMatrixModule {
}
@Provides
fun providesGetUrlResolverClient(matrixClient: MatrixClient): ClientUrlContentFetcher {
fun providesClientUrlContentFetcher(matrixClient: MatrixClient): ClientUrlContentFetcher {
return matrixClient
}
@@ -8,16 +8,8 @@
package io.element.android.libraries.mediaplayer.impl
import android.content.Context
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Provides
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.ApplicationContext
/**
* A subset of media3 [Player] that only exposes the few methods we need making it easier to mock.
@@ -44,15 +36,6 @@ interface SimplePlayer {
}
}
@ContributesTo(RoomScope::class)
@BindingContainer
object SimplePlayerModule {
@Provides
fun simplePlayerProvider(
@ApplicationContext context: Context,
): SimplePlayer = DefaultSimplePlayer(ExoPlayer.Builder(context).setWakeMode(C.WAKE_MODE_LOCAL).build())
}
/**
* Default implementation of [SimplePlayer] backed by a media3 [Player].
*/
@@ -0,0 +1,28 @@
/*
* 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.mediaplayer.impl.di
import android.content.Context
import androidx.media3.common.C
import androidx.media3.exoplayer.ExoPlayer
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Provides
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.mediaplayer.impl.DefaultSimplePlayer
import io.element.android.libraries.mediaplayer.impl.SimplePlayer
@ContributesTo(RoomScope::class)
@BindingContainer
object SimplePlayerBindingContainer {
@Provides
fun providesSimplePlayer(
@ApplicationContext context: Context,
): SimplePlayer = DefaultSimplePlayer(ExoPlayer.Builder(context).setWakeMode(C.WAKE_MODE_LOCAL).build())
}
@@ -10,28 +10,13 @@ package io.element.android.libraries.mediaviewer.impl.gallery.di
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.Multibinds
import dev.zacsweers.metro.SingleIn
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.mediaviewer.impl.model.MediaItem
import kotlin.reflect.KClass
/**
* Container that declares the [MediaItemPresenterFactory] map multi binding.
*
* Its sole purpose is to support the case of an empty map multibinding.
*/
@BindingContainer
@ContributesTo(RoomScope::class)
interface MediaItemPresenterFactoriesModule {
@Multibinds
fun multiBindMediaItemPresenterFactories(): @JvmSuppressWildcards Map<KClass<out MediaItem.Event>, MediaItemPresenterFactory<*, *>>
}
/**
* Room level caching layer for the [MediaItemPresenterFactory] instances.
*
@@ -0,0 +1,27 @@
/*
* 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.mediaviewer.impl.gallery.di
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Multibinds
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.mediaviewer.impl.model.MediaItem
import kotlin.reflect.KClass
/**
* Container that declares the [MediaItemPresenterFactory] map multi binding.
*
* Its sole purpose is to support the case of an empty map multibinding.
*/
@BindingContainer
@ContributesTo(RoomScope::class)
interface MediaItemPresenterFactoriesBindingContainer {
@Multibinds
fun multiBindMediaItemPresenterFactories(): @JvmSuppressWildcards Map<KClass<out MediaItem.Event>, MediaItemPresenterFactory<*, *>>
}
@@ -12,28 +12,13 @@ import androidx.compose.runtime.Composable
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.Binds
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.IntoMap
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.mediaviewer.impl.gallery.di.MediaItemEventContentKey
import io.element.android.libraries.mediaviewer.impl.gallery.di.MediaItemPresenterFactory
import io.element.android.libraries.mediaviewer.impl.model.MediaItem
import io.element.android.libraries.voiceplayer.api.VoiceMessagePresenterFactory
import io.element.android.libraries.voiceplayer.api.VoiceMessageState
import kotlin.time.Duration
@BindingContainer
@ContributesTo(RoomScope::class)
interface VoiceMessagePresenterModule {
@Binds
@IntoMap
@MediaItemEventContentKey(MediaItem.Voice::class)
fun bindVoiceMessagePresenterFactory(factory: VoiceMessagePresenter.Factory): MediaItemPresenterFactory<*, *>
}
@AssistedInject
class VoiceMessagePresenter(
voiceMessagePresenterFactory: VoiceMessagePresenterFactory,
@@ -0,0 +1,27 @@
/*
* 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.mediaviewer.impl.gallery.voice.di
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.Binds
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.IntoMap
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.mediaviewer.impl.gallery.di.MediaItemEventContentKey
import io.element.android.libraries.mediaviewer.impl.gallery.di.MediaItemPresenterFactory
import io.element.android.libraries.mediaviewer.impl.gallery.voice.VoiceMessagePresenter
import io.element.android.libraries.mediaviewer.impl.model.MediaItem
@BindingContainer
@ContributesTo(RoomScope::class)
interface VoiceMessagePresenterBindingContainer {
@Binds
@IntoMap
@MediaItemEventContentKey(MediaItem.Voice::class)
fun bindVoiceMessagePresenterFactory(factory: VoiceMessagePresenter.Factory): MediaItemPresenterFactory<*, *>
}
@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.network
package io.element.android.libraries.network.di
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.BindingContainer
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit
@BindingContainer
@ContributesTo(AppScope::class)
object NetworkModule {
object NetworkBindingContainer {
@Provides
@SingleIn(AppScope::class)
fun providesOkHttpClient(
@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.preferences.impl.store
package io.element.android.libraries.preferences.impl.store.di
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
@@ -15,11 +15,12 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.impl.store.DefaultSessionPreferencesStoreFactory
import kotlinx.coroutines.CoroutineScope
@BindingContainer
@ContributesTo(SessionScope::class)
object SessionPreferencesModule {
object SessionPreferencesBindingContainer {
@Provides
fun providesSessionPreferencesStore(
defaultSessionPreferencesStoreFactory: DefaultSessionPreferencesStoreFactory,
@@ -22,10 +22,10 @@ import io.element.android.libraries.push.impl.battery.BatteryOptimizationPresent
@BindingContainer
@ContributesTo(AppScope::class)
interface PushModule {
interface PushBindingContainer {
companion object {
@Provides
fun provideNotificationCompatManager(@ApplicationContext context: Context): NotificationManagerCompat {
fun providesNotificationManagerCompat(@ApplicationContext context: Context): NotificationManagerCompat {
return NotificationManagerCompat.from(context)
}
}
@@ -23,10 +23,10 @@ import timber.log.Timber
@BindingContainer
@ContributesTo(AppScope::class)
object PushHistoryModule {
object PushHistoryBindingContainer {
@Provides
@SingleIn(AppScope::class)
fun providePushDatabase(
fun providesPushDatabase(
@ApplicationContext context: Context,
): PushDatabase {
val name = "push_database"
@@ -23,10 +23,10 @@ import timber.log.Timber
@BindingContainer
@ContributesTo(AppScope::class)
object SessionStorageModule {
object SessionStorageBindingContainer {
@Provides
@SingleIn(AppScope::class)
fun provideMatrixDatabase(
fun providesSessionDatabase(
@ApplicationContext context: Context,
): SessionDatabase {
val name = "session_database"
@@ -22,9 +22,9 @@ import io.element.android.opusencoder.OggOpusEncoder
@BindingContainer
@ContributesTo(RoomScope::class)
object VoiceRecorderModule {
object VoiceRecorderBindingContainer {
@Provides
fun provideAudioConfig(): AudioConfig {
fun providesAudioConfig(): AudioConfig {
val sampleRate = SampleRate
return AudioConfig(
format = AudioFormat.Builder()
@@ -40,7 +40,7 @@ object VoiceRecorderModule {
}
@Provides
public fun provideVoiceFileConfig(): VoiceFileConfig =
public fun providesVoiceFileConfig(): VoiceFileConfig =
VoiceFileConfig(
cacheSubdir = "voice_recordings",
fileExt = "ogg",
@@ -48,5 +48,5 @@ object VoiceRecorderModule {
)
@Provides
fun provideOggOpusEncoder(): OggOpusEncoder = OggOpusEncoder.create()
fun providesOggOpusEncoder(): OggOpusEncoder = OggOpusEncoder.create()
}
@@ -18,7 +18,7 @@ import io.element.android.libraries.voicerecorder.api.VoiceRecorderState
import io.element.android.libraries.voicerecorder.impl.audio.Audio
import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig
import io.element.android.libraries.voicerecorder.impl.audio.SampleRate
import io.element.android.libraries.voicerecorder.impl.di.VoiceRecorderModule
import io.element.android.libraries.voicerecorder.impl.di.VoiceRecorderBindingContainer
import io.element.android.libraries.voicerecorder.test.FakeAudioLevelCalculator
import io.element.android.libraries.voicerecorder.test.FakeAudioReaderFactory
import io.element.android.libraries.voicerecorder.test.FakeEncoder
@@ -151,7 +151,7 @@ class DefaultVoiceRecorderTest {
}
private fun TestScope.createDefaultVoiceRecorder(): DefaultVoiceRecorder {
val fileConfig = VoiceRecorderModule.provideVoiceFileConfig()
val fileConfig = VoiceRecorderBindingContainer.providesVoiceFileConfig()
return DefaultVoiceRecorder(
dispatchers = testCoroutineDispatchers(),
timeSource = timeSource,
@@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.workmanager.impl
package io.element.android.libraries.workmanager.impl.di
import android.content.Context
import androidx.work.WorkManager
@@ -18,7 +18,7 @@ import io.element.android.libraries.di.annotations.ApplicationContext
@BindingContainer
@ContributesTo(AppScope::class)
object WorkManagerModule {
object WorkManagerBindingContainer {
@Provides
@SingleIn(AppScope::class)
fun providesWorkManager(