Apply the GetUrlResolver pattern using unauthenticated matrix clients where possible

This commit is contained in:
Jorge Martín
2026-08-10 14:26:10 +02:00
committed by Jorge Martin Espinosa
parent 25eca29cd2
commit 90d4fbe0bd
8 changed files with 97 additions and 49 deletions
@@ -7,12 +7,19 @@
package io.element.android.libraries.matrix.api.scanner
import io.element.android.libraries.matrix.api.GetUrlResolver
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 [homeserver], or `null` if no content scanner is set up.
* Returns the URL of the content scanner service for the given [sessionId], or `null` if no content scanner is set up.
*/
suspend fun getContentScannerUrl(homeserver: String): Result<String?>
suspend fun getContentScannerUrl(sessionId: SessionId): Result<String?>
fun interface Factory {
fun create(getUrlResolver: GetUrlResolver): ContentScannerUrlProvider
}
}
@@ -17,7 +17,7 @@ import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.UserId
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.scanner.ContentScannerUrlProvider
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
@@ -70,7 +70,7 @@ class RustMatrixClientFactory(
private val clientBuilderProvider: ClientBuilderProvider,
private val sqliteStoreBuilderProvider: SqliteStoreBuilderProvider,
private val workManagerScheduler: WorkManagerScheduler,
private val contentScannerUrlProvider: ContentScannerUrlProvider,
private val contentScannerUrlProviderFactory: ContentScannerUrlProvider.Factory,
) {
private val sessionDelegate = RustClientSessionDelegate(
sessionStore = sessionStore,
@@ -135,21 +135,19 @@ class RustMatrixClientFactory(
client.setUtdDelegate(UtdTracker(analyticsService))
val domainName = UserId(client.userId()).domainName
// 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 contentScanner = domainName?.let {
contentScannerUrlProvider.getContentScannerUrl(domainName)
.getOrNull()
?.let { contentScannerUrl ->
val contentScanner = ContentScanner(contentScannerUrl)
client.setContentScanner(contentScanner)
RustContentScanner(
client = client,
rustScanner = contentScanner,
)
}
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()
@@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
import io.element.android.libraries.matrix.impl.RustTemporaryMatrixClient
import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper
import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData
import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep
@@ -133,9 +134,10 @@ class RustMatrixAuthenticationService(
}
currentClient = client
client.homeserverLoginDetails().map()
}.onFailure {
clear()
clear(destroyClient = true)
}.mapFailure { failure ->
Timber.e(failure, "Failed to set homeserver to $homeserver")
failure.mapAuthenticationException()
@@ -168,7 +170,7 @@ class RustMatrixAuthenticationService(
sessionStore.addSession(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
clear(destroyClient = false)
SessionId(sessionData.userId)
}.mapFailure { failure ->
@@ -248,7 +250,7 @@ class RustMatrixAuthenticationService(
sessionStore.addSession(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
clear(destroyClient = false)
SessionId(sessionData.userId)
}
@@ -271,11 +273,13 @@ class RustMatrixAuthenticationService(
deviceId = null,
additionalScopes = emptyList(),
)
val getUrlResolver = RustTemporaryMatrixClient(client, sessionPaths)
val url = oAuthAuthorizationData.loginUrl()
.let {
enterpriseService.tweakMasUrl(
url = it,
homeserver = client.server() ?: client.homeserver(),
getUrlResolver = getUrlResolver,
)
}
pendingOAuthAuthorizationData = oAuthAuthorizationData
@@ -335,7 +339,7 @@ class RustMatrixAuthenticationService(
sessionStore.addSession(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
clear(destroyClient = false)
SessionId(sessionData.userId)
}.mapFailure { failure ->
@@ -398,7 +402,7 @@ class RustMatrixAuthenticationService(
sessionStore.addSession(sessionData)
// Clean up the strong reference held here since it's no longer necessary
currentClient = null
clear(destroyClient = false)
SessionId(sessionData.userId)
}.mapFailure {
@@ -464,8 +468,10 @@ class RustMatrixAuthenticationService(
.build()
}
private fun clear() {
currentClient?.close()
private fun clear(destroyClient: Boolean) {
if (destroyClient) {
currentClient?.close()
}
currentClient = null
}
@@ -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.libraries.matrix.impl.scanner
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
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(homeserver: String): Result<String?> = Result.success(null)
}