Merge branch 'develop' into feature/fga/knock_requests_sdk
This commit is contained in:
commit
01f7fc20f4
391 changed files with 6199 additions and 1991 deletions
|
|
@ -12,4 +12,5 @@ enum class CurrentUserMembership {
|
|||
JOINED,
|
||||
LEFT,
|
||||
KNOCKED,
|
||||
BANNED,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -241,6 +241,11 @@ interface MatrixRoom : Closeable {
|
|||
*/
|
||||
suspend fun setUnreadFlag(isUnread: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Clear the event cache storage for the current room.
|
||||
*/
|
||||
suspend fun clearEventCacheStorage(): Result<Unit>
|
||||
|
||||
/**
|
||||
* Share a location message in the room.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.roomlist
|
||||
|
||||
import io.element.android.libraries.core.extensions.withoutAccents
|
||||
|
||||
sealed interface RoomListFilter {
|
||||
companion object {
|
||||
/**
|
||||
|
|
@ -73,5 +75,7 @@ sealed interface RoomListFilter {
|
|||
*/
|
||||
data class NormalizedMatchRoomName(
|
||||
val pattern: String
|
||||
) : RoomListFilter
|
||||
) : RoomListFilter {
|
||||
val normalizedPattern: String = pattern.withoutAccents()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,17 @@ enum class UtdCause {
|
|||
UnknownDevice,
|
||||
|
||||
/**
|
||||
* Expected utd because this is a device-historical message and
|
||||
* key storage is not setup or not configured correctly.
|
||||
* We are missing the keys for this event, but it is a "device-historical" message and
|
||||
* there is no key storage backup on the server, presumably because the user has turned it off.
|
||||
*/
|
||||
HistoricalMessage,
|
||||
HistoricalMessageAndBackupIsDisabled,
|
||||
|
||||
/**
|
||||
* We are missing the keys for this event, but it is a "device-historical"
|
||||
* message, and even though a key storage backup does exist, we can't use
|
||||
* it because our device is unverified.
|
||||
*/
|
||||
HistoricalMessageAndDeviceIsUnverified,
|
||||
|
||||
/**
|
||||
* The key was withheld on purpose because your device is insecure and/or the
|
||||
|
|
|
|||
|
|
@ -109,9 +109,7 @@ class RustMatrixClientFactory @Inject constructor(
|
|||
.addRootCertificates(userCertificatesProvider.provides())
|
||||
.autoEnableBackups(true)
|
||||
.autoEnableCrossSigning(true)
|
||||
// TODO Add a feature flag to enable persistent storage
|
||||
// See https://github.com/matrix-org/matrix-rust-sdk/pull/4396
|
||||
.useEventCachePersistentStorage(false)
|
||||
.useEventCachePersistentStorage(featureFlagService.isFeatureEnabled(FeatureFlags.EventCache))
|
||||
.roomKeyRecipientStrategy(
|
||||
strategy = if (featureFlagService.isFeatureEnabled(FeatureFlags.OnlySignedDeviceIsolationMode)) {
|
||||
CollectStrategy.IdentityBasedStrategy
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ class UtdTracker(
|
|||
UtdCause.UNKNOWN_DEVICE -> {
|
||||
Error.Name.ExpectedSentByInsecureDevice
|
||||
}
|
||||
UtdCause.HISTORICAL_MESSAGE -> Error.Name.HistoricalMessage
|
||||
UtdCause.HISTORICAL_MESSAGE_AND_BACKUP_IS_DISABLED,
|
||||
UtdCause.HISTORICAL_MESSAGE_AND_DEVICE_IS_UNVERIFIED,
|
||||
-> Error.Name.HistoricalMessage
|
||||
UtdCause.WITHHELD_FOR_UNVERIFIED_OR_INSECURE_DEVICE -> Error.Name.RoomKeysWithheldForUnverifiedDevice
|
||||
UtdCause.WITHHELD_BY_SENDER -> Error.Name.OlmKeysNotSentError
|
||||
}
|
||||
|
|
@ -39,6 +41,10 @@ class UtdTracker(
|
|||
timeToDecryptMillis = info.timeToDecryptMs?.toInt() ?: -1,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = name,
|
||||
eventLocalAgeMillis = info.eventLocalAgeMillis.toInt(),
|
||||
userTrustsOwnIdentity = info.userTrustsOwnIdentity,
|
||||
isFederated = info.ownHomeserver != info.senderHomeserver,
|
||||
isMatrixDotOrg = info.ownHomeserver == "matrix.org",
|
||||
)
|
||||
analyticsService.capture(event)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ fun RustMembership.map(): CurrentUserMembership = when (this) {
|
|||
RustMembership.JOINED -> CurrentUserMembership.JOINED
|
||||
RustMembership.LEFT -> CurrentUserMembership.LEFT
|
||||
Membership.KNOCKED -> CurrentUserMembership.KNOCKED
|
||||
RustMembership.BANNED -> CurrentUserMembership.BANNED
|
||||
}
|
||||
|
||||
fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) {
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ class RustMatrixRoom(
|
|||
RoomMessageEventMessageType.VIDEO,
|
||||
RoomMessageEventMessageType.AUDIO,
|
||||
),
|
||||
dateDividerMode = DateDividerMode.DAILY,
|
||||
dateDividerMode = DateDividerMode.MONTHLY,
|
||||
).let { inner ->
|
||||
createTimeline(inner, mode = Timeline.Mode.MEDIA)
|
||||
}
|
||||
|
|
@ -582,6 +582,12 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun clearEventCacheStorage(): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.clearEventCacheStorage()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun kickUser(userId: UserId, reason: String?): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.kickUser(userId.value, reason)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.core.extensions.withoutAccents
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
|
|
@ -30,7 +31,7 @@ val RoomListFilter.predicate
|
|||
!roomSummary.isInvited() && (roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread)
|
||||
}
|
||||
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
||||
roomSummary.info.name.orEmpty().contains(pattern, ignoreCase = true)
|
||||
roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true)
|
||||
}
|
||||
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
||||
roomSummary.isInvited()
|
||||
|
|
|
|||
|
|
@ -145,7 +145,8 @@ private fun RustUtdCause.map(): UtdCause {
|
|||
RustUtdCause.VERIFICATION_VIOLATION -> UtdCause.VerificationViolation
|
||||
RustUtdCause.UNSIGNED_DEVICE -> UtdCause.UnsignedDevice
|
||||
RustUtdCause.UNKNOWN_DEVICE -> UtdCause.UnknownDevice
|
||||
RustUtdCause.HISTORICAL_MESSAGE -> UtdCause.HistoricalMessage
|
||||
RustUtdCause.HISTORICAL_MESSAGE_AND_BACKUP_IS_DISABLED -> UtdCause.HistoricalMessageAndBackupIsDisabled
|
||||
RustUtdCause.HISTORICAL_MESSAGE_AND_DEVICE_IS_UNVERIFIED -> UtdCause.HistoricalMessageAndDeviceIsUnverified
|
||||
RustUtdCause.WITHHELD_FOR_UNVERIFIED_OR_INSECURE_DEVICE -> UtdCause.WithheldUnverifiedOrInsecureDevice
|
||||
RustUtdCause.WITHHELD_BY_SENDER -> UtdCause.WithheldBySender
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ package io.element.android.libraries.matrix.impl.analytics
|
|||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.Error
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUnableToDecryptInfo
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.UnableToDecryptInfo
|
||||
import uniffi.matrix_sdk_crypto.UtdCause
|
||||
|
||||
class UtdTrackerTest {
|
||||
|
|
@ -21,10 +21,11 @@ class UtdTrackerTest {
|
|||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
UnableToDecryptInfo(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
timeToDecryptMs = null,
|
||||
cause = UtdCause.UNKNOWN,
|
||||
eventLocalAgeMillis = 100L,
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.capturedEvents).containsExactly(
|
||||
|
|
@ -34,7 +35,11 @@ class UtdTrackerTest {
|
|||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = -1,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.OlmKeysNotSentError
|
||||
name = Error.Name.OlmKeysNotSentError,
|
||||
isFederated = false,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 100,
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.screenEvents).isEmpty()
|
||||
|
|
@ -46,7 +51,7 @@ class UtdTrackerTest {
|
|||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
UnableToDecryptInfo(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
timeToDecryptMs = 123.toULong(),
|
||||
cause = UtdCause.UNKNOWN,
|
||||
|
|
@ -59,7 +64,11 @@ class UtdTrackerTest {
|
|||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = 123,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.OlmKeysNotSentError
|
||||
name = Error.Name.OlmKeysNotSentError,
|
||||
isFederated = false,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.screenEvents).isEmpty()
|
||||
|
|
@ -71,7 +80,7 @@ class UtdTrackerTest {
|
|||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
UnableToDecryptInfo(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
timeToDecryptMs = 123.toULong(),
|
||||
cause = UtdCause.SENT_BEFORE_WE_JOINED,
|
||||
|
|
@ -84,7 +93,11 @@ class UtdTrackerTest {
|
|||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = 123,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.ExpectedDueToMembership
|
||||
name = Error.Name.ExpectedDueToMembership,
|
||||
isFederated = false,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.screenEvents).isEmpty()
|
||||
|
|
@ -96,7 +109,7 @@ class UtdTrackerTest {
|
|||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
UnableToDecryptInfo(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
timeToDecryptMs = 123.toULong(),
|
||||
cause = UtdCause.UNSIGNED_DEVICE,
|
||||
|
|
@ -109,7 +122,11 @@ class UtdTrackerTest {
|
|||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = 123,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.ExpectedSentByInsecureDevice
|
||||
name = Error.Name.ExpectedSentByInsecureDevice,
|
||||
isFederated = false,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -119,7 +136,7 @@ class UtdTrackerTest {
|
|||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
UnableToDecryptInfo(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
timeToDecryptMs = 123.toULong(),
|
||||
cause = UtdCause.VERIFICATION_VIOLATION,
|
||||
|
|
@ -132,7 +149,90 @@ class UtdTrackerTest {
|
|||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = 123,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.ExpectedVerificationViolation
|
||||
name = Error.Name.ExpectedVerificationViolation,
|
||||
isFederated = false,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when onUtd is called with different sender and receiver servers, the expected analytics Event is sent`() {
|
||||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
ownHomeserver = "example.com",
|
||||
senderHomeserver = "matrix.org",
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.capturedEvents).containsExactly(
|
||||
Error(
|
||||
context = null,
|
||||
cryptoModule = Error.CryptoModule.Rust,
|
||||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = -1,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.OlmKeysNotSentError,
|
||||
isFederated = true,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when onUtd is called from a matrix-org user, the expected analytics Event is sent`() {
|
||||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
ownHomeserver = "matrix.org",
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.capturedEvents).containsExactly(
|
||||
Error(
|
||||
context = null,
|
||||
cryptoModule = Error.CryptoModule.Rust,
|
||||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = -1,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.OlmKeysNotSentError,
|
||||
isFederated = true,
|
||||
isMatrixDotOrg = true,
|
||||
userTrustsOwnIdentity = false,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when onUtd is called from a verified device, the expected analytics Event is sent`() {
|
||||
val fakeAnalyticsService = FakeAnalyticsService()
|
||||
val sut = UtdTracker(fakeAnalyticsService)
|
||||
sut.onUtd(
|
||||
aRustUnableToDecryptInfo(
|
||||
eventId = AN_EVENT_ID.value,
|
||||
userTrustsOwnIdentity = true,
|
||||
)
|
||||
)
|
||||
assertThat(fakeAnalyticsService.capturedEvents).containsExactly(
|
||||
Error(
|
||||
context = null,
|
||||
cryptoModule = Error.CryptoModule.Rust,
|
||||
cryptoSDK = Error.CryptoSDK.Rust,
|
||||
timeToDecryptMillis = -1,
|
||||
domain = Error.Domain.E2EE,
|
||||
name = Error.Name.OlmKeysNotSentError,
|
||||
isFederated = false,
|
||||
isMatrixDotOrg = false,
|
||||
userTrustsOwnIdentity = true,
|
||||
eventLocalAgeMillis = 0,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.factories
|
||||
|
||||
import org.matrix.rustcomponents.sdk.UnableToDecryptInfo
|
||||
import uniffi.matrix_sdk_crypto.UtdCause
|
||||
|
||||
internal fun aRustUnableToDecryptInfo(
|
||||
eventId: String,
|
||||
timeToDecryptMs: ULong? = null,
|
||||
cause: UtdCause = UtdCause.UNKNOWN,
|
||||
eventLocalAgeMillis: Long = 0L,
|
||||
userTrustsOwnIdentity: Boolean = false,
|
||||
senderHomeserver: String = "",
|
||||
ownHomeserver: String = "",
|
||||
): UnableToDecryptInfo {
|
||||
return UnableToDecryptInfo(
|
||||
eventId = eventId,
|
||||
timeToDecryptMs = timeToDecryptMs,
|
||||
cause = cause,
|
||||
eventLocalAgeMillis = eventLocalAgeMillis,
|
||||
userTrustsOwnIdentity = userTrustsOwnIdentity,
|
||||
senderHomeserver = senderHomeserver,
|
||||
ownHomeserver = ownHomeserver,
|
||||
)
|
||||
}
|
||||
|
|
@ -34,6 +34,9 @@ class RoomListFilterTest {
|
|||
private val roomToSearch = aRoomSummary(
|
||||
name = "Room to search"
|
||||
)
|
||||
private val roomWithAccent = aRoomSummary(
|
||||
name = "Frédéric"
|
||||
)
|
||||
private val invitedRoom = aRoomSummary(
|
||||
currentUserMembership = CurrentUserMembership.INVITED
|
||||
)
|
||||
|
|
@ -45,6 +48,7 @@ class RoomListFilterTest {
|
|||
markedAsUnreadRoom,
|
||||
unreadNotificationRoom,
|
||||
roomToSearch,
|
||||
roomWithAccent,
|
||||
invitedRoom
|
||||
)
|
||||
|
||||
|
|
@ -69,7 +73,14 @@ class RoomListFilterTest {
|
|||
@Test
|
||||
fun `Room list filter group`() = runTest {
|
||||
val filter = RoomListFilter.Category.Group
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(regularRoom, favoriteRoom, markedAsUnreadRoom, unreadNotificationRoom, roomToSearch)
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(
|
||||
regularRoom,
|
||||
favoriteRoom,
|
||||
markedAsUnreadRoom,
|
||||
unreadNotificationRoom,
|
||||
roomToSearch,
|
||||
roomWithAccent,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -96,6 +107,18 @@ class RoomListFilterTest {
|
|||
assertThat(roomSummaries.filter(filter)).containsExactly(roomToSearch)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter normalized match room name with accent`() = runTest {
|
||||
val filter = RoomListFilter.NormalizedMatchRoomName("Fred")
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(roomWithAccent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter normalized match room name with accent when searching with accent`() = runTest {
|
||||
val filter = RoomListFilter.NormalizedMatchRoomName("Fréd")
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(roomWithAccent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter all with one match`() = runTest {
|
||||
val filter = RoomListFilter.all(
|
||||
|
|
|
|||
|
|
@ -585,6 +585,10 @@ class FakeMatrixRoom(
|
|||
fun givenRoomMembersState(state: MatrixRoomMembersState) {
|
||||
membersStateFlow.value = state
|
||||
}
|
||||
|
||||
override suspend fun clearEventCacheStorage(): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun defaultRoomPowerLevels() = MatrixRoomPowerLevels(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue