From 6b05e7a6bbefd988f0d76fed69ef9df801578996 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 10 Dec 2021 15:52:04 +0100 Subject: [PATCH 01/11] Support misconfigured room encryption --- changelog.d/4711.bugfix | 1 + .../session/room/crypto/RoomCryptoService.kt | 2 +- .../room/model/RoomEncryptionAlgorithm.kt | 28 +++++++++++++ .../sdk/api/session/room/model/RoomSummary.kt | 3 +- .../crypto/CryptoSessionInfoProvider.kt | 1 - .../internal/crypto/DefaultCryptoService.kt | 33 ++++++++++------ .../model/event/EncryptionEventContent.kt | 2 +- .../internal/crypto/store/IMXCryptoStore.kt | 2 +- .../crypto/store/db/RealmCryptoStore.kt | 2 +- .../database/RealmSessionStoreMigration.kt | 26 +++++++++++++ .../database/mapper/RoomSummaryMapper.kt | 10 ++++- .../database/model/RoomSummaryEntity.kt | 5 +++ .../sdk/internal/session/room/DefaultRoom.kt | 8 ++-- .../room/summary/RoomSummaryUpdater.kt | 16 ++++---- .../sync/handler/room/RoomSyncHandler.kt | 2 + .../app/core/ui/views/NotificationAreaView.kt | 14 +++++++ .../home/room/detail/RoomDetailFragment.kt | 22 ++++++++--- .../composer/MessageComposerViewModel.kt | 34 +++++++++++++--- .../composer/MessageComposerViewState.kt | 20 ++++++++-- .../timeline/factory/EncryptionItemFactory.kt | 2 +- .../RoomMemberProfileController.kt | 11 +++++- .../RoomMemberProfileViewModel.kt | 11 +++++- .../RoomMemberProfileViewState.kt | 1 + .../features/roomprofile/RoomProfileAction.kt | 1 + .../roomprofile/RoomProfileController.kt | 39 ++++++++++++++++++- .../roomprofile/RoomProfileFragment.kt | 5 +++ .../roomprofile/RoomProfileViewEvents.kt | 1 + .../roomprofile/RoomProfileViewModel.kt | 31 +++++++++++++++ .../roomprofile/RoomProfileViewState.kt | 3 +- vector/src/main/res/values/strings.xml | 5 +++ 30 files changed, 288 insertions(+), 53 deletions(-) create mode 100644 changelog.d/4711.bugfix create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt diff --git a/changelog.d/4711.bugfix b/changelog.d/4711.bugfix new file mode 100644 index 0000000000..5a7c64e80e --- /dev/null +++ b/changelog.d/4711.bugfix @@ -0,0 +1 @@ +Better handling of misconfigured room encryption \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt index 6581247b90..6d7dffb441 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt @@ -29,7 +29,7 @@ interface RoomCryptoService { /** * Enable encryption of the room */ - suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM) + suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM, force: Boolean = false) /** * Ensures all members of the room are loaded and outbound session keys are shared. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt new file mode 100644 index 0000000000..542ede82ae --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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 org.matrix.android.sdk.api.session.room.model + +import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM + +sealed class RoomEncryptionAlgorithm { + + abstract class SupportedAlgorithm(val alg: String) : RoomEncryptionAlgorithm() + + object Megolm : SupportedAlgorithm(MXCRYPTO_ALGORITHM_MEGOLM) + + data class UnsupportedAlgorithm(val name: String?) : RoomEncryptionAlgorithm() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt index 10cad026bc..c793a04f9d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt @@ -62,7 +62,8 @@ data class RoomSummary( val roomType: String? = null, val spaceParents: List? = null, val spaceChildren: List? = null, - val flattenParentIds: List = emptyList() + val flattenParentIds: List = emptyList(), + val roomEncryptionAlgorithm: RoomEncryptionAlgorithm? = null ) { val isVersioned: Boolean diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt index 5338e7e92f..82eced4371 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt @@ -37,7 +37,6 @@ internal class CryptoSessionInfoProvider @Inject constructor( fun isRoomEncrypted(roomId: String): Boolean { val encryptionEvent = monarchy.fetchCopied { realm -> EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION) - .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"") .isEmpty(EventEntityFields.STATE_KEY) .findFirst() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 00cd278921..7dd8cc73ae 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -177,7 +177,7 @@ internal class DefaultCryptoService @Inject constructor( private val isStarted = AtomicBoolean(false) fun onStateEvent(roomId: String, event: Event) { - when (event.getClearType()) { + when (event.type) { EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event) EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event) EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event) @@ -185,10 +185,13 @@ internal class DefaultCryptoService @Inject constructor( } fun onLiveEvent(roomId: String, event: Event) { - when (event.getClearType()) { - EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event) - EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event) - EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event) + // handle state events + if (event.isStateEvent()) { + when (event.type) { + EventType.STATE_ROOM_ENCRYPTION -> onRoomEncryptionEvent(roomId, event) + EventType.STATE_ROOM_MEMBER -> onRoomMembershipEvent(roomId, event) + EventType.STATE_ROOM_HISTORY_VISIBILITY -> onRoomHistoryVisibilityEvent(roomId, event) + } } } @@ -575,26 +578,31 @@ internal class DefaultCryptoService @Inject constructor( // (for now at least. Maybe we should alert the user somehow?) val existingAlgorithm = cryptoStore.getRoomAlgorithm(roomId) - if (!existingAlgorithm.isNullOrEmpty() && existingAlgorithm != algorithm) { - Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Ignoring m.room.encryption event which requests a change of config in $roomId") + if (existingAlgorithm == algorithm) { + // ignore + Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Ignoring m.room.encryption for same alg ($algorithm) in $roomId") return false } val encryptingClass = MXCryptoAlgorithms.hasEncryptorClassForAlgorithm(algorithm) + // Always store even if not supported + cryptoStore.storeRoomAlgorithm(roomId, algorithm) + if (!encryptingClass) { Timber.tag(loggerTag.value).e("setEncryptionInRoom() : Unable to encrypt room $roomId with $algorithm") return false } - cryptoStore.storeRoomAlgorithm(roomId, algorithm!!) - - val alg: IMXEncrypting = when (algorithm) { + val alg: IMXEncrypting? = when (algorithm) { MXCRYPTO_ALGORITHM_MEGOLM -> megolmEncryptionFactory.create(roomId) - else -> olmEncryptionFactory.create(roomId) + MXCRYPTO_ALGORITHM_OLM -> olmEncryptionFactory.create(roomId) + else -> null } - roomEncryptorsStore.put(roomId, alg) + if (alg != null) { + roomEncryptorsStore.put(roomId, alg) + } // if encryption was not previously enabled in this room, we will have been // ignoring new device events for these users so far. We may well have @@ -927,6 +935,7 @@ internal class DefaultCryptoService @Inject constructor( } private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) { + if (!event.isStateEvent()) return val eventContent = event.content.toModel() eventContent?.historyVisibility?.let { cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/event/EncryptionEventContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/event/EncryptionEventContent.kt index b64cd97ff6..dd76ae1d8e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/event/EncryptionEventContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/event/EncryptionEventContent.kt @@ -27,7 +27,7 @@ data class EncryptionEventContent( * Required. The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'. */ @Json(name = "algorithm") - val algorithm: String, + val algorithm: String?, /** * How long the session should be used before changing it. 604800000 (a week) is the recommended default. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 9b75f88f91..82fb565377 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -230,7 +230,7 @@ internal interface IMXCryptoStore { * @param roomId the id of the room. * @param algorithm the algorithm. */ - fun storeRoomAlgorithm(roomId: String, algorithm: String) + fun storeRoomAlgorithm(roomId: String, algorithm: String?) /** * Provides the algorithm used in a dedicated room. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 40678a6ce6..33578ba06a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -629,7 +629,7 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun storeRoomAlgorithm(roomId: String, algorithm: String) { + override fun storeRoomAlgorithm(roomId: String, algorithm: String?) { doRealmTransaction(realmConfiguration) { CryptoRoomEntity.getOrCreate(it, roomId).algorithm = algorithm } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 508af250c2..9f4459b20f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.model.VersioningState import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.model.tag.RoomTag +import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent import org.matrix.android.sdk.internal.database.model.ChunkEntityFields import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields @@ -395,6 +396,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private fun migrateTo20(realm: DynamicRealm) { Timber.d("Step 19 -> 20") + realm.schema.get("ChunkEntity")?.apply { if (hasField("numberOfTimelineEvents")) { removeField("numberOfTimelineEvents") @@ -413,5 +415,29 @@ internal class RealmSessionStoreMigration @Inject constructor( chunkEntities.deleteAllFromRealm() } } + + realm.schema.get("RoomSummaryEntity") + ?.addField(RoomSummaryEntityFields.E2E_ALGORITHM, String::class.java) + ?.transform { obj -> + + val encryptionContentAdapter = MoshiProvider.providesMoshi().adapter(EncryptionEventContent::class.java) + + val encryptionEvent = realm.where("CurrentStateEventEntity") + .equalTo(CurrentStateEventEntityFields.ROOM_ID, obj.getString(RoomSummaryEntityFields.ROOM_ID)) + .equalTo(CurrentStateEventEntityFields.TYPE, EventType.STATE_ROOM_ENCRYPTION) + .findFirst() + + val encryptionEventRoot = encryptionEvent?.getObject(CurrentStateEventEntityFields.ROOT.`$`) + val algorithm = encryptionEventRoot + ?.getString(EventEntityFields.CONTENT)?.let { + encryptionContentAdapter.fromJson(it)?.algorithm + } + + obj.setString(RoomSummaryEntityFields.E2E_ALGORITHM, algorithm) + obj.setBoolean(RoomSummaryEntityFields.IS_ENCRYPTED, encryptionEvent != null) + encryptionEventRoot?.getLong(EventEntityFields.ORIGIN_SERVER_TS)?.let { + obj.setLong(RoomSummaryEntityFields.ENCRYPTION_EVENT_TS, it) + } + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index 3a15e0acf0..83a57fed63 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -16,12 +16,14 @@ package org.matrix.android.sdk.internal.database.mapper +import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.typing.TypingUsersTracker +import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.presence.toUserPresence import javax.inject.Inject @@ -99,7 +101,13 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa worldReadable = it.childSummaryEntity?.joinRules == RoomJoinRules.PUBLIC ) }, - flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList() + flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList(), + roomEncryptionAlgorithm = when (val alg = roomSummaryEntity.e2eAlgorithm) { + // I should probably use #hasEncryptorClassForAlgorithm but it says it supports + // OLM which is some legacy? Now only megolm allowed in rooms + MXCRYPTO_ALGORITHM_MEGOLM -> RoomEncryptionAlgorithm.Megolm + else -> RoomEncryptionAlgorithm.UnsupportedAlgorithm(alg) + } ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 67672f03ad..febedc3456 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -205,6 +205,11 @@ internal open class RoomSummaryEntity( if (value != field) field = value } + var e2eAlgorithm: String? = null + set(value) { + if (value != field) field = value + } + var encryptionEventTs: Long? = 0 set(value) { if (value != field) field = value diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index cb4bcdb606..1fe7503141 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -119,15 +119,15 @@ internal class DefaultRoom(override val roomId: String, } } - override suspend fun enableEncryption(algorithm: String) { + override suspend fun enableEncryption(algorithm: String, force: Boolean) { when { - isEncrypted() -> { + (!force && isEncrypted() && encryptionAlgorithm() == MXCRYPTO_ALGORITHM_MEGOLM) -> { throw IllegalStateException("Encryption is already enabled for this room") } - algorithm != MXCRYPTO_ALGORITHM_MEGOLM -> { + (!force && algorithm != MXCRYPTO_ALGORITHM_MEGOLM) -> { throw InvalidParameterException("Only MXCRYPTO_ALGORITHM_MEGOLM algorithm is supported") } - else -> { + else -> { val params = SendStateTask.Params( roomId = roomId, stateKey = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 70562de998..a7887d77f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -38,13 +38,11 @@ import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications import org.matrix.android.sdk.internal.crypto.EventDecryptor -import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService +import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity -import org.matrix.android.sdk.internal.database.model.EventEntity -import org.matrix.android.sdk.internal.database.model.EventEntityFields import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity @@ -57,7 +55,6 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.isEventRead import org.matrix.android.sdk.internal.database.query.where -import org.matrix.android.sdk.internal.database.query.whereType import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.extensions.clearWith import org.matrix.android.sdk.internal.query.process @@ -123,10 +120,8 @@ internal class RoomSummaryUpdater @Inject constructor( Timber.v("## Space: Updating summary room [$roomId] roomType: [$roomType]") // Don't use current state for this one as we are only interested in having MXCRYPTO_ALGORITHM_MEGOLM event in the room - val encryptionEvent = EventEntity.whereType(realm, roomId = roomId, type = EventType.STATE_ROOM_ENCRYPTION) - .contains(EventEntityFields.CONTENT, "\"algorithm\":\"$MXCRYPTO_ALGORITHM_MEGOLM\"") - .isNotNull(EventEntityFields.STATE_KEY) - .findFirst() + val encryptionEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ENCRYPTION, stateKey = "")?.root + Timber.v("## CRYPTO: currentEncryptionEvent is $encryptionEvent") val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId) @@ -152,6 +147,11 @@ internal class RoomSummaryUpdater @Inject constructor( .orEmpty() roomSummaryEntity.updateAliases(roomAliases) roomSummaryEntity.isEncrypted = encryptionEvent != null + + roomSummaryEntity.e2eAlgorithm = ContentMapper.map(encryptionEvent?.content) + ?.toModel() + ?.algorithm + roomSummaryEntity.encryptionEventTs = encryptionEvent?.originServerTs if (roomSummaryEntity.membership == Membership.INVITE && inviterId != null) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 3fdfb473db..24722445be 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -221,6 +221,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType) + Timber.v("## received state event ${event.type} and key ${event.stateKey}") CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply { // Timber.v("## Space state event: $eventEntity") eventId = event.eventId @@ -393,6 +394,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle roomMemberEventHandler.handle(realm, roomEntity.roomId, event.stateKey, fixedContent, aggregator) } } + roomMemberContentsByUser.getOrPut(event.senderId) { // If we don't have any new state on this user, get it from db val rootStateEvent = CurrentStateEventEntity.getOrNull(realm, roomId, event.senderId, EventType.STATE_ROOM_MEMBER)?.root diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt index 94809d2981..661f0fd5e8 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt @@ -73,6 +73,7 @@ class NotificationAreaView @JvmOverloads constructor( is State.Default -> renderDefault() is State.Hidden -> renderHidden() is State.NoPermissionToPost -> renderNoPermissionToPost() + is State.UnsupportedAlgorithm -> renderUnsupportedAlgorithm() is State.Tombstone -> renderTombstone() is State.ResourceLimitExceededError -> renderResourceLimitExceededError(newState) }.exhaustive @@ -106,6 +107,18 @@ class NotificationAreaView @JvmOverloads constructor( views.roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary)) } + private fun renderUnsupportedAlgorithm() { + visibility = View.VISIBLE + views.roomNotificationIcon.setImageResource(R.drawable.ic_shield_warning_small) + val message = span { + italic { + +resources.getString(R.string.room_unsupported_e2e_algorithm) + } + } + views.roomNotificationMessage.text = message + views.roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary)) + } + private fun renderResourceLimitExceededError(state: State.ResourceLimitExceededError) { visibility = View.VISIBLE val resourceLimitErrorFormatter = ResourceLimitErrorFormatter(context) @@ -163,6 +176,7 @@ class NotificationAreaView @JvmOverloads constructor( // User can't post messages to room because his power level doesn't allow it. object NoPermissionToPost : State() + object UnsupportedAlgorithm : State() // View will be Gone object Hidden : State() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 566cb2d2de..19ed0256d9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -133,12 +133,14 @@ import im.vector.app.features.command.Command import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.detail.composer.CanSendStatus import im.vector.app.features.home.room.detail.composer.MessageComposerAction import im.vector.app.features.home.room.detail.composer.MessageComposerView import im.vector.app.features.home.room.detail.composer.MessageComposerViewEvents import im.vector.app.features.home.room.detail.composer.MessageComposerViewModel import im.vector.app.features.home.room.detail.composer.MessageComposerViewState import im.vector.app.features.home.room.detail.composer.SendMode +import im.vector.app.features.home.room.detail.composer.boolean import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.RecordingUiState import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet @@ -392,7 +394,7 @@ class RoomDetailFragment @Inject constructor( } messageComposerViewModel.onEach(MessageComposerViewState::sendMode, MessageComposerViewState::canSendMessage) { mode, canSend -> - if (!canSend) { + if (!canSend.boolean()) { return@onEach } when (mode) { @@ -1268,7 +1270,7 @@ class RoomDetailFragment @Inject constructor( val canSendMessage = withState(messageComposerViewModel) { it.canSendMessage } - if (!canSendMessage) { + if (!canSendMessage.boolean()) { return false } return when (model) { @@ -1446,10 +1448,18 @@ class RoomDetailFragment @Inject constructor( views.voiceMessageRecorderView.render(messageComposerState.voiceRecordingUiState) views.composerLayout.setRoomEncrypted(summary.isEncrypted) // views.composerLayout.alwaysShowSendButton = false - if (messageComposerState.canSendMessage) { - views.notificationAreaView.render(NotificationAreaView.State.Hidden) - } else { - views.notificationAreaView.render(NotificationAreaView.State.NoPermissionToPost) + when (messageComposerState.canSendMessage) { + CanSendStatus.Allowed -> { + NotificationAreaView.State.Hidden + } + CanSendStatus.NoPermission -> { + NotificationAreaView.State.NoPermissionToPost + } + is CanSendStatus.UnSupportedE2eAlgorithm -> { + NotificationAreaView.State.UnsupportedAlgorithm + } + }.let { + views.notificationAreaView.render(it) } } else { views.hideComposerViews() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index a63a06928a..4dc4e638ba 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -38,6 +38,7 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.voice.VoicePlayerHelper import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -47,6 +48,7 @@ import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent +import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper @@ -55,6 +57,8 @@ import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.getRelationContent import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent import org.matrix.android.sdk.api.session.space.CreateSpaceParams +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber class MessageComposerViewModel @AssistedInject constructor( @@ -74,7 +78,7 @@ class MessageComposerViewModel @AssistedInject constructor( init { loadDraftIfAny() - observePowerLevel() + observePowerLevelAndEncryption() subscribeToStateInternal() } @@ -137,12 +141,30 @@ class MessageComposerViewModel @AssistedInject constructor( } } - private fun observePowerLevel() { - PowerLevelsFlowFactory(room).createFlow() - .setOnEach { - val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) - copy(canSendMessage = canSendMessage) + private fun observePowerLevelAndEncryption() { + combine( + PowerLevelsFlowFactory(room).createFlow(), + room.flow().liveRoomSummary().unwrap() + ) { pl, sum -> + val canSendMessage = PowerLevelsHelper(pl).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) + if (canSendMessage) { + val isE2E = sum.isEncrypted + if (isE2E) { + val roomEncryptionAlgorithm = sum.roomEncryptionAlgorithm + if (roomEncryptionAlgorithm is RoomEncryptionAlgorithm.UnsupportedAlgorithm) { + CanSendStatus.UnSupportedE2eAlgorithm(roomEncryptionAlgorithm.name) + } else { + CanSendStatus.Allowed + } + } else { + CanSendStatus.Allowed } + } else { + CanSendStatus.NoPermission + } + }.setOnEach { + copy(canSendMessage = it) + } } private fun handleEnterQuoteMode(action: MessageComposerAction.EnterQuoteMode) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt index 578de9bd89..edaa82165e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt @@ -43,9 +43,23 @@ sealed interface SendMode { data class Voice(val text: String) : SendMode } +sealed class CanSendStatus { + object Allowed : CanSendStatus() + object NoPermission : CanSendStatus() + data class UnSupportedE2eAlgorithm(val algorithm: String?) : CanSendStatus() +} + +fun CanSendStatus.boolean(): Boolean { + return when (this) { + CanSendStatus.Allowed -> true + CanSendStatus.NoPermission -> false + is CanSendStatus.UnSupportedE2eAlgorithm -> false + } +} + data class MessageComposerViewState( val roomId: String, - val canSendMessage: Boolean = true, + val canSendMessage: CanSendStatus = CanSendStatus.Allowed, val isSendButtonVisible: Boolean = false, val sendMode: SendMode = SendMode.Regular("", false), val voiceRecordingUiState: VoiceMessageRecorderView.RecordingUiState = VoiceMessageRecorderView.RecordingUiState.Idle @@ -60,8 +74,8 @@ data class MessageComposerViewState( val isVoiceMessageIdle = !isVoiceRecording - val isComposerVisible = canSendMessage && !isVoiceRecording - val isVoiceMessageRecorderVisible = canSendMessage && !isSendButtonVisible + val isComposerVisible = canSendMessage.boolean() && !isVoiceRecording + val isVoiceMessageRecorderVisible = canSendMessage.boolean() && !isSendButtonVisible @Suppress("UNUSED") // needed by mavericks constructor(args: RoomDetailArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt index 14d9cce28a..8792ce6d33 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt @@ -63,7 +63,7 @@ class EncryptionItemFactory @Inject constructor( ) shield = StatusTileTimelineItem.ShieldUIState.BLACK } else { - title = stringProvider.getString(R.string.encryption_not_enabled) + title = stringProvider.getString(R.string.encryption_misconfigured) description = stringProvider.getString(R.string.encryption_unknown_algorithm_tile_description) shield = StatusTileTimelineItem.ShieldUIState.RED } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index b9c7f1124c..d68fabfbc1 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -95,11 +95,14 @@ class RoomMemberProfileController @Inject constructor( private fun buildSecuritySection(state: RoomMemberProfileViewState) { // Security - buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) val host = this if (state.isRoomEncrypted) { - if (state.userMXCrossSigningInfo != null) { + if (!state.isAlgorithmSupported) { + // TODO find sensible message to display here + // For now we just remove the verify actions as well as the Security status + } else if (state.userMXCrossSigningInfo != null) { + buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) // Cross signing is enabled for this user if (state.userMXCrossSigningInfo.isTrusted()) { // User is trusted @@ -152,6 +155,8 @@ class RoomMemberProfileController @Inject constructor( } } } else { + buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) + buildProfileAction( id = "learn_more", title = stringProvider.getString(R.string.room_profile_section_security_learn_more), @@ -162,6 +167,8 @@ class RoomMemberProfileController @Inject constructor( ) } } else { + buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) + genericFooterItem { id("verify_footer_not_encrypted") text(host.stringProvider.getString(R.string.room_profile_not_encrypted_subtitle)) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 4f3d9d0776..3765d96119 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -52,6 +52,7 @@ import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.Role @@ -344,7 +345,15 @@ class RoomMemberProfileViewModel @AssistedInject constructor( }.launchIn(viewModelScope) roomSummaryLive.execute { - copy(isRoomEncrypted = it.invoke()?.isEncrypted == true) + val summary = it.invoke() ?: return@execute this + if (summary.isEncrypted) { + copy( + isRoomEncrypted = true, + isAlgorithmSupported = summary.roomEncryptionAlgorithm is RoomEncryptionAlgorithm.SupportedAlgorithm + ) + } else { + copy(isRoomEncrypted = false) + } } roomSummaryLive.combine(powerLevelsContentLive) { roomSummary, powerLevelsContent -> val roomName = roomSummary.toMatrixItem().getBestName() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt index 1f2c3d6ce4..94bf9e8f8e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -33,6 +33,7 @@ data class RoomMemberProfileViewState( val isMine: Boolean = false, val isIgnored: Async = Uninitialized, val isRoomEncrypted: Boolean = false, + val isAlgorithmSupported: Boolean = true, val powerLevelsContent: PowerLevelsContent? = null, val userPowerLevelString: Async = Uninitialized, val userMatrixItem: Async = Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt index 073d30ff8e..22b040b4c0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt @@ -26,4 +26,5 @@ sealed class RoomProfileAction : VectorViewModelAction { data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction() object ShareRoomProfile : RoomProfileAction() object CreateShortcut : RoomProfileAction() + object RestoreEncryptionState : RoomProfileAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index b237faa17d..d061b91205 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -23,6 +23,7 @@ import im.vector.app.core.epoxy.expandableTextItem import im.vector.app.core.epoxy.profiles.buildProfileAction import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericPositiveButtonItem @@ -30,7 +31,10 @@ import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod import im.vector.app.features.settings.VectorPreferences +import me.gujun.android.span.image +import me.gujun.android.span.span import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel +import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject @@ -38,6 +42,7 @@ class RoomProfileController @Inject constructor( private val stringProvider: StringProvider, private val colorProvider: ColorProvider, private val vectorPreferences: VectorPreferences, + private val drawableProvider: DrawableProvider, private val shortcutCreator: ShortcutCreator ) : TypedEpoxyController() { @@ -59,6 +64,7 @@ class RoomProfileController @Inject constructor( fun onRoomDevToolsClicked() fun onUrlInTopicLongClicked(url: String) fun doMigrateToVersion(newVersion: String) + fun restoreEncryptionState() } override fun buildModels(data: RoomProfileViewState?) { @@ -113,15 +119,44 @@ class RoomProfileController @Inject constructor( } } + var encryptionMisconfigured = false val learnMoreSubtitle = if (roomSummary.isEncrypted) { - if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_subtitle else R.string.room_profile_encrypted_subtitle + if (roomSummary.roomEncryptionAlgorithm is RoomEncryptionAlgorithm.SupportedAlgorithm) { + if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_subtitle else R.string.room_profile_encrypted_subtitle + } else { + encryptionMisconfigured = true + if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_misconfigured_subtitle else R.string.room_profile_encrypted_misconfigured_subtitle + } } else { if (roomSummary.isDirect) R.string.direct_room_profile_not_encrypted_subtitle else R.string.room_profile_not_encrypted_subtitle } genericFooterItem { id("e2e info") centered(false) - text(host.stringProvider.getString(learnMoreSubtitle)) + text( + span { + apply { + if (encryptionMisconfigured) { + host.drawableProvider.getDrawable(R.drawable.ic_warning_badge)?.let { + image(it, "baseline") + } + +" " + } + } + +host.stringProvider.getString(learnMoreSubtitle) + } + ) + } + + if (encryptionMisconfigured && data.canUpdateRoomState) { + genericPositiveButtonItem { + id("restore_encryption") + text(host.stringProvider.getString(R.string.room_profile_section_restore_security)) + iconRes(R.drawable.ic_shield_black_no_border) + buttonClickAction { + host.callback?.restoreEncryptionState() + } + } } buildEncryptionAction(data.actionPermissions, roomSummary) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index ee4d0601c6..d82b853fe3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -121,6 +121,7 @@ class RoomProfileFragment @Inject constructor( is RoomProfileViewEvents.Failure -> showFailure(it.throwable) is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink) is RoomProfileViewEvents.OnShortcutReady -> addShortcut(it) + RoomProfileViewEvents.DismissLoading -> dismissLoadingDialog() }.exhaustive } roomListQuickActionsSharedActionViewModel @@ -299,6 +300,10 @@ class RoomProfileFragment @Inject constructor( roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomPermissionsSettings) } + override fun restoreEncryptionState() { + roomProfileViewModel.handle(RoomProfileAction.RestoreEncryptionState) + } + override fun onRoomIdClicked() { copyToClipboard(requireContext(), roomProfileArgs.roomId) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt index 237df0bed5..181115091c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt @@ -24,6 +24,7 @@ import im.vector.app.core.platform.VectorViewEvents */ sealed class RoomProfileViewEvents : VectorViewEvents { data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents() + object DismissLoading : RoomProfileViewEvents() data class Failure(val throwable: Throwable) : RoomProfileViewEvents() data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index 472ddfc6b9..52f9187fff 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -29,7 +29,10 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.powerlevel.PowerLevelsFlowFactory +import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session @@ -44,6 +47,7 @@ import org.matrix.android.sdk.flow.FlowRoom import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.mapOptional import org.matrix.android.sdk.flow.unwrap +import timber.log.Timber class RoomProfileViewModel @AssistedInject constructor( @Assisted private val initialState: RoomProfileViewState, @@ -67,6 +71,19 @@ class RoomProfileViewModel @AssistedInject constructor( observeRoomCreateContent(flowRoom) observeBannedRoomMembers(flowRoom) observePermissions() + obeservePowerLevels() + } + + private fun obeservePowerLevels() { + val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() + powerLevelsContentLive + .onEach { + val powerLevelsHelper = PowerLevelsHelper(it) + val canUpdateRoomState = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) + setState { + copy(canUpdateRoomState = canUpdateRoomState) + } + }.launchIn(viewModelScope) } private fun observeRoomCreateContent(flowRoom: FlowRoom) { @@ -119,6 +136,7 @@ class RoomProfileViewModel @AssistedInject constructor( is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() RoomProfileAction.CreateShortcut -> handleCreateShortcut() + RoomProfileAction.RestoreEncryptionState -> restoreEncryptionState() }.exhaustive } @@ -182,4 +200,17 @@ class RoomProfileViewModel @AssistedInject constructor( _viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink)) } } + + private fun restoreEncryptionState() { + _viewEvents.post(RoomProfileViewEvents.Loading()) + session.coroutineScope.launch { + try { + room.enableEncryption(force = true) + } catch (failure: Throwable) { + Timber.e(failure, "Failed to restore encryption state in room ${room.roomId}") + } finally { + _viewEvents.post(RoomProfileViewEvents.DismissLoading) + } + } + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt index 14b415c53a..87db15ea3b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewState.kt @@ -34,7 +34,8 @@ data class RoomProfileViewState( val isUsingUnstableRoomVersion: Boolean = false, val recommendedRoomVersion: String? = null, val canUpgradeRoom: Boolean = false, - val isTombstoned: Boolean = false + val isTombstoned: Boolean = false, + val canUpdateRoomState: Boolean = false ) : MavericksState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index dbdf104dff..99befb083e 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -962,6 +962,7 @@ Delete unsent messages File not found You do not have permission to post to this room. + Encryption has been misconfigured. You can\'t send messages, please contact an admin to restore the room to a valid state. %d new message %d new messages @@ -2790,8 +2791,11 @@ Messages in this room are not end-to-end encrypted. Messages here are not end-to-end encrypted. Messages in this room are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them. + Encryption has been misconfigured. Please contact an admin to restore the room to a valid state. + Encryption has been misconfigured. Please contact an admin to restore room. Messages here are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them. Security + Restore Encryption Learn more More Admin Actions @@ -3052,6 +3056,7 @@ Messages in this room are end-to-end encrypted. Learn more & verify users in their profile. Messages in this room are end-to-end encrypted. Encryption not enabled + Encryption is misconfigured The encryption used by this room is not supported %s created and configured the room. From 38fbfad8d5a89d3008f105546443913b0f643344 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Dec 2021 09:25:22 +0100 Subject: [PATCH 02/11] Code review --- .../sdk/api/session/room/crypto/RoomCryptoService.kt | 5 ++++- .../home/room/detail/composer/MessageComposerViewState.kt | 8 ++++---- .../app/features/roomprofile/RoomProfileViewModel.kt | 5 +++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt index 6d7dffb441..445d16b72b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt @@ -27,7 +27,10 @@ interface RoomCryptoService { fun shouldEncryptForInvitedMembers(): Boolean /** - * Enable encryption of the room + * Enable encryption of the room. + * @param Use force to ensure that this algorithm will be used. Otherwise this call + * will throw if encryption is already setup or if the algorithm is not supported. Only to + * be used by admins to fix misconfigured encryption. */ suspend fun enableEncryption(algorithm: String = MXCRYPTO_ALGORITHM_MEGOLM, force: Boolean = false) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt index edaa82165e..915e1b3338 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt @@ -43,10 +43,10 @@ sealed interface SendMode { data class Voice(val text: String) : SendMode } -sealed class CanSendStatus { - object Allowed : CanSendStatus() - object NoPermission : CanSendStatus() - data class UnSupportedE2eAlgorithm(val algorithm: String?) : CanSendStatus() +sealed interface CanSendStatus { + object Allowed : CanSendStatus + object NoPermission : CanSendStatus + data class UnSupportedE2eAlgorithm(val algorithm: String?) : CanSendStatus } fun CanSendStatus.boolean(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index 52f9187fff..363cb1ea31 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -71,10 +71,10 @@ class RoomProfileViewModel @AssistedInject constructor( observeRoomCreateContent(flowRoom) observeBannedRoomMembers(flowRoom) observePermissions() - obeservePowerLevels() + observePowerLevels() } - private fun obeservePowerLevels() { + private fun observePowerLevels() { val powerLevelsContentLive = PowerLevelsFlowFactory(room).createFlow() powerLevelsContentLive .onEach { @@ -208,6 +208,7 @@ class RoomProfileViewModel @AssistedInject constructor( room.enableEncryption(force = true) } catch (failure: Throwable) { Timber.e(failure, "Failed to restore encryption state in room ${room.roomId}") + _viewEvents.post(RoomProfileViewEvents.Failure(failure)) } finally { _viewEvents.post(RoomProfileViewEvents.DismissLoading) } From 1fdb851845ec316074a157c9111b43330a7eec6d Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Dec 2021 10:12:31 +0100 Subject: [PATCH 03/11] open room profile for admin when clicked on notification area for e2e pb --- .../vector/app/core/ui/views/NotificationAreaView.kt | 5 +++++ .../app/features/home/room/detail/RoomDetailAction.kt | 1 + .../features/home/room/detail/RoomDetailFragment.kt | 11 ++++++++--- .../features/home/room/detail/RoomDetailViewEvents.kt | 1 + .../features/home/room/detail/RoomDetailViewModel.kt | 11 ++++++++++- .../features/home/room/detail/RoomDetailViewState.kt | 1 + 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt index 661f0fd5e8..f8cc334300 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt @@ -25,6 +25,7 @@ import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.core.text.italic import im.vector.app.R +import im.vector.app.core.epoxy.onClick import im.vector.app.core.error.ResourceLimitErrorFormatter import im.vector.app.core.extensions.exhaustive import im.vector.app.core.utils.DimensionConverter @@ -115,6 +116,9 @@ class NotificationAreaView @JvmOverloads constructor( +resources.getString(R.string.room_unsupported_e2e_algorithm) } } + views.roomNotificationMessage.onClick { + delegate?.onMisconfiguredEncryptionClicked() + } views.roomNotificationMessage.text = message views.roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary)) } @@ -193,5 +197,6 @@ class NotificationAreaView @JvmOverloads constructor( */ interface Delegate { fun onTombstoneEventClicked() + fun onMisconfiguredEncryptionClicked() } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index f20a32848c..f866bb328d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -42,6 +42,7 @@ sealed class RoomDetailAction : VectorViewModelAction { object MarkAllAsRead : RoomDetailAction() data class DownloadOrOpen(val eventId: String, val senderId: String?, val messageFileContent: MessageWithAttachmentContent) : RoomDetailAction() object JoinAndOpenReplacementRoom : RoomDetailAction() + object OnClickMisconfiguredEncryption : RoomDetailAction() object AcceptInvite : RoomDetailAction() object RejectInvite : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 19ed0256d9..3f3ac1df2f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -461,7 +461,8 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it) RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId) RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show() - RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings() + RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings(RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS) + RoomDetailViewEvents.OpenRoomProfile -> handleOpenRoomSettings() is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item -> navigator.openBigImageViewer(requireActivity(), it.view, item) } @@ -585,11 +586,11 @@ class RoomDetailFragment @Inject constructor( ) } - private fun handleOpenRoomSettings() { + private fun handleOpenRoomSettings(directAccess: Int? = null) { navigator.openRoomProfile( requireContext(), roomDetailArgs.roomId, - RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS + directAccess ) } @@ -949,6 +950,10 @@ class RoomDetailFragment @Inject constructor( override fun onTombstoneEventClicked() { roomDetailViewModel.handle(RoomDetailAction.JoinAndOpenReplacementRoom) } + + override fun onMisconfiguredEncryptionClicked() { + roomDetailViewModel.handle(RoomDetailAction.OnClickMisconfiguredEncryption) + } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index 2e7f2bfd63..86240a5ffe 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -48,6 +48,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents { object OpenInvitePeople : RoomDetailViewEvents() object OpenSetRoomAvatarDialog : RoomDetailViewEvents() object OpenRoomSettings : RoomDetailViewEvents() + object OpenRoomProfile : RoomDetailViewEvents() data class ShowRoomAvatarFullScreen(val matrixItem: MatrixItem?, val view: View?) : RoomDetailViewEvents() object ShowWaitingView : RoomDetailViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 583810b915..aba636309f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -211,11 +211,13 @@ class RoomDetailViewModel @AssistedInject constructor( val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE) + val isAllowedToSetupEncryption = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) setState { copy( canInvite = canInvite, isAllowedToManageWidgets = isAllowedToManageWidgets, - isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall + isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall, + isAllowedToSetupEncryption = isAllowedToSetupEncryption ) } }.launchIn(viewModelScope) @@ -309,6 +311,7 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) is RoomDetailAction.JoinAndOpenReplacementRoom -> handleJoinAndOpenReplacementRoom() + is RoomDetailAction.OnClickMisconfiguredEncryption -> handleClickMisconfiguredE2E() is RoomDetailAction.ResendMessage -> handleResendEvent(action) is RoomDetailAction.RemoveFailedEcho -> handleRemove(action) is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead() @@ -614,6 +617,12 @@ class RoomDetailViewModel @AssistedInject constructor( } } + private fun handleClickMisconfiguredE2E() = withState { state -> + if (state.isAllowedToSetupEncryption) { + _viewEvents.post(RoomDetailViewEvents.OpenRoomProfile) + } + } + private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled() fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state -> diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 042a415b47..e35d601887 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -64,6 +64,7 @@ data class RoomDetailViewState( val canInvite: Boolean = true, val isAllowedToManageWidgets: Boolean = false, val isAllowedToStartWebRTCCall: Boolean = true, + val isAllowedToSetupEncryption: Boolean = true, val hasFailedSending: Boolean = false, val jitsiState: JitsiState = JitsiState() ) : MavericksState { From fca32da2046273b24d39a6de43fb91a2077e0c56 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Dec 2021 11:55:34 +0100 Subject: [PATCH 04/11] Code review --- .../vector/app/core/ui/views/NotificationAreaView.kt | 11 +++++++---- .../features/home/room/detail/RoomDetailFragment.kt | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt index f8cc334300..0a9b0df3c4 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt @@ -74,7 +74,7 @@ class NotificationAreaView @JvmOverloads constructor( is State.Default -> renderDefault() is State.Hidden -> renderHidden() is State.NoPermissionToPost -> renderNoPermissionToPost() - is State.UnsupportedAlgorithm -> renderUnsupportedAlgorithm() + is State.UnsupportedAlgorithm -> renderUnsupportedAlgorithm(newState) is State.Tombstone -> renderTombstone() is State.ResourceLimitExceededError -> renderResourceLimitExceededError(newState) }.exhaustive @@ -108,12 +108,15 @@ class NotificationAreaView @JvmOverloads constructor( views.roomNotificationMessage.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary)) } - private fun renderUnsupportedAlgorithm() { + private fun renderUnsupportedAlgorithm(e2eState: State.UnsupportedAlgorithm) { visibility = View.VISIBLE views.roomNotificationIcon.setImageResource(R.drawable.ic_shield_warning_small) + val text = if (e2eState.canRestore) { + R.string.room_unsupported_e2e_algorithm_as_admin + } else R.string.room_unsupported_e2e_algorithm val message = span { italic { - +resources.getString(R.string.room_unsupported_e2e_algorithm) + +resources.getString(text) } } views.roomNotificationMessage.onClick { @@ -180,7 +183,7 @@ class NotificationAreaView @JvmOverloads constructor( // User can't post messages to room because his power level doesn't allow it. object NoPermissionToPost : State() - object UnsupportedAlgorithm : State() + data class UnsupportedAlgorithm(val canRestore: Boolean) : State() // View will be Gone object Hidden : State() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 3f3ac1df2f..5731b36ce0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -1461,7 +1461,7 @@ class RoomDetailFragment @Inject constructor( NotificationAreaView.State.NoPermissionToPost } is CanSendStatus.UnSupportedE2eAlgorithm -> { - NotificationAreaView.State.UnsupportedAlgorithm + NotificationAreaView.State.UnsupportedAlgorithm(mainState.isAllowedToSetupEncryption) } }.let { views.notificationAreaView.render(it) From 74dfddeeea1a96678d18e105987ed8db041da22d Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Dec 2021 14:54:36 +0100 Subject: [PATCH 05/11] missing resource --- vector/src/main/res/values/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 99befb083e..17ce5426db 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -962,7 +962,8 @@ Delete unsent messages File not found You do not have permission to post to this room. - Encryption has been misconfigured. You can\'t send messages, please contact an admin to restore the room to a valid state. + Encryption has been misconfigured so you can\'t send messages. Please contact an admin to restore encryption to a valid state. + Encryption has been misconfigured so you can\'t send messages. Click to open settings. %d new message %d new messages From b10bc7000aed7311a05a4236ade7d0d4c278a579 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Dec 2021 17:17:40 +0100 Subject: [PATCH 06/11] Update room badge when e2e misconfigured --- .../android/sdk/api/crypto/RoomEncryptionTrustLevel.kt | 5 ++++- .../sdk/internal/database/mapper/RoomSummaryMapper.kt | 5 ++++- .../java/im/vector/app/core/ui/views/NotificationAreaView.kt | 2 +- .../main/java/im/vector/app/core/ui/views/ShieldImageView.kt | 5 +++++ .../room/detail/timeline/factory/EncryptionItemFactory.kt | 2 +- .../home/room/detail/timeline/item/StatusTileTimelineItem.kt | 4 +++- vector/src/main/res/values/strings.xml | 1 + 7 files changed, 19 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/RoomEncryptionTrustLevel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/RoomEncryptionTrustLevel.kt index f381ae8455..8ba99ad70b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/RoomEncryptionTrustLevel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/RoomEncryptionTrustLevel.kt @@ -27,5 +27,8 @@ enum class RoomEncryptionTrustLevel { Warning, // All devices in the room are verified -> the app should display a green shield - Trusted + Trusted, + + // e2e is active but with an unsupported algorithm + E2EWithUnsupportedAlgorithm } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index 83a57fed63..63b326096a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.database.mapper +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.room.model.RoomEncryptionAlgorithm import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -70,7 +71,9 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa isEncrypted = roomSummaryEntity.isEncrypted, encryptionEventTs = roomSummaryEntity.encryptionEventTs, breadcrumbsIndex = roomSummaryEntity.breadcrumbsIndex, - roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel, + roomEncryptionTrustLevel = if (roomSummaryEntity.isEncrypted && roomSummaryEntity.e2eAlgorithm != MXCRYPTO_ALGORITHM_MEGOLM) { + RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm + } else roomSummaryEntity.roomEncryptionTrustLevel, inviterId = roomSummaryEntity.inviterId, hasFailedSending = roomSummaryEntity.hasFailedSending, roomType = roomSummaryEntity.roomType, diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt index 0a9b0df3c4..1615e77902 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt @@ -110,7 +110,7 @@ class NotificationAreaView @JvmOverloads constructor( private fun renderUnsupportedAlgorithm(e2eState: State.UnsupportedAlgorithm) { visibility = View.VISIBLE - views.roomNotificationIcon.setImageResource(R.drawable.ic_shield_warning_small) + views.roomNotificationIcon.setImageResource(R.drawable.ic_warning_badge) val text = if (e2eState.canRestore) { R.string.room_unsupported_e2e_algorithm_as_admin } else R.string.room_unsupported_e2e_algorithm diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt index 44724f7954..ac0b4408b2 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt @@ -61,6 +61,10 @@ class ShieldImageView @JvmOverloads constructor( else R.drawable.ic_shield_trusted ) } + RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> { + contentDescription = context.getString(R.string.a11y_trust_level_trusted) + setImageResource(R.drawable.ic_warning_badge) + } } } } @@ -71,5 +75,6 @@ fun RoomEncryptionTrustLevel.toDrawableRes(): Int { RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted + RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt index 8792ce6d33..0ff786d504 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt @@ -65,7 +65,7 @@ class EncryptionItemFactory @Inject constructor( } else { title = stringProvider.getString(R.string.encryption_misconfigured) description = stringProvider.getString(R.string.encryption_unknown_algorithm_tile_description) - shield = StatusTileTimelineItem.ShieldUIState.RED + shield = StatusTileTimelineItem.ShieldUIState.ERROR } return StatusTileTimelineItem_() .attributes( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt index 6531efb82d..a3d9d3995c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt @@ -57,6 +57,7 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItem R.drawable.ic_shield_trusted ShieldUIState.BLACK -> R.drawable.ic_shield_black ShieldUIState.RED -> R.drawable.ic_shield_warning + ShieldUIState.ERROR -> R.drawable.ic_warning_badge } holder.titleView.setCompoundDrawablesWithIntrinsicBounds( @@ -98,6 +99,7 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItemDefault trust level Warning trust level Trusted trust level + Misconfigured trust level Open Emoji picker Close Emoji picker Checked From 57b78a62234b0187cf6ca6ba0574c6704e134a26 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 3 Jan 2022 09:52:42 +0100 Subject: [PATCH 07/11] code review --- .../roomprofile/RoomProfileController.kt | 24 +++++++++++++++---- vector/src/main/res/values/strings.xml | 4 ++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index d061b91205..6d1b02a2fe 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -120,15 +120,29 @@ class RoomProfileController @Inject constructor( } var encryptionMisconfigured = false - val learnMoreSubtitle = if (roomSummary.isEncrypted) { + val e2eInfoText = if (roomSummary.isEncrypted) { if (roomSummary.roomEncryptionAlgorithm is RoomEncryptionAlgorithm.SupportedAlgorithm) { - if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_subtitle else R.string.room_profile_encrypted_subtitle + stringProvider.getString( + if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_subtitle + else R.string.room_profile_encrypted_subtitle + ) } else { encryptionMisconfigured = true - if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_misconfigured_subtitle else R.string.room_profile_encrypted_misconfigured_subtitle + buildString { + append(stringProvider.getString(R.string.encryption_has_been_misconfigured)) + append(" ") + apply { + if (!data.canUpdateRoomState) { + append(stringProvider.getString(R.string.contact_admin_to_restore_encryption)) + } + } + } } } else { - if (roomSummary.isDirect) R.string.direct_room_profile_not_encrypted_subtitle else R.string.room_profile_not_encrypted_subtitle + stringProvider.getString( + if (roomSummary.isDirect) R.string.direct_room_profile_not_encrypted_subtitle + else R.string.room_profile_not_encrypted_subtitle + ) } genericFooterItem { id("e2e info") @@ -143,7 +157,7 @@ class RoomProfileController @Inject constructor( +" " } } - +host.stringProvider.getString(learnMoreSubtitle) + +e2eInfoText } ) } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index bfd9d8172a..c9adbc9d4a 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2792,8 +2792,8 @@ Messages in this room are not end-to-end encrypted. Messages here are not end-to-end encrypted. Messages in this room are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them. - Encryption has been misconfigured. Please contact an admin to restore the room to a valid state. - Encryption has been misconfigured. Please contact an admin to restore room. + Encryption has been misconfigured. + Please contact an admin to restore encryption to a valid state. Messages here are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them. Security Restore Encryption From 1a92d75a54cd3a077926247765572b526baf7923 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 7 Jan 2022 17:05:34 +0100 Subject: [PATCH 08/11] Fix bad copyright --- .../sdk/api/session/room/model/RoomEncryptionAlgorithm.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt index 542ede82ae..f681216929 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomEncryptionAlgorithm.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From aa7d284dd919300c3299aa2ac7a36d42795c5547 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 7 Jan 2022 18:11:24 +0100 Subject: [PATCH 09/11] Post rebase fix --- .../java/im/vector/app/core/ui/list/GenericFooterItem.kt | 5 +++-- .../app/features/devtools/RoomDevToolSendFormController.kt | 3 ++- .../timeline/edithistory/ViewEditHistoryEpoxyController.kt | 2 +- .../timeline/reactions/ViewReactionsEpoxyController.kt | 2 +- .../home/room/detail/widget/RoomWidgetsController.kt | 3 ++- .../app/features/reactions/EmojiSearchResultController.kt | 5 +++-- .../roommemberprofile/RoomMemberProfileController.kt | 5 +++-- .../roommemberprofile/devices/DeviceListEpoxyController.kt | 2 +- .../devices/DeviceTrustInfoEpoxyController.kt | 6 +++--- .../app/features/roomprofile/RoomProfileController.kt | 5 +++-- .../roomprofile/banned/RoomBannedMemberListController.kt | 3 ++- .../settings/joinrule/RoomJoinRuleAdvancedController.kt | 5 +++-- .../joinrule/advanced/ChooseRestrictedController.kt | 5 +++-- .../devices/DeviceVerificationInfoBottomSheetController.kt | 2 +- .../settings/devtools/AccountDataEpoxyController.kt | 4 ++-- .../app/features/settings/push/PushGateWayController.kt | 5 +++-- .../app/features/settings/push/PushRulesController.kt | 3 ++- .../settings/threepids/ThreePidsSettingsController.kt | 3 ++- .../im/vector/app/features/spaces/SpaceSummaryController.kt | 3 ++- .../features/spaces/create/SpaceAdd3pidEpoxyController.kt | 4 ++-- .../spaces/create/SpaceDefaultRoomEpoxyController.kt | 5 +++-- .../features/spaces/create/SpaceDetailEpoxyController.kt | 3 ++- .../features/spaces/manage/SpaceManageRoomsController.kt | 3 ++- 23 files changed, 51 insertions(+), 35 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt index 323c21b76b..5e3ea285a5 100644 --- a/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt +++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt @@ -24,6 +24,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.themes.ThemeUtils @@ -38,7 +39,7 @@ import im.vector.app.features.themes.ThemeUtils abstract class GenericFooterItem : VectorEpoxyModel() { @EpoxyAttribute - var text: String? = null + var text: EpoxyCharSequence? = null @EpoxyAttribute var style: ItemStyle = ItemStyle.NORMAL_TEXT @@ -56,7 +57,7 @@ abstract class GenericFooterItem : VectorEpoxyModel() override fun bind(holder: Holder) { super.bind(holder) - holder.text.setTextOrHide(text) + holder.text.setTextOrHide(text?.charSequence) holder.text.typeface = style.toTypeFace() holder.text.textSize = style.toTextSize() holder.text.gravity = if (centered) Gravity.CENTER_HORIZONTAL else Gravity.START diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt index 75fcf43292..f0a6f40208 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.devtools import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.features.form.formEditTextItem @@ -36,7 +37,7 @@ class RoomDevToolSendFormController @Inject constructor( genericFooterItem { id("topSpace") - text("") + text("".toEpoxyCharSequence()) } formEditTextItem { id("event_type") diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt index 96d139f11d..19b6b8c71a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt @@ -62,7 +62,7 @@ class ViewEditHistoryEpoxyController @Inject constructor( is Fail -> { genericFooterItem { id("failure") - text(host.stringProvider.getString(R.string.unknown_error)) + text(host.stringProvider.getString(R.string.unknown_error).toEpoxyCharSequence()) } } is Success -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt index 0cdb31a8fd..17a3ac4a5f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt @@ -49,7 +49,7 @@ class ViewReactionsEpoxyController @Inject constructor( is Fail -> { genericFooterItem { id("failure") - text(host.stringProvider.getString(R.string.unknown_error)) + text(host.stringProvider.getString(R.string.unknown_error).toEpoxyCharSequence()) } } is Success -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt index 2da3dab16a..5c2ad3799b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.widget import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericButtonItem @@ -40,7 +41,7 @@ class RoomWidgetsController @Inject constructor( if (widgets.isEmpty()) { genericFooterItem { id("empty") - text(host.stringProvider.getString(R.string.room_no_active_widgets)) + text(host.stringProvider.getString(R.string.room_no_active_widgets).toEpoxyCharSequence()) } } else { widgets.forEach { widget -> diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt index c9903e396e..f1165e44d4 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultController.kt @@ -20,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.EmojiCompatFontProvider import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import javax.inject.Inject @@ -52,13 +53,13 @@ class EmojiSearchResultController @Inject constructor( // display 'Type something to find' genericFooterItem { id("type.query.item") - text(host.stringProvider.getString(R.string.reaction_search_type_hint)) + text(host.stringProvider.getString(R.string.reaction_search_type_hint).toEpoxyCharSequence()) } } else { // Display no search Results genericFooterItem { id("no.results.item") - text(host.stringProvider.getString(R.string.no_result_placeholder)) + text(host.stringProvider.getString(R.string.no_result_placeholder).toEpoxyCharSequence()) } } } else { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index d68fabfbc1..ca022edcab 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -19,6 +19,7 @@ package im.vector.app.features.roommemberprofile import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.epoxy.profiles.buildProfileAction import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.resources.StringProvider @@ -150,7 +151,7 @@ class RoomMemberProfileController @Inject constructor( genericFooterItem { id("verify_footer") - text(host.stringProvider.getString(R.string.room_profile_encrypted_subtitle)) + text(host.stringProvider.getString(R.string.room_profile_encrypted_subtitle).toEpoxyCharSequence()) centered(false) } } @@ -171,7 +172,7 @@ class RoomMemberProfileController @Inject constructor( genericFooterItem { id("verify_footer_not_encrypted") - text(host.stringProvider.getString(R.string.room_profile_not_encrypted_subtitle)) + text(host.stringProvider.getString(R.string.room_profile_not_encrypted_subtitle).toEpoxyCharSequence()) centered(false) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt index 83a4e614ec..8b49c694c4 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt @@ -97,7 +97,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider: // Can this really happen? genericFooterItem { id("empty") - text(host.stringProvider.getString(R.string.search_no_results)) + text(host.stringProvider.getString(R.string.search_no_results).toEpoxyCharSequence()) } } else { // Build list of device with status diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt index c700842c19..8ee6967afa 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt @@ -67,12 +67,12 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi // TODO FORMAT text(host.stringProvider.getString(R.string.verification_profile_device_verified_because, data.userItem?.displayName ?: "", - data.userItem?.id ?: "")) + data.userItem?.id ?: "").toEpoxyCharSequence()) } else { // TODO what if mine text(host.stringProvider.getString(R.string.verification_profile_device_new_signing, data.userItem?.displayName ?: "", - data.userItem?.id ?: "")) + data.userItem?.id ?: "").toEpoxyCharSequence()) } } // text(stringProvider.getString(R.string.verification_profile_device_untrust_info)) @@ -98,7 +98,7 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi id("warn") centered(false) textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) - text(host.stringProvider.getString(R.string.verification_profile_device_untrust_info)) + text(host.stringProvider.getString(R.string.verification_profile_device_untrust_info).toEpoxyCharSequence()) } bottomSheetVerificationActionItem { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index 6d1b02a2fe..a1eeaf9286 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -19,6 +19,7 @@ package im.vector.app.features.roomprofile import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.epoxy.expandableTextItem import im.vector.app.core.epoxy.profiles.buildProfileAction import im.vector.app.core.epoxy.profiles.buildProfileSection @@ -107,7 +108,7 @@ class RoomProfileController @Inject constructor( data.recommendedRoomVersion != null) { genericFooterItem { id("version_warning") - text(host.stringProvider.getString(R.string.room_using_unstable_room_version, roomVersion)) + text(host.stringProvider.getString(R.string.room_using_unstable_room_version, roomVersion).toEpoxyCharSequence()) textColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) centered(false) } @@ -158,7 +159,7 @@ class RoomProfileController @Inject constructor( } } +e2eInfoText - } + }.toEpoxyCharSequence() ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt index f95d1a8c24..1e0572297b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.roomprofile.banned import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.epoxy.profiles.profileMatrixItemWithProgress @@ -53,7 +54,7 @@ class RoomBannedMemberListController @Inject constructor( genericFooterItem { id("footer") - text(quantityString) + text(quantityString.toEpoxyCharSequence()) } } else { buildProfileSection(quantityString) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAdvancedController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAdvancedController.kt index 7adfc594b7..5e963b4cbc 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAdvancedController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAdvancedController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.roomprofile.settings.joinrule import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.ItemStyle @@ -49,7 +50,7 @@ class RoomJoinRuleAdvancedController @Inject constructor( genericFooterItem { id("header") - text(host.stringProvider.getString(R.string.room_settings_room_access_title)) + text(host.stringProvider.getString(R.string.room_settings_room_access_title).toEpoxyCharSequence()) centered(false) style(ItemStyle.TITLE) textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) @@ -57,7 +58,7 @@ class RoomJoinRuleAdvancedController @Inject constructor( genericFooterItem { id("desc") - text(host.stringProvider.getString(R.string.decide_who_can_find_and_join)) + text(host.stringProvider.getString(R.string.decide_who_can_find_and_join).toEpoxyCharSequence()) centered(false) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/ChooseRestrictedController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/ChooseRestrictedController.kt index 86bfd38a46..b301b8c947 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/ChooseRestrictedController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/ChooseRestrictedController.kt @@ -22,6 +22,7 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.resources.StringProvider @@ -76,7 +77,7 @@ class ChooseRestrictedController @Inject constructor( // when no filters genericFooterItem { id("h1") - text(host.stringProvider.getString(R.string.space_you_know_that_contains_this_room)) + text(host.stringProvider.getString(R.string.space_you_know_that_contains_this_room).toEpoxyCharSequence()) centered(false) } @@ -93,7 +94,7 @@ class ChooseRestrictedController @Inject constructor( if (data.unknownRestricted.isNotEmpty()) { genericFooterItem { id("others") - text(host.stringProvider.getString(R.string.other_spaces_or_rooms_you_might_not_know)) + text(host.stringProvider.getString(R.string.other_spaces_or_rooms_you_might_not_know).toEpoxyCharSequence()) centered(false) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index b35d32f473..040ee9ab84 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -293,7 +293,7 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor( genericFooterItem { id("infoCrypto${info.deviceId}") - text(host.stringProvider.getString(R.string.settings_failed_to_get_crypto_device_info)) + text(host.stringProvider.getString(R.string.settings_failed_to_get_crypto_device_info).toEpoxyCharSequence()) } info.deviceId?.let { addGenericDeviceManageActions(data, it) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt index 77ef13c2f1..86a25bb8e9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt @@ -54,7 +54,7 @@ class AccountDataEpoxyController @Inject constructor( is Fail -> { genericFooterItem { id("fail") - text(data.accountData.error.localizedMessage) + text(data.accountData.error.localizedMessage.toEpoxyCharSequence()) } } is Success -> { @@ -62,7 +62,7 @@ class AccountDataEpoxyController @Inject constructor( if (dataList.isEmpty()) { genericFooterItem { id("noResults") - text(host.stringProvider.getString(R.string.no_result_placeholder)) + text(host.stringProvider.getString(R.string.no_result_placeholder).toEpoxyCharSequence()) } } else { dataList.forEach { accountData -> diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt index 6cb19b13c5..3daeace09f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.settings.push import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import javax.inject.Inject @@ -34,7 +35,7 @@ class PushGateWayController @Inject constructor( if (pushers.isEmpty()) { genericFooterItem { id("footer") - text(host.stringProvider.getString(R.string.settings_push_gateway_no_pushers)) + text(host.stringProvider.getString(R.string.settings_push_gateway_no_pushers).toEpoxyCharSequence()) } } else { pushers.forEach { @@ -50,7 +51,7 @@ class PushGateWayController @Inject constructor( } ?: run { genericFooterItem { id("loading") - text(host.stringProvider.getString(R.string.loading)) + text(host.stringProvider.getString(R.string.loading).toEpoxyCharSequence()) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt index c0119ed3be..ed6a31d070 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.settings.push import com.airbnb.epoxy.TypedEpoxyController import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import javax.inject.Inject @@ -38,7 +39,7 @@ class PushRulesController @Inject constructor( } ?: run { genericFooterItem { id("footer") - text(host.stringProvider.getString(R.string.settings_push_rules_no_rules)) + text(host.stringProvider.getString(R.string.settings_push_rules_no_rules).toEpoxyCharSequence()) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt index cdc40185aa..0132defb1f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt @@ -22,6 +22,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.error.ErrorFormatter @@ -86,7 +87,7 @@ class ThreePidsSettingsController @Inject constructor( is Fail -> { genericFooterItem { id("fail") - text(data.threePids.error.localizedMessage) + text(data.threePids.error.localizedMessage.toEpoxyCharSequence()) } } is Success -> { diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt index 5ffc2a0fae..e8c2c4bde6 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt @@ -19,6 +19,7 @@ package im.vector.app.features.spaces import com.airbnb.epoxy.EpoxyController import im.vector.app.R import im.vector.app.RoomGroupingMethod +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem @@ -66,7 +67,7 @@ class SpaceSummaryController @Inject constructor( if (!nonNullViewState.legacyGroups.isNullOrEmpty()) { genericFooterItem { id("legacy_space") - text(" ") + text(" ".toEpoxyCharSequence()) } genericHeaderItem { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt index 7e1902aac0..816931a7c1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt @@ -43,12 +43,12 @@ class SpaceAdd3pidEpoxyController @Inject constructor( genericFooterItem { id("info_help_header") style(ItemStyle.TITLE) - text(host.stringProvider.getString(R.string.create_spaces_invite_public_header)) + text(host.stringProvider.getString(R.string.create_spaces_invite_public_header).toEpoxyCharSequence()) textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) } genericFooterItem { id("info_help_desc") - text(host.stringProvider.getString(R.string.create_spaces_invite_public_header_desc, data.name ?: "")) + text(host.stringProvider.getString(R.string.create_spaces_invite_public_header_desc, data.name ?: "").toEpoxyCharSequence()) textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt index 15e983423f..3353e66b3c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt @@ -19,6 +19,7 @@ package im.vector.app.features.spaces.create import com.airbnb.epoxy.TypedEpoxyController import com.google.android.material.textfield.TextInputLayout import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.ItemStyle @@ -45,7 +46,7 @@ class SpaceDefaultRoomEpoxyController @Inject constructor( host.stringProvider.getString(R.string.create_spaces_room_public_header, data.name) } else { host.stringProvider.getString(R.string.create_spaces_room_private_header) - } + }.toEpoxyCharSequence() ) textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) } @@ -59,7 +60,7 @@ class SpaceDefaultRoomEpoxyController @Inject constructor( } else { R.string.create_spaces_room_private_header_desc } - ) + ).toEpoxyCharSequence() ) textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index 14b0db2cd1..a22256c3e1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -20,6 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail import im.vector.app.R import im.vector.app.core.epoxy.TextListener +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.features.form.formEditTextItem @@ -61,7 +62,7 @@ class SpaceDetailEpoxyController @Inject constructor( host.stringProvider.getString(R.string.create_spaces_details_public_header) } else { host.stringProvider.getString(R.string.create_spaces_details_private_header) - } + }.toEpoxyCharSequence() ) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt index f9dfec8f40..67c9f83498 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt @@ -21,6 +21,7 @@ import com.airbnb.epoxy.VisibilityState import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete import im.vector.app.R +import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter @@ -74,7 +75,7 @@ class SpaceManageRoomsController @Inject constructor( if (filteredResult.isEmpty()) { genericFooterItem { id("empty_result") - text(host.stringProvider.getString(R.string.no_result_placeholder)) + text(host.stringProvider.getString(R.string.no_result_placeholder).toEpoxyCharSequence()) } } else { filteredResult.forEach { childInfo -> From 6798492cc3dcde388d7b6f0c930e507617a99646 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 10 Jan 2022 09:39:52 +0100 Subject: [PATCH 10/11] Quick fix warning nullable --- .../features/settings/devtools/AccountDataEpoxyController.kt | 2 +- .../features/settings/threepids/ThreePidsSettingsController.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt index 86a25bb8e9..93cac3a0f6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataEpoxyController.kt @@ -54,7 +54,7 @@ class AccountDataEpoxyController @Inject constructor( is Fail -> { genericFooterItem { id("fail") - text(data.accountData.error.localizedMessage.toEpoxyCharSequence()) + text(data.accountData.error.localizedMessage?.toEpoxyCharSequence()) } } is Success -> { diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt index 0132defb1f..f3c0469d79 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt @@ -87,7 +87,7 @@ class ThreePidsSettingsController @Inject constructor( is Fail -> { genericFooterItem { id("fail") - text(data.threePids.error.localizedMessage.toEpoxyCharSequence()) + text(data.threePids.error.localizedMessage?.toEpoxyCharSequence()) } } is Success -> { From 60ae416b36c61703fbff70c5a5a005d96baf3f3e Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 11 Jan 2022 14:56:18 +0100 Subject: [PATCH 11/11] Split this PR db change in a separate migration --- .../sdk/internal/database/RealmSessionStoreMigration.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 9f4459b20f..1f45ac2a75 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -56,7 +56,7 @@ internal class RealmSessionStoreMigration @Inject constructor( ) : RealmMigration { companion object { - const val SESSION_STORE_SCHEMA_VERSION = 20L + const val SESSION_STORE_SCHEMA_VERSION = 21L } /** @@ -89,6 +89,7 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion <= 17) migrateTo18(realm) if (oldVersion <= 18) migrateTo19(realm) if (oldVersion <= 19) migrateTo20(realm) + if (oldVersion <= 20) migrateTo21(realm) } private fun migrateTo1(realm: DynamicRealm) { @@ -415,6 +416,10 @@ internal class RealmSessionStoreMigration @Inject constructor( chunkEntities.deleteAllFromRealm() } } + } + + private fun migrateTo21(realm: DynamicRealm) { + Timber.d("Step 20 -> 21") realm.schema.get("RoomSummaryEntity") ?.addField(RoomSummaryEntityFields.E2E_ALGORITHM, String::class.java)