From 637f2476e0dca6d76c0922b3aa2f07d5e853b91a Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 12 Dec 2022 11:13:50 +0100 Subject: [PATCH 01/65] Adding changelog entry --- changelog.d/7767.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7767.feature diff --git a/changelog.d/7767.feature b/changelog.d/7767.feature new file mode 100644 index 0000000000..c4386b5e07 --- /dev/null +++ b/changelog.d/7767.feature @@ -0,0 +1 @@ +[Poll] When a poll is ended, use /relations API to ensure poll results are correct From 8c88140b3cc18efdc3fd497e859bdf6b6564b9c8 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 12 Dec 2022 11:40:36 +0100 Subject: [PATCH 02/65] Updating Room API to clarify usage --- .../matrix/android/sdk/internal/session/room/RoomAPI.kt | 9 ++++----- .../session/room/relation/FetchEditHistoryTask.kt | 2 +- .../room/relation/threads/FetchThreadTimelineTask.kt | 6 ++++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 4e55b2c40a..f69ee4dc37 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.room import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomStrippedState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams @@ -250,7 +249,7 @@ internal interface RoomAPI { * @param limit max number of Event to retrieve */ @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}/{eventType}") - suspend fun getRelations( + suspend fun getRelationsWithEventType( @Path("roomId") roomId: String, @Path("eventId") eventId: String, @Path("relationType") relationType: String, @@ -261,7 +260,7 @@ internal interface RoomAPI { ): RelationsResponse /** - * Paginate relations for thread events based in normal topological order. + * Paginate relations for events based in normal topological order. * * @param roomId the room Id * @param eventId the event Id @@ -271,10 +270,10 @@ internal interface RoomAPI { * @param limit max number of Event to retrieve */ @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}") - suspend fun getThreadsRelations( + suspend fun getRelations( @Path("roomId") roomId: String, @Path("eventId") eventId: String, - @Path("relationType") relationType: String = RelationType.THREAD, + @Path("relationType") relationType: String, @Query("from") from: String? = null, @Query("to") to: String? = null, @Query("limit") limit: Int? = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt index 93c7f143fd..50439f51eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt @@ -43,7 +43,7 @@ internal class DefaultFetchEditHistoryTask @Inject constructor( override suspend fun execute(params: FetchEditHistoryTask.Params): List { val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId) val response = executeRequest(globalErrorReceiver) { - roomAPI.getRelations( + roomAPI.getRelationsWithEventType( roomId = params.roomId, eventId = params.eventId, relationType = RelationType.REPLACE, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt index 4cf6445920..1e9a785c80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.DefaultCryptoService @@ -102,11 +103,12 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor( override suspend fun execute(params: FetchThreadTimelineTask.Params): Result { val response = executeRequest(globalErrorReceiver) { - roomAPI.getThreadsRelations( + roomAPI.getRelations( roomId = params.roomId, eventId = params.rootThreadEventId, + relationType = RelationType.THREAD, from = params.from, - limit = params.limit + limit = params.limit, ) } From 8b7c8e33519f22e287be3c251edbffb9159b6f9c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 11:26:17 +0100 Subject: [PATCH 03/65] Task to ensure aggregation of all poll responses when receiving ending poll event --- .../sdk/api/session/events/model/Event.kt | 8 +- .../sdk/internal/session/room/RoomModule.kt | 5 + .../poll/DefaultPollAggregationProcessor.kt | 29 +++- .../poll/FetchPollResponseEventsTask.kt | 130 ++++++++++++++++++ 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 40ce6ecb5c..9b5f4ac19f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -388,7 +388,13 @@ fun Event.isLocationMessage(): Boolean { } } -fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START.values || getClearType() in EventType.POLL_END.values +fun Event.isPoll(): Boolean = isPollStart() || isPollEnd() + +fun Event.isPollStart(): Boolean = getClearType() in EventType.POLL_START.values + +fun Event.isPollResponse(): Boolean = getClearType() in EventType.POLL_RESPONSE.values + +fun Event.isPollEnd(): Boolean = getClearType() in EventType.POLL_END.values fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 1475b67276..c28d24995f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -99,6 +99,8 @@ import org.matrix.android.sdk.internal.session.room.relation.DefaultUpdateQuickR import org.matrix.android.sdk.internal.session.room.relation.FetchEditHistoryTask import org.matrix.android.sdk.internal.session.room.relation.FindReactionEventForUndoTask import org.matrix.android.sdk.internal.session.room.relation.UpdateQuickReactionTask +import org.matrix.android.sdk.internal.session.room.relation.poll.DefaultFetchPollResponseEventsTask +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetchThreadSummariesTask import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetchThreadTimelineTask import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask @@ -354,4 +356,7 @@ internal abstract class RoomModule { @Binds abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask + + @Binds + abstract fun bindFetchPollResponseEventsTask(task: DefaultFetchPollResponseEventsTask): FetchPollResponseEventsTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt index 455ccabbc6..163d8e5e81 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll import io.realm.Realm +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event @@ -40,9 +41,14 @@ import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSumm import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask +import org.matrix.android.sdk.internal.task.TaskExecutor import javax.inject.Inject -class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationProcessor { +internal class DefaultPollAggregationProcessor @Inject constructor( + private val taskExecutor: TaskExecutor, + private val fetchPollResponseEventsTask: FetchPollResponseEventsTask, +) : PollAggregationProcessor { override fun handlePollStartEvent(realm: Realm, event: Event): Boolean { val content = event.getClearContent()?.toModel() @@ -174,6 +180,10 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro aggregatedPollSummaryEntity.sourceEvents.add(event.eventId) } + if (!isLocalEcho) { + ensurePollIsFullyAggregated(roomId, pollEventId) + } + return true } @@ -200,4 +210,21 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro eventAnnotationsSummaryEntity.pollResponseSummary = it } } + + // TODO add unit tests + /** + * Check that all related votes to a given poll are all retrieved and aggregated. + */ + private fun ensurePollIsFullyAggregated( + roomId: String, + pollEventId: String + ) { + taskExecutor.executorScope.launch { + val params = FetchPollResponseEventsTask.Params( + roomId = roomId, + startPollEventId = pollEventId, + ) + fetchPollResponseEventsTask.execute(params) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt new file mode 100644 index 0000000000..781a0a4d55 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2022 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.internal.session.room.relation.poll + +import com.zhuinden.monarchy.Monarchy +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.RelationType +import org.matrix.android.sdk.api.session.events.model.isPollResponse +import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.crypto.EventDecryptor +import org.matrix.android.sdk.internal.database.mapper.toEntity +import org.matrix.android.sdk.internal.database.model.EventEntity +import org.matrix.android.sdk.internal.database.model.EventInsertType +import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore +import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse +import org.matrix.android.sdk.internal.task.Task +import org.matrix.android.sdk.internal.util.awaitTransaction +import org.matrix.android.sdk.internal.util.time.Clock +import javax.inject.Inject + +private const val FETCH_RELATED_EVENTS_LIMIT = 50 + +/** + * Task to fetch all the vote events to ensure full aggregation for a given poll. + */ +internal interface FetchPollResponseEventsTask : Task> { + data class Params( + val roomId: String, + val startPollEventId: String, + ) +} + +// TODO add unit tests +internal class DefaultFetchPollResponseEventsTask @Inject constructor( + private val roomAPI: RoomAPI, + private val globalErrorReceiver: GlobalErrorReceiver, + @SessionDatabase private val monarchy: Monarchy, + private val clock: Clock, + private val eventDecryptor: EventDecryptor, +) : FetchPollResponseEventsTask { + + override suspend fun execute(params: FetchPollResponseEventsTask.Params): Result = runCatching { + var nextBatch: String? = fetchAndProcessRelatedEventsFrom(params) + + while (nextBatch?.isNotEmpty() == true) { + nextBatch = fetchAndProcessRelatedEventsFrom(params, from = nextBatch) + } + } + + private suspend fun fetchAndProcessRelatedEventsFrom(params: FetchPollResponseEventsTask.Params, from: String? = null): String? { + val response = getRelatedEvents(params, from) + + val filteredEvents = response.chunks + .map { decryptEventIfNeeded(it) } + .filter { it.isPollResponse() } + + addMissingEventsInDB(params.roomId, filteredEvents) + + return response.nextBatch + } + + private suspend fun getRelatedEvents(params: FetchPollResponseEventsTask.Params, from: String? = null): RelationsResponse { + return executeRequest(globalErrorReceiver, canRetry = true) { + roomAPI.getRelations( + roomId = params.roomId, + eventId = params.startPollEventId, + relationType = RelationType.REFERENCE, + from = from, + limit = FETCH_RELATED_EVENTS_LIMIT, + ) + } + } + + private suspend fun addMissingEventsInDB(roomId: String, events: List) { + monarchy.awaitTransaction { realm -> + val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() } + val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } + + events.filterNot { it.eventId in existingIds } + .map { + val ageLocalTs = clock.epochMillis() - (it.unsignedData?.age ?: 0) + it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = ageLocalTs) + } + .forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) } + } + } + + private suspend fun decryptEventIfNeeded(event: Event): Event { + // TODO move into a reusable task + if (event.isEncrypted()) { + tryOrNull(message = "Unable to decrypt the event") { + eventDecryptor.decryptEvent(event, "") + } + ?.let { result -> + event.mxDecryptionResult = OlmDecryptionResult( + payload = result.clearEvent, + senderKey = result.senderCurve25519Key, + keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, + forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, + isSafe = result.isSafe + ) + } + } + + event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) + + return event + } +} From 9338ec9805925f23ce4a065cb0c57ca545bb6545 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 11:49:41 +0100 Subject: [PATCH 04/65] Mutualizing decryption of event --- .../sdk/internal/crypto/EventDecryptor.kt | 23 +++++++++++++++++++ .../poll/FetchPollResponseEventsTask.kt | 16 +------------ .../session/room/timeline/GetEventTask.kt | 13 +---------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index bc3309132a..c9eabeab48 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -24,10 +24,12 @@ import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_OLM +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap +import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.content.OlmEventContent @@ -85,6 +87,27 @@ internal class EventDecryptor @Inject constructor( return internalDecryptEvent(event, timeline) } + /** + * Decrypt an event and save the result in the given event. + * + * @param event the raw event. + * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. + */ + suspend fun decryptEventAndSaveResult(event: Event, timeline: String) { + tryOrNull(message = "Unable to decrypt the event") { + decryptEvent(event, timeline) + } + ?.let { result -> + event.mxDecryptionResult = OlmDecryptionResult( + payload = result.clearEvent, + senderKey = result.senderCurve25519Key, + keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, + forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, + isSafe = result.isSafe + ) + } + } + /** * Decrypt an event asynchronously. * diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt index 781a0a4d55..3cb8fc5540 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -17,8 +17,6 @@ package org.matrix.android.sdk.internal.session.room.relation.poll import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.isPollResponse @@ -107,20 +105,8 @@ internal class DefaultFetchPollResponseEventsTask @Inject constructor( } private suspend fun decryptEventIfNeeded(event: Event): Event { - // TODO move into a reusable task if (event.isEncrypted()) { - tryOrNull(message = "Unable to decrypt the event") { - eventDecryptor.decryptEvent(event, "") - } - ?.let { result -> - event.mxDecryptionResult = OlmDecryptionResult( - payload = result.clearEvent, - senderKey = result.senderCurve25519Key, - keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, - forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, - isSafe = result.isSafe - ) - } + eventDecryptor.decryptEventAndSaveResult(event, timeline = "") } event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt index e0751865ad..c19272f08a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt @@ -48,18 +48,7 @@ internal class DefaultGetEventTask @Inject constructor( // Try to decrypt the Event if (event.isEncrypted()) { - tryOrNull(message = "Unable to decrypt the event") { - eventDecryptor.decryptEvent(event, "") - } - ?.let { result -> - event.mxDecryptionResult = OlmDecryptionResult( - payload = result.clearEvent, - senderKey = result.senderCurve25519Key, - keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, - forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, - isSafe = result.isSafe - ) - } + eventDecryptor.decryptEventAndSaveResult(event, timeline = "") } event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) From 644803dcf3615c738d6f6ad77f39cb81a6cc4166 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 14:13:49 +0100 Subject: [PATCH 05/65] Adding unit test on aggregation processor --- .../poll/DefaultPollAggregationProcessor.kt | 1 - .../DefaultPollAggregationProcessorTest.kt | 38 ++++++++++++++++++- .../fakes/FakeFetchPollResponseEventsTask.kt | 22 +++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt index 163d8e5e81..a424becbd6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt @@ -211,7 +211,6 @@ internal class DefaultPollAggregationProcessor @Inject constructor( } } - // TODO add unit tests /** * Check that all related votes to a given poll are all retrieved and aggregated. */ diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt index c1fd615e25..6c2fbacd82 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt @@ -16,9 +16,13 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.realm.RealmList +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeTrue import org.junit.Before @@ -34,6 +38,7 @@ import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSumm import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.AN_EVENT_ID import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.AN_INVALID_POLL_RESPONSE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_BROKEN_POLL_REPLACE_EVENT +import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_END_CONTENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_END_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REFERENCE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REPLACE_EVENT @@ -43,13 +48,22 @@ import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_ROOM_ID import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_TIMELINE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_USER_ID_1 +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask +import org.matrix.android.sdk.test.fakes.FakeFetchPollResponseEventsTask import org.matrix.android.sdk.test.fakes.FakeRealm +import org.matrix.android.sdk.test.fakes.FakeTaskExecutor import org.matrix.android.sdk.test.fakes.givenEqualTo import org.matrix.android.sdk.test.fakes.givenFindFirst +@OptIn(ExperimentalCoroutinesApi::class) class DefaultPollAggregationProcessorTest { - private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor() + private val fakeTaskExecutor = FakeTaskExecutor() + private val fakeFetchPollResponseEventsTask = FakeFetchPollResponseEventsTask() + private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor( + taskExecutor = fakeTaskExecutor.instance, + fetchPollResponseEventsTask = fakeFetchPollResponseEventsTask + ) private val realm = FakeRealm() private val session = mockk() @@ -135,6 +149,28 @@ class DefaultPollAggregationProcessorTest { pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse() } + @Test + fun `given a non local echo poll end event, when is processed, then ensure to aggregate all poll responses`() = runTest { + // Given + every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity() + val powerLevelsHelper = mockRedactionPowerLevels("another-sender-id", true) + val event = A_POLL_END_EVENT.copy(senderId = "another-sender-id") + every { fakeTaskExecutor.instance.executorScope } returns this + val expectedParams = FetchPollResponseEventsTask.Params( + roomId = A_POLL_END_EVENT.roomId.orEmpty(), + startPollEventId = A_POLL_END_CONTENT.relatesTo?.eventId.orEmpty(), + ) + + // When + pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event) + advanceUntilIdle() + + // Then + coVerify { + fakeFetchPollResponseEventsTask.execute(expectedParams) + } + } + private fun mockEventAnnotationsSummaryEntity() { realm.givenWhere() .givenFindFirst(EventAnnotationsSummaryEntity()) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt new file mode 100644 index 0000000000..575dd4f949 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 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.test.fakes + +import io.mockk.mockk +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask + +class FakeFetchPollResponseEventsTask : FetchPollResponseEventsTask by mockk(relaxed = true) From bd7b6d6495b1192fbf81ae0ac797354d3650620b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 16:33:27 +0100 Subject: [PATCH 06/65] Adding unit test on task to fetch the poll response events --- .../poll/FetchPollResponseEventsTask.kt | 24 +-- .../DefaultFetchPollResponseEventsTaskTest.kt | 163 ++++++++++++++++++ .../sdk/test/fakes/FakeEventDecryptor.kt | 35 ++++ .../android/sdk/test/fakes/FakeRealm.kt | 8 + .../android/sdk/test/fakes/FakeRoomApi.kt | 61 +++++++ 5 files changed, 280 insertions(+), 11 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt index 3cb8fc5540..3e70da395d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2022 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. @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.relation.poll +import androidx.annotation.VisibleForTesting import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.RelationType @@ -37,7 +38,8 @@ import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.time.Clock import javax.inject.Inject -private const val FETCH_RELATED_EVENTS_LIMIT = 50 +@VisibleForTesting +const val FETCH_RELATED_EVENTS_LIMIT = 50 /** * Task to fetch all the vote events to ensure full aggregation for a given poll. @@ -49,7 +51,6 @@ internal interface FetchPollResponseEventsTask : Task) { monarchy.awaitTransaction { realm -> val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() } - val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } + if(eventIdsToCheck.isNotEmpty()) { + val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } - events.filterNot { it.eventId in existingIds } - .map { - val ageLocalTs = clock.epochMillis() - (it.unsignedData?.age ?: 0) - it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = ageLocalTs) - } - .forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) } + events.filterNot { it.eventId in existingIds } + .map { it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = computeLocalTs(it)) } + .forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) } + } } } @@ -109,8 +109,10 @@ internal class DefaultFetchPollResponseEventsTask @Inject constructor( eventDecryptor.decryptEventAndSaveResult(event, timeline = "") } - event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) + event.ageLocalTs = computeLocalTs(event) return event } + + private fun computeLocalTs(event: Event) = clock.epochMillis() - (event.unsignedData?.age ?: 0) } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt new file mode 100644 index 0000000000..8d50bac38f --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 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. + * 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.internal.session.room.relation.poll + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.RelationType +import org.matrix.android.sdk.api.session.events.model.isPollResponse +import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.database.mapper.toEntity +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.EventInsertType +import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore +import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse +import org.matrix.android.sdk.test.fakes.FakeClock +import org.matrix.android.sdk.test.fakes.FakeEventDecryptor +import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver +import org.matrix.android.sdk.test.fakes.FakeMonarchy +import org.matrix.android.sdk.test.fakes.FakeRoomApi +import org.matrix.android.sdk.test.fakes.givenFindAll +import org.matrix.android.sdk.test.fakes.givenIn + +@OptIn(ExperimentalCoroutinesApi::class) +internal class DefaultFetchPollResponseEventsTaskTest { + + private val fakeRoomAPI = FakeRoomApi() + private val fakeGlobalErrorReceiver = FakeGlobalErrorReceiver() + private val fakeMonarchy = FakeMonarchy() + private val fakeClock = FakeClock() + private val fakeEventDecryptor = FakeEventDecryptor() + + private val defaultFetchPollResponseEventsTask = DefaultFetchPollResponseEventsTask( + roomAPI = fakeRoomAPI.instance, + globalErrorReceiver = fakeGlobalErrorReceiver, + monarchy = fakeMonarchy.instance, + clock = fakeClock, + eventDecryptor = fakeEventDecryptor.instance, + ) + + @Before + fun setup() { + mockkStatic("org.matrix.android.sdk.api.session.events.model.EventKt") + mockkStatic("org.matrix.android.sdk.internal.database.mapper.EventMapperKt") + mockkStatic("org.matrix.android.sdk.internal.database.query.EventEntityQueriesKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given a room and a poll when execute then fetch related events and store them in local if needed`() = runTest { + // Given + val aRoomId = "roomId" + val aPollEventId = "eventId" + val params = givenTaskParams(roomId = aRoomId, eventId = aPollEventId) + val aNextBatchToken = "nextBatch" + val anEventId1 = "eventId1" + val anEventId2 = "eventId2" + val anEventId3 = "eventId3" + val anEventId4 = "eventId4" + val event1 = givenAnEvent(eventId = anEventId1, isPollResponse = true, isEncrypted = true) + val event2 = givenAnEvent(eventId = anEventId2, isPollResponse = true, isEncrypted = true) + val event3 = givenAnEvent(eventId = anEventId3, isPollResponse = false, isEncrypted = false) + val event4 = givenAnEvent(eventId = anEventId4, isPollResponse = false, isEncrypted = false) + val firstEvents = listOf(event1, event2) + val secondEvents = listOf(event3, event4) + val firstResponse = givenARelationsResponse(events = firstEvents, nextBatch = aNextBatchToken) + fakeRoomAPI.givenGetRelationsReturns(from = null, relationsResponse = firstResponse) + val secondResponse = givenARelationsResponse(events = secondEvents, nextBatch = null) + fakeRoomAPI.givenGetRelationsReturns(from = aNextBatchToken, relationsResponse = secondResponse) + fakeEventDecryptor.givenDecryptEventAndSaveResultSuccess(event1) + fakeEventDecryptor.givenDecryptEventAndSaveResultSuccess(event2) + fakeClock.givenEpoch(123) + givenExistingEventEntities(eventIdsToCheck = listOf(anEventId1, anEventId2), existingIds = listOf(anEventId1)) + val eventEntityToSave = EventEntity(eventId = anEventId2) + every { event2.toEntity(any(), any(), any()) } returns eventEntityToSave + every { eventEntityToSave.copyToRealmOrIgnore(any(), any()) } returns eventEntityToSave + + // When + defaultFetchPollResponseEventsTask.execute(params) + + // Then + fakeRoomAPI.verifyGetRelations( + roomId = params.roomId, + eventId = params.startPollEventId, + relationType = RelationType.REFERENCE, + from = null, + limit = FETCH_RELATED_EVENTS_LIMIT + ) + fakeRoomAPI.verifyGetRelations( + roomId = params.roomId, + eventId = params.startPollEventId, + relationType = RelationType.REFERENCE, + from = aNextBatchToken, + limit = FETCH_RELATED_EVENTS_LIMIT + ) + fakeEventDecryptor.verifyDecryptEventAndSaveResult(event1, timeline = "") + fakeEventDecryptor.verifyDecryptEventAndSaveResult(event2, timeline = "") + // Check we save in DB the event2 which is a non stored poll response + verify { + event2.toEntity(aRoomId, SendState.SYNCED, any()) + eventEntityToSave.copyToRealmOrIgnore(fakeMonarchy.fakeRealm.instance, EventInsertType.PAGINATION) + } + } + + private fun givenTaskParams(roomId: String, eventId: String) = FetchPollResponseEventsTask.Params( + roomId = roomId, + startPollEventId = eventId, + ) + + private fun givenARelationsResponse(events: List, nextBatch: String?): RelationsResponse { + return RelationsResponse( + chunks = events, + nextBatch = nextBatch, + prevBatch = null, + ) + } + + private fun givenAnEvent( + eventId: String, + isPollResponse: Boolean, + isEncrypted: Boolean, + ): Event { + val event = mockk(relaxed = true) + every { event.eventId } returns eventId + every { event.isPollResponse() } returns isPollResponse + every { event.isEncrypted() } returns isEncrypted + return event + } + + private fun givenExistingEventEntities(eventIdsToCheck: List, existingIds: List) { + val eventEntities = existingIds.map { EventEntity(eventId = it) } + fakeMonarchy.givenWhere() + .givenIn(EventEntityFields.EVENT_ID, eventIdsToCheck) + .givenFindAll(eventEntities) + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt new file mode 100644 index 0000000000..f2b62ad3ba --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 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. + * 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.test.fakes + +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.internal.crypto.EventDecryptor + +internal class FakeEventDecryptor { + val instance: EventDecryptor = mockk() + + fun givenDecryptEventAndSaveResultSuccess(event: Event) { + coJustRun { instance.decryptEventAndSaveResult(event, any()) } + } + + fun verifyDecryptEventAndSaveResult(event: Event, timeline: String) { + coVerify { instance.decryptEventAndSaveResult(event, timeline) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt index afdcf111f8..ba124a86aa 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt @@ -109,6 +109,14 @@ inline fun RealmQuery.givenLessThan( return this } +inline fun RealmQuery.givenIn( + fieldName: String, + values: List, +): RealmQuery { + every { `in`(fieldName, values.toTypedArray()) } returns this + return this +} + /** * Should be called on a mocked RealmObject and not on a real RealmObject so that the underlying final method is mocked. */ diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt new file mode 100644 index 0000000000..68dbbe7ea6 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 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. + * 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.test.fakes + +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse + +internal class FakeRoomApi { + + val instance: RoomAPI = mockk() + + fun givenGetRelationsReturns( + from: String?, + relationsResponse: RelationsResponse, + ) { + coEvery { + instance.getRelations( + roomId = any(), + eventId = any(), + relationType = any(), + from = from, + limit = any() + ) + } returns relationsResponse + } + + fun verifyGetRelations( + roomId: String, + eventId: String, + relationType: String, + from: String?, + limit: Int, + ) { + coVerify { + instance.getRelations( + roomId = roomId, + eventId = eventId, + relationType = relationType, + from = from, + limit = limit + ) + } + } +} From 66abda63ee35bbba6a62f88206dcd3c36737b9c7 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 16:35:34 +0100 Subject: [PATCH 07/65] Removing unused imports --- .../android/sdk/internal/session/room/timeline/GetEventTask.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt index c19272f08a..3707205aef 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt @@ -16,8 +16,6 @@ package org.matrix.android.sdk.internal.session.room.timeline -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.internal.crypto.EventDecryptor import org.matrix.android.sdk.internal.network.GlobalErrorReceiver From dd13e1cb6d8a46556f2dd20d881a90a693ee95e0 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 17:02:09 +0100 Subject: [PATCH 08/65] Fixing Copyright in SDK --- .../android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt index 575dd4f949..cb75d8b708 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2022 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 4e4f72f241bc097c1e84e79747b508c1f0f63706 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 17:37:45 +0100 Subject: [PATCH 09/65] Fixing code styling issues --- .../session/room/relation/poll/FetchPollResponseEventsTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt index 3e70da395d..e7dd8c57eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -94,7 +94,7 @@ internal class DefaultFetchPollResponseEventsTask @Inject constructor( private suspend fun addMissingEventsInDB(roomId: String, events: List) { monarchy.awaitTransaction { realm -> val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() } - if(eventIdsToCheck.isNotEmpty()) { + if (eventIdsToCheck.isNotEmpty()) { val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } events.filterNot { it.eventId in existingIds } From e5663ec1c3274d3e0e827414ca2d57711ee25c8d Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 09:45:17 +0100 Subject: [PATCH 10/65] Fixing unit tests --- .../DefaultPollAggregationProcessorTest.kt | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt index 6c2fbacd82..0888d82907 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt @@ -128,16 +128,28 @@ class DefaultPollAggregationProcessorTest { } @Test - fun `given a poll end event, when processing, then is processed and return true`() { + fun `given a poll end event, when processing, then is processed and return true`() = runTest { + // Given every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity() + every { fakeTaskExecutor.instance.executorScope } returns this + + // When val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, true) + + // Then pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue() } @Test - fun `given a poll end event for my own poll without enough redaction power level, when processing, then is processed and returns true`() { + fun `given a poll end event for my own poll without enough redaction power level, when processing, then is processed and returns true`() = runTest { + // Given every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity() + every { fakeTaskExecutor.instance.executorScope } returns this + + // When val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, false) + + // Then pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue() } @@ -157,8 +169,8 @@ class DefaultPollAggregationProcessorTest { val event = A_POLL_END_EVENT.copy(senderId = "another-sender-id") every { fakeTaskExecutor.instance.executorScope } returns this val expectedParams = FetchPollResponseEventsTask.Params( - roomId = A_POLL_END_EVENT.roomId.orEmpty(), - startPollEventId = A_POLL_END_CONTENT.relatesTo?.eventId.orEmpty(), + roomId = A_POLL_END_EVENT.roomId.orEmpty(), + startPollEventId = A_POLL_END_CONTENT.relatesTo?.eventId.orEmpty(), ) // When From 8c49609aa635a0bb0e73e8d0dad82b2e3e5e41dd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 15 Dec 2022 10:20:26 +0100 Subject: [PATCH 11/65] version++ --- matrix-sdk-android/build.gradle | 2 +- vector-app/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 4be8d55614..4558f4e8b5 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -62,7 +62,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.5.12\"" + buildConfigField "String", "SDK_VERSION", "\"1.5.14\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\"" diff --git a/vector-app/build.gradle b/vector-app/build.gradle index 1df5608871..7d1073764d 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -37,7 +37,7 @@ ext.versionMinor = 5 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 12 +ext.versionPatch = 14 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From cf98963cdbfc5173bf34a996d188eb4c0f48e9eb Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 10:41:44 +0100 Subject: [PATCH 12/65] Adding changelog entry --- changelog.d/7784.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7784.bugfix diff --git a/changelog.d/7784.bugfix b/changelog.d/7784.bugfix new file mode 100644 index 0000000000..107da01877 --- /dev/null +++ b/changelog.d/7784.bugfix @@ -0,0 +1 @@ +[Session manager] Other sessions list: filter option is displayed when selection mode is enabled From dcb8aea29252481ae183926013668427405e4fc3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 11:02:59 +0100 Subject: [PATCH 13/65] Hiding the filter icon in top bar when in selection mode --- .../devices/v2/othersessions/OtherSessionsFragment.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt index 87330b087a..be87645ea6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt @@ -225,6 +225,7 @@ class OtherSessionsFragment : override fun invalidate() = withState(viewModel) { state -> updateLoading(state.isLoading) + updateFilterView(state.isSelectModeEnabled) if (state.devices is Success) { val devices = state.devices.invoke() renderDevices(devices, state.currentFilter, state.isShowingIpAddress) @@ -240,6 +241,10 @@ class OtherSessionsFragment : } } + private fun updateFilterView(isSelectModeEnabled: Boolean) { + views.otherSessionsFilterFrameLayout.isVisible = isSelectModeEnabled.not() + } + private fun updateToolbar(devices: List, isSelectModeEnabled: Boolean) { invalidateOptionsMenu() val title = if (isSelectModeEnabled) { From 9736a8f5710e3304029e13dea3fbe5a8e5c51fe7 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 11:26:12 +0100 Subject: [PATCH 14/65] Adding changelog entry --- changelog.d/7786.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7786.bugfix diff --git a/changelog.d/7786.bugfix b/changelog.d/7786.bugfix new file mode 100644 index 0000000000..60a4a324d4 --- /dev/null +++ b/changelog.d/7786.bugfix @@ -0,0 +1 @@ +[Session manager] Other sessions: Filter bottom sheet cut in landscape mode From cc33c008ba20fc93f53edfe49b0093a8332cd203 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 15 Dec 2022 15:50:02 +0300 Subject: [PATCH 15/65] Automatically show keyboard after learn more bottom sheet is dismissed. --- .../v2/more/SessionLearnMoreBottomSheet.kt | 11 ++++++++++- .../devices/v2/rename/RenameSessionFragment.kt | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/more/SessionLearnMoreBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/more/SessionLearnMoreBottomSheet.kt index 22ca06eb1e..502d9abca3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/more/SessionLearnMoreBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/more/SessionLearnMoreBottomSheet.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices.v2.more +import android.content.DialogInterface import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -42,6 +43,8 @@ class SessionLearnMoreBottomSheet : VectorBaseBottomSheetDialogFragment Unit)? = null + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetSessionLearnMoreBinding { return BottomSheetSessionLearnMoreBinding.inflate(inflater, container, false) } @@ -57,6 +60,11 @@ class SessionLearnMoreBottomSheet : VectorBaseBottomSheetDialogFragment super.invalidate() views.bottomSheetSessionLearnMoreTitle.text = viewState.title @@ -65,11 +73,12 @@ class SessionLearnMoreBottomSheet : VectorBaseBottomSheetDialogFragment viewModel.handle(RenameSessionAction.EditLocally(text.toString())) } } + private fun showKeyboard() { + views.renameSessionEditText.viewTreeObserver.addOnWindowFocusChangeListener { hasFocus -> + if (hasFocus) { + views.renameSessionEditText.showKeyboard(andRequestFocus = true) + } + } + } + private fun initSaveButton() { views.renameSessionSave.debouncedClicks { viewModel.handle(RenameSessionAction.SaveModifications) @@ -89,7 +97,13 @@ class RenameSessionFragment : title = getString(R.string.device_manager_learn_more_session_rename_title), description = getString(R.string.device_manager_learn_more_session_rename), ) - SessionLearnMoreBottomSheet.show(childFragmentManager, args) + SessionLearnMoreBottomSheet + .show(childFragmentManager, args) + .apply { + onDismiss = { + showKeyboard() + } + } } private fun observeViewEvents() { From 6d40bd157fb7000b012b31602a9fe27881844ddf Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 15 Dec 2022 16:00:03 +0300 Subject: [PATCH 16/65] Add changelog. --- changelog.d/7790.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7790.bugfix diff --git a/changelog.d/7790.bugfix b/changelog.d/7790.bugfix new file mode 100644 index 0000000000..7390f92b32 --- /dev/null +++ b/changelog.d/7790.bugfix @@ -0,0 +1 @@ +Automatically show keyboard after learn more bottom sheet is dismissed From a86f2e03cca9ecbeb6d688dcaa1976bd8c6ea5c9 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 14:14:48 +0100 Subject: [PATCH 17/65] Make the radiogroup scrollable to better support landscape on small devices --- .../bottom_sheet_device_manager_filter.xml | 120 +++++++++--------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml b/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml index a7987e70b5..56421532b5 100644 --- a/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml +++ b/vector/src/main/res/layout/bottom_sheet_device_manager_filter.xml @@ -20,73 +20,79 @@ android:layout_marginTop="12dp" android:text="@string/device_manager_filter_bottom_sheet_title" /> - + android:layout_height="match_parent"> - - - + android:layoutDirection="rtl" + android:showDividers="none"> - + - + - + - + - + - + + + + + + + From 9178426ec19564e8dec8c0e90f3fe7dc4ead57f4 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 14:31:46 +0100 Subject: [PATCH 18/65] Adding changelog entry --- changelog.d/7792.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7792.bugfix diff --git a/changelog.d/7792.bugfix b/changelog.d/7792.bugfix new file mode 100644 index 0000000000..d5c80a0825 --- /dev/null +++ b/changelog.d/7792.bugfix @@ -0,0 +1 @@ +[Session Manager] Other sessions list: cannot select/deselect session by a long press when in select mode From 67edf6685644230e2f5b6e877071b88b92f6a63a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:34:55 +0100 Subject: [PATCH 19/65] Bump sentry-android from 6.9.0 to 6.9.2 (#7731) Bumps [sentry-android](https://github.com/getsentry/sentry-java) from 6.9.0 to 6.9.2. - [Release notes](https://github.com/getsentry/sentry-java/releases) - [Changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-java/compare/6.9.0...6.9.2) --- updated-dependencies: - dependency-name: io.sentry:sentry-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index dbb5f5fe05..dd8e6bb11c 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -26,7 +26,7 @@ def jjwt = "0.11.5" // Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert // the whole commit which set version 0.16.0-SNAPSHOT def vanniktechEmoji = "0.16.0-SNAPSHOT" -def sentry = "6.9.0" +def sentry = "6.9.2" def fragment = "1.5.5" // Testing def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819 From eb3117491353478667efd6882edcc86e4e70ad8e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 14:39:22 +0100 Subject: [PATCH 20/65] Toggle selection on long press even when in selection mode --- .../settings/devices/v2/othersessions/OtherSessionsFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt index 87330b087a..510935e8e6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsFragment.kt @@ -341,6 +341,8 @@ class OtherSessionsFragment : override fun onOtherSessionLongClicked(deviceId: String) = withState(viewModel) { state -> if (!state.isSelectModeEnabled) { enableSelectMode(true, deviceId) + } else { + viewModel.handle(OtherSessionsAction.ToggleSelectionForDevice(deviceId)) } } From f0dc6e478dd679dd8d29ffe706d39d25559985f0 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 15 Dec 2022 16:48:32 +0300 Subject: [PATCH 21/65] Fix ip address visibility in the current session details. --- .../app/features/settings/devices/v2/list/SessionInfoView.kt | 4 ++-- .../features/settings/devices/v2/list/SessionInfoViewState.kt | 2 +- .../settings/devices/v2/overview/SessionOverviewFragment.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt index eecec72b0a..5d2daf2941 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt @@ -75,7 +75,7 @@ class SessionInfoView @JvmOverloads constructor( renderDeviceLastSeenDetails( sessionInfoViewState.deviceFullInfo.isInactive, sessionInfoViewState.deviceFullInfo.deviceInfo, - sessionInfoViewState.isLastSeenDetailsVisible, + sessionInfoViewState.isLastActivityVisible, sessionInfoViewState.isShowingIpAddress, dateFormatter, drawableProvider, @@ -197,7 +197,7 @@ class SessionInfoView @JvmOverloads constructor( } else { views.sessionInfoLastActivityTextView.isGone = true } - views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible && isShowingIpAddress }) + views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isShowingIpAddress }) } private fun renderDetailsButton(isDetailsButtonVisible: Boolean) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt index 5d3c4b4f4b..6c7ca809ea 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt @@ -24,6 +24,6 @@ data class SessionInfoViewState( val isVerifyButtonVisible: Boolean = true, val isDetailsButtonVisible: Boolean = true, val isLearnMoreLinkVisible: Boolean = false, - val isLastSeenDetailsVisible: Boolean = false, + val isLastActivityVisible: Boolean = false, val isShowingIpAddress: Boolean = false, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index f3df0cced0..399f99201b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -224,7 +224,7 @@ class SessionOverviewFragment : isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted, isDetailsButtonVisible = false, isLearnMoreLinkVisible = deviceInfo.roomEncryptionTrustLevel != RoomEncryptionTrustLevel.Default, - isLastSeenDetailsVisible = !isCurrentSession, + isLastActivityVisible = !isCurrentSession, isShowingIpAddress = viewState.isShowingIpAddress, ) views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider) From bc9ca3fd12f000565c70b86831c3ba7b86e5db40 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 15 Dec 2022 17:26:39 +0300 Subject: [PATCH 22/65] Revert "Fix ip address visibility in the current session details." This reverts commit f0dc6e478dd679dd8d29ffe706d39d25559985f0. --- .../app/features/settings/devices/v2/list/SessionInfoView.kt | 4 ++-- .../features/settings/devices/v2/list/SessionInfoViewState.kt | 2 +- .../settings/devices/v2/overview/SessionOverviewFragment.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt index 5d2daf2941..eecec72b0a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt @@ -75,7 +75,7 @@ class SessionInfoView @JvmOverloads constructor( renderDeviceLastSeenDetails( sessionInfoViewState.deviceFullInfo.isInactive, sessionInfoViewState.deviceFullInfo.deviceInfo, - sessionInfoViewState.isLastActivityVisible, + sessionInfoViewState.isLastSeenDetailsVisible, sessionInfoViewState.isShowingIpAddress, dateFormatter, drawableProvider, @@ -197,7 +197,7 @@ class SessionInfoView @JvmOverloads constructor( } else { views.sessionInfoLastActivityTextView.isGone = true } - views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isShowingIpAddress }) + views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible && isShowingIpAddress }) } private fun renderDetailsButton(isDetailsButtonVisible: Boolean) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt index 6c7ca809ea..5d3c4b4f4b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt @@ -24,6 +24,6 @@ data class SessionInfoViewState( val isVerifyButtonVisible: Boolean = true, val isDetailsButtonVisible: Boolean = true, val isLearnMoreLinkVisible: Boolean = false, - val isLastActivityVisible: Boolean = false, + val isLastSeenDetailsVisible: Boolean = false, val isShowingIpAddress: Boolean = false, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index 399f99201b..f3df0cced0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -224,7 +224,7 @@ class SessionOverviewFragment : isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted, isDetailsButtonVisible = false, isLearnMoreLinkVisible = deviceInfo.roomEncryptionTrustLevel != RoomEncryptionTrustLevel.Default, - isLastActivityVisible = !isCurrentSession, + isLastSeenDetailsVisible = !isCurrentSession, isShowingIpAddress = viewState.isShowingIpAddress, ) views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider) From ce23b806987dde5cab2750a18c0958c6bf470866 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 15 Dec 2022 17:29:37 +0300 Subject: [PATCH 23/65] Fix current session ip address visibility. --- .../app/features/settings/devices/v2/list/SessionInfoView.kt | 4 ++-- .../features/settings/devices/v2/list/SessionInfoViewState.kt | 2 +- .../settings/devices/v2/overview/SessionOverviewFragment.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt index eecec72b0a..5d2daf2941 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt @@ -75,7 +75,7 @@ class SessionInfoView @JvmOverloads constructor( renderDeviceLastSeenDetails( sessionInfoViewState.deviceFullInfo.isInactive, sessionInfoViewState.deviceFullInfo.deviceInfo, - sessionInfoViewState.isLastSeenDetailsVisible, + sessionInfoViewState.isLastActivityVisible, sessionInfoViewState.isShowingIpAddress, dateFormatter, drawableProvider, @@ -197,7 +197,7 @@ class SessionInfoView @JvmOverloads constructor( } else { views.sessionInfoLastActivityTextView.isGone = true } - views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible && isShowingIpAddress }) + views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isShowingIpAddress }) } private fun renderDetailsButton(isDetailsButtonVisible: Boolean) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt index 5d3c4b4f4b..6c7ca809ea 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt @@ -24,6 +24,6 @@ data class SessionInfoViewState( val isVerifyButtonVisible: Boolean = true, val isDetailsButtonVisible: Boolean = true, val isLearnMoreLinkVisible: Boolean = false, - val isLastSeenDetailsVisible: Boolean = false, + val isLastActivityVisible: Boolean = false, val isShowingIpAddress: Boolean = false, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index f3df0cced0..399f99201b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -224,7 +224,7 @@ class SessionOverviewFragment : isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted, isDetailsButtonVisible = false, isLearnMoreLinkVisible = deviceInfo.roomEncryptionTrustLevel != RoomEncryptionTrustLevel.Default, - isLastSeenDetailsVisible = !isCurrentSession, + isLastActivityVisible = !isCurrentSession, isShowingIpAddress = viewState.isShowingIpAddress, ) views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider) From a213338a22d5cbb93b2b3f50ec0a41720444e2dd Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 15 Dec 2022 17:34:35 +0300 Subject: [PATCH 24/65] Add changelog. --- changelog.d/7794.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7794.bugfix diff --git a/changelog.d/7794.bugfix b/changelog.d/7794.bugfix new file mode 100644 index 0000000000..54cd93728b --- /dev/null +++ b/changelog.d/7794.bugfix @@ -0,0 +1 @@ +Fix current session ip address visibility From 319168804475480313612606d16ffb9a6f69e50d Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 15:38:50 +0100 Subject: [PATCH 25/65] Adding changelog entry --- changelog.d/7795.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7795.feature diff --git a/changelog.d/7795.feature b/changelog.d/7795.feature new file mode 100644 index 0000000000..50c7e2a8cc --- /dev/null +++ b/changelog.d/7795.feature @@ -0,0 +1 @@ +[Session manager] Security recommendations cards: whole view should be tappable From d7a729740e971b03047fe0c6ff838fcdd6730c76 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 16:02:56 +0100 Subject: [PATCH 26/65] Adding click listener on the whole custom view --- .../settings/devices/v2/list/SecurityRecommendationView.kt | 3 +++ vector/src/main/res/layout/view_security_recommendation.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt index 07202274ad..2a43a9aade 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt @@ -53,6 +53,9 @@ class SecurityRecommendationView @JvmOverloads constructor( setImage(it) } + setOnClickListener { + callback?.onViewAllClicked() + } views.recommendationViewAllButton.setOnClickListener { callback?.onViewAllClicked() } diff --git a/vector/src/main/res/layout/view_security_recommendation.xml b/vector/src/main/res/layout/view_security_recommendation.xml index 6710864048..4a41ca961f 100644 --- a/vector/src/main/res/layout/view_security_recommendation.xml +++ b/vector/src/main/res/layout/view_security_recommendation.xml @@ -5,6 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_current_session" + android:foreground="?attr/selectableItemBackground" android:paddingHorizontal="16dp" android:paddingTop="16dp" android:paddingBottom="8dp"> From 301ecdf1f7f11645ae98e772958cf5d9c714b4e9 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 16:46:05 +0100 Subject: [PATCH 27/65] Adding changelog entry --- changelog.d/7797.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7797.feature diff --git a/changelog.d/7797.feature b/changelog.d/7797.feature new file mode 100644 index 0000000000..280f310b91 --- /dev/null +++ b/changelog.d/7797.feature @@ -0,0 +1 @@ +[Session manager] Other sessions list: Security recommendation header should not be sticky From c2d25c8564e3eeb4637138c213c74c2b90610c18 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 16 Dec 2022 09:46:52 +0100 Subject: [PATCH 28/65] Collapsing header in other sessions screen --- .../res/layout/fragment_other_sessions.xml | 208 +++++++++--------- 1 file changed, 110 insertions(+), 98 deletions(-) diff --git a/vector/src/main/res/layout/fragment_other_sessions.xml b/vector/src/main/res/layout/fragment_other_sessions.xml index e25b8b185f..62384b7ee1 100644 --- a/vector/src/main/res/layout/fragment_other_sessions.xml +++ b/vector/src/main/res/layout/fragment_other_sessions.xml @@ -1,120 +1,132 @@ - + + + + + + +