Merge branch 'develop' into julioromano/poll_history_entry_point

This commit is contained in:
ganfra 2023-12-13 17:22:55 +01:00
commit 72cdc0eee0
738 changed files with 9387 additions and 1581 deletions

View file

@ -41,7 +41,7 @@ interface MatrixClient : Closeable {
val roomListService: RoomListService
val mediaLoader: MatrixMediaLoader
suspend fun getRoom(roomId: RoomId): MatrixRoom?
suspend fun findDM(userId: UserId): MatrixRoom?
suspend fun findDM(userId: UserId): RoomId?
suspend fun ignoreUser(userId: UserId): Result<Unit>
suspend fun unignoreUser(userId: UserId): Result<Unit>
suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId>

View file

@ -43,9 +43,9 @@ interface EncryptionService {
suspend fun doesBackupExistOnServer(): Result<Boolean>
/**
* Note: accept bot recoveryKey and passphrase.
* Note: accept both recoveryKey and passphrase.
*/
suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit>
suspend fun recover(recoveryKey: String): Result<Unit>
/**
* Wait for backup upload steady state.

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api.encryption
import io.element.android.libraries.matrix.api.exception.ClientException
sealed class RecoveryException(message: String) : Exception(message) {
class SecretStorage(message: String) : RecoveryException(message)
data object BackupExistsOnServer : RecoveryException("BackupExistsOnServer")
data class Client(val exception: ClientException) : RecoveryException(exception.message ?: "Unknown error")
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api.mxc
import javax.inject.Inject
class MxcTools @Inject constructor() {
/**
* Regex to match a Matrix Content (mxc://) URI.
*
* See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris
*/
private val mxcRegex = Regex("""^mxc://([^/]+)/([^/]+)$""")
/**
* Sanitizes an mxcUri to be used as a relative file path.
*
* @param mxcUri the Matrix Content (mxc://) URI of the file.
* @return the relative file path as "<server-name>/<media-id>" or null if the mxcUri is invalid.
*/
fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match ->
buildString {
append(match.groupValues[1])
append("/")
append(match.groupValues[2])
}
}
}

View file

