From ced621d46963a11ed1a702974af1907c52f0c6ea Mon Sep 17 00:00:00 2001
From: Adam Brown <adampsbrown@gmail.com>
Date: Thu, 27 Jan 2022 11:30:53 +0000
Subject: [PATCH] refreshing the room summaries when new crypto sessions are
 recieved - matches the same flow as the timeline by starting observing in the
 ViewModel init

---
 .../sdk/api/session/crypto/CryptoService.kt   |  1 -
 .../sdk/api/session/room/RoomService.kt       |  8 ++++
 .../crypto/crosssigning/UpdateTrustWorker.kt  |  2 +-
 .../query/RoomSummaryEntityQueries.kt         |  4 ++
 .../session/room/DefaultRoomService.kt        | 21 ++++++++++
 .../room/summary/RoomSummaryUpdater.kt        | 42 ++++++++++++-------
 .../app/features/home/HomeDetailViewModel.kt  |  9 ++++
 7 files changed, 70 insertions(+), 17 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt
index 65f69e17c9..a5b442dc4a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt
@@ -140,7 +140,6 @@ interface CryptoService {
     fun getLiveCryptoDeviceInfo(userIds: List<String>): LiveData<List<CryptoDeviceInfo>>
 
     fun addNewSessionListener(newSessionListener: NewSessionListener)
-
     fun removeSessionListener(listener: NewSessionListener)
 
     fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
index aec358218b..c1c1a385b5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt
@@ -242,4 +242,12 @@ interface RoomService {
      */
     fun getFlattenRoomSummaryChildrenOfLive(spaceId: String?,
                                             memberships: List<Membership> = Membership.activeMemberships()): LiveData<List<RoomSummary>>
+
+    /**
+     * Refreshes the RoomSummary LatestPreviewContent for the given @param roomId
+     * If the roomId is null, all rooms are updated
+     *
+     * This is useful for refreshing summary content with encrypted messages after receiving new room keys
+     */
+    fun refreshJoinedRoomSummaryPreviews(roomId: String?)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
index 9325355d28..794ab04533 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt
@@ -52,7 +52,7 @@ import timber.log.Timber
 import javax.inject.Inject
 
 internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) :
-    SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
+        SessionSafeCoroutineWorker<UpdateTrustWorker.Params>(context, params, sessionManager, Params::class.java) {
 
     @JsonClass(generateAdapter = true)
     internal data class Params(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt
index d1b05a4932..8993c36a30 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt
@@ -49,6 +49,10 @@ internal fun RoomSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: Strin
     return where(realm, roomId).findFirst() ?: realm.createObject(roomId)
 }
 
+internal fun RoomSummaryEntity.Companion.getOrNull(realm: Realm, roomId: String): RoomSummaryEntity? {
+    return where(realm, roomId).findFirst()
+}
+
 internal fun RoomSummaryEntity.Companion.getDirectRooms(realm: Realm,
                                                         excludeRoomIds: Set<String>? = null): RealmResults<RoomSummaryEntity> {
     return RoomSummaryEntity.where(realm)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
index 1bc98015aa..c79c41069b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt
@@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.Transformations
 import androidx.paging.PagedList
 import com.zhuinden.monarchy.Monarchy
+import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.Room
 import org.matrix.android.sdk.api.session.room.RoomService
@@ -32,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
 import org.matrix.android.sdk.api.session.room.peeking.PeekResult
+import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
 import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.api.util.toOptional
@@ -51,6 +53,7 @@ import org.matrix.android.sdk.internal.session.room.peeking.PeekRoomTask
 import org.matrix.android.sdk.internal.session.room.peeking.ResolveRoomStateTask
 import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
 import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
+import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater
 import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
 import org.matrix.android.sdk.internal.util.fetchCopied
 import javax.inject.Inject
@@ -69,6 +72,7 @@ internal class DefaultRoomService @Inject constructor(
         private val roomSummaryDataSource: RoomSummaryDataSource,
         private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
         private val leaveRoomTask: LeaveRoomTask,
+        private val roomSummaryUpdater: RoomSummaryUpdater
 ) : RoomService {
 
     override suspend fun createRoom(createRoomParams: CreateRoomParams): String {
@@ -92,6 +96,23 @@ internal class DefaultRoomService @Inject constructor(
         return roomSummaryDataSource.getRoomSummaries(queryParams, sortOrder)
     }
 
+    override fun refreshJoinedRoomSummaryPreviews(roomId: String?) {
+        val roomSummaries = getRoomSummaries(roomSummaryQueryParams {
+            if (roomId != null) {
+                this.roomId = QueryStringValue.Equals(roomId)
+            }
+            memberships = listOf(Membership.JOIN)
+        })
+
+        if (roomSummaries.isNotEmpty()) {
+            monarchy.runTransactionSync { realm ->
+                roomSummaries.forEach {
+                    roomSummaryUpdater.refreshLatestPreviewContent(realm, it.roomId)
+                }
+            }
+        }
+    }
+
     override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams,
                                       sortOrder: RoomSortOrder): LiveData<List<RoomSummary>> {
         return roomSummaryDataSource.getRoomSummariesLive(queryParams, sortOrder)
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 c9712c5721..c9d84b1b93 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
@@ -64,7 +64,6 @@ import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataD
 import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
 import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
 import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo
-import org.matrix.android.sdk.internal.util.Normalizer
 import timber.log.Timber
 import javax.inject.Inject
 import kotlin.system.measureTimeMillis
@@ -75,8 +74,16 @@ internal class RoomSummaryUpdater @Inject constructor(
         private val roomAvatarResolver: RoomAvatarResolver,
         private val eventDecryptor: EventDecryptor,
         private val crossSigningService: DefaultCrossSigningService,
-        private val roomAccountDataDataSource: RoomAccountDataDataSource,
-        private val normalizer: Normalizer) {
+        private val roomAccountDataDataSource: RoomAccountDataDataSource
+) {
+
+    fun refreshLatestPreviewContent(realm: Realm, roomId: String) {
+        val roomSummaryEntity = RoomSummaryEntity.getOrNull(realm, roomId)
+        if (roomSummaryEntity != null) {
+            val latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
+            latestPreviewableEvent?.attemptToDecrypt()
+        }
+    }
 
     fun update(realm: Realm,
                roomId: String,
@@ -128,6 +135,7 @@ internal class RoomSummaryUpdater @Inject constructor(
         val lastActivityFromEvent = latestPreviewableEvent?.root?.originServerTs
         if (lastActivityFromEvent != null) {
             roomSummaryEntity.lastActivityTime = lastActivityFromEvent
+            latestPreviewableEvent.attemptToDecrypt()
         }
 
         roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 ||
@@ -161,18 +169,6 @@ internal class RoomSummaryUpdater @Inject constructor(
         }
         roomSummaryEntity.updateHasFailedSending()
 
-        val root = latestPreviewableEvent?.root
-        if (root?.type == EventType.ENCRYPTED && root.decryptionResultJson == null) {
-            Timber.v("Should decrypt ${latestPreviewableEvent.eventId}")
-            // mmm i want to decrypt now or is it ok to do it async?
-            tryOrNull {
-                runBlocking {
-                    eventDecryptor.decryptEvent(root.asDomain(), "")
-                }
-            }
-                    ?.let { root.setDecryptionResult(it) }
-        }
-
         if (updateMembers) {
             val otherRoomMembers = RoomMemberHelper(realm, roomId)
                     .queryActiveRoomMembersEvent()
@@ -189,6 +185,22 @@ internal class RoomSummaryUpdater @Inject constructor(
         }
     }
 
+    private fun TimelineEventEntity.attemptToDecrypt() {
+        when (val root = this.root) {
+            null -> {
+                Timber.v("Decryption skipped due to missing root event $eventId")
+            }
+            else -> {
+                if (root.type == EventType.ENCRYPTED && root.decryptionResultJson == null) {
+                    Timber.v("Should decrypt $eventId")
+                    tryOrNull {
+                        runBlocking { eventDecryptor.decryptEvent(root.asDomain(), "") }
+                    }?.let { root.setDecryptionResult(it) }
+                }
+            }
+        }
+    }
+
     private fun RoomSummaryEntity.updateHasFailedSending() {
         hasFailedSending = TimelineEventEntity.findAllInRoomWithSendStates(realm, roomId, SendState.HAS_FAILED_STATES).isNotEmpty()
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt
index 01d91f3edc..e70e7b8acb 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt
@@ -54,6 +54,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
 import org.matrix.android.sdk.api.util.toMatrixItem
 import org.matrix.android.sdk.flow.flow
+import org.matrix.android.sdk.internal.crypto.NewSessionListener
 import timber.log.Timber
 
 /**
@@ -88,9 +89,16 @@ class HomeDetailViewModel @AssistedInject constructor(
         }
     }
 
+    private val refreshRoomSummariesOnCryptoSessionChange = object : NewSessionListener {
+        override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
+            session.refreshJoinedRoomSummaryPreviews(roomId)
+        }
+    }
+
     init {
         observeSyncState()
         observeRoomGroupingMethod()
+        session.cryptoService().addNewSessionListener(refreshRoomSummariesOnCryptoSessionChange)
         observeRoomSummaries()
         updatePstnSupportFlag()
         observeDataStore()
@@ -150,6 +158,7 @@ class HomeDetailViewModel @AssistedInject constructor(
     override fun onCleared() {
         super.onCleared()
         callManager.removeProtocolsCheckerListener(this)
+        session.cryptoService().removeSessionListener(refreshRoomSummariesOnCryptoSessionChange)
     }
 
     override fun onPSTNSupportUpdated() {