mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Merge develop into the wildcard mime type branch
Develop gained resolveImageMimeType, which reads the image header so a share that declares a broad type is encoded as the format it really is. Resolving the wildcard first would hand that helper a concrete image/jpeg and make it return early, so a transparent PNG shared as image/* would be flattened and an animated GIF would be recompressed. The header is now read from the type the sender declared and the default subtype is applied to whatever comes back, so the sniffing still runs and an undecodable file no longer keeps a wildcard. A non-image shared as image/* is therefore sent as image/jpeg rather than image/*, which is the whole point of this branch, so the test covering that case now expects the default subtype.
This commit is contained in:
+12
-2
@@ -8,9 +8,19 @@
|
||||
package io.element.android.libraries.matrix.api
|
||||
|
||||
/**
|
||||
* An interface to fetch content from a URL using a simple GET request.
|
||||
* An interface to fetch content from a URL using a simple GET request launched from a Matrix client.
|
||||
*/
|
||||
fun interface UrlContentFetcher {
|
||||
interface ClientUrlContentFetcher {
|
||||
/**
|
||||
* The domain of the public server for this homeserver.
|
||||
*/
|
||||
val server: String?
|
||||
|
||||
/**
|
||||
* The URL of the actual Matrix homeserver.
|
||||
*/
|
||||
val homeserverUrl: String
|
||||
|
||||
/**
|
||||
* Execute generic GET requests through the SDKs internal HTTP client.
|
||||
*/
|
||||
+7
-4
@@ -60,16 +60,13 @@ import java.util.Optional
|
||||
* One instance exists per session, created on login or session restore and torn down by [logout], [clearCache] or account deactivation.
|
||||
* Suspending members here report failures through a [Result] instead of throwing, and run their work off the main thread.
|
||||
*/
|
||||
interface MatrixClient : UrlContentFetcher {
|
||||
interface MatrixClient : ClientUrlContentFetcher {
|
||||
/** Identifier of the logged in user; a [SessionId] and a [UserId] are the same value. */
|
||||
val sessionId: SessionId
|
||||
|
||||
/** Identifier of this device, as assigned by the homeserver when the session was created. */
|
||||
val deviceId: DeviceId
|
||||
|
||||
/** Base URL of the homeserver this client is connected to. */
|
||||
val homeserverUrl: String
|
||||
|
||||
/** Directories holding this session's databases, caches and downloaded media. */
|
||||
val sessionPaths: SessionPaths
|
||||
|
||||
@@ -136,6 +133,9 @@ interface MatrixClient : UrlContentFetcher {
|
||||
/** Scans media content before it is displayed, or `null` when no content scanner is configured for this session. */
|
||||
val contentScanner: ContentScanner?
|
||||
|
||||
/** Whether the session is in the process of shutting down, either because it is being logged out or its cache is being cleared. */
|
||||
val isShuttingDown: Boolean
|
||||
|
||||
/**
|
||||
* Returns the room with the given id if the user has joined it, along with its live [Timeline].
|
||||
* A new instance is created on each call and the caller owns it, so it must be closed once no longer needed.
|
||||
@@ -245,6 +245,9 @@ interface MatrixClient : UrlContentFetcher {
|
||||
/** Whether the homeserver advertises support for user status (MSC4426). */
|
||||
suspend fun isUserStatusSupported(): Result<Boolean>
|
||||
|
||||
/** Whether the homeserver advertises support for the Profiles sliding sync extension (MSC4262). */
|
||||
suspend fun isProfilesSlidingSyncExtensionSupported(): Result<Boolean>
|
||||
|
||||
/**
|
||||
* Enable or disable automatically setting the user's status to "in a call" (m.call) while in a call.
|
||||
*
|
||||
|
||||
+1
-1
@@ -15,4 +15,4 @@ import java.lang.AutoCloseable
|
||||
* Note: this client implements an [AutoCloseable] interface, so using [AutoCloseable.close] or [AutoCloseable.use] will clean up any resources associated
|
||||
* with it, such as temporary files.
|
||||
*/
|
||||
interface TemporaryMatrixClient : UrlContentFetcher, AutoCloseable
|
||||
interface TemporaryMatrixClient : ClientUrlContentFetcher, AutoCloseable
|
||||
|
||||
+3
-3
@@ -12,10 +12,10 @@ package io.element.android.libraries.matrix.api
|
||||
*/
|
||||
interface TemporaryMatrixClientFactory {
|
||||
/**
|
||||
* Builds an unauthenticated client pointing at [homeServerUrl], backed by a fresh set of temporary session directories.
|
||||
* Builds an unauthenticated client pointing at [serverName], backed by a fresh set of temporary session directories.
|
||||
* The caller owns the returned client and must close it to delete those directories.
|
||||
*
|
||||
* @param homeServerUrl the URL of the homeserver the client will talk to.
|
||||
* @param serverName the domain of the public-facing server for the actual homeserver
|
||||
*/
|
||||
suspend fun create(homeServerUrl: String): Result<TemporaryMatrixClient>
|
||||
suspend fun create(serverName: String): Result<TemporaryMatrixClient>
|
||||
}
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
|
||||
package io.element.android.libraries.matrix.api.scanner
|
||||
|
||||
import io.element.android.libraries.matrix.api.UrlContentFetcher
|
||||
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
/**
|
||||
@@ -26,6 +26,6 @@ fun interface ContentScannerUrlProvider {
|
||||
/**
|
||||
* @param urlContentFetcher used to read the configuration advertised by the homeserver; a client that is not authenticated yet also works.
|
||||
*/
|
||||
fun create(urlContentFetcher: UrlContentFetcher): ContentScannerUrlProvider
|
||||
fun create(urlContentFetcher: ClientUrlContentFetcher): ContentScannerUrlProvider
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -135,6 +135,7 @@ import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@@ -162,6 +163,7 @@ class RustMatrixClient(
|
||||
) : MatrixClient {
|
||||
override val sessionId: UserId = UserId(innerClient.userId())
|
||||
override val deviceId: DeviceId = DeviceId(innerClient.deviceId())
|
||||
override val server: String? = innerClient.server()
|
||||
override val homeserverUrl: String = innerClient.homeserver()
|
||||
override val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-$sessionId")
|
||||
private val sessionDispatcher = dispatchers.io.limitedParallelism(64)
|
||||
@@ -304,6 +306,10 @@ class RustMatrixClient(
|
||||
.buffer(Channel.UNLIMITED)
|
||||
.stateIn(sessionCoroutineScope, started = SharingStarted.Eagerly, initialValue = persistentListOf())
|
||||
|
||||
private val _isShuttingDown = AtomicBoolean(false)
|
||||
override val isShuttingDown: Boolean
|
||||
get() = _isShuttingDown.get()
|
||||
|
||||
init {
|
||||
// Make sure the session delegate has a reference to the client to be able to logout on auth error
|
||||
sessionDelegate.bindClient(this)
|
||||
@@ -320,7 +326,9 @@ class RustMatrixClient(
|
||||
}
|
||||
|
||||
private suspend fun setupUserProfile() {
|
||||
val supported = isUserStatusSupported().getOrDefault(false)
|
||||
// Subscribing to own profile updates only requires the Profiles sliding sync extension,
|
||||
// not the full user status capability (which also needs the status profile field to be settable).
|
||||
val supported = isProfilesSlidingSyncExtensionSupported().getOrDefault(false)
|
||||
if (supported) {
|
||||
// No need to seed the data here, it's already stored by the sdk.
|
||||
ownProfileTaskHandle = innerClient.subscribeToOwnProfile(ownProfileListener)
|
||||
@@ -530,6 +538,12 @@ class RustMatrixClient(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun isProfilesSlidingSyncExtensionSupported(): Result<Boolean> = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerClient.isProfilesSlidingSyncExtensionSupported()
|
||||
}
|
||||
}
|
||||
|
||||
override fun enableAutomaticCallStatus(enabled: Boolean) {
|
||||
innerClient.enableAutomaticCallStatus(enabled)
|
||||
}
|
||||
@@ -666,11 +680,13 @@ class RustMatrixClient(
|
||||
}
|
||||
|
||||
override suspend fun clearCache() {
|
||||
_isShuttingDown.set(true)
|
||||
innerClient.clearCaches(innerSyncService)
|
||||
destroy()
|
||||
}
|
||||
|
||||
override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean) {
|
||||
_isShuttingDown.set(true)
|
||||
sessionCoroutineScope.cancel()
|
||||
// Remove current delegate so we don't receive an auth error
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
@@ -708,6 +724,8 @@ class RustMatrixClient(
|
||||
}
|
||||
|
||||
override suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit> = withContext(sessionDispatcher) {
|
||||
_isShuttingDown.set(true)
|
||||
|
||||
Timber.w("Deactivating account")
|
||||
// Remove current delegate so we don't receive an auth error
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
|
||||
+3
@@ -22,6 +22,9 @@ class RustTemporaryMatrixClient(
|
||||
client.getUrl(url)
|
||||
}.mapFailure { it.mapClientException() }
|
||||
|
||||
override val server: String? = client.server()
|
||||
override val homeserverUrl: String = client.homeserver()
|
||||
|
||||
override fun close() {
|
||||
client.close()
|
||||
paths?.let {
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ class RustTemporaryMatrixClientFactory(
|
||||
private val sessionPathsFactory: SessionPathsFactory,
|
||||
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
||||
) : TemporaryMatrixClientFactory {
|
||||
override suspend fun create(homeServerUrl: String): Result<TemporaryMatrixClient> {
|
||||
override suspend fun create(serverName: String): Result<TemporaryMatrixClient> {
|
||||
return runCatchingExceptions {
|
||||
val sessionPaths = sessionPathsFactory.create()
|
||||
val client = rustMatrixClientFactory.getBaseClientBuilder(
|
||||
@@ -28,7 +28,7 @@ class RustTemporaryMatrixClientFactory(
|
||||
slidingSyncType = ClientBuilderSlidingSync.Native,
|
||||
isMessageSearchAvailable = false,
|
||||
)
|
||||
.homeserverUrl(homeServerUrl)
|
||||
.serverName(serverName)
|
||||
.build()
|
||||
RustTemporaryMatrixClient(client, sessionPaths)
|
||||
}
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ fun Throwable.mapAuthenticationException(): AuthenticationException {
|
||||
is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.EventCache -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.InvalidRawKey -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.WellKnownLookupDisabled -> AuthenticationException.Generic(message)
|
||||
}
|
||||
is OAuthException -> when (this) {
|
||||
is OAuthException.Generic -> AuthenticationException.OAuth(message)
|
||||
|
||||
-1
@@ -278,7 +278,6 @@ class RustMatrixAuthenticationService(
|
||||
.let {
|
||||
enterpriseService.tweakMasUrl(
|
||||
url = it,
|
||||
homeserver = client.server() ?: client.homeserver(),
|
||||
urlContentFetcher = getUrlResolver,
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,9 +13,9 @@ import dev.zacsweers.metro.ContributesTo
|
||||
import dev.zacsweers.metro.Provides
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
|
||||
import io.element.android.libraries.matrix.api.HomeserverCapabilitiesProvider
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.UrlContentFetcher
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
@@ -111,7 +111,7 @@ object SessionMatrixModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesGetUrlResolverClient(matrixClient: MatrixClient): UrlContentFetcher {
|
||||
fun providesGetUrlResolverClient(matrixClient: MatrixClient): ClientUrlContentFetcher {
|
||||
return matrixClient
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -8,11 +8,13 @@
|
||||
|
||||
package io.element.android.libraries.matrix.impl.permalink
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.MatrixPatterns
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
@@ -51,6 +53,10 @@ class DefaultPermalinkParser(
|
||||
matrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri)
|
||||
}
|
||||
|
||||
matrixToUri.userIdWithSlash()?.let { userId ->
|
||||
return PermalinkData.UserLink(userId = userId)
|
||||
}
|
||||
|
||||
val result = runCatchingExceptions {
|
||||
parseMatrixEntityFrom(matrixToUri.toString())
|
||||
}.getOrNull()
|
||||
@@ -84,3 +90,17 @@ class DefaultPermalinkParser(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A slash is allowed in the local part of a user id, but it also separates an identifier from an
|
||||
* event id in a permalink, so [parseMatrixEntityFrom] cannot tell the two apart. Only a user id can
|
||||
* start with an `@`, so recover it here rather than letting the ambiguity reach the SDK.
|
||||
*/
|
||||
private fun Uri.userIdWithSlash(): UserId? {
|
||||
val identifier = fragment
|
||||
?.removePrefix("/")
|
||||
?.substringBefore('?')
|
||||
?: return null
|
||||
if (!identifier.startsWith("@") || !identifier.contains('/')) return null
|
||||
return identifier.takeIf { MatrixPatterns.isUserId(it) }?.let(::UserId)
|
||||
}
|
||||
|
||||
+3
-2
@@ -75,6 +75,7 @@ import org.matrix.rustcomponents.sdk.PollData
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.UploadParameters
|
||||
import org.matrix.rustcomponents.sdk.UploadSource
|
||||
import org.matrix.rustcomponents.sdk.createCaptionEdit
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk.PaginationStatus
|
||||
@@ -341,7 +342,7 @@ class RustTimeline(
|
||||
formattedCaption: String?,
|
||||
): Result<Unit> = withContext(dispatcher) {
|
||||
runCatchingExceptions<Unit> {
|
||||
val editedContent = EditedContent.MediaCaption(
|
||||
val editedContent = createCaptionEdit(
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption?.let {
|
||||
FormattedBody(body = it, format = MessageFormat.Html)
|
||||
@@ -365,7 +366,7 @@ class RustTimeline(
|
||||
fromNotification: Boolean,
|
||||
msgType: MsgType,
|
||||
): Result<Unit> = withContext(dispatcher) {
|
||||
runCatchingExceptions {
|
||||
runCatchingExceptions<Unit> {
|
||||
val msg = MessageEventContent.from(
|
||||
body = body,
|
||||
htmlBody = htmlBody,
|
||||
|
||||
+1
-2
@@ -16,7 +16,6 @@ import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.NoHandle
|
||||
import java.io.File
|
||||
import kotlin.io.path.createTempDirectory
|
||||
import org.matrix.rustcomponents.sdk.ClientException as RustClientException
|
||||
@@ -84,7 +83,7 @@ class RustTemporaryMatrixClientTest {
|
||||
}
|
||||
|
||||
private fun createRustTemporaryMatrixClient(
|
||||
client: Client = Client(NoHandle),
|
||||
client: Client = FakeFfiClient(),
|
||||
paths: SessionPaths,
|
||||
): RustTemporaryMatrixClient {
|
||||
return RustTemporaryMatrixClient(client, paths)
|
||||
|
||||
+6
@@ -12,6 +12,7 @@ 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.test.A_DEVICE_ID
|
||||
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_USER_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
@@ -45,6 +46,7 @@ class FakeFfiClient(
|
||||
private val userId: String = A_USER_ID.value,
|
||||
private val deviceId: String = A_DEVICE_ID.value,
|
||||
private val homeserver: String = A_HOMESERVER_URL,
|
||||
private val server: String? = A_SERVER_NAME,
|
||||
private val notificationClient: NotificationClient = FakeFfiNotificationClient(),
|
||||
private val notificationSettings: NotificationSettings = FakeFfiNotificationSettings(),
|
||||
private val encryption: Encryption = FakeFfiEncryption(),
|
||||
@@ -59,6 +61,7 @@ class FakeFfiClient(
|
||||
private val accountDataResult: (String) -> String? = { lambdaError() },
|
||||
private val setAccountDataResult: (String, String) -> Unit = { _, _ -> lambdaError() },
|
||||
private val isUserStatusSupportedResult: () -> Boolean = { false },
|
||||
private val isProfilesSlidingSyncExtensionSupportedResult: () -> Boolean = { false },
|
||||
private val subscribeToOwnProfileResult: (ProfileListener) -> Unit = {},
|
||||
private val getUrlResult: (String) -> ByteArray = { lambdaError() },
|
||||
private val closeResult: () -> Unit = {},
|
||||
@@ -66,6 +69,7 @@ class FakeFfiClient(
|
||||
override fun userId(): String = userId
|
||||
override fun deviceId(): String = deviceId
|
||||
override fun homeserver(): String = homeserver
|
||||
override fun server(): String? = server
|
||||
override suspend fun notificationClient(processSetup: NotificationProcessSetup) = notificationClient
|
||||
override suspend fun getNotificationSettings(): NotificationSettings = notificationSettings
|
||||
override fun encryption(): Encryption = encryption
|
||||
@@ -100,6 +104,8 @@ class FakeFfiClient(
|
||||
|
||||
override suspend fun isUserStatusSupported(): Boolean = isUserStatusSupportedResult()
|
||||
|
||||
override suspend fun isProfilesSlidingSyncExtensionSupported(): Boolean = isProfilesSlidingSyncExtensionSupportedResult()
|
||||
|
||||
override fun subscribeToOwnProfile(listener: ProfileListener): TaskHandle {
|
||||
subscribeToOwnProfileResult(listener)
|
||||
return FakeFfiTaskHandle()
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.permalink
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultPermalinkParserTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `a matrix-to link to a user id with a slash is parsed as a user link`() {
|
||||
val result = createParser().parse("https://matrix.to/#/@it/sme:matrix.org")
|
||||
assertThat(result).isEqualTo(PermalinkData.UserLink(UserId("@it/sme:matrix.org")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an escaped slash in a user id is parsed as a user link`() {
|
||||
val result = createParser().parse("https://matrix.to/#/@it%2Fsme:matrix.org")
|
||||
assertThat(result).isEqualTo(PermalinkData.UserLink(UserId("@it/sme:matrix.org")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an element web link to a user id with a slash is parsed as a user link`() {
|
||||
val result = createParser().parse("https://app.element.io/#/user/@it/sme:matrix.org")
|
||||
assertThat(result).isEqualTo(PermalinkData.UserLink(UserId("@it/sme:matrix.org")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a user id with a slash that is not a valid user id is not a user link`() {
|
||||
val result = createParser().parse("https://example.org/foo/@it/sme")
|
||||
assertThat(result).isInstanceOf(PermalinkData.FallbackLink::class.java)
|
||||
}
|
||||
|
||||
private fun createParser() = DefaultPermalinkParser(
|
||||
matrixToConverter = DefaultMatrixToConverter(),
|
||||
)
|
||||
}
|
||||
+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.libraries.matrix.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeClientUrlContentFetcher(
|
||||
override val server: String = A_SERVER_NAME,
|
||||
override val homeserverUrl: String = A_HOMESERVER_URL,
|
||||
private val getUrlResult: (String) -> Result<ByteArray> = { lambdaError() },
|
||||
) : ClientUrlContentFetcher {
|
||||
override suspend fun getUrl(url: String): Result<ByteArray> {
|
||||
return getUrlResult(url)
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -79,6 +79,7 @@ class FakeMatrixClient(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val sessionPaths: SessionPaths = SessionPaths(fileDirectory = File("files"), cacheDirectory = File("cache")),
|
||||
override val deviceId: DeviceId = A_DEVICE_ID,
|
||||
override val server: String = A_SERVER_NAME,
|
||||
override val homeserverUrl: String = A_HOMESERVER_URL,
|
||||
override val sessionCoroutineScope: CoroutineScope = TestScope(),
|
||||
private val userDisplayName: String? = A_USER_NAME,
|
||||
@@ -99,7 +100,7 @@ class FakeMatrixClient(
|
||||
override val roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(),
|
||||
private val homeserverCapabilitiesProvider: FakeHomeserverCapabilitiesProvider = FakeHomeserverCapabilitiesProvider(),
|
||||
private val accountManagementUrlResult: (AccountManagementAction?) -> Result<String?> = { lambdaError() },
|
||||
private val resolveRoomAliasResult: (RoomAlias) -> Result<Optional<ResolvedRoomAlias>> = {
|
||||
private val resolveRoomAliasResult: suspend (RoomAlias) -> Result<Optional<ResolvedRoomAlias>> = {
|
||||
Result.success(
|
||||
Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList()))
|
||||
)
|
||||
@@ -133,6 +134,7 @@ class FakeMatrixClient(
|
||||
private val resetWellKnownConfigLambda: () -> Result<Unit> = { lambdaError() },
|
||||
private val enableAutomaticCallStatusLambda: (Boolean) -> Unit = { },
|
||||
override val contentScanner: ContentScanner? = null,
|
||||
private val isShuttingDownResult: () -> Boolean = { false },
|
||||
) : MatrixClient {
|
||||
var setDisplayNameCalled: Boolean = false
|
||||
private set
|
||||
@@ -147,6 +149,7 @@ class FakeMatrixClient(
|
||||
var setUserStatusResult: Result<Unit> = Result.success(Unit)
|
||||
var clearUserStatusResult: Result<Unit> = Result.success(Unit)
|
||||
var isUserStatusSupportedResult: Result<Boolean> = Result.success(true)
|
||||
var isProfilesSlidingSyncExtensionSupportedResult: Result<Boolean> = Result.success(true)
|
||||
|
||||
private val _userProfile: MutableStateFlow<MatrixUser> = MutableStateFlow(MatrixUser(sessionId, userDisplayName, userAvatarUrl))
|
||||
override val userProfile: StateFlow<MatrixUser> = _userProfile
|
||||
@@ -293,6 +296,8 @@ class FakeMatrixClient(
|
||||
|
||||
override suspend fun isUserStatusSupported(): Result<Boolean> = isUserStatusSupportedResult
|
||||
|
||||
override suspend fun isProfilesSlidingSyncExtensionSupported(): Result<Boolean> = isProfilesSlidingSyncExtensionSupportedResult
|
||||
|
||||
override fun enableAutomaticCallStatus(enabled: Boolean) = enableAutomaticCallStatusLambda(enabled)
|
||||
|
||||
override suspend fun joinRoom(roomId: RoomId): Result<RoomInfo?> = joinRoomLambda(roomId)
|
||||
@@ -305,6 +310,9 @@ class FakeMatrixClient(
|
||||
return knockRoomLambda(roomIdOrAlias, message, serverNames)
|
||||
}
|
||||
|
||||
override val isShuttingDown: Boolean
|
||||
get() = isShuttingDownResult()
|
||||
|
||||
// Mocks
|
||||
|
||||
fun givenCreateRoomResult(result: Result<RoomId>) {
|
||||
|
||||
+2
-1
@@ -11,10 +11,11 @@ import io.element.android.libraries.matrix.api.TemporaryMatrixClient
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeTemporaryMatrixClient(
|
||||
override val server: String = A_SERVER_NAME,
|
||||
override val homeserverUrl: String = A_HOMESERVER_URL,
|
||||
private val getUrlResult: (String) -> Result<ByteArray> = { lambdaError() },
|
||||
private val closeLambda: () -> Unit = {},
|
||||
) : TemporaryMatrixClient {
|
||||
override suspend fun getUrl(url: String): Result<ByteArray> = getUrlResult(url)
|
||||
|
||||
override fun close() = closeLambda()
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@ import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory
|
||||
class FakeTemporaryMatrixClientFactory(
|
||||
private val createResult: (String) -> Result<TemporaryMatrixClient> = { Result.success(FakeTemporaryMatrixClient()) },
|
||||
) : TemporaryMatrixClientFactory {
|
||||
override suspend fun create(homeServerUrl: String): Result<TemporaryMatrixClient> = createResult(homeServerUrl)
|
||||
override suspend fun create(serverName: String): Result<TemporaryMatrixClient> = createResult(serverName)
|
||||
}
|
||||
|
||||
+1
@@ -72,6 +72,7 @@ const val A_SPACE_NAME = "A space name"
|
||||
|
||||
const val A_REDACTION_REASON = "A redaction reason"
|
||||
|
||||
const val A_SERVER_NAME = "matrix.org"
|
||||
const val A_HOMESERVER_URL = "matrix.org"
|
||||
const val A_HOMESERVER_URL_2 = "matrix-client.org"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user