From 30fe3773ae5bd430995057c8a22d375c61e6fe7c Mon Sep 17 00:00:00 2001 From: chagai95 <31655082+chagai95@users.noreply.github.com> Date: Thu, 19 May 2022 15:03:51 +0200 Subject: [PATCH 001/174] refactor - better naming, return native user id and not sip user id and create a dm with the native user instead of with the sip user --- .../features/call/dialpad/DialPadLookup.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index e835a74fd6..14ce5f2dc0 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -42,18 +42,23 @@ class DialPadLookup @Inject constructor( val sipUserId = thirdPartyUser.userId val nativeLookupResults = session.sipNativeLookup(thirdPartyUser.userId) // If I have a native user I check for an existing native room with him... - val roomId = if (nativeLookupResults.isNotEmpty()) { + if (nativeLookupResults.isNotEmpty()) { val nativeUserId = nativeLookupResults.first().userId if (nativeUserId == session.myUserId) { throw Failure.NumberIsYours } - session.roomService().getExistingDirectRoomWithUser(nativeUserId) - // if there is not, just create a DM with the sip user - ?: directRoomHelper.ensureDMExists(sipUserId) - } else { - // do the same if there is no corresponding native user. - directRoomHelper.ensureDMExists(sipUserId) + var nativeRoomId = session.getExistingDirectRoomWithUser(nativeUserId) + if (nativeRoomId == null) { + // if there is no existing native room with the existing native user, + // just create a DM with the native user + nativeRoomId = directRoomHelper.ensureDMExists(nativeUserId) + } + Timber.d("lookupPhoneNumber with nativeUserId: $nativeUserId and nativeRoomId: $nativeRoomId") + return Result(userId = nativeUserId, roomId = nativeRoomId) } - return Result(userId = sipUserId, roomId = roomId) + // If there is no native user then we return sipUserId and sipRoomId - this is usually a PSTN call. + val sipRoomId = directRoomHelper.ensureDMExists(sipUserId) + Timber.d("lookupPhoneNumber with sipRoomId: $sipRoomId and sipUserId: $sipUserId") + return Result(userId = sipUserId, roomId = sipRoomId) } } From 8c783f94142eaa2e36c01aa6d4885370e8d49528 Mon Sep 17 00:00:00 2001 From: chagai95 <31655082+chagai95@users.noreply.github.com> Date: Thu, 19 May 2022 15:12:04 +0200 Subject: [PATCH 002/174] Create 6101.bugfix --- changelog.d/6101.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6101.bugfix diff --git a/changelog.d/6101.bugfix b/changelog.d/6101.bugfix new file mode 100644 index 0000000000..2d8da5327d --- /dev/null +++ b/changelog.d/6101.bugfix @@ -0,0 +1 @@ +Refactor - better naming, return native user id and not sip user id and create a dm with the native user instead of with the sip user. From f949c517b6f369d79664fc0160aba1646006c200 Mon Sep 17 00:00:00 2001 From: chagai95 <31655082+chagai95@users.noreply.github.com> Date: Fri, 20 May 2022 15:52:43 +0200 Subject: [PATCH 003/174] import timber and use .roomService() --- .../java/im/vector/app/features/call/dialpad/DialPadLookup.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index 14ce5f2dc0..3ab2ee50c0 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -23,6 +23,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper import org.matrix.android.sdk.api.session.Session import javax.inject.Inject +import timber.log.Timber class DialPadLookup @Inject constructor( private val session: Session, @@ -47,7 +48,7 @@ class DialPadLookup @Inject constructor( if (nativeUserId == session.myUserId) { throw Failure.NumberIsYours } - var nativeRoomId = session.getExistingDirectRoomWithUser(nativeUserId) + var nativeRoomId = session.roomService().getExistingDirectRoomWithUser(nativeUserId) if (nativeRoomId == null) { // if there is no existing native room with the existing native user, // just create a DM with the native user From 2cc881a5d02abdefee760df3b6f3286b84389a92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 12:12:55 +0000 Subject: [PATCH 004/174] Bump dependency-check-gradle from 7.1.0.1 to 7.1.1 Bumps dependency-check-gradle from 7.1.0.1 to 7.1.1. --- updated-dependencies: - dependency-name: org.owasp:dependency-check-gradle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2cb67b7795..3a6022771d 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ buildscript { classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5' classpath "com.likethesalad.android:stem-plugin:2.1.1" - classpath 'org.owasp:dependency-check-gradle:7.1.0.1' + classpath 'org.owasp:dependency-check-gradle:7.1.1' classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21" classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0" // NOTE: Do not place your application dependencies here; they belong From 53c0609c38c7b49e075cdbed31f7dd0b71c168ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 15:37:13 +0000 Subject: [PATCH 005/174] Bump mavericks from 2.6.1 to 2.7.0 Bumps `mavericks` from 2.6.1 to 2.7.0. Updates `mavericks` from 2.6.1 to 2.7.0 - [Release notes](https://github.com/airbnb/mavericks/releases) - [Changelog](https://github.com/airbnb/mavericks/blob/main/CHANGELOG.md) - [Commits](https://github.com/airbnb/mavericks/commits) Updates `mavericks-testing` from 2.6.1 to 2.7.0 - [Release notes](https://github.com/airbnb/mavericks/releases) - [Changelog](https://github.com/airbnb/mavericks/blob/main/CHANGELOG.md) - [Commits](https://github.com/airbnb/mavericks/commits) --- updated-dependencies: - dependency-name: com.airbnb.android:mavericks dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.airbnb.android:mavericks-testing dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 451ad4449b..4463489277 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -22,7 +22,7 @@ def moshi = "1.13.0" def lifecycle = "2.4.1" def flowBinding = "1.2.0" def epoxy = "4.6.2" -def mavericks = "2.6.1" +def mavericks = "2.7.0" def glide = "4.13.2" def bigImageViewer = "1.8.1" def jjwt = "0.11.5" From c2707d4538c44aa4e9ec3ddbbc727eb01787e8da Mon Sep 17 00:00:00 2001 From: chagai95 <31655082+chagai95@users.noreply.github.com> Date: Tue, 14 Jun 2022 14:08:22 +0200 Subject: [PATCH 006/174] Wrong import order --- .../java/im/vector/app/features/call/dialpad/DialPadLookup.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index 3ab2ee50c0..8f904c8ab8 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -22,8 +22,8 @@ import im.vector.app.features.call.vectorCallService import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper import org.matrix.android.sdk.api.session.Session -import javax.inject.Inject import timber.log.Timber +import javax.inject.Inject class DialPadLookup @Inject constructor( private val session: Session, From 3367c059e9fc956a4257f89db379a3deba9befa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jun 2022 08:40:08 +0000 Subject: [PATCH 007/174] Bump annotation from 1.3.0 to 1.4.0 Bumps annotation from 1.3.0 to 1.4.0. --- updated-dependencies: - dependency-name: androidx.annotation:annotation dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 604174fe57..7bd3b930ac 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -48,7 +48,7 @@ ext.libs = [ 'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines" ], androidx : [ - 'annotation' : "androidx.annotation:annotation:1.3.0", + 'annotation' : "androidx.annotation:annotation:1.4.0", 'activity' : "androidx.activity:activity:1.4.0", 'appCompat' : "androidx.appcompat:appcompat:1.4.2", 'core' : "androidx.core:core-ktx:1.8.0", From a3774c11612ba32829be400b2b76cd948e8236ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jun 2022 23:08:07 +0000 Subject: [PATCH 008/174] Bump android-embedded_fcm_distributor from 2.0.0 to 2.1.1 Bumps [android-embedded_fcm_distributor](https://github.com/UnifiedPush/android-embedded_fcm_distributor) from 2.0.0 to 2.1.1. - [Release notes](https://github.com/UnifiedPush/android-embedded_fcm_distributor/releases) - [Commits](https://github.com/UnifiedPush/android-embedded_fcm_distributor/compare/2.0.0...2.1.1) --- updated-dependencies: - dependency-name: com.github.UnifiedPush:android-embedded_fcm_distributor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- vector/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/build.gradle b/vector/build.gradle index 8d704141e5..735c953db7 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -465,7 +465,7 @@ dependencies { // UnifiedPush implementation 'com.github.UnifiedPush:android-connector:2.0.0' // UnifiedPush gplay flavor only - gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.0.0') { + gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.1') { exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' From ba0898831b75a9482860a7b22e9c233d0ae151fb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Jun 2022 17:22:31 +0200 Subject: [PATCH 009/174] Fix compilation issue --- .../gplay/features/settings/troubleshoot/TestPlayServices.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt index f1ea4a4153..e78132908d 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt @@ -46,7 +46,7 @@ class TestPlayServices @Inject constructor( if (apiAvailability.isUserResolvableError(resultCode)) { quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_play_services_quickfix) { override fun doFix() { - apiAvailability.getErrorDialog(context, resultCode, 9000 /*hey does the magic number*/).show() + apiAvailability.getErrorDialog(context, resultCode, 9000 /*hey does the magic number*/)?.show() } } Timber.e("Play Services apk error $resultCode -> ${apiAvailability.getErrorString(resultCode)}.") From dc95f4553e18526e242569756f3fa970e5364eaf Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 10:38:05 +0200 Subject: [PATCH 010/174] Adding changelog entry --- changelog.d/6155.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6155.misc diff --git a/changelog.d/6155.misc b/changelog.d/6155.misc new file mode 100644 index 0000000000..044e21408e --- /dev/null +++ b/changelog.d/6155.misc @@ -0,0 +1 @@ +Add unit tests for LiveLocationAggregationProcessor code From 51b930147aa3df3a8728808599bc645eceff2625 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 11:30:08 +0200 Subject: [PATCH 011/174] Adding some tests on ignored cases --- .../LiveLocationAggregationProcessor.kt | 28 +++- .../LiveLocationAggregationProcessorTest.kt | 128 ++++++++++++++++++ .../android/sdk/test/fakes/FakeClock.kt | 27 ++++ .../sdk/test/fakes/FakeWorkManagerProvider.kt | 25 ++++ 4 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt index 05bde8f83f..a254552bb3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt @@ -36,16 +36,22 @@ import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject -// TODO add unit tests +/** + * Aggregates all live location sharing related events in local database. + */ internal class LiveLocationAggregationProcessor @Inject constructor( @SessionId private val sessionId: String, private val workManagerProvider: WorkManagerProvider, private val clock: Clock, ) { - fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) { + /** + * Handle the content of a beacon info. + * @return true if it has been processed, false if ignored. + */ + fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean): Boolean { if (event.senderId.isNullOrEmpty() || isLocalEcho) { - return + return false } val isLive = content.isLive.orTrue() @@ -58,7 +64,7 @@ internal class LiveLocationAggregationProcessor @Inject constructor( if (targetEventId.isNullOrEmpty()) { Timber.w("no target event id found for the beacon content") - return + return false } val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate( @@ -83,6 +89,8 @@ internal class LiveLocationAggregationProcessor @Inject constructor( } else { cancelDeactivationAfterTimeout(targetEventId, roomId) } + + return true } private fun scheduleDeactivationAfterTimeout(eventId: String, roomId: String, endOfLiveTimestampMillis: Long?) { @@ -110,6 +118,10 @@ internal class LiveLocationAggregationProcessor @Inject constructor( workManagerProvider.workManager.cancelUniqueWork(workName) } + /** + * Handle the content of a beacon location data. + * @return true if it has been processed, false if ignored. + */ fun handleBeaconLocationData( realm: Realm, event: Event, @@ -117,14 +129,14 @@ internal class LiveLocationAggregationProcessor @Inject constructor( roomId: String, relatedEventId: String?, isLocalEcho: Boolean - ) { + ): Boolean { if (event.senderId.isNullOrEmpty() || isLocalEcho) { - return + return false } if (relatedEventId.isNullOrEmpty()) { Timber.w("no related event id found for the live location content") - return + return false } val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate( @@ -143,6 +155,8 @@ internal class LiveLocationAggregationProcessor @Inject constructor( Timber.d("updating last location of the summary of id=$relatedEventId") aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent()) } + + return true } private fun deactivateAllPreviousBeacons(realm: Realm, roomId: String, userId: String, currentEventId: String) { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt new file mode 100644 index 0000000000..d8c7f7477b --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -0,0 +1,128 @@ +/* + * 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.aggregation.livelocation + +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent +import org.matrix.android.sdk.test.fakes.FakeClock +import org.matrix.android.sdk.test.fakes.FakeRealm +import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider + +private const val A_SESSION_ID = "session_id" +private const val A_SENDER_ID = "sender_id" +private const val AN_EVENT_ID = "event_id" + +internal class LiveLocationAggregationProcessorTest { + + private val fakeWorkManagerProvider = FakeWorkManagerProvider() + private val fakeClock = FakeClock() + private val fakeRealm = FakeRealm() + + private val liveLocationAggregationProcessor = LiveLocationAggregationProcessor( + sessionId = A_SESSION_ID, + workManagerProvider = fakeWorkManagerProvider.instance, + clock = fakeClock + ) + + @Test + fun `given beacon info when it is local echo then it is ignored`() { + val event = Event(senderId = A_SENDER_ID) + val beaconInfo = MessageBeaconInfoContent() + + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = event, + content = beaconInfo, + roomId = "", + isLocalEcho = true + ) + + result shouldBeEqualTo false + } + + @Test + fun `given beacon info and event when senderId is null or empty then it is ignored`() { + val eventNoSenderId = Event(eventId = AN_EVENT_ID) + val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") + val beaconInfo = MessageBeaconInfoContent() + + val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventNoSenderId, + content = beaconInfo, + roomId = "", + isLocalEcho = false + ) + val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventEmptySenderId, + content = beaconInfo, + roomId = "", + isLocalEcho = false + ) + + resultNoSenderId shouldBeEqualTo false + resultEmptySenderId shouldBeEqualTo false + } + + @Test + fun `given beacon location data when it is local echo then it is ignored`() { + val event = Event(senderId = A_SENDER_ID) + val beaconLocationData = MessageBeaconLocationDataContent() + + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = "", + relatedEventId = "", + isLocalEcho = true + ) + + result shouldBeEqualTo false + } + + @Test + fun `given beacon location data and event when senderId is null or empty then it is ignored`() { + val eventNoSenderId = Event(eventId = AN_EVENT_ID) + val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") + val beaconLocationData = MessageBeaconLocationDataContent() + + val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = eventNoSenderId, + content = beaconLocationData, + roomId = "", + relatedEventId = "", + isLocalEcho = false + ) + val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = eventEmptySenderId, + content = beaconLocationData, + roomId = "", + relatedEventId = "", + isLocalEcho = false + ) + + resultNoSenderId shouldBeEqualTo false + resultEmptySenderId shouldBeEqualTo false + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt new file mode 100644 index 0000000000..febf94f4cf --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeClock.kt @@ -0,0 +1,27 @@ +/* + * 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.every +import io.mockk.mockk +import org.matrix.android.sdk.internal.util.time.Clock + +internal class FakeClock : Clock by mockk() { + fun givenEpoch(epoch: Long) { + every { epochMillis() } returns epoch + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt new file mode 100644 index 0000000000..9ba072d35c --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt @@ -0,0 +1,25 @@ +/* + * 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.mockk +import org.matrix.android.sdk.internal.di.WorkManagerProvider + +internal class FakeWorkManagerProvider { + + val instance = mockk() +} From dccc3b457debc693bf40650e9cc409c2807ae965 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 11:45:35 +0200 Subject: [PATCH 012/174] Adding more tests on ignored cases --- .../LiveLocationAggregationProcessorTest.kt | 91 +++++++++++++++++-- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index d8c7f7477b..6a52a03880 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent import org.matrix.android.sdk.test.fakes.FakeClock @@ -28,6 +29,7 @@ import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider private const val A_SESSION_ID = "session_id" private const val A_SENDER_ID = "sender_id" private const val AN_EVENT_ID = "event_id" +private const val A_ROOM_ID = "room_id" internal class LiveLocationAggregationProcessorTest { @@ -50,7 +52,7 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = event, content = beaconInfo, - roomId = "", + roomId = A_ROOM_ID, isLocalEcho = true ) @@ -67,14 +69,14 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = eventNoSenderId, content = beaconInfo, - roomId = "", + roomId = A_ROOM_ID, isLocalEcho = false ) val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconInfo( realm = fakeRealm.instance, event = eventEmptySenderId, content = beaconInfo, - roomId = "", + roomId = A_ROOM_ID, isLocalEcho = false ) @@ -82,6 +84,55 @@ internal class LiveLocationAggregationProcessorTest { resultEmptySenderId shouldBeEqualTo false } + @Test + fun `given beacon info when no target eventId is found then it is ignored`() { + val unsignedDataWithNoEventId = UnsignedData( + age = 123 + ) + val unsignedDataWithEmptyEventId = UnsignedData( + age = 123, + replacesState = "" + ) + val eventWithNoEventId = Event(senderId = A_SENDER_ID, unsignedData = unsignedDataWithNoEventId) + val eventWithEmptyEventId = Event(senderId = A_SENDER_ID, eventId = "", unsignedData = unsignedDataWithEmptyEventId) + val beaconInfoLive = MessageBeaconInfoContent(isLive = true) + val beaconInfoNotLive = MessageBeaconInfoContent(isLive = false) + + val resultLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithNoEventId, + content = beaconInfoLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + val resultLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithEmptyEventId, + content = beaconInfoLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + val resultNotLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithNoEventId, + content = beaconInfoNotLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + val resultNotLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = eventWithEmptyEventId, + content = beaconInfoNotLive, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + resultLiveNoEventId shouldBeEqualTo false + resultLiveEmptyEventId shouldBeEqualTo false + resultNotLiveNoEventId shouldBeEqualTo false + resultNotLiveEmptyEventId shouldBeEqualTo false + } + @Test fun `given beacon location data when it is local echo then it is ignored`() { val event = Event(senderId = A_SENDER_ID) @@ -91,14 +142,40 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = event, content = beaconLocationData, - roomId = "", - relatedEventId = "", + roomId = A_ROOM_ID, + relatedEventId = AN_EVENT_ID, isLocalEcho = true ) result shouldBeEqualTo false } + @Test + fun `given beacon location data when relatedEventId is null or empty then it is ignored`() { + val event = Event(senderId = A_SENDER_ID) + val beaconLocationData = MessageBeaconLocationDataContent() + + val resultNoRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = null, + isLocalEcho = false + ) + val resultEmptyRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = "", + isLocalEcho = false + ) + + resultNoRelatedEventId shouldBeEqualTo false + resultEmptyRelatedEventId shouldBeEqualTo false + } + @Test fun `given beacon location data and event when senderId is null or empty then it is ignored`() { val eventNoSenderId = Event(eventId = AN_EVENT_ID) @@ -110,7 +187,7 @@ internal class LiveLocationAggregationProcessorTest { event = eventNoSenderId, content = beaconLocationData, roomId = "", - relatedEventId = "", + relatedEventId = AN_EVENT_ID, isLocalEcho = false ) val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconLocationData( @@ -118,7 +195,7 @@ internal class LiveLocationAggregationProcessorTest { event = eventEmptySenderId, content = beaconLocationData, roomId = "", - relatedEventId = "", + relatedEventId = AN_EVENT_ID, isLocalEcho = false ) From 6386c1603f402dfc2dc4010b603e6efd83a5104e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 16:56:29 +0200 Subject: [PATCH 013/174] Adding tests on beacon info aggregation --- ...cationShareAggregatedSummaryEntityQuery.kt | 1 + .../LiveLocationAggregationProcessorTest.kt | 120 ++++++++++++++++++ .../poll/PollAggregationProcessorTest.kt | 15 +-- .../android/sdk/test/fakes/FakeRealm.kt | 53 +++++++- .../sdk/test/fakes/FakeWorkManagerProvider.kt | 10 ++ 5 files changed, 185 insertions(+), 14 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt index 7dfeb6884a..6bcd737474 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt @@ -84,6 +84,7 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveIn .equalTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) .notEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, ignoredEventId) .findAll() + .toList() } /** diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index 6a52a03880..1638e4b4b4 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -16,26 +16,37 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation +import androidx.work.OneTimeWorkRequest +import io.mockk.verify import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.UnsignedData import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent +import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity +import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields import org.matrix.android.sdk.test.fakes.FakeClock import org.matrix.android.sdk.test.fakes.FakeRealm import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider +import org.matrix.android.sdk.test.fakes.givenEqualTo +import org.matrix.android.sdk.test.fakes.givenFindAll +import org.matrix.android.sdk.test.fakes.givenFindFirst +import org.matrix.android.sdk.test.fakes.givenNotEqualTo private const val A_SESSION_ID = "session_id" private const val A_SENDER_ID = "sender_id" private const val AN_EVENT_ID = "event_id" private const val A_ROOM_ID = "room_id" +private const val A_TIMESTAMP = 1654689143L +private const val A_TIMEOUT_MILLIS = 15 * 60 * 1000L internal class LiveLocationAggregationProcessorTest { private val fakeWorkManagerProvider = FakeWorkManagerProvider() private val fakeClock = FakeClock() private val fakeRealm = FakeRealm() + private val fakeQuery = fakeRealm.givenWhere() private val liveLocationAggregationProcessor = LiveLocationAggregationProcessor( sessionId = A_SESSION_ID, @@ -133,6 +144,85 @@ internal class LiveLocationAggregationProcessorTest { resultNotLiveEmptyEventId shouldBeEqualTo false } + @Test + fun `given beacon info and existing entity when beacon content is correct and active then it is aggregated`() { + val event = Event( + senderId = A_SENDER_ID, + eventId = AN_EVENT_ID + ) + val beaconInfo = MessageBeaconInfoContent( + isLive = true, + unstableTimestampMillis = A_TIMESTAMP, + timeout = A_TIMEOUT_MILLIS + ) + fakeClock.givenEpoch(A_TIMESTAMP + 5000) + val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() + val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = event, + content = beaconInfo, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo true + aggregatedEntity.eventId shouldBeEqualTo AN_EVENT_ID + aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID + aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID + aggregatedEntity.isActive shouldBeEqualTo true + aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS + aggregatedEntity.lastLocationContent shouldBeEqualTo null + previousEntities.forEach { entity -> + entity.isActive shouldBeEqualTo false + } + val workManager = fakeWorkManagerProvider.instance.workManager + verify { workManager.enqueueUniqueWork(any(), any(), any()) } + } + + @Test + fun `given beacon info and existing entity when beacon content is correct and inactive then it is aggregated`() { + val unsignedData = UnsignedData( + age = 123, + replacesState = AN_EVENT_ID + ) + val event = Event( + senderId = A_SENDER_ID, + eventId = "", + unsignedData = unsignedData + ) + val beaconInfo = MessageBeaconInfoContent( + isLive = false, + unstableTimestampMillis = A_TIMESTAMP, + timeout = A_TIMEOUT_MILLIS + ) + fakeClock.givenEpoch(A_TIMESTAMP + 5000) + val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() + val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = event, + content = beaconInfo, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo true + aggregatedEntity.eventId shouldBeEqualTo AN_EVENT_ID + aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID + aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID + aggregatedEntity.isActive shouldBeEqualTo false + aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS + aggregatedEntity.lastLocationContent shouldBeEqualTo null + previousEntities.forEach { entity -> + entity.isActive shouldBeEqualTo false + } + val workManager = fakeWorkManagerProvider.instance.workManager + verify { workManager.cancelUniqueWork(any()) } + } + @Test fun `given beacon location data when it is local echo then it is ignored`() { val event = Event(senderId = A_SENDER_ID) @@ -202,4 +292,34 @@ internal class LiveLocationAggregationProcessorTest { resultNoSenderId shouldBeEqualTo false resultEmptySenderId shouldBeEqualTo false } + + private fun mockLiveLocationShareAggregatedSummaryEntityForEvent(): LiveLocationShareAggregatedSummaryEntity { + val result = LiveLocationShareAggregatedSummaryEntity( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID + ) + fakeQuery + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) + .givenFindFirst(result) + return result + } + + private fun mockPreviousLiveLocationShareAggregatedSummaryEntities(): List { + val results = listOf( + LiveLocationShareAggregatedSummaryEntity( + eventId = "", + roomId = A_ROOM_ID, + userId = A_SENDER_ID, + isActive = true + ) + ) + fakeQuery + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) + .givenNotEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.USER_ID, A_SENDER_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) + .givenFindAll(results) + return results + } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt index 837bbeea26..3044ca5d43 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt @@ -19,8 +19,6 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll import io.mockk.every import io.mockk.mockk import io.realm.RealmList -import io.realm.RealmModel -import io.realm.RealmQuery import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeTrue import org.junit.Before @@ -46,6 +44,8 @@ import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsT 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.test.fakes.FakeRealm +import org.matrix.android.sdk.test.fakes.givenEqualTo +import org.matrix.android.sdk.test.fakes.givenFindFirst class PollAggregationProcessorTest { @@ -135,14 +135,11 @@ class PollAggregationProcessorTest { pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse() } - private inline fun RealmQuery.givenEqualTo(fieldName: String, value: String, result: RealmQuery) { - every { equalTo(fieldName, value) } returns result - } - private fun mockEventAnnotationsSummaryEntity() { - val queryResult = realm.givenWhereReturns(result = EventAnnotationsSummaryEntity()) - queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!, queryResult) - queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!, queryResult) + realm.givenWhere() + .givenFindFirst(EventAnnotationsSummaryEntity()) + .givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!) + .givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!) } private fun mockRoom( 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 c07f8e1873..1697921a8d 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 @@ -21,16 +21,59 @@ import io.mockk.mockk import io.realm.Realm import io.realm.RealmModel import io.realm.RealmQuery +import io.realm.RealmResults import io.realm.kotlin.where internal class FakeRealm { val instance = mockk(relaxed = true) - inline fun givenWhereReturns(result: T?): RealmQuery { - val queryResult = mockk>() - every { queryResult.findFirst() } returns result - every { instance.where() } returns queryResult - return queryResult + inline fun givenWhere(): RealmQuery { + val query = mockk>() + every { instance.where() } returns query + return query } } + +inline fun RealmQuery.givenFindFirst( + result: T? +): RealmQuery { + every { findFirst() } returns result + return this +} + +inline fun RealmQuery.givenFindAll( + result: List +): RealmQuery { + val realmResults = mockk>() + result.forEachIndexed { index, t -> + every { realmResults[index] } returns t + } + every { realmResults.size } returns result.size + every { findAll() } returns realmResults + return this +} + +inline fun RealmQuery.givenEqualTo( + fieldName: String, + value: String +): RealmQuery { + every { equalTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenEqualTo( + fieldName: String, + value: Boolean +): RealmQuery { + every { equalTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenNotEqualTo( + fieldName: String, + value: String +): RealmQuery { + every { notEqualTo(fieldName, value) } returns this + return this +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt index 9ba072d35c..b6b435f531 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt @@ -16,10 +16,20 @@ package org.matrix.android.sdk.test.fakes +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.internal.di.WorkManagerProvider internal class FakeWorkManagerProvider { val instance = mockk() + + init { + val workManager = mockk() + every { workManager.enqueueUniqueWork(any(), any(), any()) } returns mockk() + every { workManager.cancelUniqueWork(any()) } returns mockk() + every { instance.workManager } returns workManager + } } From b9b1e2b3973e2a1d77218d5151cbadbc84714965 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 8 Jun 2022 17:43:10 +0200 Subject: [PATCH 014/174] Adding tests on location data aggregation --- .../LiveLocationAggregationProcessor.kt | 7 +- .../LiveLocationAggregationProcessorTest.kt | 68 ++++++++++++++++++- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt index a254552bb3..921749122b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt @@ -151,12 +151,13 @@ internal class LiveLocationAggregationProcessor @Inject constructor( ?.getBestTimestampMillis() ?: 0 - if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) { + return if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) { Timber.d("updating last location of the summary of id=$relatedEventId") aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent()) + true + } else { + false } - - return true } private fun deactivateAllPreviousBeacons(realm: Realm, roomId: String, userId: String, currentEventId: String) { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index 1638e4b4b4..b6e5ea8479 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -22,8 +22,12 @@ import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.UnsignedData +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.message.LocationInfo import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent +import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields import org.matrix.android.sdk.test.fakes.FakeClock @@ -40,6 +44,10 @@ private const val AN_EVENT_ID = "event_id" private const val A_ROOM_ID = "room_id" private const val A_TIMESTAMP = 1654689143L private const val A_TIMEOUT_MILLIS = 15 * 60 * 1000L +private const val A_LATITUDE = 40.05 +private const val A_LONGITUDE = 29.24 +private const val A_UNCERTAINTY = 30.0 +private const val A_GEO_URI = "geo:$A_LATITUDE,$A_LONGITUDE;$A_UNCERTAINTY" internal class LiveLocationAggregationProcessorTest { @@ -284,7 +292,7 @@ internal class LiveLocationAggregationProcessorTest { realm = fakeRealm.instance, event = eventEmptySenderId, content = beaconLocationData, - roomId = "", + roomId = A_ROOM_ID, relatedEventId = AN_EVENT_ID, isLocalEcho = false ) @@ -293,10 +301,64 @@ internal class LiveLocationAggregationProcessorTest { resultEmptySenderId shouldBeEqualTo false } - private fun mockLiveLocationShareAggregatedSummaryEntityForEvent(): LiveLocationShareAggregatedSummaryEntity { + @Test + fun `given beacon location data when location is less recent than the saved one then it is ignored`() { + val event = Event(eventId = AN_EVENT_ID, senderId = A_SENDER_ID) + val beaconLocationData = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP - 60_000 + ) + val lastBeaconLocationContent = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP + ) + mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = AN_EVENT_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo false + } + + @Test + fun `given beacon location data when location is more recent than the saved one then it is aggregated`() { + val event = Event(eventId = AN_EVENT_ID, senderId = A_SENDER_ID) + val locationInfo = LocationInfo(geoUri = A_GEO_URI) + val beaconLocationData = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP, + unstableLocationInfo = locationInfo + ) + val lastBeaconLocationContent = MessageBeaconLocationDataContent( + unstableTimestampMillis = A_TIMESTAMP - 60_000 + ) + val entity = mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = AN_EVENT_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo true + val savedLocationData = ContentMapper.map(entity.lastLocationContent).toModel() + savedLocationData?.getBestTimestampMillis() shouldBeEqualTo A_TIMESTAMP + savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI + } + + private fun mockLiveLocationShareAggregatedSummaryEntityForEvent( + lastBeaconLocationContent: MessageBeaconLocationDataContent? = null + ): LiveLocationShareAggregatedSummaryEntity { val result = LiveLocationShareAggregatedSummaryEntity( eventId = AN_EVENT_ID, - roomId = A_ROOM_ID + roomId = A_ROOM_ID, + lastLocationContent = ContentMapper.map(lastBeaconLocationContent?.toContent()) ) fakeQuery .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) From e3981f42e99ecfde92f114ab927308abe8366e5e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 10 Jun 2022 15:17:26 +0200 Subject: [PATCH 015/174] Introducing FakeWorkManager --- .../LiveLocationAggregationProcessorTest.kt | 16 ++++--- .../android/sdk/test/fakes/FakeWorkManager.kt | 45 +++++++++++++++++++ .../sdk/test/fakes/FakeWorkManagerProvider.kt | 15 +++---- 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index b6e5ea8479..646a2194e1 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -16,8 +16,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation -import androidx.work.OneTimeWorkRequest -import io.mockk.verify +import androidx.work.ExistingWorkPolicy import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.events.model.Event @@ -164,6 +163,7 @@ internal class LiveLocationAggregationProcessorTest { timeout = A_TIMEOUT_MILLIS ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) + fakeWorkManagerProvider.fakeWorkManager.expectEnqueueUniqueWork() val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() @@ -185,8 +185,10 @@ internal class LiveLocationAggregationProcessorTest { previousEntities.forEach { entity -> entity.isActive shouldBeEqualTo false } - val workManager = fakeWorkManagerProvider.instance.workManager - verify { workManager.enqueueUniqueWork(any(), any(), any()) } + fakeWorkManagerProvider.fakeWorkManager.verifyEnqueueUniqueWork( + workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = AN_EVENT_ID, roomId = A_ROOM_ID), + policy = ExistingWorkPolicy.REPLACE + ) } @Test @@ -206,6 +208,7 @@ internal class LiveLocationAggregationProcessorTest { timeout = A_TIMEOUT_MILLIS ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) + fakeWorkManagerProvider.fakeWorkManager.expectCancelUniqueWork() val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() @@ -227,8 +230,9 @@ internal class LiveLocationAggregationProcessorTest { previousEntities.forEach { entity -> entity.isActive shouldBeEqualTo false } - val workManager = fakeWorkManagerProvider.instance.workManager - verify { workManager.cancelUniqueWork(any()) } + fakeWorkManagerProvider.fakeWorkManager.verifyCancelUniqueWork( + workName = DeactivateLiveLocationShareWorker.getWorkName(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + ) } @Test diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt new file mode 100644 index 0000000000..b29d015a43 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManager.kt @@ -0,0 +1,45 @@ +/* + * 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 androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify + +class FakeWorkManager { + + val instance = mockk() + + fun expectEnqueueUniqueWork() { + every { instance.enqueueUniqueWork(any(), any(), any()) } returns mockk() + } + + fun verifyEnqueueUniqueWork(workName: String, policy: ExistingWorkPolicy) { + verify { instance.enqueueUniqueWork(workName, policy, any()) } + } + + fun expectCancelUniqueWork() { + every { instance.cancelUniqueWork(any()) } returns mockk() + } + + fun verifyCancelUniqueWork(workName: String) { + verify { instance.cancelUniqueWork(workName) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt index b6b435f531..51ff24c01d 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeWorkManagerProvider.kt @@ -16,20 +16,15 @@ package org.matrix.android.sdk.test.fakes -import androidx.work.OneTimeWorkRequest -import androidx.work.WorkManager import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.internal.di.WorkManagerProvider -internal class FakeWorkManagerProvider { +internal class FakeWorkManagerProvider( + val fakeWorkManager: FakeWorkManager = FakeWorkManager(), +) { - val instance = mockk() - - init { - val workManager = mockk() - every { workManager.enqueueUniqueWork(any(), any(), any()) } returns mockk() - every { workManager.cancelUniqueWork(any()) } returns mockk() - every { instance.workManager } returns workManager + val instance = mockk().also { + every { it.workManager } returns fakeWorkManager.instance } } From 2c961793832e37884e418f3ef3f2c457f3084d2e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 10 Jun 2022 16:41:44 +0200 Subject: [PATCH 016/174] Renaming helpers to clarify purpose --- .../LiveLocationAggregationProcessorTest.kt | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index 646a2194e1..dd4ece6af3 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -164,8 +164,17 @@ internal class LiveLocationAggregationProcessorTest { ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) fakeWorkManagerProvider.fakeWorkManager.expectEnqueueUniqueWork() - val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() - val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + val aggregatedEntity = givenLastSummaryQueryReturns(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + val previousEntities = givenActiveSummaryListQueryReturns( + listOf( + LiveLocationShareAggregatedSummaryEntity( + eventId = "${AN_EVENT_ID}1", + roomId = A_ROOM_ID, + userId = A_SENDER_ID, + isActive = true + ) + ) + ) val result = liveLocationAggregationProcessor.handleBeaconInfo( realm = fakeRealm.instance, @@ -209,8 +218,18 @@ internal class LiveLocationAggregationProcessorTest { ) fakeClock.givenEpoch(A_TIMESTAMP + 5000) fakeWorkManagerProvider.fakeWorkManager.expectCancelUniqueWork() - val aggregatedEntity = mockLiveLocationShareAggregatedSummaryEntityForEvent() - val previousEntities = mockPreviousLiveLocationShareAggregatedSummaryEntities() + val aggregatedEntity = givenLastSummaryQueryReturns(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + val previousEntities = givenActiveSummaryListQueryReturns( + listOf( + LiveLocationShareAggregatedSummaryEntity( + eventId = "${AN_EVENT_ID}1", + roomId = A_ROOM_ID, + userId = A_SENDER_ID, + isActive = true + ) + ) + + ) val result = liveLocationAggregationProcessor.handleBeaconInfo( realm = fakeRealm.instance, @@ -314,7 +333,11 @@ internal class LiveLocationAggregationProcessorTest { val lastBeaconLocationContent = MessageBeaconLocationDataContent( unstableTimestampMillis = A_TIMESTAMP ) - mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + givenLastSummaryQueryReturns( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + beaconLocationContent = lastBeaconLocationContent + ) val result = liveLocationAggregationProcessor.handleBeaconLocationData( realm = fakeRealm.instance, @@ -339,7 +362,11 @@ internal class LiveLocationAggregationProcessorTest { val lastBeaconLocationContent = MessageBeaconLocationDataContent( unstableTimestampMillis = A_TIMESTAMP - 60_000 ) - val entity = mockLiveLocationShareAggregatedSummaryEntityForEvent(lastBeaconLocationContent = lastBeaconLocationContent) + val entity = givenLastSummaryQueryReturns( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + beaconLocationContent = lastBeaconLocationContent + ) val result = liveLocationAggregationProcessor.handleBeaconLocationData( realm = fakeRealm.instance, @@ -356,36 +383,32 @@ internal class LiveLocationAggregationProcessorTest { savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI } - private fun mockLiveLocationShareAggregatedSummaryEntityForEvent( - lastBeaconLocationContent: MessageBeaconLocationDataContent? = null + private fun givenLastSummaryQueryReturns( + eventId: String, + roomId: String, + beaconLocationContent: MessageBeaconLocationDataContent? = null ): LiveLocationShareAggregatedSummaryEntity { val result = LiveLocationShareAggregatedSummaryEntity( - eventId = AN_EVENT_ID, - roomId = A_ROOM_ID, - lastLocationContent = ContentMapper.map(lastBeaconLocationContent?.toContent()) + eventId = eventId, + roomId = roomId, + lastLocationContent = ContentMapper.map(beaconLocationContent?.toContent()) ) fakeQuery - .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) - .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, roomId) .givenFindFirst(result) return result } - private fun mockPreviousLiveLocationShareAggregatedSummaryEntities(): List { - val results = listOf( - LiveLocationShareAggregatedSummaryEntity( - eventId = "", - roomId = A_ROOM_ID, - userId = A_SENDER_ID, - isActive = true - ) - ) + private fun givenActiveSummaryListQueryReturns( + summaryList: List + ): List { fakeQuery .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, A_ROOM_ID) .givenNotEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID) .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.USER_ID, A_SENDER_ID) .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) - .givenFindAll(results) - return results + .givenFindAll(summaryList) + return summaryList } } From ac4b33647d9828e82a7695e7626b6ed3f75a1ab3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 10:09:09 +0200 Subject: [PATCH 017/174] Mutualizing some similar tests with different parameters --- .../LiveLocationAggregationProcessorTest.kt | 219 +++++++++--------- 1 file changed, 105 insertions(+), 114 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt index dd4ece6af3..e6d63f5e5e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt @@ -77,78 +77,71 @@ internal class LiveLocationAggregationProcessorTest { result shouldBeEqualTo false } - @Test - fun `given beacon info and event when senderId is null or empty then it is ignored`() { - val eventNoSenderId = Event(eventId = AN_EVENT_ID) - val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") - val beaconInfo = MessageBeaconInfoContent() - - val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventNoSenderId, - content = beaconInfo, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventEmptySenderId, - content = beaconInfo, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - - resultNoSenderId shouldBeEqualTo false - resultEmptySenderId shouldBeEqualTo false - } + private data class IgnoredBeaconInfoEvent( + val event: Event, + val beaconInfo: MessageBeaconInfoContent + ) @Test - fun `given beacon info when no target eventId is found then it is ignored`() { - val unsignedDataWithNoEventId = UnsignedData( - age = 123 - ) - val unsignedDataWithEmptyEventId = UnsignedData( - age = 123, - replacesState = "" - ) - val eventWithNoEventId = Event(senderId = A_SENDER_ID, unsignedData = unsignedDataWithNoEventId) - val eventWithEmptyEventId = Event(senderId = A_SENDER_ID, eventId = "", unsignedData = unsignedDataWithEmptyEventId) - val beaconInfoLive = MessageBeaconInfoContent(isLive = true) - val beaconInfoNotLive = MessageBeaconInfoContent(isLive = false) - - val resultLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithNoEventId, - content = beaconInfoLive, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithEmptyEventId, - content = beaconInfoLive, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultNotLiveNoEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithNoEventId, - content = beaconInfoNotLive, - roomId = A_ROOM_ID, - isLocalEcho = false - ) - val resultNotLiveEmptyEventId = liveLocationAggregationProcessor.handleBeaconInfo( - realm = fakeRealm.instance, - event = eventWithEmptyEventId, - content = beaconInfoNotLive, - roomId = A_ROOM_ID, - isLocalEcho = false + fun `given beacon info and event when some values are missing then it is ignored`() { + val ignoredInfoEvents = listOf( + // missing senderId + IgnoredBeaconInfoEvent( + event = Event(eventId = AN_EVENT_ID, senderId = null), + beaconInfo = MessageBeaconInfoContent() + ), + // empty senderId + IgnoredBeaconInfoEvent( + event = Event(eventId = AN_EVENT_ID, senderId = ""), + beaconInfo = MessageBeaconInfoContent() + ), + // beacon is live and no eventId + IgnoredBeaconInfoEvent( + event = Event(eventId = null, senderId = A_SENDER_ID), + beaconInfo = MessageBeaconInfoContent(isLive = true) + ), + // beacon is live and eventId is empty + IgnoredBeaconInfoEvent( + event = Event(eventId = "", senderId = A_SENDER_ID), + beaconInfo = MessageBeaconInfoContent(isLive = true) + ), + // beacon is not live and replaced event id is null + IgnoredBeaconInfoEvent( + event = Event( + eventId = AN_EVENT_ID, + senderId = A_SENDER_ID, + unsignedData = UnsignedData( + age = 123, + replacesState = null + ) + ), + beaconInfo = MessageBeaconInfoContent(isLive = false) + ), + // beacon is not live and replaced event id is empty + IgnoredBeaconInfoEvent( + event = Event( + eventId = AN_EVENT_ID, + senderId = A_SENDER_ID, + unsignedData = UnsignedData( + age = 123, + replacesState = "" + ) + ), + beaconInfo = MessageBeaconInfoContent(isLive = false) + ), ) - resultLiveNoEventId shouldBeEqualTo false - resultLiveEmptyEventId shouldBeEqualTo false - resultNotLiveNoEventId shouldBeEqualTo false - resultNotLiveEmptyEventId shouldBeEqualTo false + ignoredInfoEvents.forEach { + val result = liveLocationAggregationProcessor.handleBeaconInfo( + realm = fakeRealm.instance, + event = it.event, + content = it.beaconInfo, + roomId = A_ROOM_ID, + isLocalEcho = false + ) + + result shouldBeEqualTo false + } } @Test @@ -271,57 +264,55 @@ internal class LiveLocationAggregationProcessorTest { result shouldBeEqualTo false } + private data class IgnoredBeaconLocationDataEvent( + val event: Event, + val beaconLocationData: MessageBeaconLocationDataContent + ) + + @Test + fun `given event and beacon location data when some values are missing then it is ignored`() { + val ignoredLocationDataEvents = listOf( + // missing sender id + IgnoredBeaconLocationDataEvent( + event = Event(eventId = AN_EVENT_ID), + beaconLocationData = MessageBeaconLocationDataContent() + ), + // empty sender id + IgnoredBeaconLocationDataEvent( + event = Event(eventId = AN_EVENT_ID, senderId = ""), + beaconLocationData = MessageBeaconLocationDataContent() + ), + ) + + ignoredLocationDataEvents.forEach { + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = it.event, + content = it.beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = "", + isLocalEcho = false + ) + result shouldBeEqualTo false + } + } + @Test fun `given beacon location data when relatedEventId is null or empty then it is ignored`() { val event = Event(senderId = A_SENDER_ID) val beaconLocationData = MessageBeaconLocationDataContent() - val resultNoRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = event, - content = beaconLocationData, - roomId = A_ROOM_ID, - relatedEventId = null, - isLocalEcho = false - ) - val resultEmptyRelatedEventId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = event, - content = beaconLocationData, - roomId = A_ROOM_ID, - relatedEventId = "", - isLocalEcho = false - ) - - resultNoRelatedEventId shouldBeEqualTo false - resultEmptyRelatedEventId shouldBeEqualTo false - } - - @Test - fun `given beacon location data and event when senderId is null or empty then it is ignored`() { - val eventNoSenderId = Event(eventId = AN_EVENT_ID) - val eventEmptySenderId = Event(eventId = AN_EVENT_ID, senderId = "") - val beaconLocationData = MessageBeaconLocationDataContent() - - val resultNoSenderId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = eventNoSenderId, - content = beaconLocationData, - roomId = "", - relatedEventId = AN_EVENT_ID, - isLocalEcho = false - ) - val resultEmptySenderId = liveLocationAggregationProcessor.handleBeaconLocationData( - realm = fakeRealm.instance, - event = eventEmptySenderId, - content = beaconLocationData, - roomId = A_ROOM_ID, - relatedEventId = AN_EVENT_ID, - isLocalEcho = false - ) - - resultNoSenderId shouldBeEqualTo false - resultEmptySenderId shouldBeEqualTo false + listOf(null, "").forEach { + val result = liveLocationAggregationProcessor.handleBeaconLocationData( + realm = fakeRealm.instance, + event = event, + content = beaconLocationData, + roomId = A_ROOM_ID, + relatedEventId = it, + isLocalEcho = false + ) + result shouldBeEqualTo false + } } @Test From 3db8e0b045032121d7adedd49462476be84c32d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jun 2022 23:18:10 +0000 Subject: [PATCH 018/174] Bump flipper-network-plugin from 0.149.0 to 0.150.0 Bumps [flipper-network-plugin](https://github.com/facebook/flipper) from 0.149.0 to 0.150.0. - [Release notes](https://github.com/facebook/flipper/releases) - [Commits](https://github.com/facebook/flipper/compare/v0.149.0...v0.150.0) --- updated-dependencies: - dependency-name: com.facebook.flipper:flipper-network-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- vector/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/build.gradle b/vector/build.gradle index 8d704141e5..f3761c4c78 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -532,7 +532,7 @@ dependencies { debugImplementation('com.facebook.flipper:flipper:0.149.0') { exclude group: 'com.facebook.fbjni', module: 'fbjni' } - debugImplementation('com.facebook.flipper:flipper-network-plugin:0.149.0') { + debugImplementation('com.facebook.flipper:flipper-network-plugin:0.150.0') { exclude group: 'com.facebook.fbjni', module: 'fbjni' } debugImplementation 'com.facebook.soloader:soloader:0.10.3' From 29f48249e2670c99a0ea91af53b9c9cdc6d21d9f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 9 Jun 2022 15:32:28 +0200 Subject: [PATCH 019/174] Start live location share API --- .../room/location/LocationSharingService.kt | 10 ++++ .../api/session/room/state/StateService.kt | 2 + .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../location/DefaultLocationSharingService.kt | 9 +++ .../location/StartLiveLocationShareTask.kt | 57 +++++++++++++++++++ .../location/LocationSharingService.kt | 20 +------ 6 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index dd48d51f45..418ba6fbf2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -23,5 +23,15 @@ import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationSh * Manage all location sharing related features. */ interface LocationSharingService { + /** + * Starts sharing live location in the room. + * @param timeoutMillis timeout of the live in milliseconds + * @return the id of the created beacon info event + */ + suspend fun startLiveLocationShare(timeoutMillis: Long): String + + /** + * Returns a LiveData on the list of current running live location shares. + */ fun getRunningLiveLocationShareSummaries(): LiveData> } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 49c0debe1b..8fb3afe36b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -66,12 +66,14 @@ interface StateService { */ suspend fun deleteAvatar() + // TODO delete /** * Stops sharing live location in the room. * @param userId user id */ suspend fun stopLiveLocation(userId: String) + // TODO delete /** * Returns beacon info state event of a user. * @param userId user id who is sharing location 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 f3845f1f15..164b6cd4e5 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 @@ -51,6 +51,8 @@ import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDire import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask +import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask +import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask @@ -299,4 +301,7 @@ internal abstract class RoomModule { @Binds abstract fun bindFetchThreadSummariesTask(task: DefaultFetchThreadSummariesTask): FetchThreadSummariesTask + + @Binds + abstract fun bindStartLiveLocationShareTask(task: DefaultStartLiveLocationShareTask): StartLiveLocationShareTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index 8cf6fcdfbf..078c261b8a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -32,6 +32,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase internal class DefaultLocationSharingService @AssistedInject constructor( @Assisted private val roomId: String, @SessionDatabase private val monarchy: Monarchy, + private val startLiveLocationShareTask: StartLiveLocationShareTask, private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper, ) : LocationSharingService { @@ -40,6 +41,14 @@ internal class DefaultLocationSharingService @AssistedInject constructor( fun create(roomId: String): DefaultLocationSharingService } + override suspend fun startLiveLocationShare(timeoutMillis: Long): String { + val params = StartLiveLocationShareTask.Params( + roomId = roomId, + timeoutMillis = timeoutMillis + ) + return startLiveLocationShareTask.execute(params) + } + override fun getRunningLiveLocationShareSummaries(): LiveData> { return monarchy.findAllMappedWithChanges( { LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) }, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt new file mode 100644 index 0000000000..85bd90cf20 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt @@ -0,0 +1,57 @@ +/* + * 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.location + +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.session.room.state.SendStateTask +import org.matrix.android.sdk.internal.task.Task +import org.matrix.android.sdk.internal.util.time.Clock +import javax.inject.Inject + +internal interface StartLiveLocationShareTask : Task { + data class Params( + val roomId: String, + val timeoutMillis: Long, + ) +} + +// TODO add unit tests +internal class DefaultStartLiveLocationShareTask @Inject constructor( + @UserId private val userId: String, + private val clock: Clock, + private val sendStateTask: SendStateTask +) : StartLiveLocationShareTask { + + override suspend fun execute(params: StartLiveLocationShareTask.Params): String { + val beaconContent = MessageBeaconInfoContent( + timeout = params.timeoutMillis, + isLive = true, + unstableTimestampMillis = clock.epochMillis() + ).toContent() + val eventType = EventType.STATE_ROOM_BEACON_INFO.first() + val sendStateTaskParams = SendStateTask.Params( + roomId = params.roomId, + stateKey = userId, + eventType = eventType, + body = beaconContent + ) + return sendStateTask.executeRetry(sendStateTaskParams, 3) + } +} diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 62aba9318c..c9479f4536 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -23,16 +23,12 @@ import android.os.Parcelable import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.services.VectorService -import im.vector.app.core.time.Clock import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.getRoom -import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import timber.log.Timber import java.util.Timer import java.util.TimerTask @@ -51,7 +47,6 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { @Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var locationTracker: LocationTracker @Inject lateinit var activeSessionHolder: ActiveSessionHolder - @Inject lateinit var clock: Clock private val binder = LocalBinder() @@ -97,21 +92,10 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } private suspend fun sendStartingLiveBeaconInfo(session: Session, roomArgs: RoomArgs) { - val beaconContent = MessageBeaconInfoContent( - timeout = roomArgs.durationMillis, - isLive = true, - unstableTimestampMillis = clock.epochMillis() - ).toContent() - - val stateKey = session.myUserId val beaconEventId = session .getRoom(roomArgs.roomId) - ?.stateService() - ?.sendStateEvent( - eventType = EventType.STATE_ROOM_BEACON_INFO.first(), - stateKey = stateKey, - body = beaconContent - ) + ?.locationSharingService() + ?.startLiveLocationShare(timeoutMillis = roomArgs.durationMillis) beaconEventId ?.takeUnless { it.isEmpty() } From 632064ffdedfa03b0d920cc96968c57e4d9edfed Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 9 Jun 2022 16:19:40 +0200 Subject: [PATCH 020/174] Stop live location share API --- .../room/location/LocationSharingService.kt | 5 ++ .../api/session/room/state/StateService.kt | 15 ---- .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../location/DefaultLocationSharingService.kt | 8 +++ .../location/StartLiveLocationShareTask.kt | 2 +- .../location/StopLiveLocationShareTask.kt | 72 +++++++++++++++++++ .../session/room/state/DefaultStateService.kt | 37 ---------- .../location/LocationSharingService.kt | 4 +- 8 files changed, 94 insertions(+), 54 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index 418ba6fbf2..5f80aa5c9b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -30,6 +30,11 @@ interface LocationSharingService { */ suspend fun startLiveLocationShare(timeoutMillis: Long): String + /** + * Stops sharing live location in the room. + */ + suspend fun stopLiveLocationShare() + /** * Returns a LiveData on the list of current running live location shares. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 8fb3afe36b..6ca63c2c49 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -66,21 +66,6 @@ interface StateService { */ suspend fun deleteAvatar() - // TODO delete - /** - * Stops sharing live location in the room. - * @param userId user id - */ - suspend fun stopLiveLocation(userId: String) - - // TODO delete - /** - * Returns beacon info state event of a user. - * @param userId user id who is sharing location - * @param filterOnlyLive filters only ongoing live location sharing beacons if true else ended event is included - */ - suspend fun getLiveLocationBeaconInfo(userId: String, filterOnlyLive: Boolean): Event? - /** * Send a state event to the room. * @param eventType The type of event to send. 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 164b6cd4e5..0c85993c54 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 @@ -52,7 +52,9 @@ import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask +import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask +import org.matrix.android.sdk.internal.session.room.location.StopLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask @@ -304,4 +306,7 @@ internal abstract class RoomModule { @Binds abstract fun bindStartLiveLocationShareTask(task: DefaultStartLiveLocationShareTask): StartLiveLocationShareTask + + @Binds + abstract fun bindStopLiveLocationShareTask(task: DefaultStopLiveLocationShareTask): StopLiveLocationShareTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index 078c261b8a..43a10f6785 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -33,6 +33,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor( @Assisted private val roomId: String, @SessionDatabase private val monarchy: Monarchy, private val startLiveLocationShareTask: StartLiveLocationShareTask, + private val stopLiveLocationShareTask: StopLiveLocationShareTask, private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper, ) : LocationSharingService { @@ -49,6 +50,13 @@ internal class DefaultLocationSharingService @AssistedInject constructor( return startLiveLocationShareTask.execute(params) } + override suspend fun stopLiveLocationShare() { + val params = StopLiveLocationShareTask.Params( + roomId = roomId, + ) + return stopLiveLocationShareTask.execute(params) + } + override fun getRunningLiveLocationShareSummaries(): LiveData> { return monarchy.findAllMappedWithChanges( { LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) }, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt index 85bd90cf20..c13f625a41 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt @@ -36,7 +36,7 @@ internal interface StartLiveLocationShareTask : Task { + data class Params( + val roomId: String, + ) +} + +// TODO add unit tests +internal class DefaultStopLiveLocationShareTask @Inject constructor( + @UserId private val userId: String, + private val sendStateTask: SendStateTask, + private val stateEventDataSource: StateEventDataSource, +) : StopLiveLocationShareTask { + + override suspend fun execute(params: StopLiveLocationShareTask.Params) { + val beaconInfoStateEvent = getLiveLocationBeaconInfoForUser(userId, params.roomId) ?: return + val stateKey = beaconInfoStateEvent.stateKey ?: return + val content = beaconInfoStateEvent.getClearContent()?.toModel() ?: return + val updatedContent = content.copy(isLive = false).toContent() + val sendStateTaskParams = SendStateTask.Params( + roomId = params.roomId, + stateKey = stateKey, + eventType = EventType.STATE_ROOM_BEACON_INFO.first(), + body = updatedContent + ) + sendStateTask.executeRetry(sendStateTaskParams, 3) + } + + private fun getLiveLocationBeaconInfoForUser(userId: String, roomId: String): Event? { + return EventType.STATE_ROOM_BEACON_INFO + .mapNotNull { + stateEventDataSource.getStateEvent( + roomId = roomId, + eventType = it, + stateKey = QueryStringValue.Equals(userId) + ) + } + .firstOrNull { beaconInfoEvent -> + beaconInfoEvent.getClearContent()?.toModel()?.isLive.orFalse() + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index c15bcb1c1a..ad47b82428 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -21,33 +21,27 @@ import androidx.lifecycle.LiveData import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.query.QueryStateEventValue -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.events.model.EventType 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.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent -import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.api.session.room.state.StateService import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.MimeTypes import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.session.content.FileUploader -import org.matrix.android.sdk.internal.session.permalinks.ViaParameterFinder internal class DefaultStateService @AssistedInject constructor( @Assisted private val roomId: String, private val stateEventDataSource: StateEventDataSource, private val sendStateTask: SendStateTask, private val fileUploader: FileUploader, - private val viaParameterFinder: ViaParameterFinder ) : StateService { @AssistedFactory @@ -191,35 +185,4 @@ internal class DefaultStateService @AssistedInject constructor( } updateJoinRule(RoomJoinRules.RESTRICTED, null, allowEntries) } - - override suspend fun stopLiveLocation(userId: String) { - getLiveLocationBeaconInfo(userId, true)?.let { beaconInfoStateEvent -> - beaconInfoStateEvent.getClearContent()?.toModel()?.let { content -> - val updatedContent = content.copy(isLive = false).toContent() - - beaconInfoStateEvent.stateKey?.let { - sendStateEvent( - eventType = EventType.STATE_ROOM_BEACON_INFO.first(), - body = updatedContent, - stateKey = it - ) - } - } - } - } - - override suspend fun getLiveLocationBeaconInfo(userId: String, filterOnlyLive: Boolean): Event? { - return EventType.STATE_ROOM_BEACON_INFO - .mapNotNull { - stateEventDataSource.getStateEvent( - roomId = roomId, - eventType = it, - stateKey = QueryStringValue.Equals(userId) - ) - } - .firstOrNull { beaconInfoEvent -> - !filterOnlyLive || - beaconInfoEvent.getClearContent()?.toModel()?.isLive.orFalse() - } - } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index c9479f4536..39fc526f80 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -147,7 +147,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { .getSafeActiveSession() ?.let { session -> session.coroutineScope.launch(session.coroutineDispatchers.io) { - session.getRoom(roomId)?.stateService()?.stopLiveLocation(session.myUserId) + session.getRoom(roomId) + ?.locationSharingService() + ?.stopLiveLocationShare() } } } From 9b61c1aeadf7066314abb19ed4c1d59160f6272d Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 9 Jun 2022 17:10:33 +0200 Subject: [PATCH 021/174] Send static location API --- .../room/location/LocationSharingService.kt | 10 ++++ .../sdk/api/session/room/send/SendService.kt | 9 ---- .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../location/DefaultLocationSharingService.kt | 13 +++++ .../room/location/SendStaticLocationTask.kt | 52 +++++++++++++++++++ .../session/room/send/DefaultSendService.kt | 6 --- .../location/LocationSharingViewModel.kt | 16 +++--- 7 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index 5f80aa5c9b..3ff629ac7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -18,11 +18,21 @@ package org.matrix.android.sdk.api.session.room.location import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary +import org.matrix.android.sdk.api.util.Cancelable /** * Manage all location sharing related features. */ interface LocationSharingService { + /** + * Send a static location event to the room. + * @param latitude required latitude of the location + * @param longitude required longitude of the location + * @param uncertainty Accuracy of the location in meters + * @param isUserLocation indicates whether the location data corresponds to the user location or not (pinned location) + */ + suspend fun sendStaticLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable + /** * Starts sharing live location in the room. * @param timeoutMillis timeout of the live in milliseconds diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 661c3be5bd..201e6c5c0c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -142,15 +142,6 @@ interface SendService { */ fun resendMediaMessage(localEcho: TimelineEvent): Cancelable - /** - * Send a location event to the room. - * @param latitude required latitude of the location - * @param longitude required longitude of the location - * @param uncertainty Accuracy of the location in meters - * @param isUserLocation indicates whether the location data corresponds to the user location or not - */ - fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable - /** * Send a live location event to the room. beacon_info state event has to be sent before sending live location updates. * @param beaconInfoEventId event id of the initial beacon info state event 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 0c85993c54..ee332890e4 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 @@ -51,8 +51,10 @@ import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDire import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask +import org.matrix.android.sdk.internal.session.room.location.DefaultSendStaticLocationTask import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask +import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.location.StopLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask @@ -309,4 +311,7 @@ internal abstract class RoomModule { @Binds abstract fun bindStopLiveLocationShareTask(task: DefaultStopLiveLocationShareTask): StopLiveLocationShareTask + + @Binds + abstract fun bindSendStaticLocationTask(task: DefaultSendStaticLocationTask): SendStaticLocationTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index 43a10f6785..407ae60e4b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -23,6 +23,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.session.room.location.LocationSharingService import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary +import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.query.findRunningLiveInRoom @@ -32,6 +33,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase internal class DefaultLocationSharingService @AssistedInject constructor( @Assisted private val roomId: String, @SessionDatabase private val monarchy: Monarchy, + private val sendStaticLocationTask: SendStaticLocationTask, private val startLiveLocationShareTask: StartLiveLocationShareTask, private val stopLiveLocationShareTask: StopLiveLocationShareTask, private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper, @@ -42,6 +44,17 @@ internal class DefaultLocationSharingService @AssistedInject constructor( fun create(roomId: String): DefaultLocationSharingService } + override suspend fun sendStaticLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable { + val params = SendStaticLocationTask.Params( + roomId = roomId, + latitude = latitude, + longitude = longitude, + uncertainty = uncertainty, + isUserLocation = isUserLocation + ) + return sendStaticLocationTask.execute(params) + } + override suspend fun startLiveLocationShare(timeoutMillis: Long): String { val params = StartLiveLocationShareTask.Params( roomId = roomId, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt new file mode 100644 index 0000000000..d26609196f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt @@ -0,0 +1,52 @@ +/* + * 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.location + +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface SendStaticLocationTask : Task { + data class Params( + val roomId: String, + val latitude: Double, + val longitude: Double, + val uncertainty: Double?, + val isUserLocation: Boolean + ) +} + +// TODO add unit tests +internal class DefaultSendStaticLocationTask @Inject constructor( + private val localEchoEventFactory: LocalEchoEventFactory, + private val eventSenderProcessor: EventSenderProcessor, +) : SendStaticLocationTask { + + override suspend fun execute(params: SendStaticLocationTask.Params): Cancelable { + val event = localEchoEventFactory.createLocationEvent( + roomId = params.roomId, + latitude = params.latitude, + longitude = params.longitude, + uncertainty = params.uncertainty, + isUserLocation = params.isUserLocation + ) + localEchoEventFactory.createLocalEcho(event) + return eventSenderProcessor.postEvent(event) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index fc78abcfd9..e83175151e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -129,12 +129,6 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } - override fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable { - return localEchoEventFactory.createLocationEvent(roomId, latitude, longitude, uncertainty, isUserLocation) - .also { createLocalEcho(it) } - .let { sendEvent(it) } - } - override fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable { return localEchoEventFactory.createLiveLocationEvent(beaconInfoEventId, roomId, latitude, longitude, uncertainty) .also { createLocalEcho(it) } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index 71f59c6fdf..30476d064f 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -136,13 +136,15 @@ class LocationSharingViewModel @AssistedInject constructor( private fun shareLocation(locationData: LocationData?, isUserLocation: Boolean) { locationData?.let { location -> - room.sendService().sendLocation( - latitude = location.latitude, - longitude = location.longitude, - uncertainty = location.uncertainty, - isUserLocation = isUserLocation - ) - _viewEvents.post(LocationSharingViewEvents.Close) + viewModelScope.launch { + room.locationSharingService().sendStaticLocation( + latitude = location.latitude, + longitude = location.longitude, + uncertainty = location.uncertainty, + isUserLocation = isUserLocation + ) + _viewEvents.post(LocationSharingViewEvents.Close) + } } ?: run { _viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError) } From 7b159c5b71ec879b25ee7435d3ec80194072037a Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 9 Jun 2022 17:40:14 +0200 Subject: [PATCH 022/174] Send live location API --- .../room/location/LocationSharingService.kt | 10 ++++ .../sdk/api/session/room/send/SendService.kt | 9 --- .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../location/DefaultLocationSharingService.kt | 14 ++++- .../room/location/SendLiveLocationTask.kt | 52 +++++++++++++++++ .../session/room/send/DefaultSendService.kt | 6 -- .../location/LocationSharingService.kt | 57 +++++++++---------- 7 files changed, 108 insertions(+), 45 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index 3ff629ac7a..11b74ecd7f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -33,6 +33,16 @@ interface LocationSharingService { */ suspend fun sendStaticLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable + /** + * Send a live location event to the room. + * To get the beacon info event id, [startLiveLocationShare] must be called before sending live location updates. + * @param beaconInfoEventId event id of the initial beacon info state event + * @param latitude required latitude of the location + * @param longitude required longitude of the location + * @param uncertainty Accuracy of the location in meters + */ + suspend fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable + /** * Starts sharing live location in the room. * @param timeoutMillis timeout of the live in milliseconds diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 201e6c5c0c..9cf062356f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -142,15 +142,6 @@ interface SendService { */ fun resendMediaMessage(localEcho: TimelineEvent): Cancelable - /** - * Send a live location event to the room. beacon_info state event has to be sent before sending live location updates. - * @param beaconInfoEventId event id of the initial beacon info state event - * @param latitude required latitude of the location - * @param longitude required longitude of the location - * @param uncertainty Accuracy of the location in meters - */ - fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable - /** * Remove this failed message from the timeline. * @param localEcho the unsent local echo 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 ee332890e4..271e82a1e0 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 @@ -51,9 +51,11 @@ import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDire import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask +import org.matrix.android.sdk.internal.session.room.location.DefaultSendLiveLocationTask import org.matrix.android.sdk.internal.session.room.location.DefaultSendStaticLocationTask import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask +import org.matrix.android.sdk.internal.session.room.location.SendLiveLocationTask import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask import org.matrix.android.sdk.internal.session.room.location.StopLiveLocationShareTask @@ -314,4 +316,7 @@ internal abstract class RoomModule { @Binds abstract fun bindSendStaticLocationTask(task: DefaultSendStaticLocationTask): SendStaticLocationTask + + @Binds + abstract fun bindSendLiveLocationTask(task: DefaultSendLiveLocationTask): SendLiveLocationTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index 407ae60e4b..ba6e7bd3fa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -34,6 +34,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor( @Assisted private val roomId: String, @SessionDatabase private val monarchy: Monarchy, private val sendStaticLocationTask: SendStaticLocationTask, + private val sendLiveLocationTask: SendLiveLocationTask, private val startLiveLocationShareTask: StartLiveLocationShareTask, private val stopLiveLocationShareTask: StopLiveLocationShareTask, private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper, @@ -50,11 +51,22 @@ internal class DefaultLocationSharingService @AssistedInject constructor( latitude = latitude, longitude = longitude, uncertainty = uncertainty, - isUserLocation = isUserLocation + isUserLocation = isUserLocation, ) return sendStaticLocationTask.execute(params) } + override suspend fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable { + val params = SendLiveLocationTask.Params( + beaconInfoEventId = beaconInfoEventId, + roomId = roomId, + latitude = latitude, + longitude = longitude, + uncertainty = uncertainty, + ) + return sendLiveLocationTask.execute(params) + } + override suspend fun startLiveLocationShare(timeoutMillis: Long): String { val params = StartLiveLocationShareTask.Params( roomId = roomId, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt new file mode 100644 index 0000000000..ed40c82b66 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt @@ -0,0 +1,52 @@ +/* + * 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.location + +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory +import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface SendLiveLocationTask : Task { + data class Params( + val roomId: String, + val beaconInfoEventId: String, + val latitude: Double, + val longitude: Double, + val uncertainty: Double?, + ) +} + +// TODO add unit tests +internal class DefaultSendLiveLocationTask @Inject constructor( + private val localEchoEventFactory: LocalEchoEventFactory, + private val eventSenderProcessor: EventSenderProcessor, +) : SendLiveLocationTask { + + override suspend fun execute(params: SendLiveLocationTask.Params): Cancelable { + val event = localEchoEventFactory.createLiveLocationEvent( + beaconInfoEventId = params.beaconInfoEventId, + roomId = params.roomId, + latitude = params.latitude, + longitude = params.longitude, + uncertainty = params.uncertainty, + ) + localEchoEventFactory.createLocalEcho(event) + return eventSenderProcessor.postEvent(event) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index e83175151e..418000abed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -129,12 +129,6 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } - override fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable { - return localEchoEventFactory.createLiveLocationEvent(beaconInfoEventId, roomId, latitude, longitude, uncertainty) - .also { createLocalEcho(it) } - .let { sendEvent(it) } - } - override fun redactEvent(event: Event, reason: String?): Cancelable { // TODO manage media/attachements? val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 39fc526f80..77f3abcc28 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -25,6 +25,7 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.services.VectorService import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.session.coroutineScope +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session @@ -79,13 +80,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { scheduleTimer(roomArgs.roomId, roomArgs.durationMillis) // Send beacon info state event - activeSessionHolder - .getSafeActiveSession() - ?.let { session -> - session.coroutineScope.launch(session.coroutineDispatchers.io) { - sendStartingLiveBeaconInfo(session, roomArgs) - } - } + launchInIO { session -> + sendStartingLiveBeaconInfo(session, roomArgs) + } } return START_STICKY @@ -143,15 +140,11 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } private fun sendStoppedBeaconInfo(roomId: String) { - activeSessionHolder - .getSafeActiveSession() - ?.let { session -> - session.coroutineScope.launch(session.coroutineDispatchers.io) { - session.getRoom(roomId) - ?.locationSharingService() - ?.stopLiveLocationShare() - } - } + launchInIO { session -> + session.getRoom(roomId) + ?.locationSharingService() + ?.stopLiveLocationShare() + } } override fun onLocationUpdate(locationData: LocationData) { @@ -168,20 +161,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { beaconInfoEventId: String, locationData: LocationData ) { - val session = activeSessionHolder.getSafeActiveSession() - val room = session?.getRoom(roomId) - val userId = session?.myUserId - - if (room == null || userId == null) { - return + launchInIO { session -> + session.getRoom(roomId) + ?.locationSharingService() + ?.sendLiveLocation( + beaconInfoEventId = beaconInfoEventId, + latitude = locationData.latitude, + longitude = locationData.longitude, + uncertainty = locationData.uncertainty + ) } - - room.sendService().sendLiveLocation( - beaconInfoEventId = beaconInfoEventId, - latitude = locationData.latitude, - longitude = locationData.longitude, - uncertainty = locationData.uncertainty - ) } override fun onNoLocationProviderAvailable() { @@ -202,6 +191,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { destroyMe() } + private fun launchInIO(block: suspend CoroutineScope.(Session) -> Unit) = + activeSessionHolder + .getSafeActiveSession() + ?.let { session -> + session.coroutineScope.launch( + context = session.coroutineDispatchers.io, + block = { block(session) } + ) + } + override fun onBind(intent: Intent?): IBinder { return binder } From 752434acb4d84cbc58d691bfd87d1e3c8eda072f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 9 Jun 2022 17:44:24 +0200 Subject: [PATCH 023/174] Adding changelog entry --- changelog.d/5864.sdk | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5864.sdk diff --git a/changelog.d/5864.sdk b/changelog.d/5864.sdk new file mode 100644 index 0000000000..b0a9d1c67d --- /dev/null +++ b/changelog.d/5864.sdk @@ -0,0 +1 @@ +Group all location sharing related API into LocationSharingService From 7d4df8be0910dea6b4a6d6e924fec87d0082dd5e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 16:32:45 +0200 Subject: [PATCH 024/174] Unit test for method to get live data of active lives --- ...iveLocationShareAggregatedSummaryMapper.kt | 6 +- .../location/DefaultLocationSharingService.kt | 3 +- .../DefaultLocationSharingServiceTest.kt | 85 +++++++++++++++++++ .../android/sdk/test/fakes/FakeMonarchy.kt | 40 +++++++-- .../android/sdk/test/fakes/FakeRealm.kt | 63 ++++++++++++++ 5 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LiveLocationShareAggregatedSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LiveLocationShareAggregatedSummaryMapper.kt index 9460e4c6ba..4a4c730a0b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LiveLocationShareAggregatedSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/LiveLocationShareAggregatedSummaryMapper.kt @@ -16,15 +16,17 @@ package org.matrix.android.sdk.internal.database.mapper +import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import javax.inject.Inject -internal class LiveLocationShareAggregatedSummaryMapper @Inject constructor() { +internal class LiveLocationShareAggregatedSummaryMapper @Inject constructor() : + Monarchy.Mapper { - fun map(entity: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary { + override fun map(entity: LiveLocationShareAggregatedSummaryEntity): LiveLocationShareAggregatedSummary { return LiveLocationShareAggregatedSummary( userId = entity.userId, isActive = entity.isActive, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index ba6e7bd3fa..3fa00fa077 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -29,7 +29,6 @@ import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationS import org.matrix.android.sdk.internal.database.query.findRunningLiveInRoom import org.matrix.android.sdk.internal.di.SessionDatabase -// TODO add unit tests internal class DefaultLocationSharingService @AssistedInject constructor( @Assisted private val roomId: String, @SessionDatabase private val monarchy: Monarchy, @@ -85,7 +84,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor( override fun getRunningLiveLocationShareSummaries(): LiveData> { return monarchy.findAllMappedWithChanges( { LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) }, - { liveLocationShareAggregatedSummaryMapper.map(it) } + liveLocationShareAggregatedSummaryMapper ) } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt new file mode 100644 index 0000000000..42db5761d7 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt @@ -0,0 +1,85 @@ +/* + * 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.location + +import io.mockk.mockk +import io.mockk.unmockkAll +import org.amshove.kluent.shouldBeEqualTo +import org.junit.After +import org.junit.Test +import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary +import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper +import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity +import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields +import org.matrix.android.sdk.test.fakes.FakeMonarchy +import org.matrix.android.sdk.test.fakes.givenEqualTo +import org.matrix.android.sdk.test.fakes.givenIsNotEmpty +import org.matrix.android.sdk.test.fakes.givenIsNotNull + +private const val A_ROOM_ID = "room_id" + +internal class DefaultLocationSharingServiceTest { + + private val fakeRoomId = A_ROOM_ID + private val fakeMonarchy = FakeMonarchy() + private val sendStaticLocationTask = mockk() + private val sendLiveLocationTask = mockk() + private val startLiveLocationShareTask = mockk() + private val stopLiveLocationShareTask = mockk() + private val fakeLiveLocationShareAggregatedSummaryMapper = mockk() + + private val defaultLocationSharingService = DefaultLocationSharingService( + roomId = fakeRoomId, + monarchy = fakeMonarchy.instance, + sendStaticLocationTask = sendStaticLocationTask, + sendLiveLocationTask = sendLiveLocationTask, + startLiveLocationShareTask = startLiveLocationShareTask, + stopLiveLocationShareTask = stopLiveLocationShareTask, + liveLocationShareAggregatedSummaryMapper = fakeLiveLocationShareAggregatedSummaryMapper + ) + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `livedata of live summaries is correctly computed`() { + val entity = LiveLocationShareAggregatedSummaryEntity() + val summary = LiveLocationShareAggregatedSummary( + userId = "", + isActive = true, + endOfLiveTimestampMillis = 123, + lastLocationDataContent = null + ) + + fakeMonarchy.givenWhere() + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.ROOM_ID, fakeRoomId) + .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.IS_ACTIVE, true) + .givenIsNotEmpty(LiveLocationShareAggregatedSummaryEntityFields.USER_ID) + .givenIsNotNull(LiveLocationShareAggregatedSummaryEntityFields.LAST_LOCATION_CONTENT) + fakeMonarchy.givenFindAllMappedWithChangesReturns( + realmEntities = listOf(entity), + mappedResult = listOf(summary), + fakeLiveLocationShareAggregatedSummaryMapper + ) + + val result = defaultLocationSharingService.getRunningLiveLocationShareSummaries().value + + result shouldBeEqualTo listOf(summary) + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt index 0a22ef8996..9b4ca332d5 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeMonarchy.kt @@ -16,40 +16,62 @@ package org.matrix.android.sdk.test.fakes +import androidx.lifecycle.MutableLiveData import com.zhuinden.monarchy.Monarchy import io.mockk.MockKVerificationScope import io.mockk.coEvery import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic -import io.mockk.verify +import io.mockk.slot import io.realm.Realm import io.realm.RealmModel import io.realm.RealmQuery -import io.realm.kotlin.where import org.matrix.android.sdk.internal.util.awaitTransaction internal class FakeMonarchy { val instance = mockk() - private val realm = mockk(relaxed = true) + private val fakeRealm = FakeRealm() init { mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt") coEvery { instance.awaitTransaction(any Any>()) } coAnswers { - secondArg Any>().invoke(realm) + secondArg Any>().invoke(fakeRealm.instance) } } - inline fun givenWhereReturns(result: T?) { - val queryResult = mockk>(relaxed = true) - every { queryResult.findFirst() } returns result - every { realm.where() } returns queryResult + inline fun givenWhere(): RealmQuery { + return fakeRealm.givenWhere() + } + + inline fun givenWhereReturns(result: T?): RealmQuery { + return fakeRealm.givenWhere() + .givenFindFirst(result) } inline fun verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) { - verify { realm.insertOrUpdate(verification()) } + fakeRealm.verifyInsertOrUpdate(verification) + } + + inline fun givenFindAllMappedWithChangesReturns( + realmEntities: List, + mappedResult: List, + mapper: Monarchy.Mapper + ) { + every { mapper.map(any()) } returns mockk() + val monarchyQuery = slot>() + val monarchyMapper = slot>() + every { + instance.findAllMappedWithChanges(capture(monarchyQuery), capture(monarchyMapper)) + } answers { + monarchyQuery.captured.createQuery(fakeRealm.instance) + realmEntities.forEach { + monarchyMapper.captured.map(it) + } + MutableLiveData(mappedResult) + } } } 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 1697921a8d..d60c9a627e 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 @@ -16,8 +16,10 @@ package org.matrix.android.sdk.test.fakes +import io.mockk.MockKVerificationScope import io.mockk.every import io.mockk.mockk +import io.mockk.verify import io.realm.Realm import io.realm.RealmModel import io.realm.RealmQuery @@ -33,6 +35,67 @@ internal class FakeRealm { every { instance.where() } returns query return query } + + inline fun verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) { + verify { instance.insertOrUpdate(verification()) } + } +} + +inline fun RealmQuery.givenFindFirst( + result: T? +): RealmQuery { + every { findFirst() } returns result + return this +} + +inline fun RealmQuery.givenFindAll( + result: List +): RealmQuery { + val realmResults = mockk>() + result.forEachIndexed { index, t -> + every { realmResults[index] } returns t + } + every { realmResults.size } returns result.size + every { findAll() } returns realmResults + return this +} + +inline fun RealmQuery.givenEqualTo( + fieldName: String, + value: String +): RealmQuery { + every { equalTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenEqualTo( + fieldName: String, + value: Boolean +): RealmQuery { + every { equalTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenNotEqualTo( + fieldName: String, + value: String +): RealmQuery { + every { notEqualTo(fieldName, value) } returns this + return this +} + +inline fun RealmQuery.givenIsNotEmpty( + fieldName: String +): RealmQuery { + every { isNotEmpty(fieldName) } returns this + return this +} + +inline fun RealmQuery.givenIsNotNull( + fieldName: String +): RealmQuery { + every { isNotNull(fieldName) } returns this + return this } inline fun RealmQuery.givenFindFirst( From 7332c08bb408965f509c9e3ec424944932cf0d91 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 17:07:39 +0200 Subject: [PATCH 025/174] Unit test for static location sending --- .../DefaultLocationSharingServiceTest.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt index 42db5761d7..c816cd4d01 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt @@ -16,12 +16,17 @@ package org.matrix.android.sdk.internal.session.room.location +import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.mockk import io.mockk.unmockkAll +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Test import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary +import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields @@ -31,7 +36,11 @@ import org.matrix.android.sdk.test.fakes.givenIsNotEmpty import org.matrix.android.sdk.test.fakes.givenIsNotNull private const val A_ROOM_ID = "room_id" +private const val A_LATITUDE = 1.4 +private const val A_LONGITUDE = 40.0 +private const val AN_UNCERTAINTY = 5.0 +@ExperimentalCoroutinesApi internal class DefaultLocationSharingServiceTest { private val fakeRoomId = A_ROOM_ID @@ -57,6 +66,42 @@ internal class DefaultLocationSharingServiceTest { unmockkAll() } + @Test + fun `static location can be sent`() = runTest { + val isUserLocation = true + val cancelable = mockk() + coEvery { sendStaticLocationTask.execute(any()) } returns cancelable + + val result = defaultLocationSharingService.sendStaticLocation( + latitude = A_LATITUDE, + longitude = A_LONGITUDE, + uncertainty = AN_UNCERTAINTY, + isUserLocation = isUserLocation + ) + + result shouldBeEqualTo cancelable + val expectedParams = SendStaticLocationTask.Params( + roomId = A_ROOM_ID, + latitude = A_LATITUDE, + longitude = A_LONGITUDE, + uncertainty = AN_UNCERTAINTY, + isUserLocation = isUserLocation, + ) + coVerify { sendStaticLocationTask.execute(expectedParams) } + } + + @Test + fun `live location can be sent`() { + } + + @Test + fun `live location share can be started with a given timeout`() { + } + + @Test + fun `live location share can be stopped`() { + } + @Test fun `livedata of live summaries is correctly computed`() { val entity = LiveLocationShareAggregatedSummaryEntity() From fb7fbced3985d47a421feb96a59bb4a31067eff2 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 17:11:47 +0200 Subject: [PATCH 026/174] Unit test for live location sending --- .../DefaultLocationSharingServiceTest.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt index c816cd4d01..fe37383b5e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt @@ -36,6 +36,7 @@ import org.matrix.android.sdk.test.fakes.givenIsNotEmpty import org.matrix.android.sdk.test.fakes.givenIsNotNull private const val A_ROOM_ID = "room_id" +private const val AN_EVENT_ID = "event_id" private const val A_LATITUDE = 1.4 private const val A_LONGITUDE = 40.0 private const val AN_UNCERTAINTY = 5.0 @@ -91,7 +92,26 @@ internal class DefaultLocationSharingServiceTest { } @Test - fun `live location can be sent`() { + fun `live location can be sent`() = runTest { + val cancelable = mockk() + coEvery { sendLiveLocationTask.execute(any()) } returns cancelable + + val result = defaultLocationSharingService.sendLiveLocation( + beaconInfoEventId = AN_EVENT_ID, + latitude = A_LATITUDE, + longitude = A_LONGITUDE, + uncertainty = AN_UNCERTAINTY + ) + + result shouldBeEqualTo cancelable + val expectedParams = SendLiveLocationTask.Params( + roomId = A_ROOM_ID, + beaconInfoEventId = AN_EVENT_ID, + latitude = A_LATITUDE, + longitude = A_LONGITUDE, + uncertainty = AN_UNCERTAINTY + ) + coVerify { sendLiveLocationTask.execute(expectedParams) } } @Test From f981900cf31798490cd207c96b16e1a7676a9c13 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 17:22:04 +0200 Subject: [PATCH 027/174] Unit test for start/stop live location share --- .../DefaultLocationSharingServiceTest.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt index fe37383b5e..003842be28 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt @@ -18,7 +18,9 @@ package org.matrix.android.sdk.internal.session.room.location import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.just import io.mockk.mockk +import io.mockk.runs import io.mockk.unmockkAll import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -40,6 +42,7 @@ private const val AN_EVENT_ID = "event_id" private const val A_LATITUDE = 1.4 private const val A_LONGITUDE = 40.0 private const val AN_UNCERTAINTY = 5.0 +private const val A_TIMEOUT = 15_000L @ExperimentalCoroutinesApi internal class DefaultLocationSharingServiceTest { @@ -115,11 +118,29 @@ internal class DefaultLocationSharingServiceTest { } @Test - fun `live location share can be started with a given timeout`() { + fun `live location share can be started with a given timeout`() = runTest { + coEvery { startLiveLocationShareTask.execute(any()) } returns AN_EVENT_ID + + val eventId = defaultLocationSharingService.startLiveLocationShare(A_TIMEOUT) + + eventId shouldBeEqualTo AN_EVENT_ID + val expectedParams = StartLiveLocationShareTask.Params( + roomId = A_ROOM_ID, + timeoutMillis = A_TIMEOUT + ) + coVerify { startLiveLocationShareTask.execute(expectedParams) } } @Test - fun `live location share can be stopped`() { + fun `live location share can be stopped`() = runTest { + coEvery { stopLiveLocationShareTask.execute(any()) } just runs + + defaultLocationSharingService.stopLiveLocationShare() + + val expectedParams = StopLiveLocationShareTask.Params( + roomId = A_ROOM_ID + ) + coVerify { stopLiveLocationShareTask.execute(expectedParams) } } @Test From 1ecc42c903ccf43c52246fcfdbc048ba88ecba3f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 13 Jun 2022 17:40:35 +0200 Subject: [PATCH 028/174] Unit test for send static location task --- .../room/location/SendStaticLocationTask.kt | 1 - .../DefaultSendStaticLocationTaskTest.kt | 73 +++++++++++++++++++ .../test/fakes/FakeEventSenderProcessor.kt | 27 +++++++ .../test/fakes/FakeLocalEchoEventFactory.kt | 70 ++++++++++++++++++ 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt index d26609196f..65b11472ea 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt @@ -32,7 +32,6 @@ internal interface SendStaticLocationTask : Task() + + fun givenCreateLocationEvent(withLocalEcho: Boolean): Event { + val event = Event() + every { + instance.createLocationEvent( + roomId = any(), + latitude = any(), + longitude = any(), + uncertainty = any(), + isUserLocation = any() + ) + } returns event + + if (withLocalEcho) { + every { instance.createLocalEcho(event) } just runs + } + return event + } + + fun verifyCreateLocationEvent( + roomId: String, + latitude: Double, + longitude: Double, + uncertainty: Double?, + isUserLocation: Boolean + ) { + verify { + instance.createLocationEvent( + roomId = roomId, + latitude = latitude, + longitude = longitude, + uncertainty = uncertainty, + isUserLocation = isUserLocation + ) + } + } + + fun verifyCreateLocalEcho(event: Event) { + verify { instance.createLocalEcho(event) } + } +} From 879cafc8d1f873db0e66abcde4d02a64df8d2cb0 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 14 Jun 2022 11:04:57 +0200 Subject: [PATCH 029/174] Unit test for send live location task --- .../room/location/SendLiveLocationTask.kt | 1 - .../DefaultSendLiveLocationTaskTest.kt | 74 +++++++++++++++++++ .../test/fakes/FakeLocalEchoEventFactory.kt | 36 +++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendLiveLocationTaskTest.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt index ed40c82b66..bebd9c774a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendLiveLocationTask.kt @@ -32,7 +32,6 @@ internal interface SendLiveLocationTask : Task Date: Tue, 14 Jun 2022 11:06:17 +0200 Subject: [PATCH 030/174] Renaming method to create static location event --- .../session/room/location/SendStaticLocationTask.kt | 2 +- .../internal/session/room/send/LocalEchoEventFactory.kt | 2 +- .../room/location/DefaultSendStaticLocationTaskTest.kt | 4 ++-- .../android/sdk/test/fakes/FakeLocalEchoEventFactory.kt | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt index 65b11472ea..e08b82f3d4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/SendStaticLocationTask.kt @@ -38,7 +38,7 @@ internal class DefaultSendStaticLocationTask @Inject constructor( ) : SendStaticLocationTask { override suspend fun execute(params: SendStaticLocationTask.Params): Cancelable { - val event = localEchoEventFactory.createLocationEvent( + val event = localEchoEventFactory.createStaticLocationEvent( roomId = params.roomId, latitude = params.latitude, longitude = params.longitude, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 3b9ca44d18..bcaa257d78 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -244,7 +244,7 @@ internal class LocalEchoEventFactory @Inject constructor( ) } - fun createLocationEvent( + fun createStaticLocationEvent( roomId: String, latitude: Double, longitude: Double, diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt index 440d27657e..3a09ea51c1 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt @@ -55,13 +55,13 @@ internal class DefaultSendStaticLocationTaskTest { isUserLocation = true ) - val event = fakeLocalEchoEventFactory.givenCreateLocationEvent( + val event = fakeLocalEchoEventFactory.givenCreateStaticLocationEvent( withLocalEcho = true ) defaultSendStaticLocationTask.execute(params) - fakeLocalEchoEventFactory.verifyCreateLocationEvent( + fakeLocalEchoEventFactory.verifyCreateStaticLocationEvent( roomId = params.roomId, latitude = params.latitude, longitude = params.longitude, diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt index 72fc1cc97e..50ec85f14a 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt @@ -28,10 +28,10 @@ internal class FakeLocalEchoEventFactory { val instance = mockk() - fun givenCreateLocationEvent(withLocalEcho: Boolean): Event { + fun givenCreateStaticLocationEvent(withLocalEcho: Boolean): Event { val event = Event() every { - instance.createLocationEvent( + instance.createStaticLocationEvent( roomId = any(), latitude = any(), longitude = any(), @@ -64,7 +64,7 @@ internal class FakeLocalEchoEventFactory { return event } - fun verifyCreateLocationEvent( + fun verifyCreateStaticLocationEvent( roomId: String, latitude: Double, longitude: Double, @@ -72,7 +72,7 @@ internal class FakeLocalEchoEventFactory { isUserLocation: Boolean ) { verify { - instance.createLocationEvent( + instance.createStaticLocationEvent( roomId = roomId, latitude = latitude, longitude = longitude, From 8d2a914c64482cc58a03fd7b0f76eb4d8fdf47e2 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 14 Jun 2022 11:18:47 +0200 Subject: [PATCH 031/174] Wip --- .../location/StartLiveLocationShareTask.kt | 1 - .../DefaultStartLiveLocationShareTaskTest.kt | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt index c13f625a41..7da67d7539 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt @@ -32,7 +32,6 @@ internal interface StartLiveLocationShareTask : Task Date: Tue, 14 Jun 2022 14:30:24 +0200 Subject: [PATCH 032/174] Improving send locations tasks tests --- .../room/location/DefaultSendLiveLocationTaskTest.kt | 9 +++++++-- .../room/location/DefaultSendStaticLocationTaskTest.kt | 9 +++++++-- .../android/sdk/test/fakes/FakeEventSenderProcessor.kt | 5 ++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendLiveLocationTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendLiveLocationTaskTest.kt index 0cc3f29b52..423c680054 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendLiveLocationTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendLiveLocationTaskTest.kt @@ -16,11 +16,14 @@ package org.matrix.android.sdk.internal.session.room.location +import io.mockk.mockk import io.mockk.unmockkAll import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Test +import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.test.fakes.FakeEventSenderProcessor import org.matrix.android.sdk.test.fakes.FakeLocalEchoEventFactory @@ -55,13 +58,15 @@ internal class DefaultSendLiveLocationTaskTest { longitude = A_LONGITUDE, uncertainty = AN_UNCERTAINTY ) - val event = fakeLocalEchoEventFactory.givenCreateLiveLocationEvent( withLocalEcho = true ) + val cancelable = mockk() + fakeEventSenderProcessor.givenPostEventReturns(event, cancelable) - defaultSendLiveLocationTask.execute(params) + val result = defaultSendLiveLocationTask.execute(params) + result shouldBeEqualTo cancelable fakeLocalEchoEventFactory.verifyCreateLiveLocationEvent( roomId = params.roomId, beaconInfoEventId = params.beaconInfoEventId, diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt index 3a09ea51c1..cfde568b71 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultSendStaticLocationTaskTest.kt @@ -16,11 +16,14 @@ package org.matrix.android.sdk.internal.session.room.location +import io.mockk.mockk import io.mockk.unmockkAll import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Test +import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.test.fakes.FakeEventSenderProcessor import org.matrix.android.sdk.test.fakes.FakeLocalEchoEventFactory @@ -54,13 +57,15 @@ internal class DefaultSendStaticLocationTaskTest { uncertainty = AN_UNCERTAINTY, isUserLocation = true ) - val event = fakeLocalEchoEventFactory.givenCreateStaticLocationEvent( withLocalEcho = true ) + val cancelable = mockk() + fakeEventSenderProcessor.givenPostEventReturns(event, cancelable) - defaultSendStaticLocationTask.execute(params) + val result = defaultSendStaticLocationTask.execute(params) + result shouldBeEqualTo cancelable fakeLocalEchoEventFactory.verifyCreateStaticLocationEvent( roomId = params.roomId, latitude = params.latitude, diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt index 1f6938d1dd..fbdcf5bfd7 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.test.fakes +import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.util.Cancelable @@ -23,5 +24,7 @@ import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProces internal class FakeEventSenderProcessor : EventSenderProcessor by mockk() { - override fun postEvent(event: Event): Cancelable = mockk() + fun givenPostEventReturns(event: Event, cancelable: Cancelable) { + every { postEvent(event) } returns cancelable + } } From af039371e1ee7adbf03f9cff7c763b0c930b4dec Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 14 Jun 2022 14:47:08 +0200 Subject: [PATCH 033/174] Adding test for start live location share task --- .../DefaultStartLiveLocationShareTaskTest.kt | 63 ++++++++++++++++++- .../sdk/test/fakes/FakeSendStateTask.kt | 33 ++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt index fe96fdd902..c435e60db3 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt @@ -16,9 +16,68 @@ package org.matrix.android.sdk.internal.session.room.location +import io.mockk.unmockkAll +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeEqualTo +import org.junit.After +import org.junit.Test +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent +import org.matrix.android.sdk.internal.session.room.state.SendStateTask +import org.matrix.android.sdk.test.fakes.FakeClock +import org.matrix.android.sdk.test.fakes.FakeSendStateTask + private const val A_USER_ID = "user-id" +private const val A_ROOM_ID = "room-id" +private const val AN_EVENT_ID = "event-id" +private const val A_TIMEOUT = 15_000L +private const val AN_EPOCH = 1655210176L -class DefaultStartLiveLocationShareTaskTest { +@ExperimentalCoroutinesApi +internal class DefaultStartLiveLocationShareTaskTest { - private val fakeClock = FakeClock + private val fakeClock = FakeClock() + private val fakeSendStateTask = FakeSendStateTask() + + private val defaultStartLiveLocationShareTask = DefaultStartLiveLocationShareTask( + userId = A_USER_ID, + clock = fakeClock, + sendStateTask = fakeSendStateTask + ) + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given parameters when calling the task then it is correctly executed`() = runTest { + val params = StartLiveLocationShareTask.Params( + roomId = A_ROOM_ID, + timeoutMillis = A_TIMEOUT + ) + fakeClock.givenEpoch(AN_EPOCH) + fakeSendStateTask.givenExecuteRetryReturns(AN_EVENT_ID) + + val result = defaultStartLiveLocationShareTask.execute(params) + + result shouldBeEqualTo AN_EVENT_ID + val expectedBeaconContent = MessageBeaconInfoContent( + timeout = params.timeoutMillis, + isLive = true, + unstableTimestampMillis = AN_EPOCH + ).toContent() + val expectedParams = SendStateTask.Params( + roomId = params.roomId, + stateKey = A_USER_ID, + eventType = EventType.STATE_ROOM_BEACON_INFO.first(), + body = expectedBeaconContent + ) + fakeSendStateTask.verifyExecuteRetry( + params = expectedParams, + remainingRetry = 3 + ) + } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt new file mode 100644 index 0000000000..0999ba619b --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt @@ -0,0 +1,33 @@ +/* + * 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.state.SendStateTask + +internal class FakeSendStateTask : SendStateTask by mockk() { + + fun givenExecuteRetryReturns(eventId: String) { + coEvery { executeRetry(any(), any()) } returns eventId + } + + fun verifyExecuteRetry(params: SendStateTask.Params, remainingRetry: Int) { + coVerify { executeRetry(params, remainingRetry) } + } +} From d0b598463f8747ff66a07b47a7d41e9bb428900b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 14 Jun 2022 15:16:06 +0200 Subject: [PATCH 034/174] Adding test for stop live location share task --- .../location/StopLiveLocationShareTask.kt | 1 - .../DefaultStopLiveLocationShareTaskTest.kt | 93 +++++++++++++++++++ .../test/fakes/FakeStateEventDataSource.kt | 49 ++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt index 44712e75d9..1c282684a4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt @@ -35,7 +35,6 @@ internal interface StopLiveLocationShareTask : Task Date: Tue, 14 Jun 2022 15:21:51 +0200 Subject: [PATCH 035/174] Fixing pusherTask tests --- .../internal/session/pushers/DefaultAddPusherTaskTest.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt index 32b1d44fb9..dac33069f3 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/pushers/DefaultAddPusherTaskTest.kt @@ -23,10 +23,12 @@ import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.session.pushers.PusherState import org.matrix.android.sdk.internal.database.model.PusherEntity +import org.matrix.android.sdk.internal.database.model.PusherEntityFields import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver import org.matrix.android.sdk.test.fakes.FakeMonarchy import org.matrix.android.sdk.test.fakes.FakePushersAPI import org.matrix.android.sdk.test.fakes.FakeRequestExecutor +import org.matrix.android.sdk.test.fakes.givenEqualTo import java.net.SocketException private val A_JSON_PUSHER = JsonPusher( @@ -56,6 +58,7 @@ class DefaultAddPusherTaskTest { @Test fun `given no persisted pusher when adding Pusher then updates api and inserts result with Registered state`() { monarchy.givenWhereReturns(result = null) + .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey) runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) } @@ -71,6 +74,7 @@ class DefaultAddPusherTaskTest { fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() { val realmResult = PusherEntity(appDisplayName = null) monarchy.givenWhereReturns(result = realmResult) + .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey) runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) } @@ -84,6 +88,7 @@ class DefaultAddPusherTaskTest { fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows`() { val realmResult = PusherEntity() monarchy.givenWhereReturns(result = realmResult) + .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey) pushersAPI.givenSetPusherErrors(SocketException()) assertFailsWith { @@ -96,6 +101,7 @@ class DefaultAddPusherTaskTest { @Test fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() { monarchy.givenWhereReturns(result = null) + .givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey) pushersAPI.givenSetPusherErrors(SocketException()) assertFailsWith { From b16ccf5098d917dac7291c6d3470cf6a16bb2274 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 20 Jun 2022 10:16:27 +0200 Subject: [PATCH 036/174] Fix unit tests after rebase --- .../DefaultStopLiveLocationShareTaskTest.kt | 3 +- .../android/sdk/test/fakes/FakeRealm.kt | 43 ------------------- .../test/fakes/FakeStateEventDataSource.kt | 4 +- 3 files changed, 3 insertions(+), 47 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt index 55d13803b9..81a5742f90 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt @@ -21,7 +21,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Test -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.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent @@ -87,7 +86,7 @@ class DefaultStopLiveLocationShareTaskTest { fakeStateEventDataSource.verifyGetStateEvent( roomId = params.roomId, eventType = EventType.STATE_ROOM_BEACON_INFO.first(), - stateKey = QueryStringValue.Equals(A_USER_ID) + stateKey = A_USER_ID ) } } 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 d60c9a627e..0ebff87278 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 @@ -97,46 +97,3 @@ inline fun RealmQuery.givenIsNotNull( every { isNotNull(fieldName) } returns this return this } - -inline fun RealmQuery.givenFindFirst( - result: T? -): RealmQuery { - every { findFirst() } returns result - return this -} - -inline fun RealmQuery.givenFindAll( - result: List -): RealmQuery { - val realmResults = mockk>() - result.forEachIndexed { index, t -> - every { realmResults[index] } returns t - } - every { realmResults.size } returns result.size - every { findAll() } returns realmResults - return this -} - -inline fun RealmQuery.givenEqualTo( - fieldName: String, - value: String -): RealmQuery { - every { equalTo(fieldName, value) } returns this - return this -} - -inline fun RealmQuery.givenEqualTo( - fieldName: String, - value: Boolean -): RealmQuery { - every { equalTo(fieldName, value) } returns this - return this -} - -inline fun RealmQuery.givenNotEqualTo( - fieldName: String, - value: String -): RealmQuery { - every { notEqualTo(fieldName, value) } returns this - return this -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt index e4f19abaa7..498901bdac 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt @@ -37,12 +37,12 @@ internal class FakeStateEventDataSource { } returns event } - fun verifyGetStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue) { + fun verifyGetStateEvent(roomId: String, eventType: String, stateKey: String) { verify { instance.getStateEvent( roomId = roomId, eventType = eventType, - stateKey = stateKey + stateKey = QueryStringValue.Equals(stateKey) ) } } From e55c378683f5026055051ce6eb80fa0e32fb2c11 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 10:01:35 +0200 Subject: [PATCH 037/174] Catching crash when offline during start of a live location share --- .../room/location/LocationSharingService.kt | 4 +-- .../location/UpdateLiveLocationShareResult.kt | 32 +++++++++++++++++++ .../location/DefaultLocationSharingService.kt | 3 +- .../location/StartLiveLocationShareTask.kt | 17 ++++++++-- .../location/StopLiveLocationShareTask.kt | 1 + .../location/LocationSharingService.kt | 28 +++++++++++----- 6 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index 11b74ecd7f..ce0b746d4f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -46,9 +46,9 @@ interface LocationSharingService { /** * Starts sharing live location in the room. * @param timeoutMillis timeout of the live in milliseconds - * @return the id of the created beacon info event + * @return the result of the update of the live */ - suspend fun startLiveLocationShare(timeoutMillis: Long): String + suspend fun startLiveLocationShare(timeoutMillis: Long): UpdateLiveLocationShareResult /** * Stops sharing live location in the room. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt new file mode 100644 index 0000000000..6c6bc1029a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt @@ -0,0 +1,32 @@ +/* + * 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.api.session.room.location + +/** + * Represents the result of an update of live location share like a start or a stop. + */ +sealed interface UpdateLiveLocationShareResult { + /** + * @param beaconEventId event id of the updated state event + */ + data class Success(val beaconEventId: String) : UpdateLiveLocationShareResult + + /** + * @param error thrown during the update + */ + data class Failure(val error: Throwable) : UpdateLiveLocationShareResult +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index 3fa00fa077..c15cdda6f3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -22,6 +22,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.session.room.location.LocationSharingService +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper @@ -66,7 +67,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor( return sendLiveLocationTask.execute(params) } - override suspend fun startLiveLocationShare(timeoutMillis: Long): String { + override suspend fun startLiveLocationShare(timeoutMillis: Long): UpdateLiveLocationShareResult { val params = StartLiveLocationShareTask.Params( roomId = roomId, timeoutMillis = timeoutMillis diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt index 7da67d7539..bf6a0049d8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.location import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.state.SendStateTask @@ -25,20 +26,21 @@ import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.time.Clock import javax.inject.Inject -internal interface StartLiveLocationShareTask : Task { +internal interface StartLiveLocationShareTask : Task { data class Params( val roomId: String, val timeoutMillis: Long, ) } +// TODO update unit test internal class DefaultStartLiveLocationShareTask @Inject constructor( @UserId private val userId: String, private val clock: Clock, private val sendStateTask: SendStateTask, ) : StartLiveLocationShareTask { - override suspend fun execute(params: StartLiveLocationShareTask.Params): String { + override suspend fun execute(params: StartLiveLocationShareTask.Params): UpdateLiveLocationShareResult { val beaconContent = MessageBeaconInfoContent( timeout = params.timeoutMillis, isLive = true, @@ -51,6 +53,15 @@ internal class DefaultStartLiveLocationShareTask @Inject constructor( eventType = eventType, body = beaconContent ) - return sendStateTask.executeRetry(sendStateTaskParams, 3) + return try { + val eventId = sendStateTask.executeRetry(sendStateTaskParams, 3) + if (eventId.isNotEmpty()) { + UpdateLiveLocationShareResult.Success(eventId) + } else { + UpdateLiveLocationShareResult.Failure(Exception("empty event id for new state event")) + } + } catch (error: Throwable) { + UpdateLiveLocationShareResult.Failure(error) + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt index 1c282684a4..8f2fa27288 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt @@ -41,6 +41,7 @@ internal class DefaultStopLiveLocationShareTask @Inject constructor( private val stateEventDataSource: StateEventDataSource, ) : StopLiveLocationShareTask { + @Throws override suspend fun execute(params: StopLiveLocationShareTask.Params) { val beaconInfoStateEvent = getLiveLocationBeaconInfoForUser(userId, params.roomId) ?: return val stateKey = beaconInfoStateEvent.stateKey ?: return diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 77f3abcc28..7df84a4cee 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import timber.log.Timber import java.util.Timer import java.util.TimerTask @@ -95,13 +96,20 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { ?.startLiveLocationShare(timeoutMillis = roomArgs.durationMillis) beaconEventId - ?.takeUnless { it.isEmpty() } - ?.let { - roomArgsMap[it] = roomArgs - locationTracker.requestLastKnownLocation() + ?.let { result -> + when (result) { + is UpdateLiveLocationShareResult.Success -> { + roomArgsMap[result.beaconEventId] = roomArgs + locationTracker.requestLastKnownLocation() + } + is UpdateLiveLocationShareResult.Failure -> { + tryToDestroyMe() + } + } } ?: run { Timber.w("### LocationSharingService.sendStartingLiveBeaconInfo error, no received beacon info id") + tryToDestroyMe() } } @@ -132,10 +140,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { .map { it.key } beaconIds.forEach { roomArgsMap.remove(it) } - if (roomArgsMap.isEmpty()) { - Timber.i("### LocationSharingService. Destroying self, time is up for all rooms") - destroyMe() - } + tryToDestroyMe() } } @@ -178,6 +183,13 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { stopSelf() } + private fun tryToDestroyMe() { + if (roomArgsMap.isEmpty()) { + Timber.i("### LocationSharingService. Destroying self, time is up for all rooms") + destroyMe() + } + } + private fun destroyMe() { locationTracker.removeCallback(this) timers.forEach { it.cancel() } From 9eba3034db3a3bfa22833520437639bcf43f8f91 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 12:09:39 +0200 Subject: [PATCH 038/174] Catching crash when offline during stop of a live location share --- .../room/location/LocationSharingService.kt | 2 +- .../location/DefaultLocationSharingService.kt | 2 +- .../location/StopLiveLocationShareTask.kt | 27 +++++++++++---- .../location/LocationSharingService.kt | 33 ++++++++++--------- .../LocationSharingServiceConnection.kt | 1 + 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index ce0b746d4f..7e5906b517 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -53,7 +53,7 @@ interface LocationSharingService { /** * Stops sharing live location in the room. */ - suspend fun stopLiveLocationShare() + suspend fun stopLiveLocationShare(): UpdateLiveLocationShareResult /** * Returns a LiveData on the list of current running live location shares. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt index c15cdda6f3..015c1cca0b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt @@ -75,7 +75,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor( return startLiveLocationShareTask.execute(params) } - override suspend fun stopLiveLocationShare() { + override suspend fun stopLiveLocationShare(): UpdateLiveLocationShareResult { val params = StopLiveLocationShareTask.Params( roomId = roomId, ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt index 8f2fa27288..dc12054d7b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StopLiveLocationShareTask.kt @@ -22,6 +22,7 @@ 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.toContent import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.state.SendStateTask @@ -29,23 +30,23 @@ import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject -internal interface StopLiveLocationShareTask : Task { +internal interface StopLiveLocationShareTask : Task { data class Params( val roomId: String, ) } +// TODO update unit tests internal class DefaultStopLiveLocationShareTask @Inject constructor( @UserId private val userId: String, private val sendStateTask: SendStateTask, private val stateEventDataSource: StateEventDataSource, ) : StopLiveLocationShareTask { - @Throws - override suspend fun execute(params: StopLiveLocationShareTask.Params) { - val beaconInfoStateEvent = getLiveLocationBeaconInfoForUser(userId, params.roomId) ?: return - val stateKey = beaconInfoStateEvent.stateKey ?: return - val content = beaconInfoStateEvent.getClearContent()?.toModel() ?: return + override suspend fun execute(params: StopLiveLocationShareTask.Params): UpdateLiveLocationShareResult { + val beaconInfoStateEvent = getLiveLocationBeaconInfoForUser(userId, params.roomId) ?: return getResultForIncorrectBeaconInfoEvent() + val stateKey = beaconInfoStateEvent.stateKey ?: return getResultForIncorrectBeaconInfoEvent() + val content = beaconInfoStateEvent.getClearContent()?.toModel() ?: return getResultForIncorrectBeaconInfoEvent() val updatedContent = content.copy(isLive = false).toContent() val sendStateTaskParams = SendStateTask.Params( roomId = params.roomId, @@ -53,9 +54,21 @@ internal class DefaultStopLiveLocationShareTask @Inject constructor( eventType = EventType.STATE_ROOM_BEACON_INFO.first(), body = updatedContent ) - sendStateTask.executeRetry(sendStateTaskParams, 3) + return try { + val eventId = sendStateTask.executeRetry(sendStateTaskParams, 3) + if (eventId.isNotEmpty()) { + UpdateLiveLocationShareResult.Success(eventId) + } else { + UpdateLiveLocationShareResult.Failure(Exception("empty event id for new state event")) + } + } catch (error: Throwable) { + UpdateLiveLocationShareResult.Failure(error) + } } + private fun getResultForIncorrectBeaconInfoEvent() = + UpdateLiveLocationShareResult.Failure(Exception("incorrect last beacon info event")) + private fun getLiveLocationBeaconInfoForUser(userId: String, roomId: String): Event? { return EventType.STATE_ROOM_BEACON_INFO .mapNotNull { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 7df84a4cee..27eea498e4 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -131,25 +131,28 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { fun stopSharingLocation(roomId: String) { Timber.i("### LocationSharingService.stopSharingLocation for $roomId") - // Send a new beacon info state by setting live field as false - sendStoppedBeaconInfo(roomId) + launchInIO { session -> + // Send a new beacon info state by setting live field as false + when (sendStoppedBeaconInfo(session, roomId)) { + is UpdateLiveLocationShareResult.Success -> { + synchronized(roomArgsMap) { + val beaconIds = roomArgsMap + .filter { it.value.roomId == roomId } + .map { it.key } + beaconIds.forEach { roomArgsMap.remove(it) } - synchronized(roomArgsMap) { - val beaconIds = roomArgsMap - .filter { it.value.roomId == roomId } - .map { it.key } - beaconIds.forEach { roomArgsMap.remove(it) } - - tryToDestroyMe() + tryToDestroyMe() + } + } + else -> Unit + } } } - private fun sendStoppedBeaconInfo(roomId: String) { - launchInIO { session -> - session.getRoom(roomId) - ?.locationSharingService() - ?.stopLiveLocationShare() - } + private suspend fun sendStoppedBeaconInfo(session: Session, roomId: String): UpdateLiveLocationShareResult? { + return session.getRoom(roomId) + ?.locationSharingService() + ?.stopLiveLocationShare() } override fun onLocationUpdate(locationData: LocationData) { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt index e72f77531b..97f447ec4b 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt @@ -28,6 +28,7 @@ class LocationSharingServiceConnection @Inject constructor( ) : ServiceConnection { interface Callback { + // TODO add onLocationServiceError() fun onLocationServiceRunning() fun onLocationServiceStopped() } From 31bb9eaac8adf4454d33849f946c7212d9e24012 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 14:34:13 +0200 Subject: [PATCH 039/174] Forward error to UI in timeline screen --- .../features/home/room/detail/TimelineViewModel.kt | 4 ++++ .../features/location/LocationSharingService.kt | 14 ++++++++++---- .../location/LocationSharingServiceConnection.kt | 13 ++++++++++--- .../location/live/map/LocationLiveMapViewModel.kt | 4 ++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 07b20b4914..99a01211c3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -1293,6 +1293,10 @@ class TimelineViewModel @AssistedInject constructor( locationSharingServiceConnection.bind(this) } + override fun onLocationServiceError(error: Throwable) { + _viewEvents.post(RoomDetailViewEvents.Failure(error)) + } + override fun onCleared() { timeline.dispose() timeline.removeAllListeners() diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 27eea498e4..4a15a9d643 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -55,8 +55,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { /** * Keep track of a map between beacon event Id starting the live and RoomArgs. */ - private var roomArgsMap = mutableMapOf() - private var timers = mutableListOf() + private val roomArgsMap = mutableMapOf() + private val timers = mutableListOf() + var callback: Callback? = null override fun onCreate() { super.onCreate() @@ -103,6 +104,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { locationTracker.requestLastKnownLocation() } is UpdateLiveLocationShareResult.Failure -> { + callback?.onServiceError(result.error) tryToDestroyMe() } } @@ -132,8 +134,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { Timber.i("### LocationSharingService.stopSharingLocation for $roomId") launchInIO { session -> - // Send a new beacon info state by setting live field as false - when (sendStoppedBeaconInfo(session, roomId)) { + when (val result = sendStoppedBeaconInfo(session, roomId)) { is UpdateLiveLocationShareResult.Success -> { synchronized(roomArgsMap) { val beaconIds = roomArgsMap @@ -144,6 +145,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { tryToDestroyMe() } } + is UpdateLiveLocationShareResult.Failure -> callback?.onServiceError(result.error) else -> Unit } } @@ -224,6 +226,10 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { fun getService(): LocationSharingService = this@LocationSharingService } + interface Callback { + fun onServiceError(error: Throwable) + } + companion object { const val EXTRA_ROOM_ARGS = "EXTRA_ROOM_ARGS" } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt index 97f447ec4b..af09e0b1e0 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt @@ -25,12 +25,12 @@ import javax.inject.Inject class LocationSharingServiceConnection @Inject constructor( private val context: Context -) : ServiceConnection { +) : ServiceConnection, LocationSharingService.Callback { interface Callback { - // TODO add onLocationServiceError() fun onLocationServiceRunning() fun onLocationServiceStopped() + fun onLocationServiceError(error: Throwable) } private var callback: Callback? = null @@ -58,14 +58,21 @@ class LocationSharingServiceConnection @Inject constructor( } override fun onServiceConnected(className: ComponentName, binder: IBinder) { - locationSharingService = (binder as LocationSharingService.LocalBinder).getService() + locationSharingService = (binder as LocationSharingService.LocalBinder).getService().also { + it.callback = this + } isBound = true callback?.onLocationServiceRunning() } override fun onServiceDisconnected(className: ComponentName) { isBound = false + locationSharingService?.callback = null locationSharingService = null callback?.onLocationServiceStopped() } + + override fun onServiceError(error: Throwable) { + callback?.onLocationServiceError(error) + } } diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt index eb5bccff0f..9ef6449ea0 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt @@ -80,4 +80,8 @@ class LocationLiveMapViewModel @AssistedInject constructor( override fun onLocationServiceStopped() { // NOOP } + + override fun onLocationServiceError(error: Throwable) { + // TODO + } } From fc980570424762329efd1b2bc1ed1d03925eeb78 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 14:45:32 +0200 Subject: [PATCH 040/174] Forward error to UI in map screen --- .../app/features/home/room/detail/TimelineViewModel.kt | 2 +- .../location/live/map/LocationLiveMapViewEvents.kt | 4 +++- .../location/live/map/LocationLiveMapViewFragment.kt | 10 ++++++++++ .../location/live/map/LocationLiveMapViewModel.kt | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 99a01211c3..1c2255246b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -1294,7 +1294,7 @@ class TimelineViewModel @AssistedInject constructor( } override fun onLocationServiceError(error: Throwable) { - _viewEvents.post(RoomDetailViewEvents.Failure(error)) + _viewEvents.post(RoomDetailViewEvents.Failure(throwable = error, showInDialog = true)) } override fun onCleared() { diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewEvents.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewEvents.kt index 6645ff58d9..23771299c8 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewEvents.kt @@ -18,4 +18,6 @@ package im.vector.app.features.location.live.map import im.vector.app.core.platform.VectorViewEvents -sealed interface LocationLiveMapViewEvents : VectorViewEvents +sealed interface LocationLiveMapViewEvents : VectorViewEvents { + data class Error(val error: Throwable) : LocationLiveMapViewEvents +} diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt index 5f2410d697..09522ce4c8 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt @@ -76,6 +76,8 @@ class LocationLiveMapViewFragment @Inject constructor() : VectorBaseFragment + when(viewEvent) { + is LocationLiveMapViewEvents.Error -> displayErrorDialog(viewEvent.error) + } + } + } + override fun onResume() { super.onResume() setupMap() diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt index 9ef6449ea0..e89649709a 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewModel.kt @@ -82,6 +82,6 @@ class LocationLiveMapViewModel @AssistedInject constructor( } override fun onLocationServiceError(error: Throwable) { - // TODO + _viewEvents.post(LocationLiveMapViewEvents.Error(error)) } } From 6c0b7f7b4337142ace9145f1fd4f25c2bc479f38 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 14:51:28 +0200 Subject: [PATCH 041/174] Renaming a variable to be more precise --- .../im/vector/app/features/location/LocationSharingService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 4a15a9d643..ef612eeec2 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -91,12 +91,12 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } private suspend fun sendStartingLiveBeaconInfo(session: Session, roomArgs: RoomArgs) { - val beaconEventId = session + val updateLiveResult = session .getRoom(roomArgs.roomId) ?.locationSharingService() ?.startLiveLocationShare(timeoutMillis = roomArgs.durationMillis) - beaconEventId + updateLiveResult ?.let { result -> when (result) { is UpdateLiveLocationShareResult.Success -> { From 3e05431e6f70bcdfc704814ee5b1bc8a9eb264ba Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 14:56:00 +0200 Subject: [PATCH 042/174] Fixing unit tests --- .../location/DefaultLocationSharingServiceTest.kt | 12 +++++++----- .../DefaultStartLiveLocationShareTaskTest.kt | 5 +++-- .../location/DefaultStopLiveLocationShareTaskTest.kt | 5 ++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt index 003842be28..d9b3526bf5 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Test +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.database.mapper.LiveLocationShareAggregatedSummaryMapper @@ -119,11 +120,11 @@ internal class DefaultLocationSharingServiceTest { @Test fun `live location share can be started with a given timeout`() = runTest { - coEvery { startLiveLocationShareTask.execute(any()) } returns AN_EVENT_ID + coEvery { startLiveLocationShareTask.execute(any()) } returns UpdateLiveLocationShareResult.Success(AN_EVENT_ID) - val eventId = defaultLocationSharingService.startLiveLocationShare(A_TIMEOUT) + val result = defaultLocationSharingService.startLiveLocationShare(A_TIMEOUT) - eventId shouldBeEqualTo AN_EVENT_ID + result shouldBeEqualTo UpdateLiveLocationShareResult.Success(AN_EVENT_ID) val expectedParams = StartLiveLocationShareTask.Params( roomId = A_ROOM_ID, timeoutMillis = A_TIMEOUT @@ -133,10 +134,11 @@ internal class DefaultLocationSharingServiceTest { @Test fun `live location share can be stopped`() = runTest { - coEvery { stopLiveLocationShareTask.execute(any()) } just runs + coEvery { stopLiveLocationShareTask.execute(any()) } returns UpdateLiveLocationShareResult.Success(AN_EVENT_ID) - defaultLocationSharingService.stopLiveLocationShare() + val result = defaultLocationSharingService.stopLiveLocationShare() + result shouldBeEqualTo UpdateLiveLocationShareResult.Success(AN_EVENT_ID) val expectedParams = StopLiveLocationShareTask.Params( roomId = A_ROOM_ID ) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt index c435e60db3..6a487a4e81 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStartLiveLocationShareTaskTest.kt @@ -24,6 +24,7 @@ import org.junit.After import org.junit.Test import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.test.fakes.FakeClock @@ -53,7 +54,7 @@ internal class DefaultStartLiveLocationShareTaskTest { } @Test - fun `given parameters when calling the task then it is correctly executed`() = runTest { + fun `given parameters an no error when calling the task then it is correctly executed`() = runTest { val params = StartLiveLocationShareTask.Params( roomId = A_ROOM_ID, timeoutMillis = A_TIMEOUT @@ -63,7 +64,7 @@ internal class DefaultStartLiveLocationShareTaskTest { val result = defaultStartLiveLocationShareTask.execute(params) - result shouldBeEqualTo AN_EVENT_ID + result shouldBeEqualTo UpdateLiveLocationShareResult.Success(AN_EVENT_ID) val expectedBeaconContent = MessageBeaconInfoContent( timeout = params.timeoutMillis, isLive = true, diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt index 81a5742f90..78433d8b6e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt @@ -19,11 +19,13 @@ package org.matrix.android.sdk.internal.session.room.location import io.mockk.unmockkAll import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeEqualTo import org.junit.After import org.junit.Test 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.toContent +import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.test.fakes.FakeSendStateTask @@ -66,8 +68,9 @@ class DefaultStopLiveLocationShareTaskTest { fakeStateEventDataSource.givenGetStateEventReturns(currentStateEvent) fakeSendStateTask.givenExecuteRetryReturns(AN_EVENT_ID) - defaultStopLiveLocationShareTask.execute(params) + val result = defaultStopLiveLocationShareTask.execute(params) + result shouldBeEqualTo UpdateLiveLocationShareResult.Success(AN_EVENT_ID) val expectedBeaconContent = MessageBeaconInfoContent( timeout = A_TIMEOUT, isLive = false, From e1fc6fa727b1964098c6a6493a3260bacce35041 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 15:16:25 +0200 Subject: [PATCH 043/174] Adding tests to cover errors thrown during start/stop process --- .../location/StartLiveLocationShareTask.kt | 1 - .../location/StopLiveLocationShareTask.kt | 1 - .../DefaultStartLiveLocationShareTaskTest.kt | 32 +++++++- .../DefaultStopLiveLocationShareTaskTest.kt | 78 ++++++++++++++++++- .../sdk/test/fakes/FakeSendStateTask.kt | 4 + .../test/fakes/FakeStateEventDataSource.kt | 2 +- 6 files changed, 113 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt index bf6a0049d8..b943c27977 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/StartLiveLocationShareTask.kt @@ -33,7 +33,6 @@ internal interface StartLiveLocationShareTask : Task + fakeStateEventDataSource.givenGetStateEventReturns(currentStateEvent) + fakeSendStateTask.givenExecuteRetryReturns(AN_EVENT_ID) + val params = StopLiveLocationShareTask.Params(roomId = A_ROOM_ID) + + val result = defaultStopLiveLocationShareTask.execute(params) + + result shouldBeInstanceOf UpdateLiveLocationShareResult.Failure::class + } + } + + @Test + fun `given parameters and an empty returned event id when calling the task then result is failure`() = runTest { + val params = StopLiveLocationShareTask.Params(roomId = A_ROOM_ID) + val currentStateEvent = Event( + stateKey = A_USER_ID, + content = MessageBeaconInfoContent( + timeout = A_TIMEOUT, + isLive = true, + unstableTimestampMillis = AN_EPOCH + ).toContent() + ) + fakeStateEventDataSource.givenGetStateEventReturns(currentStateEvent) + fakeSendStateTask.givenExecuteRetryReturns("") + + val result = defaultStopLiveLocationShareTask.execute(params) + + result shouldBeInstanceOf UpdateLiveLocationShareResult.Failure::class + } + + @Test + fun `given parameters and error during event sending when calling the task then result is failure`() = runTest { + val params = StopLiveLocationShareTask.Params(roomId = A_ROOM_ID) + val currentStateEvent = Event( + stateKey = A_USER_ID, + content = MessageBeaconInfoContent( + timeout = A_TIMEOUT, + isLive = true, + unstableTimestampMillis = AN_EPOCH + ).toContent() + ) + fakeStateEventDataSource.givenGetStateEventReturns(currentStateEvent) + val error = Throwable() + fakeSendStateTask.givenExecuteRetryThrows(error) + + val result = defaultStopLiveLocationShareTask.execute(params) + + result shouldBeEqualTo UpdateLiveLocationShareResult.Failure(error) + } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt index 0999ba619b..08a25be93e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSendStateTask.kt @@ -27,6 +27,10 @@ internal class FakeSendStateTask : SendStateTask by mockk() { coEvery { executeRetry(any(), any()) } returns eventId } + fun givenExecuteRetryThrows(error: Throwable) { + coEvery { executeRetry(any(), any()) } throws error + } + fun verifyExecuteRetry(params: SendStateTask.Params, remainingRetry: Int) { coVerify { executeRetry(params, remainingRetry) } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt index 498901bdac..ca03316fa7 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt @@ -27,7 +27,7 @@ internal class FakeStateEventDataSource { val instance: StateEventDataSource = mockk() - fun givenGetStateEventReturns(event: Event) { + fun givenGetStateEventReturns(event: Event?) { every { instance.getStateEvent( roomId = any(), From eb503b8ab62641b3600d5d88bcb53bd02d1fce43 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 15:36:08 +0200 Subject: [PATCH 044/174] Adding a changelog entry --- changelog.d/6315.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6315.bugfix diff --git a/changelog.d/6315.bugfix b/changelog.d/6315.bugfix new file mode 100644 index 0000000000..0b5eb6064d --- /dev/null +++ b/changelog.d/6315.bugfix @@ -0,0 +1 @@ +[Location sharing] Fix crash when starting/stopping a live when offline From 082b39e651f23b30cc3bb2515ce51154a02a58f1 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 15:48:03 +0200 Subject: [PATCH 045/174] Adding return type in the doc for stop API --- .../sdk/api/session/room/location/LocationSharingService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index 7e5906b517..0f88f891cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -52,6 +52,7 @@ interface LocationSharingService { /** * Stops sharing live location in the room. + * @return the result of the update of the live */ suspend fun stopLiveLocationShare(): UpdateLiveLocationShareResult From 9047d9d62c562ea28c412bca3f321cf53ed04056 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 15 Jun 2022 16:04:09 +0200 Subject: [PATCH 046/174] Fixing coding style issues --- .../room/location/UpdateLiveLocationShareResult.kt | 9 +-------- .../room/location/DefaultLocationSharingServiceTest.kt | 2 -- .../location/live/map/LocationLiveMapViewFragment.kt | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt index 6c6bc1029a..6f8c03be46 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/UpdateLiveLocationShareResult.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. @@ -20,13 +20,6 @@ package org.matrix.android.sdk.api.session.room.location * Represents the result of an update of live location share like a start or a stop. */ sealed interface UpdateLiveLocationShareResult { - /** - * @param beaconEventId event id of the updated state event - */ data class Success(val beaconEventId: String) : UpdateLiveLocationShareResult - - /** - * @param error thrown during the update - */ data class Failure(val error: Throwable) : UpdateLiveLocationShareResult } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt index d9b3526bf5..30a9671733 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt @@ -18,9 +18,7 @@ package org.matrix.android.sdk.internal.session.room.location import io.mockk.coEvery import io.mockk.coVerify -import io.mockk.just import io.mockk.mockk -import io.mockk.runs import io.mockk.unmockkAll import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt index 09522ce4c8..a57ba74685 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/map/LocationLiveMapViewFragment.kt @@ -93,7 +93,7 @@ class LocationLiveMapViewFragment @Inject constructor() : VectorBaseFragment - when(viewEvent) { + when (viewEvent) { is LocationLiveMapViewEvents.Error -> displayErrorDialog(viewEvent.error) } } From 65bc4acbabf6c315160b61c8bf5f6947b7307b9c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 20 Jun 2022 11:23:02 +0200 Subject: [PATCH 047/174] Fix flaky tests for voice recording feature (#6330) --- changelog.d/6329.misc | 1 + .../im/vector/app/core/utils/WaitUntil.kt | 69 +++++++++++++++++++ .../app/features/voice/VoiceRecorderLTests.kt | 8 +-- .../app/features/voice/VoiceRecorderQTests.kt | 51 +++++--------- .../features/voice/VoiceRecorderTestExt.kt | 36 ++++++++++ .../features/voice/AbstractVoiceRecorder.kt | 3 +- 6 files changed, 130 insertions(+), 38 deletions(-) create mode 100644 changelog.d/6329.misc create mode 100644 vector/src/androidTest/java/im/vector/app/core/utils/WaitUntil.kt create mode 100644 vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderTestExt.kt diff --git a/changelog.d/6329.misc b/changelog.d/6329.misc new file mode 100644 index 0000000000..dd87c11f6e --- /dev/null +++ b/changelog.d/6329.misc @@ -0,0 +1 @@ +Fix flaky test in voice recording feature. diff --git a/vector/src/androidTest/java/im/vector/app/core/utils/WaitUntil.kt b/vector/src/androidTest/java/im/vector/app/core/utils/WaitUntil.kt new file mode 100644 index 0000000000..16abada04c --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/core/utils/WaitUntil.kt @@ -0,0 +1,69 @@ +/* + * 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 im.vector.app.core.utils + +import kotlinx.coroutines.delay +import org.amshove.kluent.fail +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +/** + * Tries a [condition] several times until it returns true or a [timeout] is reached waiting for some [retryDelay] time between retries. + * On timeout it fails with an [errorMessage]. + */ +suspend fun waitUntilCondition( + errorMessage: String, + timeout: Duration = 1.seconds, + retryDelay: Duration = 50.milliseconds, + condition: () -> Boolean, +) { + val start = System.currentTimeMillis() + do { + if (condition()) return + delay(retryDelay.inWholeMilliseconds) + } while (System.currentTimeMillis() - start < timeout.inWholeMilliseconds) + fail(errorMessage) +} + +/** + * Tries a [block] several times until it runs with no errors or a [timeout] is reached waiting for some [retryDelay] time between retries. + * On timeout it fails with a custom [errorMessage] or a caught [AssertionError]. + */ +suspend fun waitUntil( + errorMessage: String? = null, + timeout: Duration = 1.seconds, + retryDelay: Duration = 50.milliseconds, + block: () -> Unit, +) { + var error: AssertionError? + val start = System.currentTimeMillis() + do { + try { + block() + return + } catch (e: AssertionError) { + error = e + } + delay(retryDelay.inWholeMilliseconds) + } while (System.currentTimeMillis() - start < timeout.inWholeMilliseconds) + if (errorMessage != null) { + fail(errorMessage) + } else { + throw error!! + } +} diff --git a/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderLTests.kt b/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderLTests.kt index c02c2cac80..1687ee4388 100644 --- a/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderLTests.kt +++ b/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderLTests.kt @@ -21,6 +21,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import io.mockk.spyk import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeNull import org.amshove.kluent.shouldExist import org.amshove.kluent.shouldNotBeNull @@ -42,8 +43,7 @@ class VoiceRecorderLTests { getVoiceMessageFile().shouldBeNull() startRecord("some_room_id") - - getVoiceMessageFile().shouldNotBeNullAndExist() + runBlocking { waitUntilRecordingFileExists() } stopRecord() } @@ -53,6 +53,7 @@ class VoiceRecorderLTests { getVoiceMessageFile().shouldBeNull() startRecord("some_room_id") + runBlocking { waitUntilRecordingFileExists() } stopRecord() getVoiceMessageFile().shouldNotBeNullAndExist() @@ -61,8 +62,7 @@ class VoiceRecorderLTests { @Test fun cancelRecordRemovesFile() = with(recorder) { startRecord("some_room_id") - val file = recorder.getVoiceMessageFile() - file.shouldNotBeNullAndExist() + val file = runBlocking { waitUntilRecordingFileExists() } cancelRecord() diff --git a/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderQTests.kt b/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderQTests.kt index 446d9e5b21..b9dc3f6d41 100644 --- a/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderQTests.kt +++ b/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderQTests.kt @@ -23,7 +23,6 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import io.mockk.spyk import io.mockk.verify -import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeNull import org.amshove.kluent.shouldExist @@ -43,50 +42,36 @@ class VoiceRecorderQTests { private val recorder = spyk(VoiceRecorderQ(context)) @Test - fun startRecordCreatesOggFile() = runBlocking { - with(recorder) { - getVoiceMessageFile().shouldBeNull() + fun startRecordCreatesOggFile() = with(recorder) { + getVoiceMessageFile().shouldBeNull() - startRecord("some_room_id") - waitForRecording() + startRecord("some_room_id") + runBlocking { waitUntilRecordingFileExists() } - getVoiceMessageFile().shouldNotBeNullAndExist() - - stopRecord() - } + stopRecord() } @Test - fun stopRecordKeepsFile() = runBlocking { - with(recorder) { - getVoiceMessageFile().shouldBeNull() + fun stopRecordKeepsFile() = with(recorder) { + getVoiceMessageFile().shouldBeNull() - startRecord("some_room_id") - waitForRecording() - stopRecord() + startRecord("some_room_id") + runBlocking { waitUntilRecordingFileExists() } + stopRecord() - getVoiceMessageFile().shouldNotBeNullAndExist() - } + getVoiceMessageFile().shouldNotBeNullAndExist() } @Test - fun cancelRecordRemovesFileAfterStopping() = runBlocking { - with(recorder) { - startRecord("some_room_id") - val file = recorder.getVoiceMessageFile() - file.shouldNotBeNullAndExist() + fun cancelRecordRemovesFileAfterStopping() = with(recorder) { + startRecord("some_room_id") + val file = runBlocking { waitUntilRecordingFileExists() } + cancelRecord() - waitForRecording() - cancelRecord() - - verify { stopRecord() } - getVoiceMessageFile().shouldBeNull() - file!!.shouldNotExist() - } + verify { stopRecord() } + getVoiceMessageFile().shouldBeNull() + file!!.shouldNotExist() } - - // Give MediaRecorder some time to actually start recording - private suspend fun waitForRecording() = delay(10) } private fun File?.shouldNotBeNullAndExist() { diff --git a/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderTestExt.kt b/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderTestExt.kt new file mode 100644 index 0000000000..4275ae89b3 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/features/voice/VoiceRecorderTestExt.kt @@ -0,0 +1,36 @@ +/* + * 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 im.vector.app.features.voice + +import im.vector.app.core.utils.waitUntil +import org.amshove.kluent.shouldExist +import org.amshove.kluent.shouldNotBeNull +import java.io.File +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +// Give voice recorders some time to start recording and create the audio file +suspend fun VoiceRecorder.waitUntilRecordingFileExists(timeout: Duration = 1.seconds, delay: Duration = 10.milliseconds): File? { + waitUntil(timeout = timeout, retryDelay = delay) { + getVoiceMessageFile().run { + shouldNotBeNull() + shouldExist() + } + } + return getVoiceMessageFile() +} diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt index 91eb371f42..5e27aa5bb2 100644 --- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt +++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt @@ -19,6 +19,7 @@ package im.vector.app.features.voice import android.content.Context import android.media.MediaRecorder import android.os.Build +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.util.md5 import java.io.File @@ -80,7 +81,7 @@ abstract class AbstractVoiceRecorder( override fun stopRecord() { // Can throw when the record is less than 1 second. mediaRecorder?.let { - it.stop() + tryOrNull { it.stop() } it.reset() it.release() } From 9641ff132d520d54306a333cf31c1b26f08fe7a1 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 20 Jun 2022 13:58:28 +0300 Subject: [PATCH 048/174] Show live location sharing option even if labs flag is disabled. --- .../app/features/location/LocationSharingFragment.kt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index 1b25f3fcec..e25520ed81 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -41,7 +41,6 @@ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.location.live.duration.ChooseLiveDurationBottomSheet import im.vector.app.features.location.option.LocationSharingOption -import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.util.MatrixItem import java.lang.ref.WeakReference import javax.inject.Inject @@ -53,7 +52,6 @@ class LocationSharingFragment @Inject constructor( private val urlMapProvider: UrlMapProvider, private val avatarRenderer: AvatarRenderer, private val matrixItemColorProvider: MatrixItemColorProvider, - private val vectorPreferences: VectorPreferences, ) : VectorBaseFragment(), LocationTargetChangeListener, VectorBaseBottomSheetDialogFragment.ResultListener { @@ -223,13 +221,7 @@ class LocationSharingFragment @Inject constructor( private fun updateMap(state: LocationSharingViewState) { // first, update the options view val options: Set = when (state.areTargetAndUserLocationEqual) { - true -> { - if (vectorPreferences.labsEnableLiveLocation()) { - setOf(LocationSharingOption.USER_CURRENT, LocationSharingOption.USER_LIVE) - } else { - setOf(LocationSharingOption.USER_CURRENT) - } - } + true -> setOf(LocationSharingOption.USER_CURRENT, LocationSharingOption.USER_LIVE) false -> setOf(LocationSharingOption.PINNED) else -> emptySet() } From 7ddec674fb0ba587f106e745f0b6bcac4e6c2faa Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 20 Jun 2022 10:16:27 +0200 Subject: [PATCH 049/174] Fixing unit tests after rebase --- .../DefaultStopLiveLocationShareTaskTest.kt | 3 +- .../android/sdk/test/fakes/FakeRealm.kt | 43 ------------------- .../test/fakes/FakeStateEventDataSource.kt | 4 +- 3 files changed, 3 insertions(+), 47 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt index 55d13803b9..81a5742f90 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultStopLiveLocationShareTaskTest.kt @@ -21,7 +21,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Test -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.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent @@ -87,7 +86,7 @@ class DefaultStopLiveLocationShareTaskTest { fakeStateEventDataSource.verifyGetStateEvent( roomId = params.roomId, eventType = EventType.STATE_ROOM_BEACON_INFO.first(), - stateKey = QueryStringValue.Equals(A_USER_ID) + stateKey = A_USER_ID ) } } 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 d60c9a627e..0ebff87278 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 @@ -97,46 +97,3 @@ inline fun RealmQuery.givenIsNotNull( every { isNotNull(fieldName) } returns this return this } - -inline fun RealmQuery.givenFindFirst( - result: T? -): RealmQuery { - every { findFirst() } returns result - return this -} - -inline fun RealmQuery.givenFindAll( - result: List -): RealmQuery { - val realmResults = mockk>() - result.forEachIndexed { index, t -> - every { realmResults[index] } returns t - } - every { realmResults.size } returns result.size - every { findAll() } returns realmResults - return this -} - -inline fun RealmQuery.givenEqualTo( - fieldName: String, - value: String -): RealmQuery { - every { equalTo(fieldName, value) } returns this - return this -} - -inline fun RealmQuery.givenEqualTo( - fieldName: String, - value: Boolean -): RealmQuery { - every { equalTo(fieldName, value) } returns this - return this -} - -inline fun RealmQuery.givenNotEqualTo( - fieldName: String, - value: String -): RealmQuery { - every { notEqualTo(fieldName, value) } returns this - return this -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt index e4f19abaa7..498901bdac 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeStateEventDataSource.kt @@ -37,12 +37,12 @@ internal class FakeStateEventDataSource { } returns event } - fun verifyGetStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue) { + fun verifyGetStateEvent(roomId: String, eventType: String, stateKey: String) { verify { instance.getStateEvent( roomId = roomId, eventType = eventType, - stateKey = stateKey + stateKey = QueryStringValue.Equals(stateKey) ) } } From b37dce7da7aa4df47d46fa27f343e429d3acdacf Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 20 Jun 2022 15:19:22 +0300 Subject: [PATCH 050/174] Create layout for promoting live location labs flag. --- ...heet_live_location_labs_flag_promotion.xml | 56 +++++++++++++++++++ vector/src/main/res/values/strings.xml | 6 ++ 2 files changed, 62 insertions(+) create mode 100644 vector/src/main/res/layout/bottom_sheet_live_location_labs_flag_promotion.xml diff --git a/vector/src/main/res/layout/bottom_sheet_live_location_labs_flag_promotion.xml b/vector/src/main/res/layout/bottom_sheet_live_location_labs_flag_promotion.xml new file mode 100644 index 0000000000..438e60a4d3 --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_live_location_labs_flag_promotion.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + +