Merge pull request #7495 from element-hq/refactor/add-client-enterprise-hooks

Add `ClientEnterpriseHook` and `ClientBuilderEnterpriseHook`
This commit is contained in:
Benoit Marty
2026-08-22 08:10:50 +02:00
committed by GitHub
17 changed files with 149 additions and 91 deletions
@@ -0,0 +1,18 @@
/*
* 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.enterprise.api
import io.element.android.libraries.matrix.api.MatrixClientBuilder
import io.element.android.libraries.matrix.api.core.SessionId
/**
* A hook that can be used to customize the [MatrixClientBuilder] for enterprise features.
*/
fun interface ClientBuilderEnterpriseHook {
suspend operator fun invoke(clientBuilder: MatrixClientBuilder, sessionId: SessionId): MatrixClientBuilder
}
@@ -0,0 +1,17 @@
/*
* 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.enterprise.api
import io.element.android.libraries.matrix.api.MatrixClient
/**
* A hook that can be used to customize the [MatrixClient] for enterprise features.
*/
fun interface ClientEnterpriseHook {
suspend operator fun invoke(client: MatrixClient)
}
@@ -19,11 +19,9 @@ sealed interface RemoteEnterpriseConfigResult<out T> {
data class Outdated<out T>(val data: T) : RemoteEnterpriseConfigResult<T> data class Outdated<out T>(val data: T) : RemoteEnterpriseConfigResult<T>
/** /**
* Well-known data is not found (file does not exist server side, we got a 404). `needsRefresh` indicates whether a refresh must be done * Well-known data is not found (file does not exist server side, we got a 404).
* to replace this value: if it's `true`, the caller must trigger a refresh to try to fetch the data again;
* if it's `false`, the caller can assume that the data is not available and no refresh is needed before returning the data.
*/ */
data class NotFound(val needsRefresh: Boolean) : RemoteEnterpriseConfigResult<Nothing> data object NotFound : RemoteEnterpriseConfigResult<Nothing>
/** /**
* Any other error. * Any other error.
@@ -0,0 +1,25 @@
/*
* 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.enterprise.impl
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.enterprise.api.ClientBuilderEnterpriseHook
import io.element.android.libraries.matrix.api.MatrixClientBuilder
import io.element.android.libraries.matrix.api.core.SessionId
/**
* Default implementation of [ClientBuilderEnterpriseHook] that does nothing for FOSS.
*/
@ContributesBinding(AppScope::class)
class DefaultClientBuilderEnterpriseHook : ClientBuilderEnterpriseHook {
override suspend fun invoke(clientBuilder: MatrixClientBuilder, sessionId: SessionId): MatrixClientBuilder {
// No modification
return clientBuilder
}
}
@@ -0,0 +1,23 @@
/*
* 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.enterprise.impl
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.enterprise.api.ClientEnterpriseHook
import io.element.android.libraries.matrix.api.MatrixClient
/**
* Default implementation of [ClientEnterpriseHook] that does nothing for FOSS.
*/
@ContributesBinding(AppScope::class)
class DefaultClientEnterpriseHook : ClientEnterpriseHook {
override suspend fun invoke(client: MatrixClient) {
// No-op
}
}
@@ -1,29 +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.enterprise.impl.scanner
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider
/**
* Default FOSS implementation of [ContentScannerUrlProvider] that returns `null` for the content scanner URL.
*/
@ContributesBinding(AppScope::class)
class DefaultContentScannerUrlProvider : ContentScannerUrlProvider {
override suspend fun getContentScannerUrl(sessionId: SessionId): Result<String?> = Result.success(null)
@ContributesBinding(AppScope::class)
class Factory : ContentScannerUrlProvider.Factory {
override fun create(urlContentFetcher: ClientUrlContentFetcher): ContentScannerUrlProvider {
return DefaultContentScannerUrlProvider()
}
}
}
@@ -0,0 +1,13 @@
/*
* 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.matrix.api
/**
* An abstraction over the Matrix SDK's `ClientBuilder` to allow sharing it with other modules without exposing the SDK directly.
*/
interface MatrixClientBuilder
@@ -1,31 +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.libraries.matrix.api.scanner
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
import io.element.android.libraries.matrix.api.core.SessionId
/**
* Provides the URL of the content scanner service for a given homeserver, if any is set up.
*/
fun interface ContentScannerUrlProvider {
/**
* Returns the URL of the content scanner service for the given [sessionId], or `null` if no content scanner is set up.
*/
suspend fun getContentScannerUrl(sessionId: SessionId): Result<String?>
/**
* Creates a provider, which needs a fetcher because the scanner URL is itself discovered over HTTP.
*/
fun interface Factory {
/**
* @param urlContentFetcher used to read the configuration advertised by the homeserver; a client that is not authenticated yet also works.
*/
fun create(urlContentFetcher: ClientUrlContentFetcher): ContentScannerUrlProvider
}
}
@@ -146,7 +146,7 @@ import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
@Suppress("LargeClass") @Suppress("LargeClass")
class RustMatrixClient( class RustMatrixClient(
override val sessionPaths: SessionPaths, override val sessionPaths: SessionPaths,
private val innerClient: Client, val innerClient: Client,
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
private val sessionDelegate: RustClientSessionDelegate, private val sessionDelegate: RustClientSessionDelegate,
private val innerSyncService: ClientSyncService, private val innerSyncService: ClientSyncService,
@@ -0,0 +1,18 @@
/*
* 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.matrix.impl
import io.element.android.libraries.matrix.api.MatrixClientBuilder
import org.matrix.rustcomponents.sdk.ClientBuilder
/**
* A wrapper around the Matrix SDK's [ClientBuilder] to allow sharing it with other modules without exposing the SDK directly.
*/
class RustMatrixClientBuilder(
val inner: ClientBuilder,
) : MatrixClientBuilder
@@ -9,6 +9,7 @@
package io.element.android.libraries.matrix.impl package io.element.android.libraries.matrix.impl
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.features.enterprise.api.ClientBuilderEnterpriseHook
import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.android.libraries.androidutils.crypto.ClientSecret
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.data.ByteUnit import io.element.android.libraries.core.data.ByteUnit
@@ -19,7 +20,6 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.paths.SessionPaths import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider
import io.element.android.libraries.matrix.impl.analytics.UtdTracker import io.element.android.libraries.matrix.impl.analytics.UtdTracker
import io.element.android.libraries.matrix.impl.paths.getSessionPaths import io.element.android.libraries.matrix.impl.paths.getSessionPaths
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
@@ -37,7 +37,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.ClientBuilder
import org.matrix.rustcomponents.sdk.ContentScanner
import org.matrix.rustcomponents.sdk.CrossProcessLockConfig import org.matrix.rustcomponents.sdk.CrossProcessLockConfig
import org.matrix.rustcomponents.sdk.RequestConfig import org.matrix.rustcomponents.sdk.RequestConfig
import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.Session
@@ -70,7 +69,7 @@ class RustMatrixClientFactory(
private val clientBuilderProvider: ClientBuilderProvider, private val clientBuilderProvider: ClientBuilderProvider,
private val sqliteStoreBuilderProvider: SqliteStoreBuilderProvider, private val sqliteStoreBuilderProvider: SqliteStoreBuilderProvider,
private val workManagerScheduler: WorkManagerScheduler, private val workManagerScheduler: WorkManagerScheduler,
private val contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory, private val clientBuilderEnterpriseHook: ClientBuilderEnterpriseHook,
) { ) {
private val sessionDelegate = RustClientSessionDelegate( private val sessionDelegate = RustClientSessionDelegate(
sessionStore = sessionStore, sessionStore = sessionStore,
@@ -101,6 +100,7 @@ class RustMatrixClientFactory(
) )
.homeserverUrl(sessionData.homeserverUrl) .homeserverUrl(sessionData.homeserverUrl)
.username(sessionData.userId) .username(sessionData.userId)
.let { (clientBuilderEnterpriseHook(RustMatrixClientBuilder(it), SessionId(sessionData.userId)) as RustMatrixClientBuilder).inner }
.use { it.build() } .use { it.build() }
client.setMediaRetentionPolicy( client.setMediaRetentionPolicy(
@@ -135,21 +135,6 @@ class RustMatrixClientFactory(
client.setUtdDelegate(UtdTracker(analyticsService)) client.setUtdDelegate(UtdTracker(analyticsService))
// If a content scanner URL is available for the homeserver, create a RustContentScanner and set it on the client.
// This allows the SDK to use the content scanner for automatic media scanning.
// If no content scanner URL is available, the contentScanner will be null.
val contentScannerUrlProvider = contentScannerUrlProviderFactory.create(RustTemporaryMatrixClient(client, null))
val contentScanner = contentScannerUrlProvider.getContentScannerUrl(SessionId(client.userId()))
.getOrNull()
?.let { contentScannerUrl ->
val contentScanner = ContentScanner(contentScannerUrl)
client.setContentScanner(contentScanner)
RustContentScanner(
client = client,
rustScanner = contentScanner,
)
}
val syncService = client.syncService() val syncService = client.syncService()
.withSharePos(true) .withSharePos(true)
.withOfflineMode() .withOfflineMode()
@@ -170,7 +155,7 @@ class RustMatrixClientFactory(
featureFlagService = featureFlagService, featureFlagService = featureFlagService,
analyticsService = analyticsService, analyticsService = analyticsService,
workManagerScheduler = workManagerScheduler, workManagerScheduler = workManagerScheduler,
contentScanner = contentScanner, contentScanner = client.contentScanner()?.let { RustContentScanner(client, it) },
isMessageSearchAvailable = isMessageSearchAvailable, isMessageSearchAvailable = isMessageSearchAvailable,
).also { ).also {
Timber.tag("RustMatrixClient").i("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") Timber.tag("RustMatrixClient").i("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'")
@@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.auth
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.SingleIn import dev.zacsweers.metro.SingleIn
import io.element.android.features.enterprise.api.ClientEnterpriseHook
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.android.libraries.androidutils.crypto.ClientSecret
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
@@ -73,6 +74,7 @@ class RustMatrixAuthenticationService(
private val oAuthConfigurationProvider: OAuthConfigurationProvider, private val oAuthConfigurationProvider: OAuthConfigurationProvider,
private val enterpriseService: EnterpriseService, private val enterpriseService: EnterpriseService,
private val featureFlagService: FeatureFlagService, private val featureFlagService: FeatureFlagService,
private val clientEnterpriseHook: ClientEnterpriseHook,
) : MatrixAuthenticationService { ) : MatrixAuthenticationService {
// Any existing Element Classic session that we want to try to import secrets from during login. // Any existing Element Classic session that we want to try to import secrets from during login.
private var elementClassicSession: ElementClassicSession? = null private var elementClassicSession: ElementClassicSession? = null
@@ -166,6 +168,10 @@ class RustMatrixAuthenticationService(
sessionPaths = currentSessionPaths, sessionPaths = currentSessionPaths,
) )
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable())
// Apply enterprise hooks to the newly created client as soon as possible
clientEnterpriseHook(matrixClient)
newMatrixClientObservers.forEach { it.invoke(matrixClient) } newMatrixClientObservers.forEach { it.invoke(matrixClient) }
sessionStore.addSession(sessionData) sessionStore.addSession(sessionData)
@@ -242,6 +248,9 @@ class RustMatrixAuthenticationService(
client.restoreSession(sessionData.toSession()) client.restoreSession(sessionData.toSession())
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable())
// Apply enterprise hooks to the newly created client as soon as possible
clientEnterpriseHook(matrixClient)
// We wait for the verification state to be known // We wait for the verification state to be known
matrixClient.waitForKnownVerificationState() matrixClient.waitForKnownVerificationState()
@@ -332,6 +341,10 @@ class RustMatrixAuthenticationService(
sessionPaths = currentSessionPaths, sessionPaths = currentSessionPaths,
) )
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable())
// Apply enterprise hooks to the newly created client as soon as possible
clientEnterpriseHook(matrixClient)
matrixClient.waitForKnownVerificationState() matrixClient.waitForKnownVerificationState()
newMatrixClientObservers.forEach { it.invoke(matrixClient) } newMatrixClientObservers.forEach { it.invoke(matrixClient) }
@@ -397,6 +410,10 @@ class RustMatrixAuthenticationService(
sessionPaths = emptySessionPaths, sessionPaths = emptySessionPaths,
) )
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable())
// Apply enterprise hooks to the newly created client as soon as possible
clientEnterpriseHook(matrixClient)
newMatrixClientObservers.forEach { it.invoke(matrixClient) } newMatrixClientObservers.forEach { it.invoke(matrixClient) }
sessionStore.addSession(sessionData) sessionStore.addSession(sessionData)
@@ -11,7 +11,6 @@ package io.element.android.libraries.matrix.impl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider
import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider
@@ -52,7 +51,6 @@ fun TestScope.createRustMatrixClientFactory(
), ),
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(), workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(),
contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory = { ContentScannerUrlProvider { Result.success(null) } },
) = RustMatrixClientFactory( ) = RustMatrixClientFactory(
cacheDirectory = cacheDirectory, cacheDirectory = cacheDirectory,
appCoroutineScope = backgroundScope, appCoroutineScope = backgroundScope,
@@ -67,5 +65,5 @@ fun TestScope.createRustMatrixClientFactory(
clientBuilderProvider = clientBuilderProvider, clientBuilderProvider = clientBuilderProvider,
sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(), sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(),
workManagerScheduler = workManagerScheduler, workManagerScheduler = workManagerScheduler,
contentScannerUrlProviderFactory = contentScannerUrlProviderFactory, clientBuilderEnterpriseHook = { builder, _ -> builder },
) )
@@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.scanner.ContentScannerUrlProvider
import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
@@ -64,7 +63,6 @@ class RustTemporaryMatrixClientFactoryTest {
), ),
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(), workManagerScheduler: FakeWorkManagerScheduler = FakeWorkManagerScheduler(),
contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory = { ContentScannerUrlProvider { Result.success(null) } },
) = RustMatrixClientFactory( ) = RustMatrixClientFactory(
cacheDirectory = cacheDirectory, cacheDirectory = cacheDirectory,
appCoroutineScope = backgroundScope, appCoroutineScope = backgroundScope,
@@ -79,6 +77,6 @@ class RustTemporaryMatrixClientFactoryTest {
clientBuilderProvider = clientBuilderProvider, clientBuilderProvider = clientBuilderProvider,
sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(), sqliteStoreBuilderProvider = FakeSqliteStoreBuilderProvider(),
workManagerScheduler = workManagerScheduler, workManagerScheduler = workManagerScheduler,
contentScannerUrlProviderFactory = contentScannerUrlProviderFactory, clientBuilderEnterpriseHook = { builder, _ -> builder },
) )
} }
@@ -98,6 +98,7 @@ class RustMatrixAuthenticationServiceTest {
), ),
enterpriseService = enterpriseService, enterpriseService = enterpriseService,
featureFlagService = FakeFeatureFlagService(), featureFlagService = FakeFeatureFlagService(),
clientEnterpriseHook = {},
) )
} }
} }
@@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.fixtures.fakes
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUserProfile import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUserProfile
import io.element.android.libraries.matrix.impl.scanner.FakeFfiContentScanner
import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_DEVICE_ID
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
import io.element.android.libraries.matrix.test.A_SERVER_NAME import io.element.android.libraries.matrix.test.A_SERVER_NAME
@@ -18,6 +19,7 @@ import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask import io.element.android.tests.testutils.simulateLongTask
import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientDelegate import org.matrix.rustcomponents.sdk.ClientDelegate
import org.matrix.rustcomponents.sdk.ContentScanner
import org.matrix.rustcomponents.sdk.CreateRoomParameters import org.matrix.rustcomponents.sdk.CreateRoomParameters
import org.matrix.rustcomponents.sdk.Encryption import org.matrix.rustcomponents.sdk.Encryption
import org.matrix.rustcomponents.sdk.HomeserverCapabilities import org.matrix.rustcomponents.sdk.HomeserverCapabilities
@@ -64,6 +66,7 @@ class FakeFfiClient(
private val isProfilesSlidingSyncExtensionSupportedResult: () -> Boolean = { false }, private val isProfilesSlidingSyncExtensionSupportedResult: () -> Boolean = { false },
private val subscribeToOwnProfileResult: (ProfileListener) -> Unit = {}, private val subscribeToOwnProfileResult: (ProfileListener) -> Unit = {},
private val getUrlResult: (String) -> ByteArray = { lambdaError() }, private val getUrlResult: (String) -> ByteArray = { lambdaError() },
private val contentScannerResult: () -> ContentScanner = { FakeFfiContentScanner() },
private val closeResult: () -> Unit = {}, private val closeResult: () -> Unit = {},
) : Client(NoHandle) { ) : Client(NoHandle) {
override fun userId(): String = userId override fun userId(): String = userId
@@ -145,5 +148,9 @@ class FakeFfiClient(
getUrlResult(url) getUrlResult(url)
} }
override suspend fun contentScanner(): ContentScanner = simulateLongTask {
contentScannerResult()
}
override fun close() = closeResult() override fun close() = closeResult()
} }