@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
data class NotificationData(
val eventId: EventId,
val roomId: RoomId,
// mxc url
val senderAvatarUrl: String?,
val senderDisplayName: String?,
val roomAvatarUrl: String?,
@ -34,8 +35,6 @@ data class NotificationData(
val isNoisy: Boolean,
val timestamp: Long,
val content: NotificationContent,
// For images for instance
val contentUrl: String?,
val hasMention: Boolean,
)

View file

@ -38,5 +38,7 @@ interface NotificationSettingsService {
suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit>
suspend fun isCallEnabled(): Result<Boolean>
suspend fun setCallEnabled(enabled: Boolean): Result<Unit>
suspend fun isInviteForMeEnabled(): Result<Boolean>
suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit>
suspend fun getRoomsWithUserDefinedRules(): Result<List<String>>
}

View file

@ -132,7 +132,8 @@ interface MatrixRoom : Closeable {
suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean>
suspend fun canUserJoinCall(userId: UserId): Result<Boolean>
suspend fun canUserJoinCall(userId: UserId): Result<Boolean> =
canUserSendState(userId, StateEventType.CALL_MEMBER)
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api.room
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
/**
* Try to find an existing DM with the given user, or create one if none exists.
*/
suspend fun MatrixClient.startDM(userId: UserId): StartDMResult {
val existingDM = findDM(userId)
return if (existingDM != null) {
StartDMResult.Success(existingDM, isNew = false)
} else {
createDM(userId).fold(
{ StartDMResult.Success(it, isNew = true) },
{ StartDMResult.Failure(it) }
)
}
}
sealed interface StartDMResult {
data class Success(val roomId: RoomId, val isNew: Boolean) : StartDMResult
data class Failure(val throwable: Throwable) : StartDMResult
}

View file

@ -20,6 +20,7 @@ enum class StateEventType {
POLICY_RULE_ROOM,
POLICY_RULE_SERVER,
POLICY_RULE_USER,
CALL_MEMBER,
ROOM_ALIASES,
ROOM_AVATAR,
ROOM_CANONICAL_ALIAS,

View file

@ -16,7 +16,24 @@
package io.element.android.libraries.matrix.api.verification
import androidx.compose.runtime.Immutable
@Immutable
sealed interface SessionVerificationData {
data class Emojis(
// 7 emojis
val emojis: List<VerificationEmoji>,
) : SessionVerificationData
data class Decimals(
// 3 numbers
val decimals: List<Int>,
) : SessionVerificationData
}
// https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji
data class VerificationEmoji(
val code: String,
val name: String,
val number: Int,
val emoji: String,
val description: String,
)

View file

@ -17,7 +17,6 @@
package io.element.android.libraries.matrix.api.verification
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@ -26,7 +25,7 @@ interface SessionVerificationService {
/**
* State of the current verification flow ([VerificationFlowState.Initial] if not started).
*/
val verificationFlowState : StateFlow<VerificationFlowState>
val verificationFlowState: StateFlow<VerificationFlowState>
/**
* The internal service that checks verification can only run after the initial sync.
@ -101,8 +100,8 @@ sealed interface VerificationFlowState {
/** Short Authentication String (SAS) verification started between the 2 devices. */
data object StartedSasVerification : VerificationFlowState
/** Verification data for the SAS verification (emojis) received. */
data class ReceivedVerificationData(val emoji: ImmutableList<VerificationEmoji>) : VerificationFlowState
/** Verification data for the SAS verification received. */
data class ReceivedVerificationData(val data: SessionVerificationData) : VerificationFlowState
/** Verification completed successfully. */
data object Finished : VerificationFlowState

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api.mxc
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class MxcToolsTest {
@Test
fun `mxcUri2FilePath returns extracted path`() {
val mxcTools = MxcTools()
val mxcUri = "mxc://server.org/abc123"
val filePath = mxcTools.mxcUri2FilePath(mxcUri)
assertThat(filePath).isEqualTo("server.org/abc123")
}
@Test
fun `mxcUri2FilePath returns null for invalid data`() {
val mxcTools = MxcTools()
assertThat(mxcTools.mxcUri2FilePath("")).isNull()
assertThat(mxcTools.mxcUri2FilePath("mxc://server.org")).isNull()
assertThat(mxcTools.mxcUri2FilePath("mxc://server.org/")).isNull()
assertThat(mxcTools.mxcUri2FilePath("m://server.org/abc123")).isNull()
}
}

View file

@ -44,7 +44,7 @@ dependencies {
api(projects.libraries.matrix.api)
implementation(libs.dagger)
implementation(projects.libraries.core)
implementation("net.java.dev.jna:jna:5.13.0@aar")
implementation("net.java.dev.jna:jna:5.14.0@aar")
implementation(libs.androidx.datastore.preferences)
implementation(libs.serialization.json)
implementation(libs.kotlinx.collections.immutable)

View file

@ -91,7 +91,7 @@ import org.matrix.rustcomponents.sdk.RoomVisibility as RustRoomVisibility
import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
@OptIn(ExperimentalCoroutinesApi::class)
class RustMatrixClient constructor(
class RustMatrixClient(
private val client: Client,
private val syncService: ClientSyncService,
private val sessionStore: SessionStore,
@ -119,10 +119,9 @@ class RustMatrixClient constructor(
.filterByPushRules()
.finish()
}
private val notificationSettings = client.getNotificationSettings()
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
private val notificationSettingsService = RustNotificationSettingsService(notificationSettings, dispatchers)
private val notificationSettingsService = RustNotificationSettingsService(client, dispatchers)
.apply { start() }
private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
private val encryptionService = RustEncryptionService(
client = client,
@ -241,9 +240,8 @@ class RustMatrixClient constructor(
}
}
override suspend fun findDM(userId: UserId): MatrixRoom? {
val roomId = client.getDmRoom(userId.value)?.use { RoomId(it.id()) }
return roomId?.let { getRoom(it) }
override suspend fun findDM(userId: UserId): RoomId? {
return client.getDmRoom(userId.value)?.use { RoomId(it.id()) }
}
override suspend fun ignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) {
@ -347,8 +345,7 @@ class RustMatrixClient constructor(
override fun close() {
sessionCoroutineScope.cancel()
clientDelegateTaskHandle?.cancelAndDestroy()
notificationSettings.setDelegate(null)
notificationSettings.destroy()
notificationSettingsService.destroy()
verificationService.destroy()
syncService.destroy()
innerRoomListService.destroy()

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.impl.encryption
import io.element.android.libraries.matrix.api.encryption.RecoveryException
import io.element.android.libraries.matrix.api.exception.ClientException
import io.element.android.libraries.matrix.impl.exception.mapClientException
import org.matrix.rustcomponents.sdk.RecoveryException as RustRecoveryException
fun Throwable.mapRecoveryException(): RecoveryException {
return when (this) {
is RustRecoveryException.SecretStorage -> RecoveryException.SecretStorage(
message = errorMessage
)
is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer
is RustRecoveryException.Client -> RecoveryException.Client(
source.mapClientException()
)
else -> RecoveryException.Client(
ClientException.Other("Unknown error")
)
}
}

View file

@ -17,6 +17,7 @@
package io.element.android.libraries.matrix.impl.encryption
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.matrix.api.encryption.BackupState
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress
@ -110,6 +111,8 @@ internal class RustEncryptionService(
override suspend fun enableBackups(): Result<Unit> = withContext(dispatchers.io) {
runCatching {
service.enableBackups()
}.mapFailure {
it.mapRecoveryException()
}
}
@ -127,6 +130,8 @@ internal class RustEncryptionService(
)
// enableRecovery returns the encryption key, but we read it from the state flow
.let { }
}.mapFailure {
it.mapRecoveryException()
}
}
@ -164,24 +169,32 @@ internal class RustEncryptionService(
override suspend fun disableRecovery(): Result<Unit> = withContext(dispatchers.io) {
runCatching {
service.disableRecovery()
}.mapFailure {
it.mapRecoveryException()
}
}
override suspend fun isLastDevice(): Result<Boolean> = withContext(dispatchers.io) {
runCatching {
service.isLastDevice()
}.mapFailure {
it.mapRecoveryException()
}
}
override suspend fun resetRecoveryKey(): Result<String> = withContext(dispatchers.io) {
runCatching {
service.resetRecoveryKey()
}.mapFailure {
it.mapRecoveryException()
}
}
override suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
override suspend fun recover(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
runCatching {
service.recover(recoveryKey)
}.mapFailure {
it.mapRecoveryException()
}
}
}

View file

@ -23,13 +23,13 @@ class SteadyStateExceptionMapper {
fun map(data: RustSteadyStateException): SteadyStateException {
return when (data) {
is RustSteadyStateException.BackupDisabled -> SteadyStateException.BackupDisabled(
message = data.message
message = data.message.orEmpty()
)
is RustSteadyStateException.Connection -> SteadyStateException.Connection(
message = data.message
message = data.message.orEmpty()
)
is RustSteadyStateException.Lagged -> SteadyStateException.Lagged(
message = data.message
message = data.message.orEmpty()
)
}
}

View file

@ -52,7 +52,6 @@ class NotificationMapper(
isNoisy = item.isNoisy.orFalse(),
timestamp = item.timestamp() ?: clock.epochMillis(),
content = item.event.use { notificationContentMapper.map(it) },
contentUrl = null,
hasMention = item.hasMention.orFalse(),
)
}

View file

@ -26,16 +26,16 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.NotificationSettings
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate
import org.matrix.rustcomponents.sdk.NotificationSettingsException
import timber.log.Timber
class RustNotificationSettingsService(
private val notificationSettings: NotificationSettings,
client: Client,
private val dispatchers: CoroutineDispatchers,
) : NotificationSettingsService {
private val notificationSettings = client.getNotificationSettings()
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()
@ -45,10 +45,15 @@ class RustNotificationSettingsService(
}
}
init {
fun start() {
notificationSettings.setDelegate(notificationSettingsDelegate)
}
fun destroy() {
notificationSettings.setDelegate(null)
notificationSettings.destroy()
}
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> =
runCatching {
notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
@ -119,6 +124,18 @@ class RustNotificationSettingsService(
}
}
override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) {
runCatching {
notificationSettings.isInviteForMeEnabled()
}
}
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
runCatching {
notificationSettings.setInviteForMeEnabled(enabled)
}
}
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
runCatching {
notificationSettings.getRoomsWithUserDefinedRules(enabled = true)

View file

@ -360,12 +360,6 @@ class RustMatrixRoom(
}
}
override suspend fun canUserJoinCall(userId: UserId): Result<Boolean> {
return runCatching {
innerRoom.canUserSendState(userId.value, StateEventType.ROOM_MEMBER_EVENT.map())
}
}
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> {
return sendAttachment(listOf(file, thumbnailFile)) {
innerTimeline.sendImage(file.path, thumbnailFile.path, imageInfo.map(), progressCallback?.toProgressWatcher())

View file

@ -23,6 +23,7 @@ fun StateEventType.map(): RustStateEventType = when (this) {
StateEventType.POLICY_RULE_ROOM -> RustStateEventType.POLICY_RULE_ROOM
StateEventType.POLICY_RULE_SERVER -> RustStateEventType.POLICY_RULE_SERVER
StateEventType.POLICY_RULE_USER -> RustStateEventType.POLICY_RULE_USER
StateEventType.CALL_MEMBER -> RustStateEventType.CALL_MEMBER
StateEventType.ROOM_ALIASES -> RustStateEventType.ROOM_ALIASES
StateEventType.ROOM_AVATAR -> RustStateEventType.ROOM_AVATAR
StateEventType.ROOM_CANONICAL_ALIAS -> RustStateEventType.ROOM_CANONICAL_ALIAS
@ -47,6 +48,7 @@ fun RustStateEventType.map(): StateEventType = when (this) {
RustStateEventType.POLICY_RULE_ROOM -> StateEventType.POLICY_RULE_ROOM
RustStateEventType.POLICY_RULE_SERVER -> StateEventType.POLICY_RULE_SERVER
RustStateEventType.POLICY_RULE_USER -> StateEventType.POLICY_RULE_USER
RustStateEventType.CALL_MEMBER -> StateEventType.CALL_MEMBER
RustStateEventType.ROOM_ALIASES -> StateEventType.ROOM_ALIASES
RustStateEventType.ROOM_AVATAR -> StateEventType.ROOM_AVATAR
RustStateEventType.ROOM_CANONICAL_ALIAS -> StateEventType.ROOM_CANONICAL_ALIAS

View file

@ -18,12 +18,12 @@ package io.element.android.libraries.matrix.impl.verification
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -33,7 +33,8 @@ import kotlinx.coroutines.launch
import org.matrix.rustcomponents.sdk.SessionVerificationController
import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate
import org.matrix.rustcomponents.sdk.SessionVerificationControllerInterface
import org.matrix.rustcomponents.sdk.SessionVerificationEmoji
import org.matrix.rustcomponents.sdk.use
import org.matrix.rustcomponents.sdk.SessionVerificationData as RustSessionVerificationData
class RustSessionVerificationService(
private val syncService: RustSyncService,
@ -105,12 +106,8 @@ class RustSessionVerificationService(
updateVerificationStatus(isVerified = true)
}
override fun didReceiveVerificationData(data: List<SessionVerificationEmoji>) {
val emojis = data.map { emoji ->
emoji.use { VerificationEmoji(it.symbol(), it.description()) }
}
.toImmutableList()
_verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(emojis)
override fun didReceiveVerificationData(data: RustSessionVerificationData) {
_verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(data.map())
}
// When the actual SAS verification starts
@ -142,3 +139,28 @@ class RustSessionVerificationService(
_sessionVerifiedStatus.value = newValue
}
}
private fun RustSessionVerificationData.map(): SessionVerificationData {
return use { sessionVerificationData ->
when (sessionVerificationData) {
is RustSessionVerificationData.Emojis -> {
SessionVerificationData.Emojis(
emojis = sessionVerificationData.emojis.mapIndexed { index, emoji ->
emoji.use { sessionVerificationEmoji ->
VerificationEmoji(
number = sessionVerificationData.indices[index].toInt(),
emoji = sessionVerificationEmoji.symbol(),
description = sessionVerificationEmoji.description(),
)
}
},
)
}
is RustSessionVerificationData.Decimals -> {
SessionVerificationData.Decimals(
decimals = sessionVerificationData.values.map { it.toInt() },
)
}
}
}
}

View file

@ -39,7 +39,6 @@ import io.element.android.libraries.matrix.test.media.FakeMediaLoader
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.libraries.matrix.test.pushers.FakePushersService
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
@ -72,8 +71,7 @@ class FakeMatrixClient(
private var unignoreUserResult: Result<Unit> = Result.success(Unit)
private var createRoomResult: Result<RoomId> = Result.success(A_ROOM_ID)
private var createDmResult: Result<RoomId> = Result.success(A_ROOM_ID)
private var createDmFailure: Throwable? = null
private var findDmResult: MatrixRoom? = FakeMatrixRoom()
private var findDmResult: RoomId? = A_ROOM_ID
private var logoutFailure: Throwable? = null
private val getRoomResults = mutableMapOf<RoomId, MatrixRoom>()
private val searchUserResults = mutableMapOf<String, Result<MatrixSearchUserResults>>()
@ -87,7 +85,7 @@ class FakeMatrixClient(
return getRoomResults[roomId]
}
override suspend fun findDM(userId: UserId): MatrixRoom? {
override suspend fun findDM(userId: UserId): RoomId? {
return findDmResult
}
@ -99,14 +97,11 @@ class FakeMatrixClient(
return unignoreUserResult
}
override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> {
delay(100)
override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> = simulateLongTask {
return createRoomResult
}
override suspend fun createDM(userId: UserId): Result<RoomId> {
delay(100)
createDmFailure?.let { throw it }
override suspend fun createDM(userId: UserId): Result<RoomId> = simulateLongTask {
return createDmResult
}
@ -206,11 +201,7 @@ class FakeMatrixClient(
unignoreUserResult = result
}
fun givenCreateDmError(failure: Throwable?) {
createDmFailure = failure
}
fun givenFindDmResult(result: MatrixRoom?) {
fun givenFindDmResult(result: RoomId?) {
findDmResult = result
}

View file

@ -33,7 +33,7 @@ class FakeEncryptionService : EncryptionService {
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
private var fixRecoveryIssuesFailure: Exception? = null
private var recoverFailure: Exception? = null
private var doesBackupExistOnServerResult: Result<Boolean> = Result.success(true)
override suspend fun enableBackups(): Result<Unit> = simulateLongTask {
@ -44,8 +44,8 @@ class FakeEncryptionService : EncryptionService {
disableRecoveryFailure = exception
}
fun givenFixRecoveryIssuesFailure(exception: Exception?) {
fixRecoveryIssuesFailure = exception
fun givenRecoverFailure(exception: Exception?) {
recoverFailure = exception
}
override suspend fun disableRecovery(): Result<Unit> = simulateLongTask {
@ -61,8 +61,8 @@ class FakeEncryptionService : EncryptionService {
return doesBackupExistOnServerResult
}
override suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit> = simulateLongTask {
fixRecoveryIssuesFailure?.let { return Result.failure(it) }
override suspend fun recover(recoveryKey: String): Result<Unit> = simulateLongTask {
recoverFailure?.let { return Result.failure(it) }
return Result.success(Unit)
}

View file

@ -41,6 +41,7 @@ class FakeNotificationSettingsService(
private var roomNotificationMode: RoomNotificationMode = initialRoomMode
private var roomNotificationModeIsDefault: Boolean = initialRoomModeIsDefault
private var callNotificationsEnabled = false
private var inviteNotificationsEnabled = false
private var atRoomNotificationsEnabled = false
private var setNotificationModeError: Throwable? = null
private var restoreDefaultNotificationModeError: Throwable? = null
@ -52,7 +53,7 @@ class FakeNotificationSettingsService(
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> {
return Result.success(
RoomNotificationSettings(
mode = if(roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode,
mode = if (roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode,
isDefault = roomNotificationModeIsDefault
)
)
@ -149,6 +150,15 @@ class FakeNotificationSettingsService(
return Result.success(Unit)
}
override suspend fun isInviteForMeEnabled(): Result<Boolean> {
return Result.success(inviteNotificationsEnabled)
}
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> {
inviteNotificationsEnabled = enabled
return Result.success(Unit)
}
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> {
return Result.success(if (roomNotificationModeIsDefault) listOf() else listOf(A_ROOM_ID.value))
}
@ -168,5 +178,4 @@ class FakeNotificationSettingsService(
fun givenSetDefaultNotificationModeError(throwable: Throwable?) {
setDefaultNotificationModeError = throwable
}
}

View file

@ -16,12 +16,10 @@
package io.element.android.libraries.matrix.test.verification
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -31,10 +29,9 @@ class FakeSessionVerificationService : SessionVerificationService {
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
private var _canVerifySessionFlow = MutableStateFlow(true)
private var emojiList = persistentListOf<VerificationEmoji>()
var shouldFail = false
override val verificationFlowState: StateFlow<VerificationFlowState> =_verificationFlowState
override val verificationFlowState: StateFlow<VerificationFlowState> = _verificationFlowState
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus
override val canVerifySessionFlow: Flow<Boolean> = _canVerifySessionFlow
@ -64,8 +61,8 @@ class FakeSessionVerificationService : SessionVerificationService {
}
}
fun triggerReceiveVerificationData() {
_verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(emojiList)
fun triggerReceiveVerificationData(sessionVerificationData: SessionVerificationData) {
_verificationFlowState.value = VerificationFlowState.ReceivedVerificationData(sessionVerificationData)
}
override suspend fun startVerification() {
@ -88,10 +85,6 @@ class FakeSessionVerificationService : SessionVerificationService {
_isReady.value = value
}
fun givenEmojiList(emojis: List<VerificationEmoji>) {
this.emojiList = emojis.toPersistentList()
}
override suspend fun reset() {
_verificationFlowState.value = VerificationFlowState.Initial
}