diff --git a/.github/workflows/triage-move-labelled.yml b/.github/workflows/triage-move-labelled.yml index f910cdf7ea..39f7a5de09 100644 --- a/.github/workflows/triage-move-labelled.yml +++ b/.github/workflows/triage-move-labelled.yml @@ -6,7 +6,7 @@ on: jobs: move_needs_info_issues: - name: Move X-Needs-Info issues to Need info on triage board + name: X-Needs-Info issues to Need info column on triage board runs-on: ubuntu-latest steps: - uses: konradpabjan/move-labeled-or-milestoned-issue@219d384e03fa4b6460cd24f9f37d19eb033a4338 @@ -17,15 +17,17 @@ jobs: label-name: "X-Needs-Info" add_priority_design_issues_to_project: - name: Move priority X-Needs-Design issues to Design project board + name: P1 X-Needs-Design to Design project board runs-on: ubuntu-latest if: > contains(github.event.issue.labels.*.name, 'X-Needs-Design') && - (contains(github.event.issue.labels.*.name, 'O-Frequent') || - contains(github.event.issue.labels.*.name, 'O-Occasional')) && - (contains(github.event.issue.labels.*.name, 'S-Critical') || - contains(github.event.issue.labels.*.name, 'S-Major') || - contains(github.event.issue.labels.*.name, 'S-Minor')) + (contains(github.event.issue.labels.*.name, 'S-Critical') && + (contains(github.event.issue.labels.*.name, 'O-Frequent') || + contains(github.event.issue.labels.*.name, 'O-Occasional')) || + contains(github.event.issue.labels.*.name, 'S-Major') && + contains(github.event.issue.labels.*.name, 'O-Frequent') || + contains(github.event.issue.labels.*.name, 'A11y') && + contains(github.event.issue.labels.*.name, 'O-Frequent')) steps: - uses: octokit/graphql-action@v2.x id: add_to_project @@ -45,8 +47,8 @@ jobs: PROJECT_ID: "PN_kwDOAM0swc0sUA" GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} - move_spaces_issues: - name: Move Spaces issues to Delight project board + spaces_issues_to_old_board: + name: Spaces issues to old Delight project board runs-on: ubuntu-latest if: > contains(github.event.issue.labels.*.name, 'A-Spaces') || @@ -59,8 +61,16 @@ jobs: project-url: "https://github.com/orgs/vector-im/projects/6" column-name: "📥 Inbox" label-name: "A-Spaces" + + spaces_issues_to_new_board: + name: Spaces issues to new Delight project board + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'A-Spaces') || + contains(github.event.issue.labels.*.name, 'A-Space-Settings') || + contains(github.event.issue.labels.*.name, 'A-Subspaces') + steps: - uses: octokit/graphql-action@v2.x - id: add_to_delight2 with: headers: '{"GraphQL-Features": "projects_next_graphql"}' query: | @@ -78,7 +88,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} move_voice-message_issues: - name: Move A-Voice Messages to Voice message board + name: A-Voice Messages to voice message board runs-on: ubuntu-latest if: > contains(github.event.issue.labels.*.name, 'A-Voice Messages') @@ -101,7 +111,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} move_threads_issues: - name: Move A-Threads to Thread board + name: A-Threads to Thread board runs-on: ubuntu-latest if: > contains(github.event.issue.labels.*.name, 'A-Threads') diff --git a/.github/workflows/triage-priority-bugs.yml b/.github/workflows/triage-priority-bugs.yml index 018bb8bb55..976879a3ae 100644 --- a/.github/workflows/triage-priority-bugs.yml +++ b/.github/workflows/triage-priority-bugs.yml @@ -1,4 +1,4 @@ -name: Move P1 issues into the P1 column for the App Team and Crypto team +name: Move P1 bugs to boards on: issues: diff --git a/changelog.d/4324.bugfix b/changelog.d/4324.bugfix new file mode 100644 index 0000000000..8f04357344 --- /dev/null +++ b/changelog.d/4324.bugfix @@ -0,0 +1 @@ +Fixes message menu showing when copying message urls \ No newline at end of file diff --git a/changelog.d/4546.bugfix b/changelog.d/4546.bugfix new file mode 100644 index 0000000000..a59fa6557a --- /dev/null +++ b/changelog.d/4546.bugfix @@ -0,0 +1 @@ +Fix lots of integration tests by introducing TestMatrix class and MatrixWorkerFactory. \ No newline at end of file diff --git a/changelog.d/4604.misc b/changelog.d/4604.misc new file mode 100644 index 0000000000..2a3d3f70c3 --- /dev/null +++ b/changelog.d/4604.misc @@ -0,0 +1 @@ +Cleanup the layout files \ No newline at end of file diff --git a/changelog.d/4617.misc b/changelog.d/4617.misc new file mode 100644 index 0000000000..0044511eee --- /dev/null +++ b/changelog.d/4617.misc @@ -0,0 +1 @@ +Improve issue automation workflows diff --git a/changelog.d/4621.bugfix b/changelog.d/4621.bugfix new file mode 100644 index 0000000000..2bc21e8f8e --- /dev/null +++ b/changelog.d/4621.bugfix @@ -0,0 +1 @@ +Fix for outgoing voip call via sip bridge failing after 1 minute. \ No newline at end of file diff --git a/changelog.d/4626.misc b/changelog.d/4626.misc new file mode 100644 index 0000000000..9f2d979cbb --- /dev/null +++ b/changelog.d/4626.misc @@ -0,0 +1 @@ +Introducing feature flagging to the login and notification settings flows \ No newline at end of file diff --git a/changelog.d/4636.bugfix b/changelog.d/4636.bugfix new file mode 100644 index 0000000000..ec95696a4f --- /dev/null +++ b/changelog.d/4636.bugfix @@ -0,0 +1 @@ +Update log warning for call selection during voip calls. \ No newline at end of file diff --git a/changelog.d/4645.misc b/changelog.d/4645.misc new file mode 100644 index 0000000000..7f96fc551e --- /dev/null +++ b/changelog.d/4645.misc @@ -0,0 +1 @@ +Debounce some clicks \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index 1a04fe07ea..aa57546de0 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -11,7 +11,7 @@ def gradle = "7.0.3" // Ref: https://kotlinlang.org/releases.html def kotlin = "1.5.31" def kotlinCoroutines = "1.5.2" -def dagger = "2.40.3" +def dagger = "2.40.5" def retrofit = "2.9.0" def arrow = "0.8.2" def markwon = "4.6.2" diff --git a/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml b/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml index 4828810e34..c1ae9ef117 100644 --- a/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml +++ b/library/ui-styles/src/debug/res/layout/activity_debug_material_theme.xml @@ -34,7 +34,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/appBarLayout"> + app:layout_constraintTop_toBottomOf="@id/appBarLayout"> - visible - diff --git a/library/ui-styles/src/main/res/transition/image_preview_transition.xml b/library/ui-styles/src/main/res/transition/image_preview_transition.xml index 3674324c4e..c1af6d7973 100644 --- a/library/ui-styles/src/main/res/transition/image_preview_transition.xml +++ b/library/ui-styles/src/main/res/transition/image_preview_transition.xml @@ -11,5 +11,3 @@ - - diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index e2e50449ce..519920786c 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -32,7 +32,6 @@ 88dp 8dp - 76dp @@ -40,5 +39,4 @@ 320dp - \ No newline at end of file diff --git a/library/ui-styles/src/main/res/values/palette.xml b/library/ui-styles/src/main/res/values/palette.xml index ed12f10af3..e37fd8a7c6 100644 --- a/library/ui-styles/src/main/res/values/palette.xml +++ b/library/ui-styles/src/main/res/values/palette.xml @@ -20,7 +20,6 @@ #5C56F5 #0086E6 - #F4F6FA #E3E8F0 diff --git a/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml b/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml index c9be5175e0..88bee99f84 100644 --- a/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml +++ b/library/ui-styles/src/main/res/values/stylable_badge_floating_action_button.xml @@ -1,5 +1,4 @@ - diff --git a/library/ui-styles/src/main/res/values/styles_social_login.xml b/library/ui-styles/src/main/res/values/styles_social_login.xml index f601f36480..5a76f70f2e 100644 --- a/library/ui-styles/src/main/res/values/styles_social_login.xml +++ b/library/ui-styles/src/main/res/values/styles_social_login.xml @@ -49,7 +49,6 @@ @android:color/black - @@ -68,7 +67,6 @@ #3877EA - @@ -85,7 +83,6 @@ #5D9EC9 - @@ -118,5 +115,4 @@ @android:color/black - \ No newline at end of file diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index cf9b8f87c1..8e21828562 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -20,9 +20,10 @@ import android.content.Context import android.net.Uri import androidx.lifecycle.Observer import androidx.test.internal.runner.junit4.statement.UiThreadStatement -import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -30,7 +31,6 @@ import kotlinx.coroutines.withTimeout import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue -import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig @@ -45,7 +45,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.sync.SyncState -import java.util.ArrayList +import timber.log.Timber import java.util.UUID import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -56,13 +56,14 @@ import java.util.concurrent.TimeUnit */ class CommonTestHelper(context: Context) { - val matrix: Matrix + internal val matrix: TestMatrix + val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor + fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor init { UiThreadStatement.runOnUiThread { - Matrix.initialize( + TestMatrix.initialize( context, MatrixConfiguration( applicationFlavor = "TestFlavor", @@ -70,7 +71,7 @@ class CommonTestHelper(context: Context) { ) ) } - matrix = Matrix.getInstance(context) + matrix = TestMatrix.getInstance(context) } fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session { @@ -95,33 +96,47 @@ class CommonTestHelper(context: Context) { * * @param session the session to sync */ - @Suppress("EXPERIMENTAL_API_USAGE") - fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis) { + fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) { val lock = CountDownLatch(1) - - val job = GlobalScope.launch(Dispatchers.Main) { - session.open() - } - runBlocking { job.join() } - - session.startSync(true) - - val syncLiveData = runBlocking(Dispatchers.Main) { - session.getSyncStateLive() - } - val syncObserver = object : Observer { - override fun onChanged(t: SyncState?) { - if (session.hasAlreadySynced()) { - lock.countDown() - syncLiveData.removeObserver(this) + coroutineScope.launch { + session.startSync(true) + val syncLiveData = session.getSyncStateLive() + val syncObserver = object : Observer { + override fun onChanged(t: SyncState?) { + if (session.hasAlreadySynced()) { + lock.countDown() + syncLiveData.removeObserver(this) + } } } + syncLiveData.observeForever(syncObserver) } - GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) } - await(lock, timeout) } + /** + * This methods clear the cache and waits for initialSync + * + * @param session the session to sync + */ + fun clearCacheAndSync(session: Session, timeout: Long = TestConstants.timeOutMillis) { + waitWithLatch(timeout) { latch -> + session.clearCache() + val syncLiveData = session.getSyncStateLive() + val syncObserver = object : Observer { + override fun onChanged(t: SyncState?) { + if (session.hasAlreadySynced()) { + Timber.v("Clear cache and synced") + syncLiveData.removeObserver(this) + latch.countDown() + } + } + } + syncLiveData.observeForever(syncObserver) + session.startSync(true) + } + } + /** * Sends text messages in a room * @@ -130,46 +145,57 @@ class CommonTestHelper(context: Context) { * @param nbOfMessages the number of time the message will be sent */ fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List { - val timeline = room.createTimeline(null, TimelineSettings(10)) val sentEvents = ArrayList(nbOfMessages) - val latch = CountDownLatch(1) - val timelineListener = object : Timeline.Listener { - override fun onTimelineFailure(throwable: Throwable) { - } + val timeline = room.createTimeline(null, TimelineSettings(10)) + timeline.start() + waitWithLatch(timeout + 1_000L * nbOfMessages) { latch -> + val timelineListener = object : Timeline.Listener { + override fun onTimelineFailure(throwable: Throwable) { + } - override fun onNewTimelineEvents(eventIds: List) { - // noop - } + override fun onNewTimelineEvents(eventIds: List) { + // noop + } - override fun onTimelineUpdated(snapshot: List) { - val newMessages = snapshot - .filter { it.root.sendState == SendState.SYNCED } - .filter { it.root.getClearType() == EventType.MESSAGE } - .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true } + override fun onTimelineUpdated(snapshot: List) { + val newMessages = snapshot + .filter { it.root.sendState == SendState.SYNCED } + .filter { it.root.getClearType() == EventType.MESSAGE } + .filter { it.root.getClearContent().toModel()?.body?.startsWith(message) == true } - if (newMessages.size == nbOfMessages) { - sentEvents.addAll(newMessages) - // Remove listener now, if not at the next update sendEvents could change - timeline.removeListener(this) - latch.countDown() + Timber.v("New synced message size: ${newMessages.size}") + if (newMessages.size == nbOfMessages) { + sentEvents.addAll(newMessages) + // Remove listener now, if not at the next update sendEvents could change + timeline.removeListener(this) + latch.countDown() + } } } + timeline.addListener(timelineListener) + sendTextMessagesBatched(room, message, nbOfMessages) } - timeline.start() - timeline.addListener(timelineListener) - for (i in 0 until nbOfMessages) { - room.sendTextMessage(message + " #" + (i + 1)) - } - // Wait 3 second more per message - await(latch, timeout = timeout + 3_000L * nbOfMessages) timeline.dispose() - // Check that all events has been created assertEquals("Message number do not match $sentEvents", nbOfMessages.toLong(), sentEvents.size.toLong()) - return sentEvents } + /** + * Will send nb of messages provided by count parameter but waits a bit every 10 messages to avoid gap in sync + */ + private fun sendTextMessagesBatched(room: Room, message: String, count: Int) { + (1 until count + 1) + .map { "$message #$it" } + .chunked(10) + .forEach { batchedMessages -> + batchedMessages.forEach { formattedMessage -> + room.sendTextMessage(formattedMessage) + } + Thread.sleep(1_000L) + } + } + // PRIVATE METHODS ***************************************************************************** /** @@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) { assertTrue(registrationResult is RegistrationResult.Success) val session = (registrationResult as RegistrationResult.Success).session + session.open() if (sessionTestParams.withInitialSync) { syncSession(session, 60_000) } - return session } @@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) { .getLoginWizard() .login(userName, password, "myDevice") } - + session.open() if (sessionTestParams.withInitialSync) { syncSession(session) } @@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) { assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)) } - @Suppress("EXPERIMENTAL_API_USAGE") - fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { - GlobalScope.launch { - while (true) { - delay(1000) - if (condition()) { - latch.countDown() - return@launch - } + suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { + while (true) { + delay(1000) + if (condition()) { + latch.countDown() + return } } } - fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, block: (CountDownLatch) -> Unit) { + fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, dispatcher: CoroutineDispatcher = Dispatchers.Main, block: suspend (CountDownLatch) -> Unit) { val latch = CountDownLatch(1) - block(latch) + coroutineScope.launch(dispatcher) { + block(latch) + } await(latch, timeout) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index a8cbc160dd..ccea6f53b9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -19,10 +19,6 @@ package org.matrix.android.sdk.common import android.os.SystemClock import android.util.Log import androidx.lifecycle.Observer -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -31,6 +27,7 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.OutgoingSasVerificationTransaction @@ -44,16 +41,16 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import java.util.UUID -import java.util.concurrent.CountDownLatch import kotlin.coroutines.Continuation import kotlin.coroutines.resume -class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { +class CryptoTestHelper(private val testHelper: CommonTestHelper) { private val messagesFromAlice: List = listOf("0 - Hello I'm Alice!", "4 - Go!") private val messagesFromBob: List = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.") @@ -64,27 +61,33 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { * @return alice session */ fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) - val roomId = mTestHelper.runBlockingTest { + val roomId = testHelper.runBlockingTest { aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) } - if (encryptedRoom) { - val room = aliceSession.getRoom(roomId)!! - - mTestHelper.runBlockingTest { + testHelper.waitWithLatch { latch -> + val room = aliceSession.getRoom(roomId)!! room.enableEncryption() + val roomSummaryLive = room.getRoomSummaryLive() + val roomSummaryObserver = object : Observer> { + override fun onChanged(roomSummary: Optional) { + if (roomSummary.getOrNull()?.isEncrypted.orFalse()) { + roomSummaryLive.removeObserver(this) + latch.countDown() + } + } + } + roomSummaryLive.observeForever(roomSummaryObserver) } } - return CryptoTestData(roomId, listOf(aliceSession)) } /** * @return alice and bob sessions */ - @Suppress("EXPERIMENTAL_API_USAGE") fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData { val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom) val aliceSession = cryptoTestData.firstSession @@ -92,54 +95,37 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { val aliceRoom = aliceSession.getRoom(aliceRoomId)!! - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, defaultSessionParams) - val lock1 = CountDownLatch(1) - - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { - bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) - } - - val newRoomObserver = object : Observer> { - override fun onChanged(t: List?) { - if (t?.isNotEmpty() == true) { - lock1.countDown() - bobRoomSummariesLive.removeObserver(this) + testHelper.waitWithLatch { latch -> + val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) + val newRoomObserver = object : Observer> { + override fun onChanged(t: List?) { + if (t?.isNotEmpty() == true) { + bobRoomSummariesLive.removeObserver(this) + latch.countDown() + } } } - } - - GlobalScope.launch(Dispatchers.Main) { bobRoomSummariesLive.observeForever(newRoomObserver) - } - - mTestHelper.runBlockingTest { aliceRoom.invite(bobSession.myUserId) } - mTestHelper.await(lock1) - - val lock = CountDownLatch(1) - - val roomJoinedObserver = object : Observer> { - override fun onChanged(t: List?) { - if (bobSession.getRoom(aliceRoomId) - ?.getRoomMember(aliceSession.myUserId) - ?.membership == Membership.JOIN) { - lock.countDown() - bobRoomSummariesLive.removeObserver(this) + testHelper.waitWithLatch { latch -> + val bobRoomSummariesLive = bobSession.getRoomSummariesLive(roomSummaryQueryParams { }) + val roomJoinedObserver = object : Observer> { + override fun onChanged(t: List?) { + if (bobSession.getRoom(aliceRoomId) + ?.getRoomMember(bobSession.myUserId) + ?.membership == Membership.JOIN) { + bobRoomSummariesLive.removeObserver(this) + latch.countDown() + } } } - } - - GlobalScope.launch(Dispatchers.Main) { bobRoomSummariesLive.observeForever(roomJoinedObserver) + bobSession.joinRoom(aliceRoomId) } - - mTestHelper.runBlockingTest { bobSession.joinRoom(aliceRoomId) } - - mTestHelper.await(lock) - // Ensure bob can send messages to the room // val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! // assertNotNull(roomFromBobPOV.powerLevels) @@ -171,13 +157,13 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { * @Return Sam session */ fun createSamAccountAndInviteToTheRoom(room: Room): Session { - val samSession = mTestHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) + val samSession = testHelper.createAccount(TestConstants.USER_SAM, defaultSessionParams) - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { room.invite(samSession.myUserId, null) } - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { samSession.joinRoom(room.roomId, null, emptyList()) } @@ -194,23 +180,20 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { val bobSession = cryptoTestData.secondSession!! bobSession.cryptoService().setWarnOnUnknownDevices(false) - aliceSession.cryptoService().setWarnOnUnknownDevices(false) val roomFromBobPOV = bobSession.getRoom(aliceRoomId)!! val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! // Alice sends a message - mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1) -// roomFromAlicePOV.sendTextMessage(messagesFromAlice[0]) + testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[0], 1) // Bob send 3 messages - mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1) - mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1) - mTestHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1) + testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[0], 1) + testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[1], 1) + testHelper.sendTextMessage(roomFromBobPOV, messagesFromBob[2], 1) // Alice sends a message - mTestHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1) - + testHelper.sendTextMessage(roomFromAlicePOV, messagesFromAlice[1], 1) return cryptoTestData } @@ -256,60 +239,44 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { ) } - @Suppress("EXPERIMENTAL_API_USAGE") fun createDM(alice: Session, bob: Session): String { - val roomId = mTestHelper.runBlockingTest { - alice.createDirectRoom(bob.myUserId) - } - - mTestHelper.waitWithLatch { latch -> - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { - bob.getRoomSummariesLive(roomSummaryQueryParams { }) - } - + var roomId: String = "" + testHelper.waitWithLatch { latch -> + roomId = alice.createDirectRoom(bob.myUserId) + val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { }) val newRoomObserver = object : Observer> { override fun onChanged(t: List?) { val indexOfFirst = t?.indexOfFirst { it.roomId == roomId } ?: -1 if (indexOfFirst != -1) { - latch.countDown() bobRoomSummariesLive.removeObserver(this) + latch.countDown() } } } - - GlobalScope.launch(Dispatchers.Main) { - bobRoomSummariesLive.observeForever(newRoomObserver) - } + bobRoomSummariesLive.observeForever(newRoomObserver) } - mTestHelper.waitWithLatch { latch -> - val bobRoomSummariesLive = runBlocking(Dispatchers.Main) { - bob.getRoomSummariesLive(roomSummaryQueryParams { }) - } - + testHelper.waitWithLatch { latch -> + val bobRoomSummariesLive = bob.getRoomSummariesLive(roomSummaryQueryParams { }) val newRoomObserver = object : Observer> { override fun onChanged(t: List?) { if (bob.getRoom(roomId) ?.getRoomMember(bob.myUserId) ?.membership == Membership.JOIN) { - latch.countDown() bobRoomSummariesLive.removeObserver(this) + latch.countDown() } } } - - GlobalScope.launch(Dispatchers.Main) { - bobRoomSummariesLive.observeForever(newRoomObserver) - } - - mTestHelper.runBlockingTest { bob.joinRoom(roomId) } + bobRoomSummariesLive.observeForever(newRoomObserver) + bob.joinRoom(roomId) } return roomId } fun initializeCrossSigning(session: Session) { - mTestHelper.doSync { + testHelper.doSync { session.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -346,8 +313,8 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { var bobPovTx: IncomingSasVerificationTransaction? = null // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") if (bobPovTx?.state == VerificationTxState.OnStarted) { @@ -359,16 +326,16 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { } } - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alicePovTx = aliceVerificationService.getExistingTransaction(bob.myUserId, requestID) as? OutgoingSasVerificationTransaction Log.v("TEST", "== alicePovTx is ${alicePovTx?.uxState}") alicePovTx?.state == VerificationTxState.ShortCodeReady } } // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { bobPovTx = bobVerificationService.getExistingTransaction(alice.myUserId, requestID) as? IncomingSasVerificationTransaction Log.v("TEST", "== bobPovTx is ${alicePovTx?.uxState}") if (bobPovTx?.state == VerificationTxState.OnStarted) { @@ -383,38 +350,38 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { bobPovTx!!.userHasVerifiedShortCode() alicePovTx!!.userHasVerifiedShortCode() - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) } } - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId) } } } fun doE2ETestWithManyMembers(numberOfMembers: Int): CryptoTestData { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) aliceSession.cryptoService().setWarnOnUnknownDevices(false) - val roomId = mTestHelper.runBlockingTest { + val roomId = testHelper.runBlockingTest { aliceSession.createRoom(CreateRoomParams().apply { name = "MyRoom" }) } val room = aliceSession.getRoom(roomId)!! - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { room.enableEncryption() } val sessions = mutableListOf(aliceSession) for (index in 1 until numberOfMembers) { - val session = mTestHelper.createAccount("User_$index", defaultSessionParams) - mTestHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } + val session = testHelper.createAccount("User_$index", defaultSessionParams) + testHelper.runBlockingTest(timeout = 600_000) { room.invite(session.myUserId, null) } println("TEST -> " + session.myUserId + " invited") - mTestHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) } + testHelper.runBlockingTest { session.joinRoom(room.roomId, null, emptyList()) } println("TEST -> " + session.myUserId + " joined") sessions.add(session) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt similarity index 53% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt index 68e1953eff..c1596c281c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/DelegateWorkerFactory.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestBackgroundDetectionObserver.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,18 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.worker +package org.matrix.android.sdk.common -import android.content.Context -import androidx.work.ListenableWorker -import androidx.work.WorkerParameters +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver -interface DelegateWorkerFactory { +/** + * Force foreground for testing + */ +internal class TestBackgroundDetectionObserver : BackgroundDetectionObserver { - fun create(context: Context, params: WorkerParameters): ListenableWorker + override val isInBackground: Boolean = false + + override fun register(listener: BackgroundDetectionObserver.Listener) = Unit + + override fun unregister(listener: BackgroundDetectionObserver.Listener) = Unit } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt similarity index 80% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt rename to matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt index 8b9b6efa11..e92232a7c5 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.api +package org.matrix.android.sdk.common import android.content.Context import android.os.Handler @@ -24,27 +24,27 @@ import androidx.work.Configuration import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.BuildConfig +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.HomeServerHistoryService import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.api.network.ApiInterceptorListener import org.matrix.android.sdk.api.network.ApiPath import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.common.DaggerTestMatrixComponent import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import org.matrix.olm.OlmManager import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject /** - * This is the main entry point to the matrix sdk. - * To get the singleton instance, use getInstance static method. + * This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent. */ -class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) { +internal class TestMatrix constructor(context: Context, matrixConfiguration: MatrixConfiguration) { @Inject internal lateinit var legacySessionImporter: LegacySessionImporter @Inject internal lateinit var authenticationService: AuthenticationService @@ -55,15 +55,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo @Inject internal lateinit var sessionManager: SessionManager @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var apiInterceptor: ApiInterceptor + @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory private val uiHandler = Handler(Looper.getMainLooper()) init { Monarchy.init(context) DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this) - if (context.applicationContext !is Configuration.Provider) { - WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) - } + val configuration = Configuration.Builder() + .setExecutor(Executors.newCachedThreadPool()) + .setWorkerFactory(matrixWorkerFactory) + .build() + WorkManager.initialize(context, configuration) uiHandler.post { ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) } @@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo companion object { - private lateinit var instance: Matrix + private lateinit var instance: TestMatrix private val isInit = AtomicBoolean(false) fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) { if (isInit.compareAndSet(false, true)) { - instance = Matrix(context.applicationContext, matrixConfiguration) + instance = TestMatrix(context.applicationContext, matrixConfiguration) } } - fun getInstance(context: Context): Matrix { + fun getInstance(context: Context): TestMatrix { if (isInit.compareAndSet(false, true)) { val appContext = context.applicationContext if (appContext is MatrixConfiguration.Provider) { val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration() - instance = Matrix(appContext, matrixConfiguration) + instance = TestMatrix(appContext, matrixConfiguration) } else { throw IllegalStateException("Matrix is not initialized properly." + " You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.") diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt index 1d05e655af..d0f0e23152 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt @@ -34,12 +34,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule NetworkModule::class, AuthModule::class, RawModule::class, - SystemModule::class, - TestNetworkModule::class + SystemModule::class ]) @MatrixScope internal interface TestMatrixComponent : MatrixComponent { + fun inject(matrix: TestMatrix) + @Component.Factory interface Factory { fun create(@BindsInstance context: Context, diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt index 3e4d5fe08e..d82bac3700 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt @@ -18,10 +18,39 @@ package org.matrix.android.sdk.common import dagger.Binds import dagger.Module +import dagger.Provides import org.matrix.android.sdk.internal.di.MatrixComponent +import org.matrix.android.sdk.internal.di.MatrixScope +import org.matrix.android.sdk.internal.session.MockHttpInterceptor +import org.matrix.android.sdk.internal.session.TestInterceptor +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver @Module internal abstract class TestModule { @Binds abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent + + @Module + companion object { + + val interceptors = ArrayList() + + fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId } + + @Provides + @JvmStatic + @MockHttpInterceptor + fun providesTestInterceptor(): TestInterceptor? { + return MockOkHttpInterceptor().also { + interceptors.add(it) + } + } + + @Provides + @JvmStatic + @MatrixScope + fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver { + return TestBackgroundDetectionObserver() + } + } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt deleted file mode 100644 index 4cd92ca272..0000000000 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2020 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.common - -import dagger.Module -import dagger.Provides -import org.matrix.android.sdk.internal.session.MockHttpInterceptor -import org.matrix.android.sdk.internal.session.TestInterceptor - -@Module -internal object TestNetworkModule { - - val interceptors = ArrayList() - - fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId } - - @Provides - @JvmStatic - @MockHttpInterceptor - fun providesTestInterceptor(): TestInterceptor? { - return MockOkHttpInterceptor().also { - interceptors.add(it) - } - } -} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt index 825fba570a..d0f63227f5 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt @@ -36,12 +36,12 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent @FixMethodOrder(MethodSorters.JVM) class PreShareKeysTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun ensure_outbound_session_happy_path() { - val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true) val e2eRoomID = testData.roomId val aliceSession = testData.firstSession val bobSession = testData.secondSession!! @@ -58,12 +58,12 @@ class PreShareKeysTest : InstrumentedTest { Log.d("#Test", "Room Key Received from alice $preShareCount") // Force presharing of new outbound key - mTestHelper.doSync { + testHelper.doSync { aliceSession.cryptoService().prepareToEncrypt(e2eRoomID, it) } - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val newGossipCount = bobSession.cryptoService().getGossipingEvents().count { it.senderId == aliceSession.myUserId && it.getClearType() == EventType.ROOM_KEY @@ -88,16 +88,16 @@ class PreShareKeysTest : InstrumentedTest { assertEquals("The session received by bob should match what alice sent", 0, sharedIndex) // Just send a real message as test - val sentEvent = mTestHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first() + val sentEvent = testHelper.sendTextMessage(aliceSession.getRoom(e2eRoomID)!!, "Allo", 1).first() assertEquals(megolmSessionId, sentEvent.root.content.toModel()?.sessionId, "Unexpected megolm session") - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE } } - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt index cf31294e2f..458eae6ab2 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt @@ -62,8 +62,8 @@ import kotlin.coroutines.resume class UnwedgingTest : InstrumentedTest { private lateinit var messagesReceivedByBob: List - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Before fun init() { @@ -85,7 +85,7 @@ class UnwedgingTest : InstrumentedTest { */ @Test fun testUnwedging() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId @@ -133,7 +133,7 @@ class UnwedgingTest : InstrumentedTest { roomFromAlicePOV.sendTextMessage("First message") // Wait for the message to be received by Bob - mTestHelper.await(latch) + testHelper.await(latch) bobTimeline.removeListener(bobEventsListener) messagesReceivedByBob.size shouldBe 1 @@ -161,7 +161,7 @@ class UnwedgingTest : InstrumentedTest { roomFromAlicePOV.sendTextMessage("Second message") // Wait for the message to be received by Bob - mTestHelper.await(latch) + testHelper.await(latch) bobTimeline.removeListener(bobEventsListener) messagesReceivedByBob.size shouldBe 2 @@ -179,7 +179,7 @@ class UnwedgingTest : InstrumentedTest { aliceSession.cryptoService().discardOutboundSession(roomFromAlicePOV.roomId) // Wait for the message to be received by Bob - mTestHelper.waitWithLatch { + testHelper.waitWithLatch { bobEventsListener = createEventListener(it, 3) bobTimeline.addListener(bobEventsListener) messagesReceivedByBob = emptyList() @@ -201,11 +201,11 @@ class UnwedgingTest : InstrumentedTest { Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[1].root.getClearType()) Assert.assertEquals(EventType.MESSAGE, messagesReceivedByBob[2].root.getClearType()) // Bob Should not be able to decrypt last message, because session could not be sent as the olm channel was wedged - mTestHelper.await(bobFinalLatch) + testHelper.await(bobFinalLatch) bobTimeline.removeListener(bobHasThreeDecryptedEventsListener) // It's a trick to force key request on fail to decrypt - mTestHelper.doSync { + testHelper.doSync { bobSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -222,8 +222,8 @@ class UnwedgingTest : InstrumentedTest { } // Wait until we received back the key - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { // we should get back the key and be able to decrypt val result = tryOrNull { bobSession.cryptoService().decryptEvent(messagesReceivedByBob[0].root, "") @@ -235,7 +235,7 @@ class UnwedgingTest : InstrumentedTest { bobTimeline.dispose() - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt index 44af87bcbe..d9cc7a8ac0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt @@ -45,14 +45,14 @@ import kotlin.coroutines.resume @FixMethodOrder(MethodSorters.NAME_ASCENDING) class XSigningTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_InitializeAndStoreKeys() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - mTestHelper.doSync { + testHelper.doSync { aliceSession.cryptoService().crossSigningService() .initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { @@ -79,12 +79,12 @@ class XSigningTest : InstrumentedTest { assertTrue("Signing Keys should be trusted", aliceSession.cryptoService().crossSigningService().checkUserTrust(aliceSession.myUserId).isVerified()) - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_CrossSigningCheckBobSeesTheKeys() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -98,21 +98,21 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - mTestHelper.doSync { + testHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(aliceAuthParams) } }, it) } - mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(bobAuthParams) } }, it) } // Check that alice can see bob keys - mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } + testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobSession.myUserId), true, it) } val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId) assertNotNull("Alice can see bob Master key", bobKeysFromAlicePOV!!.masterKey()) @@ -124,13 +124,13 @@ class XSigningTest : InstrumentedTest { assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted()) - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) } @Test fun test_CrossSigningTestAliceTrustBobNewDevice() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -144,12 +144,12 @@ class XSigningTest : InstrumentedTest { password = TestConstants.PASSWORD ) - mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + testHelper.doSync { aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(aliceAuthParams) } }, it) } - mTestHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { + testHelper.doSync { bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { promise.resume(bobAuthParams) } @@ -157,21 +157,21 @@ class XSigningTest : InstrumentedTest { // Check that alice can see bob keys val bobUserId = bobSession.myUserId - mTestHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } + testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } val bobKeysFromAlicePOV = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobUserId) assertTrue("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV?.isTrusted() == false) - mTestHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } + testHelper.doSync { aliceSession.cryptoService().crossSigningService().trustUser(bobUserId, it) } // Now bobs logs in on a new device and verifies it // We will want to test that in alice POV, this new device would be trusted by cross signing - val bobSession2 = mTestHelper.logIntoAccount(bobUserId, SessionTestParams(true)) + val bobSession2 = testHelper.logIntoAccount(bobUserId, SessionTestParams(true)) val bobSecondDeviceId = bobSession2.sessionParams.deviceId!! // Check that bob first session sees the new login - val data = mTestHelper.doSync> { + val data = testHelper.doSync> { bobSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } @@ -183,12 +183,12 @@ class XSigningTest : InstrumentedTest { assertNotNull("Bob Second device should be known and persisted from first", bobSecondDevicePOVFirstDevice) // Manually mark it as trusted from first session - mTestHelper.doSync { + testHelper.doSync { bobSession.cryptoService().crossSigningService().trustDevice(bobSecondDeviceId, it) } // Now alice should cross trust bob's second device - val data2 = mTestHelper.doSync> { + val data2 = testHelper.doSync> { aliceSession.cryptoService().downloadKeys(listOf(bobUserId), true, it) } @@ -200,8 +200,8 @@ class XSigningTest : InstrumentedTest { val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null) assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified()) - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) - mTestHelper.signOutAndClose(bobSession2) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession2) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt index da5e90abdd..189fc405eb 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt @@ -40,8 +40,9 @@ import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EncryptionTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_EncryptionEvent() { @@ -69,7 +70,7 @@ class EncryptionTest : InstrumentedTest { } private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false) + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false) val aliceSession = cryptoTestData.firstSession val room = aliceSession.getRoom(cryptoTestData.roomId)!! @@ -101,12 +102,12 @@ class EncryptionTest : InstrumentedTest { timeline.addListener(timelineListener) action.invoke(room) - - mTestHelper.await(latch) + testHelper.await(latch) timeline.dispose() - - room.isEncrypted() shouldBe roomShouldBeEncrypted - - cryptoTestData.cleanUp(mTestHelper) + testHelper.waitWithLatch { + room.isEncrypted() shouldBe roomShouldBeEncrypted + it.countDown() + } + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt index 40659cef11..975d481628 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt @@ -44,7 +44,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.common.CommonTestHelper -import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.SessionTestParams import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.internal.crypto.GossipingRequestState @@ -55,7 +54,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent -import java.util.concurrent.CountDownLatch import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -63,15 +61,14 @@ import kotlin.coroutines.resume @FixMethodOrder(MethodSorters.JVM) class KeyShareTests : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val commonTestHelper = CommonTestHelper(context()) @Test fun test_DoNotSelfShareIfNotTrusted() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) // Create an encrypted room and add a message - val roomId = mTestHelper.runBlockingTest { + val roomId = commonTestHelper.runBlockingTest { aliceSession.createRoom( CreateRoomParams().apply { visibility = RoomDirectoryVisibility.PRIVATE @@ -83,11 +80,11 @@ class KeyShareTests : InstrumentedTest { assertNotNull(room) Thread.sleep(4_000) assertTrue(room?.isEncrypted() == true) - val sentEventId = mTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId + val sentEventId = commonTestHelper.sendTextMessage(room!!, "My Message", 1).first().eventId // Open a new sessionx - val aliceSession2 = mTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true)) + val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true)) val roomSecondSessionPOV = aliceSession2.getRoom(roomId) @@ -105,25 +102,24 @@ class KeyShareTests : InstrumentedTest { // Try to request aliceSession2.cryptoService().requestRoomKeyForEvent(receivedEvent.root) - val waitLatch = CountDownLatch(1) val eventMegolmSessionId = receivedEvent.root.content.toModel()?.sessionId var outGoingRequestId: String? = null - mTestHelper.retryPeriodicallyWithLatch(waitLatch) { - aliceSession2.cryptoService().getOutgoingRoomKeyRequests() - .filter { req -> - // filter out request that was known before - !outgoingRequestsBefore.any { req.requestId == it.requestId } - } - .let { - val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId } - outGoingRequestId = outgoing?.requestId - outgoing != null - } + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + aliceSession2.cryptoService().getOutgoingRoomKeyRequests() + .filter { req -> + // filter out request that was known before + !outgoingRequestsBefore.any { req.requestId == it.requestId } + } + .let { + val outgoing = it.firstOrNull { it.sessionId == eventMegolmSessionId } + outGoingRequestId = outgoing?.requestId + outgoing != null + } + } } - mTestHelper.await(waitLatch) - Log.v("TEST", "=======> Outgoing requet Id is $outGoingRequestId") val outgoingRequestAfter = aliceSession2.cryptoService().getOutgoingRoomKeyRequests() @@ -134,8 +130,8 @@ class KeyShareTests : InstrumentedTest { // The first session should see an incoming request // the request should be refused, because the device is not trusted - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { // DEBUG LOGS aliceSession.cryptoService().getIncomingRoomKeyRequests().let { Log.v("TEST", "Incoming request Session 1 (looking for $outGoingRequestId)") @@ -164,8 +160,8 @@ class KeyShareTests : InstrumentedTest { // Re request aliceSession2.cryptoService().reRequestRoomKeyForEvent(receivedEvent.root) - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { aliceSession.cryptoService().getIncomingRoomKeyRequests().let { Log.v("TEST", "Incoming request Session 1") Log.v("TEST", "=========================") @@ -180,8 +176,8 @@ class KeyShareTests : InstrumentedTest { } Thread.sleep(6_000) - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { aliceSession2.cryptoService().getOutgoingRoomKeyRequests().let { it.any { it.requestBody?.sessionId == eventMegolmSessionId && it.state == OutgoingGossipingRequestState.CANCELLED } } @@ -194,15 +190,15 @@ class KeyShareTests : InstrumentedTest { fail("should have been able to decrypt") } - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(aliceSession2) + commonTestHelper.signOutAndClose(aliceSession) + commonTestHelper.signOutAndClose(aliceSession2) } @Test fun test_ShareSSSSSecret() { - val aliceSession1 = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession1 = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - mTestHelper.doSync { + commonTestHelper.doSync { aliceSession1.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -218,25 +214,25 @@ class KeyShareTests : InstrumentedTest { } // Also bootstrap keybackup on first session - val creationInfo = mTestHelper.doSync { + val creationInfo = commonTestHelper.doSync { aliceSession1.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) } - val version = mTestHelper.doSync { + val version = commonTestHelper.doSync { aliceSession1.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it) } // Save it for gossiping aliceSession1.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version) - val aliceSession2 = mTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true)) + val aliceSession2 = commonTestHelper.logIntoAccount(aliceSession1.myUserId, SessionTestParams(true)) val aliceVerificationService1 = aliceSession1.cryptoService().verificationService() val aliceVerificationService2 = aliceSession2.cryptoService().verificationService() // force keys download - mTestHelper.doSync> { + commonTestHelper.doSync> { aliceSession1.cryptoService().downloadKeys(listOf(aliceSession1.myUserId), true, it) } - mTestHelper.doSync> { + commonTestHelper.doSync> { aliceSession2.cryptoService().downloadKeys(listOf(aliceSession2.myUserId), true, it) } @@ -276,8 +272,8 @@ class KeyShareTests : InstrumentedTest { aliceVerificationService2.beginKeyVerification(VerificationMethod.SAS, aliceSession1.myUserId, aliceSession1.sessionParams.deviceId ?: "", txId) - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { aliceSession1.cryptoService().getDeviceInfo(aliceSession1.myUserId, aliceSession2.sessionParams.deviceId ?: "")?.isVerified == true } } @@ -290,31 +286,31 @@ class KeyShareTests : InstrumentedTest { // SSK and USK private keys should have been shared - mTestHelper.waitWithLatch(60_000) { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.waitWithLatch(60_000) { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { Log.d("#TEST", "CAN XS :${aliceSession2.cryptoService().crossSigningService().getMyCrossSigningKeys()}") aliceSession2.cryptoService().crossSigningService().canCrossSign() } } // Test that key backup key has been shared to - mTestHelper.waitWithLatch(60_000) { latch -> + commonTestHelper.waitWithLatch(60_000) { latch -> val keysBackupService = aliceSession2.cryptoService().keysBackupService() - mTestHelper.retryPeriodicallyWithLatch(latch) { + commonTestHelper.retryPeriodicallyWithLatch(latch) { Log.d("#TEST", "Recovery :${keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey}") keysBackupService.getKeyBackupRecoveryKeyInfo()?.recoveryKey == creationInfo.recoveryKey } } - mTestHelper.signOutAndClose(aliceSession1) - mTestHelper.signOutAndClose(aliceSession2) + commonTestHelper.signOutAndClose(aliceSession1) + commonTestHelper.signOutAndClose(aliceSession2) } @Test fun test_ImproperKeyShareBug() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - mTestHelper.doSync { + commonTestHelper.doSync { aliceSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -331,7 +327,7 @@ class KeyShareTests : InstrumentedTest { } // Create an encrypted room and send a couple of messages - val roomId = mTestHelper.runBlockingTest { + val roomId = commonTestHelper.runBlockingTest { aliceSession.createRoom( CreateRoomParams().apply { visibility = RoomDirectoryVisibility.PRIVATE @@ -343,12 +339,12 @@ class KeyShareTests : InstrumentedTest { assertNotNull(roomAlicePov) Thread.sleep(1_000) assertTrue(roomAlicePov?.isEncrypted() == true) - val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId + val secondEventId = commonTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId // Create bob session - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) - mTestHelper.doSync { + val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) + commonTestHelper.doSync { bobSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -365,11 +361,11 @@ class KeyShareTests : InstrumentedTest { } // Let alice invite bob - mTestHelper.runBlockingTest { + commonTestHelper.runBlockingTest { roomAlicePov.invite(bobSession.myUserId, null) } - mTestHelper.runBlockingTest { + commonTestHelper.runBlockingTest { bobSession.joinRoom(roomAlicePov.roomId, null, emptyList()) } @@ -377,7 +373,7 @@ class KeyShareTests : InstrumentedTest { aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId) // and now resend a new message to reset index to 0 - mTestHelper.sendTextMessage(roomAlicePov, "After", 1) + commonTestHelper.sendTextMessage(roomAlicePov, "After", 1) val roomRoomBobPov = aliceSession.getRoom(roomId) val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt index c939952dc9..c835c2d40b 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt @@ -41,8 +41,8 @@ import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode @FixMethodOrder(MethodSorters.JVM) class WithHeldTests : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_WithHeldUnverifiedReason() { @@ -50,19 +50,19 @@ class WithHeldTests : InstrumentedTest { // ARRANGE // ============================= - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true)) // Initialize cross signing on both - mCryptoTestHelper.initializeCrossSigning(aliceSession) - mCryptoTestHelper.initializeCrossSigning(bobSession) + cryptoTestHelper.initializeCrossSigning(aliceSession) + cryptoTestHelper.initializeCrossSigning(bobSession) - val roomId = mCryptoTestHelper.createDM(aliceSession, bobSession) - mCryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId) + val roomId = cryptoTestHelper.createDM(aliceSession, bobSession) + cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, roomId) val roomAlicePOV = aliceSession.getRoom(roomId)!! - val bobUnverifiedSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) + val bobUnverifiedSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) // ============================= // ACT @@ -71,11 +71,11 @@ class WithHeldTests : InstrumentedTest { // Alice decide to not send to unverified sessions aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(true) - val timelineEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first() + val timelineEvent = testHelper.sendTextMessage(roomAlicePOV, "Hello Bob", 1).first() // await for bob unverified session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null } } @@ -101,10 +101,10 @@ class WithHeldTests : InstrumentedTest { // enable back sending to unverified aliceSession.cryptoService().setGlobalBlacklistUnverifiedDevices(false) - val secondEvent = mTestHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first() + val secondEvent = testHelper.sendTextMessage(roomAlicePOV, "Verify your device!!", 1).first() - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId) // wait until it's decrypted ev?.root?.getClearType() == EventType.MESSAGE @@ -123,17 +123,17 @@ class WithHeldTests : InstrumentedTest { Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage) } - mTestHelper.signOutAndClose(aliceSession) - mTestHelper.signOutAndClose(bobSession) - mTestHelper.signOutAndClose(bobUnverifiedSession) + testHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobUnverifiedSession) } @Test fun test_WithHeldNoOlm() { - val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = testData.firstSession val bobSession = testData.secondSession!! - val aliceInterceptor = mTestHelper.getTestInterceptor(aliceSession) + val aliceInterceptor = testHelper.getTestInterceptor(aliceSession) // Simulate no OTK aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule( @@ -147,11 +147,11 @@ class WithHeldTests : InstrumentedTest { val roomAlicePov = aliceSession.getRoom(testData.roomId)!! - val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId + val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId // await for bob session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null } } @@ -177,14 +177,14 @@ class WithHeldTests : InstrumentedTest { // Add a new device for bob aliceInterceptor.clearRules() - val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true)) + val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true)) // send a second message - val secondMessageId = mTestHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId + val secondMessageId = testHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId // Check that the // await for bob SecondSession session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null } } @@ -194,27 +194,27 @@ class WithHeldTests : InstrumentedTest { Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2) aliceInterceptor.clearRules() - testData.cleanUp(mTestHelper) - mTestHelper.signOutAndClose(bobSecondSession) + testData.cleanUp(testHelper) + testHelper.signOutAndClose(bobSecondSession) } @Test fun test_WithHeldKeyRequest() { - val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = testData.firstSession val bobSession = testData.secondSession!! val roomAlicePov = aliceSession.getRoom(testData.roomId)!! - val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId + val eventId = testHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession) // Create a new session for bob - val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) + val bobSecondSession = testHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(true)) // initialize to force request keys if missing - mCryptoTestHelper.initializeCrossSigning(bobSecondSession) + cryptoTestHelper.initializeCrossSigning(bobSecondSession) // Trust bob second device from Alice POV aliceSession.cryptoService().crossSigningService().trustDevice(bobSecondSession.sessionParams.deviceId!!, NoOpMatrixCallback()) @@ -223,8 +223,8 @@ class WithHeldTests : InstrumentedTest { var sessionId: String? = null // Check that the // await for bob SecondSession session to get the message - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also { // try to decrypt and force key request tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") } @@ -235,8 +235,8 @@ class WithHeldTests : InstrumentedTest { } // Check that bob second session requested the key - mTestHelper.waitWithLatch { latch -> - mTestHelper.retryPeriodicallyWithLatch(latch) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { val wc = bobSecondSession.cryptoService().getWithHeldMegolmSession(roomAlicePov.roomId, sessionId!!) wc?.code == WithHeldCode.UNAUTHORISED } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index 0785dba8b9..2a07b74115 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -22,7 +22,6 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue -import org.junit.Assert.fail import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -43,7 +42,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreat import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult -import java.util.ArrayList import java.util.Collections import java.util.concurrent.CountDownLatch @@ -51,9 +49,9 @@ import java.util.concurrent.CountDownLatch @FixMethodOrder(MethodSorters.JVM) class KeysBackupTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) - private val mKeysBackupTestHelper = KeysBackupTestHelper(mTestHelper, mCryptoTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) + private val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper) /** * - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys @@ -62,7 +60,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun roomKeysTest_testBackupStore_ok() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() // From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store @@ -92,7 +90,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(sessionsCount, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false)) assertEquals(0, cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true)) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -100,7 +98,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun prepareKeysBackupVersionTest() { - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) assertNotNull(bobSession.cryptoService().keysBackupService()) @@ -110,7 +108,7 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val megolmBackupCreationInfo = mTestHelper.doSync { + val megolmBackupCreationInfo = testHelper.doSync { keysBackup.prepareKeysBackupVersion(null, null, it) } @@ -120,7 +118,7 @@ class KeysBackupTest : InstrumentedTest { assertNotNull(megolmBackupCreationInfo.recoveryKey) stateObserver.stopAndCheckStates(null) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession) } /** @@ -128,7 +126,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun createKeysBackupVersionTest() { - val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) + val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams) val keysBackup = bobSession.cryptoService().keysBackupService() @@ -136,14 +134,14 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val megolmBackupCreationInfo = mTestHelper.doSync { + val megolmBackupCreationInfo = testHelper.doSync { keysBackup.prepareKeysBackupVersion(null, null, it) } assertFalse(keysBackup.isEnabled) // Create the version - mTestHelper.doSync { + testHelper.doSync { keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) } @@ -151,7 +149,7 @@ class KeysBackupTest : InstrumentedTest { assertTrue(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - mTestHelper.signOutAndClose(bobSession) + testHelper.signOutAndClose(bobSession) } /** @@ -160,8 +158,9 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun backupAfterCreateKeysBackupVersionTest() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + keysBackupTestHelper.waitForKeybackUpBatching() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val latch = CountDownLatch(1) @@ -171,9 +170,9 @@ class KeysBackupTest : InstrumentedTest { val stateObserver = StateObserver(keysBackup, latch, 5) - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) - mTestHelper.await(latch) + testHelper.await(latch) val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false) val backedUpKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(true) @@ -191,7 +190,7 @@ class KeysBackupTest : InstrumentedTest { KeysBackupState.ReadyToBackUp ) ) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -199,13 +198,13 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun backupAllGroupSessionsTest() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val stateObserver = StateObserver(keysBackup) - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Check that backupAllGroupSessions returns valid data val nbOfKeys = cryptoTestData.firstSession.cryptoService().inboundGroupSessionsCount(false) @@ -214,7 +213,7 @@ class KeysBackupTest : InstrumentedTest { var lastBackedUpKeysProgress = 0 - mTestHelper.doSync { + testHelper.doSync { keysBackup.backupAllGroupSessions(object : ProgressListener { override fun onProgress(progress: Int, total: Int) { assertEquals(nbOfKeys, total) @@ -230,7 +229,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -243,7 +242,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testEncryptAndDecryptKeysBackupData() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService @@ -252,7 +251,7 @@ class KeysBackupTest : InstrumentedTest { // - Pick a megolm key val session = keysBackup.store.inboundGroupSessionsToBackup(1)[0] - val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo + val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo // - Check encryptGroupSession() returns stg val keyBackupData = keysBackup.encryptGroupSession(session) @@ -270,10 +269,10 @@ class KeysBackupTest : InstrumentedTest { decryption!!) assertNotNull(sessionData) // - Compare the decrypted megolm key with the original one - mKeysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData) + keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -284,10 +283,10 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun restoreKeysBackupTest() { - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) // - Restore the e2e backup from the homeserver - val importRoomKeysResult = mTestHelper.doSync { + val importRoomKeysResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, null, @@ -297,9 +296,9 @@ class KeysBackupTest : InstrumentedTest { ) } - mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) + keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -369,7 +368,7 @@ class KeysBackupTest : InstrumentedTest { fun trustKeyBackupVersionTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -379,7 +378,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device - mTestHelper.doSync { + testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersion( testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, true, @@ -388,21 +387,21 @@ class KeysBackupTest : InstrumentedTest { } // Wait for backup state to be ReadyToBackUp - mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) + keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) // - Backup must be enabled on the new device, on the same version assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) } @@ -411,7 +410,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -428,7 +427,7 @@ class KeysBackupTest : InstrumentedTest { fun trustKeyBackupVersionWithRecoveryKeyTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -438,7 +437,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device with the recovery key - mTestHelper.doSync { + testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithRecoveryKey( testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, @@ -447,21 +446,21 @@ class KeysBackupTest : InstrumentedTest { } // Wait for backup state to be ReadyToBackUp - mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) + keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) // - Backup must be enabled on the new device, on the same version assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) } @@ -470,7 +469,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -485,7 +484,7 @@ class KeysBackupTest : InstrumentedTest { fun trustKeyBackupVersionWithWrongRecoveryKeyTest() { // - Do an e2e backup to the homeserver with a recovery key // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -501,7 +500,7 @@ class KeysBackupTest : InstrumentedTest { "Bad recovery key", TestMatrixCallback(latch, false) ) - mTestHelper.await(latch) + testHelper.await(latch) // - The new device must still see the previous backup as not trusted assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion) @@ -509,7 +508,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -528,7 +527,7 @@ class KeysBackupTest : InstrumentedTest { // - Do an e2e backup to the homeserver with a password // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -538,7 +537,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) // - Trust the backup from the new device with the password - mTestHelper.doSync { + testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().trustKeysBackupVersionWithPassphrase( testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, password, @@ -547,21 +546,21 @@ class KeysBackupTest : InstrumentedTest { } // Wait for backup state to be ReadyToBackUp - mKeysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) + keysBackupTestHelper.waitForKeysBackupToBeInState(testData.aliceSession2, KeysBackupState.ReadyToBackUp) // - Backup must be enabled on the new device, on the same version assertEquals(testData.prepareKeysBackupDataResult.version, testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion?.version) assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) } // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getKeysBackupTrust(keysVersionResult, it) } @@ -570,7 +569,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(2, keysBackupVersionTrust.signatures.size) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -588,7 +587,7 @@ class KeysBackupTest : InstrumentedTest { // - Do an e2e backup to the homeserver with a password // - And log Alice on a new device - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) val stateObserver = StateObserver(testData.aliceSession2.cryptoService().keysBackupService()) @@ -604,7 +603,7 @@ class KeysBackupTest : InstrumentedTest { badPassword, TestMatrixCallback(latch, false) ) - mTestHelper.await(latch) + testHelper.await(latch) // - The new device must still see the previous backup as not trusted assertNotNull(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion) @@ -612,7 +611,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state) stateObserver.stopAndCheckStates(null) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -623,7 +622,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun restoreKeysBackupWithAWrongRecoveryKeyTest() { - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) // - Try to restore the e2e backup with a wrong recovery key val latch2 = CountDownLatch(1) @@ -640,12 +639,12 @@ class KeysBackupTest : InstrumentedTest { } } ) - mTestHelper.await(latch2) + testHelper.await(latch2) // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -658,12 +657,12 @@ class KeysBackupTest : InstrumentedTest { fun testBackupWithPassword() { val password = "password" - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) // - Restore the e2e backup with the password val steps = ArrayList() - val importRoomKeysResult = mTestHelper.doSync { + val importRoomKeysResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().restoreKeyBackupWithPassword(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, password, null, @@ -698,9 +697,9 @@ class KeysBackupTest : InstrumentedTest { assertEquals(50, (steps[103] as StepProgressListener.Step.ImportingKey).progress) assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress) - mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) + keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -714,7 +713,7 @@ class KeysBackupTest : InstrumentedTest { val password = "password" val wrongPassword = "passw0rd" - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) // - Try to restore the e2e backup with a wrong password val latch2 = CountDownLatch(1) @@ -731,12 +730,12 @@ class KeysBackupTest : InstrumentedTest { } } ) - mTestHelper.await(latch2) + testHelper.await(latch2) // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -749,10 +748,10 @@ class KeysBackupTest : InstrumentedTest { fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() { val password = "password" - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(password) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password) // - Restore the e2e backup with the recovery key. - val importRoomKeysResult = mTestHelper.doSync { + val importRoomKeysResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(testData.aliceSession2.cryptoService().keysBackupService().keysBackupVersion!!, testData.prepareKeysBackupDataResult.megolmBackupCreationInfo.recoveryKey, null, @@ -762,9 +761,9 @@ class KeysBackupTest : InstrumentedTest { ) } - mKeysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) + keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -775,7 +774,7 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() { - val testData = mKeysBackupTestHelper.createKeysBackupScenarioWithPassword(null) + val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null) // - Try to restore the e2e backup with a password val latch2 = CountDownLatch(1) @@ -792,12 +791,12 @@ class KeysBackupTest : InstrumentedTest { } } ) - mTestHelper.await(latch2) + testHelper.await(latch2) // onSuccess may not have been called assertNull(importRoomKeysResult) - testData.cleanUp(mTestHelper) + testData.cleanUp(testHelper) } /** @@ -807,22 +806,22 @@ class KeysBackupTest : InstrumentedTest { @Test fun testIsKeysBackupTrusted() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val stateObserver = StateObserver(keysBackup) // - Do an e2e backup to the homeserver - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Get key backup version from the homeserver - val keysVersionResult = mTestHelper.doSync { + val keysVersionResult = testHelper.doSync { keysBackup.getCurrentVersion(it) } // - Check the returned KeyBackupVersion is trusted - val keysBackupVersionTrust = mTestHelper.doSync { + val keysBackupVersionTrust = testHelper.doSync { keysBackup.getKeysBackupTrust(keysVersionResult!!, it) } @@ -837,7 +836,7 @@ class KeysBackupTest : InstrumentedTest { assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -849,9 +848,8 @@ class KeysBackupTest : InstrumentedTest { */ @Test fun testCheckAndStartKeysBackupWhenRestartingAMatrixSession() { - fail("This test still fail. To investigate") // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -859,15 +857,15 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) assertTrue(keysBackup.isEnabled) // - Restart alice session // - Log Alice on a new device - val aliceSession2 = mTestHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) + val aliceSession2 = testHelper.logIntoAccount(cryptoTestData.firstSession.myUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) val keysBackup2 = aliceSession2.cryptoService().keysBackupService() @@ -891,13 +889,13 @@ class KeysBackupTest : InstrumentedTest { } } }) - mTestHelper.await(latch) + testHelper.await(latch) assertEquals(keyBackupCreationInfo.version, keysBackup2.currentBackupVersion) stateObserver.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null) - mTestHelper.signOutAndClose(aliceSession2) + testHelper.signOutAndClose(aliceSession2) } /** @@ -911,7 +909,7 @@ class KeysBackupTest : InstrumentedTest { @Test fun testBackupWhenAnotherBackupWasCreated() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -939,15 +937,15 @@ class KeysBackupTest : InstrumentedTest { }) // - Make alice back up her keys to her homeserver - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) assertTrue(keysBackup.isEnabled) - mTestHelper.await(latch0) + testHelper.await(latch0) // - Create a new backup with fake data on the homeserver, directly using the rest client - val megolmBackupCreationInfo = mCryptoTestHelper.createFakeMegolmBackupCreationInfo() - mTestHelper.doSync { + val megolmBackupCreationInfo = cryptoTestHelper.createFakeMegolmBackupCreationInfo() + testHelper.doSync { (keysBackup as DefaultKeysBackupService).createFakeKeysBackupVersion(megolmBackupCreationInfo, it) } @@ -957,14 +955,14 @@ class KeysBackupTest : InstrumentedTest { // - Make alice back up all her keys again val latch2 = CountDownLatch(1) keysBackup.backupAllGroupSessions(null, TestMatrixCallback(latch2, false)) - mTestHelper.await(latch2) + testHelper.await(latch2) // -> That must fail and her backup state must be WrongBackUpVersion assertEquals(KeysBackupState.WrongBackUpVersion, keysBackup.state) assertFalse(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -982,17 +980,17 @@ class KeysBackupTest : InstrumentedTest { @Test fun testBackupAfterVerifyingADevice() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() val stateObserver = StateObserver(keysBackup) // - Make alice back up her keys to her homeserver - mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Wait for keys backup to finish by asking again to backup keys. - mTestHelper.doSync { + testHelper.doSync { keysBackup.backupAllGroupSessions(null, it) } @@ -1001,14 +999,14 @@ class KeysBackupTest : InstrumentedTest { val aliceUserId = cryptoTestData.firstSession.myUserId // - Log Alice on a new device - val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) + val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) // - Post a message to have a new megolm session aliceSession2.cryptoService().setWarnOnUnknownDevices(false) val room2 = aliceSession2.getRoom(cryptoTestData.roomId)!! - mTestHelper.sendTextMessage(room2, "New key", 1) + testHelper.sendTextMessage(room2, "New key", 1) // - Try to backup all in aliceSession2, it must fail val keysBackup2 = aliceSession2.cryptoService().keysBackupService() @@ -1025,7 +1023,7 @@ class KeysBackupTest : InstrumentedTest { super.onSuccess(data) } }) - mTestHelper.await(latch2) + testHelper.await(latch2) assertFalse(isSuccessful) @@ -1049,12 +1047,12 @@ class KeysBackupTest : InstrumentedTest { } } }) - mTestHelper.await(latch4) + testHelper.await(latch4) // -> It must use the same backup version assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion) - mTestHelper.doSync { + testHelper.doSync { aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it) } @@ -1063,8 +1061,8 @@ class KeysBackupTest : InstrumentedTest { stateObserver.stopAndCheckStates(null) stateObserver2.stopAndCheckStates(null) - mTestHelper.signOutAndClose(aliceSession2) - cryptoTestData.cleanUp(mTestHelper) + testHelper.signOutAndClose(aliceSession2) + cryptoTestData.cleanUp(testHelper) } /** @@ -1074,7 +1072,7 @@ class KeysBackupTest : InstrumentedTest { @Test fun deleteKeysBackupTest() { // - Create a backup version - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -1082,17 +1080,17 @@ class KeysBackupTest : InstrumentedTest { assertFalse(keysBackup.isEnabled) - val keyBackupCreationInfo = mKeysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) + val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) assertTrue(keysBackup.isEnabled) // Delete the backup - mTestHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) } + testHelper.doSync { keysBackup.deleteBackup(keyBackupCreationInfo.version, it) } // Backup is now disabled assertFalse(keysBackup.isEnabled) stateObserver.stopAndCheckStates(null) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt index a625ffc0e9..592b798bcc 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt @@ -32,8 +32,12 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import java.util.concurrent.CountDownLatch class KeysBackupTestHelper( - private val mTestHelper: CommonTestHelper, - private val mCryptoTestHelper: CryptoTestHelper) { + private val testHelper: CommonTestHelper, + private val cryptoTestHelper: CryptoTestHelper) { + + fun waitForKeybackUpBatching() { + Thread.sleep(400) + } /** * Common initial condition @@ -43,7 +47,9 @@ class KeysBackupTestHelper( * @param password optional password */ fun createKeysBackupScenarioWithPassword(password: String?): KeysBackupScenarioData { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages() + + waitForKeybackUpBatching() val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService() @@ -57,7 +63,7 @@ class KeysBackupTestHelper( var lastProgress = 0 var lastTotal = 0 - mTestHelper.doSync { + testHelper.doSync { keysBackup.backupAllGroupSessions(object : ProgressListener { override fun onProgress(progress: Int, total: Int) { lastProgress = progress @@ -72,7 +78,7 @@ class KeysBackupTestHelper( val aliceUserId = cryptoTestData.firstSession.myUserId // - Log Alice on a new device - val aliceSession2 = mTestHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) + val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync) // Test check: aliceSession2 has no keys at login Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false)) @@ -92,7 +98,7 @@ class KeysBackupTestHelper( password: String? = null): PrepareKeysBackupDataResult { val stateObserver = StateObserver(keysBackup) - val megolmBackupCreationInfo = mTestHelper.doSync { + val megolmBackupCreationInfo = testHelper.doSync { keysBackup.prepareKeysBackupVersion(password, null, it) } @@ -101,7 +107,7 @@ class KeysBackupTestHelper( Assert.assertFalse(keysBackup.isEnabled) // Create the version - val keysVersion = mTestHelper.doSync { + val keysVersion = testHelper.doSync { keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it) } @@ -136,7 +142,7 @@ class KeysBackupTestHelper( } }) - mTestHelper.await(latch) + testHelper.await(latch) } fun assertKeysEquals(keys1: MegolmSessionData?, keys2: MegolmSessionData?) { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt index b343d7334a..43f8dc0762 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt @@ -18,10 +18,6 @@ package org.matrix.android.sdk.internal.crypto.ssss import androidx.lifecycle.Observer import androidx.test.ext.junit.runners.AndroidJUnit4 -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -36,6 +32,7 @@ import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent import org.matrix.android.sdk.api.session.securestorage.KeySigner import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec import org.matrix.android.sdk.api.session.securestorage.SecretStorageKeyContent +import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageError import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo import org.matrix.android.sdk.api.util.Optional @@ -45,13 +42,12 @@ import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2 import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService -import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) class QuadSTests : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) + private val testHelper = CommonTestHelper(context()) private val emptyKeySigner = object : KeySigner { override fun sign(canonicalJson: String): Map>? { @@ -60,35 +56,29 @@ class QuadSTests : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun test_Generate4SKey() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val quadS = aliceSession.sharedSecretStorageService val TEST_KEY_ID = "my.test.Key" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner) } - // Assert Account data is updated - val accountDataLock = CountDownLatch(1) var accountData: UserAccountDataEvent? = null - - val liveAccountData = runBlocking(Dispatchers.Main) { - aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") - } - val accountDataObserver = Observer?> { t -> - if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") { - accountData = t.getOrNull() - accountDataLock.countDown() + // Assert Account data is updated + testHelper.waitWithLatch { + val liveAccountData = aliceSession.accountDataService().getLiveUserAccountDataEvent("${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") + val accountDataObserver = Observer?> { t -> + if (t?.getOrNull()?.type == "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$TEST_KEY_ID") { + accountData = t.getOrNull() + } + it.countDown() } + liveAccountData.observeForever(accountDataObserver) } - GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) } - - mTestHelper.await(accountDataLock) - assertNotNull("Key should be stored in account data", accountData) val parsed = SecretStorageKeyContent.fromJson(accountData!!.content) assertNotNull("Key Content cannot be parsed", parsed) @@ -96,36 +86,29 @@ class QuadSTests : InstrumentedTest { assertEquals("Unexpected key name", "Test Key", parsed.name) assertNull("Key was not generated from passphrase", parsed.passphrase) - // Set as default key - GlobalScope.launch { - quadS.setDefaultKey(TEST_KEY_ID) - } - var defaultKeyAccountData: UserAccountDataEvent? = null - val defaultDataLock = CountDownLatch(1) - - val liveDefAccountData = runBlocking(Dispatchers.Main) { - aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID) - } - val accountDefDataObserver = Observer?> { t -> - if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) { - defaultKeyAccountData = t.getOrNull()!! - defaultDataLock.countDown() + // Set as default key + testHelper.waitWithLatch { latch -> + quadS.setDefaultKey(TEST_KEY_ID) + val liveDefAccountData = + aliceSession.accountDataService().getLiveUserAccountDataEvent(DefaultSharedSecretStorageService.DEFAULT_KEY_ID) + val accountDefDataObserver = Observer?> { t -> + if (t?.getOrNull()?.type == DefaultSharedSecretStorageService.DEFAULT_KEY_ID) { + defaultKeyAccountData = t.getOrNull()!! + latch.countDown() + } } + liveDefAccountData.observeForever(accountDefDataObserver) } - GlobalScope.launch(Dispatchers.Main) { liveDefAccountData.observeForever(accountDefDataObserver) } - - mTestHelper.await(defaultDataLock) - assertNotNull(defaultKeyAccountData?.content) assertEquals("Unexpected default key ${defaultKeyAccountData?.content}", TEST_KEY_ID, defaultKeyAccountData?.content?.get("key")) - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_StoreSecret() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId = "My.Key" val info = generatedSecret(aliceSession, keyId, true) @@ -133,7 +116,7 @@ class QuadSTests : InstrumentedTest { // Store a secret val clearSecret = "42".toByteArray().toBase64NoPadding() - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.storeSecret( "secret.of.life", clearSecret, @@ -154,7 +137,7 @@ class QuadSTests : InstrumentedTest { // Try to decrypt?? - val decryptedSecret = mTestHelper.runBlockingTest { + val decryptedSecret = testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret( "secret.of.life", null, // default key @@ -163,32 +146,32 @@ class QuadSTests : InstrumentedTest { } assertEquals("Secret mismatch", clearSecret, decryptedSecret) - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_SetDefaultLocalEcho() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val quadS = aliceSession.sharedSecretStorageService val TEST_KEY_ID = "my.test.Key" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.generateKey(TEST_KEY_ID, null, "Test Key", emptyKeySigner) } // Test that we don't need to wait for an account data sync to access directly the keyid from DB - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.setDefaultKey(TEST_KEY_ID) } - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_StoreSecretWithMultipleKey() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId1 = "Key.1" val key1Info = generatedSecret(aliceSession, keyId1, true) val keyId2 = "Key2" @@ -196,7 +179,7 @@ class QuadSTests : InstrumentedTest { val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.storeSecret( "my.secret", mySecretText.toByteArray().toBase64NoPadding(), @@ -216,33 +199,33 @@ class QuadSTests : InstrumentedTest { assertNotNull(encryptedContent?.get(keyId2)) // Assert that can decrypt with both keys - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret("my.secret", keyId1, RawBytesKeySpec.fromRecoveryKey(key1Info.recoveryKey)!! ) } - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret("my.secret", keyId2, RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!! ) } - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } @Test fun test_GetSecretWithBadPassphrase() { - val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) + val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true)) val keyId1 = "Key.1" val passphrase = "The good pass phrase" val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true) val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit" - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.storeSecret( "my.secret", mySecretText.toByteArray().toBase64NoPadding(), @@ -250,19 +233,23 @@ class QuadSTests : InstrumentedTest { ) } - mTestHelper.runBlockingTest { - aliceSession.sharedSecretStorageService.getSecret("my.secret", - keyId1, - RawBytesKeySpec.fromPassphrase( - "A bad passphrase", - key1Info.content?.passphrase?.salt ?: "", - key1Info.content?.passphrase?.iterations ?: 0, - null) - ) + testHelper.runBlockingTest { + try { + aliceSession.sharedSecretStorageService.getSecret("my.secret", + keyId1, + RawBytesKeySpec.fromPassphrase( + "A bad passphrase", + key1Info.content?.passphrase?.salt ?: "", + key1Info.content?.passphrase?.iterations ?: 0, + null) + ) + } catch (throwable: Throwable) { + assert(throwable is SharedSecretStorageError.BadMac) + } } // Now try with correct key - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { aliceSession.sharedSecretStorageService.getSecret("my.secret", keyId1, RawBytesKeySpec.fromPassphrase( @@ -273,42 +260,36 @@ class QuadSTests : InstrumentedTest { ) } - mTestHelper.signOutAndClose(aliceSession) + testHelper.signOutAndClose(aliceSession) } - @Suppress("EXPERIMENTAL_API_USAGE") private fun assertAccountData(session: Session, type: String): UserAccountDataEvent { - val accountDataLock = CountDownLatch(1) var accountData: UserAccountDataEvent? = null - - val liveAccountData = runBlocking(Dispatchers.Main) { - session.accountDataService().getLiveUserAccountDataEvent(type) - } - val accountDataObserver = Observer?> { t -> - if (t?.getOrNull()?.type == type) { - accountData = t.getOrNull() - accountDataLock.countDown() + testHelper.waitWithLatch { + val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type) + val accountDataObserver = Observer?> { t -> + if (t?.getOrNull()?.type == type) { + accountData = t.getOrNull() + it.countDown() + } } + liveAccountData.observeForever(accountDataObserver) } - GlobalScope.launch(Dispatchers.Main) { liveAccountData.observeForever(accountDataObserver) } - mTestHelper.await(accountDataLock) - assertNotNull("Account Data type:$type should be found", accountData) - return accountData!! } private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { val quadS = session.sharedSecretStorageService - val creationInfo = mTestHelper.runBlockingTest { + val creationInfo = testHelper.runBlockingTest { quadS.generateKey(keyId, null, keyId, emptyKeySigner) } assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") if (asDefault) { - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.setDefaultKey(keyId) } assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID) @@ -320,7 +301,7 @@ class QuadSTests : InstrumentedTest { private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo { val quadS = session.sharedSecretStorageService - val creationInfo = mTestHelper.runBlockingTest { + val creationInfo = testHelper.runBlockingTest { quadS.generateKeyWithPassphrase( keyId, keyId, @@ -331,7 +312,7 @@ class QuadSTests : InstrumentedTest { assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId") if (asDefault) { - mTestHelper.runBlockingTest { + testHelper.runBlockingTest { quadS.setDefaultKey(keyId) } assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index e0d49b3f5e..c914da6f71 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -53,12 +53,12 @@ import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SASTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) @Test fun test_aliceStartThenAliceCancel() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -83,7 +83,7 @@ class SASTest : InstrumentedTest { val aliceKeyTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID!!) assertNotNull("Alice should have a started transaction", aliceKeyTx) - mTestHelper.await(bobTxCreatedLatch) + testHelper.await(bobTxCreatedLatch) bobVerificationService.removeListener(bobListener) val bobKeyTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID) @@ -116,7 +116,7 @@ class SASTest : InstrumentedTest { bobVerificationService.addListener(bobListener2) aliceSasTx.cancel(CancelCode.User) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) assertTrue("Should be cancelled on alice side", aliceSasTx.state is VerificationTxState.Cancelled) assertTrue("Should be cancelled on bob side", bobSasTx.state is VerificationTxState.Cancelled) @@ -133,13 +133,13 @@ class SASTest : InstrumentedTest { assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID)) assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID)) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_key_agreement_protocols_must_include_curve25519() { fail("Not passing for the moment") - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val bobSession = cryptoTestData.secondSession!! @@ -186,17 +186,17 @@ class SASTest : InstrumentedTest { fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, protocols = protocols) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_key_agreement_macs_Must_include_hmac_sha256() { fail("Not passing for the moment") - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val bobSession = cryptoTestData.secondSession!! @@ -223,18 +223,18 @@ class SASTest : InstrumentedTest { fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, mac = mac) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) val cancelReq = canceledToDeviceEvent!!.content.toModel()!! assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_key_agreement_short_code_include_decimal() { fail("Not passing for the moment") - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val bobSession = cryptoTestData.secondSession!! @@ -261,12 +261,12 @@ class SASTest : InstrumentedTest { fakeBobStart(bobSession, aliceUserID, aliceDevice, tid, codes = codes) - mTestHelper.await(cancelLatch) + testHelper.await(cancelLatch) val cancelReq = canceledToDeviceEvent!!.content.toModel()!! assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } private fun fakeBobStart(bobSession: Session, @@ -303,7 +303,7 @@ class SASTest : InstrumentedTest { // If a device has two verifications in progress with the same device, then it should cancel both verifications. @Test fun test_aliceStartTwoRequests() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -332,10 +332,10 @@ class SASTest : InstrumentedTest { aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceCreatedLatch) - mTestHelper.await(aliceCancelledLatch) + testHelper.await(aliceCreatedLatch) + testHelper.await(aliceCancelledLatch) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } /** @@ -343,7 +343,7 @@ class SASTest : InstrumentedTest { */ @Test fun test_aliceAndBobAgreement() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -383,7 +383,7 @@ class SASTest : InstrumentedTest { val bobUserId = bobSession.myUserId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceAcceptedLatch) + testHelper.await(aliceAcceptedLatch) assertTrue("Should have receive a commitment", accepted!!.commitment?.trim()?.isEmpty() == false) @@ -397,12 +397,12 @@ class SASTest : InstrumentedTest { assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it)) } - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_aliceAndBobSASCode() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -444,8 +444,8 @@ class SASTest : InstrumentedTest { val bobUserId = bobSession.myUserId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId val verificationSAS = aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceSASLatch) - mTestHelper.await(bobSASLatch) + testHelper.await(aliceSASLatch) + testHelper.await(bobSASLatch) val aliceTx = aliceVerificationService.getExistingTransaction(bobUserId, verificationSAS!!) as SASDefaultVerificationTransaction val bobTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, verificationSAS) as SASDefaultVerificationTransaction @@ -453,12 +453,12 @@ class SASTest : InstrumentedTest { assertEquals("Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL), bobTx.getShortCodeRepresentation(SasMode.DECIMAL)) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_happyPath() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -520,8 +520,8 @@ class SASTest : InstrumentedTest { val bobUserId = bobSession.myUserId val bobDeviceId = bobSession.cryptoService().getMyDevice().deviceId aliceVerificationService.beginKeyVerification(VerificationMethod.SAS, bobUserId, bobDeviceId, null) - mTestHelper.await(aliceSASLatch) - mTestHelper.await(bobSASLatch) + testHelper.await(aliceSASLatch) + testHelper.await(bobSASLatch) // Assert that devices are verified val bobDeviceInfoFromAlicePOV: CryptoDeviceInfo? = aliceSession.cryptoService().getDeviceInfo(bobUserId, bobDeviceId) @@ -532,12 +532,12 @@ class SASTest : InstrumentedTest { assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified) assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified) - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } @Test fun test_ConcurrentStart() { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession @@ -553,8 +553,8 @@ class SASTest : InstrumentedTest { var requestID: String? = null - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() requestID = prAlicePOV?.transactionId Log.v("TEST", "== alicePOV is $prAlicePOV") @@ -564,8 +564,8 @@ class SASTest : InstrumentedTest { Log.v("TEST", "== requestID is $requestID") - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() Log.v("TEST", "== prBobPOV is $prBobPOV") prBobPOV?.transactionId == requestID @@ -579,8 +579,8 @@ class SASTest : InstrumentedTest { ) // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() Log.v("TEST", "== prAlicePOV is $prAlicePOV") prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null @@ -606,22 +606,22 @@ class SASTest : InstrumentedTest { var alicePovTx: SasVerificationTransaction? var bobPovTx: SasVerificationTransaction? - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { alicePovTx = aliceVerificationService.getExistingTransaction(bobSession.myUserId, requestID!!) as? SasVerificationTransaction Log.v("TEST", "== alicePovTx is $alicePovTx") alicePovTx?.state == VerificationTxState.ShortCodeReady } } // wait for alice to get the ready - mTestHelper.waitWithLatch { - mTestHelper.retryPeriodicallyWithLatch(it) { + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { bobPovTx = bobVerificationService.getExistingTransaction(aliceSession.myUserId, requestID!!) as? SasVerificationTransaction Log.v("TEST", "== bobPovTx is $bobPovTx") bobPovTx?.state == VerificationTxState.ShortCodeReady } } - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt index 397f7f9441..36306aa383 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt @@ -40,8 +40,8 @@ import kotlin.coroutines.resume @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) class VerificationTest : InstrumentedTest { - private val mTestHelper = CommonTestHelper(context()) - private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) data class ExpectedResult( val sasIsSupported: Boolean = false, @@ -155,12 +155,12 @@ class VerificationTest : InstrumentedTest { bobSupportedMethods: List, expectedResultForAlice: ExpectedResult, expectedResultForBob: ExpectedResult) { - val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom() + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom() val aliceSession = cryptoTestData.firstSession val bobSession = cryptoTestData.secondSession!! - mTestHelper.doSync { callback -> + testHelper.doSync { callback -> aliceSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -176,7 +176,7 @@ class VerificationTest : InstrumentedTest { }, callback) } - mTestHelper.doSync { callback -> + testHelper.doSync { callback -> bobSession.cryptoService().crossSigningService() .initializeCrossSigning( object : UserInteractiveAuthInterceptor { @@ -234,7 +234,7 @@ class VerificationTest : InstrumentedTest { val bobUserId = bobSession.myUserId // Step 1: Alice starts a verification request aliceVerificationService.requestKeyVerificationInDMs(aliceSupportedMethods, bobUserId, cryptoTestData.roomId) - mTestHelper.await(latch) + testHelper.await(latch) aliceReadyPendingVerificationRequest!!.let { pr -> pr.isSasSupported() shouldBe expectedResultForAlice.sasIsSupported @@ -248,6 +248,6 @@ class VerificationTest : InstrumentedTest { pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode } - cryptoTestData.cleanUp(mTestHelper) + cryptoTestData.cleanUp(testHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt index 1adf31be5f..8a4429db45 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt @@ -45,7 +45,7 @@ object RoomDataHelper { content: Content? = null, prevContent: Content? = null, sender: String = FAKE_TEST_SENDER, - stateKey: String = FAKE_TEST_SENDER + stateKey: String? = null ): Event { return Event( type = type, @@ -64,6 +64,6 @@ object RoomDataHelper { private fun createFakeRoomMemberEvent(): Event { val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() - return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember) + return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember, stateKey = FAKE_TEST_SENDER) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt index dfa6ec10ae..bc9722c922 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt @@ -65,14 +65,8 @@ class TimelineForwardPaginationTest : InstrumentedTest { message, numberOfMessagesToSend) - // Alice clear the cache - commonTestHelper.runBlockingTest { - aliceSession.clearCache() - } - - // And restarts the sync - aliceSession.startSync(true) - + // Alice clear the cache and restart the sync + commonTestHelper.clearCacheAndSync(aliceSession) val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(30)) aliceTimeline.start() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt index 1baf490dfc..45e4b53c77 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -24,14 +24,10 @@ import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.message.MessageContent -import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestData import org.matrix.android.sdk.common.CryptoTestHelper -import java.util.concurrent.CountDownLatch @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) @@ -84,24 +80,12 @@ class SearchMessagesTest : InstrumentedTest { val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! - val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10)) - aliceTimeline.start() - - val lock = CountDownLatch(1) - - val eventListener = commonTestHelper.createEventListener(lock) { snapshot -> - snapshot.count { it.root.content.toModel()?.body?.startsWith(MESSAGE).orFalse() } == 2 - } - - aliceTimeline.addListener(eventListener) commonTestHelper.sendTextMessage( roomFromAlicePOV, MESSAGE, 2) - commonTestHelper.await(lock) - val data = commonTestHelper.runBlockingTest { block.invoke(cryptoTestData) } @@ -114,7 +98,6 @@ class SearchMessagesTest : InstrumentedTest { }.orFalse() ) - aliceTimeline.removeAllListeners() cryptoTestData.cleanUp(commonTestHelper) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt index 5911414c25..d7be19295c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt @@ -16,9 +16,7 @@ package org.matrix.android.sdk.session.space -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -50,18 +48,15 @@ class SpaceCreationTest : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun createSimplePublicSpace() { val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true)) val roomName = "My Space" val topic = "A public space for test" var spaceId: String = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(roomName, topic, null, true) - // wait a bit to let the summary update it self :/ - it.countDown() - } + spaceId = session.spaceService().createSpace(roomName, topic, null, true) + // wait a bit to let the summary update it self :/ + it.countDown() } val syncedSpace = session.spaceService().getSpace(spaceId) @@ -134,7 +129,6 @@ class SpaceCreationTest : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testSimplePublicSpaceWithChildren() { val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true)) val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true)) @@ -148,50 +142,40 @@ class SpaceCreationTest : InstrumentedTest { // create a room var firstChild: String? = null commonTestHelper.waitWithLatch { - GlobalScope.launch { - firstChild = aliceSession.createRoom(CreateRoomParams().apply { - this.name = "FirstRoom" - this.topic = "Description of first room" - this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT - }) - it.countDown() - } + firstChild = aliceSession.createRoom(CreateRoomParams().apply { + this.name = "FirstRoom" + this.topic = "Description of first room" + this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT + }) + it.countDown() } commonTestHelper.waitWithLatch { - GlobalScope.launch { - syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true) - it.countDown() - } + syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true) + it.countDown() } var secondChild: String? = null commonTestHelper.waitWithLatch { - GlobalScope.launch { - secondChild = aliceSession.createRoom(CreateRoomParams().apply { - this.name = "SecondRoom" - this.topic = "Description of second room" - this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT - }) - it.countDown() - } + secondChild = aliceSession.createRoom(CreateRoomParams().apply { + this.name = "SecondRoom" + this.topic = "Description of second room" + this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT + }) + it.countDown() } commonTestHelper.waitWithLatch { - GlobalScope.launch { - syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true) - it.countDown() - } + syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true) + it.countDown() } // Try to join from bob, it's a public space no need to invite var joinResult: JoinSpaceResult? = null commonTestHelper.waitWithLatch { - GlobalScope.launch { - joinResult = bobSession.spaceService().joinSpace(spaceId) - // wait a bit to let the summary update it self :/ - it.countDown() - } + joinResult = bobSession.spaceService().joinSpace(spaceId) + // wait a bit to let the summary update it self :/ + it.countDown() } assertEquals(JoinSpaceResult.Success, joinResult) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 436daf001b..1c38edbbd9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -18,9 +18,6 @@ package org.matrix.android.sdk.session.space import android.util.Log import androidx.lifecycle.Observer -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -56,43 +53,34 @@ class SpaceHierarchyTest : InstrumentedTest { private val commonTestHelper = CommonTestHelper(context()) @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun createCanonicalChildRelation() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) val spaceName = "My Space" val topic = "A public space for test" - var spaceId: String = "" + var spaceId = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(spaceName, topic, null, true) - it.countDown() - } + spaceId = session.spaceService().createSpace(spaceName, topic, null, true) + it.countDown() } val syncedSpace = session.spaceService().getSpace(spaceId) - var roomId: String = "" + var roomId = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - roomId = session.createRoom(CreateRoomParams().apply { name = "General" }) - it.countDown() - } + roomId = session.createRoom(CreateRoomParams().apply { name = "General" }) + it.countDown() } val viaServers = listOf(session.sessionParams.homeServerHost ?: "") commonTestHelper.waitWithLatch { - GlobalScope.launch { - syncedSpace!!.addChildren(roomId, viaServers, null, true) - it.countDown() - } + syncedSpace!!.addChildren(roomId, viaServers, null, true) + it.countDown() } commonTestHelper.waitWithLatch { - GlobalScope.launch { - session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers) - it.countDown() - } + session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers) + it.countDown() } Thread.sleep(9000) @@ -181,7 +169,6 @@ class SpaceHierarchyTest : InstrumentedTest { // } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testFilteringBySpace() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -205,29 +192,23 @@ class SpaceHierarchyTest : InstrumentedTest { val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId) val viaServers = listOf(session.sessionParams.homeServerHost ?: "") commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) - session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) - it.countDown() - } + spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) + session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) + it.countDown() } // Create orphan rooms var orphan1 = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" }) - it.countDown() - } + orphan1 = session.createRoom(CreateRoomParams().apply { name = "O1" }) + it.countDown() } var orphan2 = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" }) - it.countDown() - } + orphan2 = session.createRoom(CreateRoomParams().apply { name = "O2" }) + it.countDown() } val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) }) @@ -250,11 +231,9 @@ class SpaceHierarchyTest : InstrumentedTest { // Add a non canonical child and check that it does not appear as orphan commonTestHelper.waitWithLatch { - GlobalScope.launch { - val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" }) - spaceA!!.addChildren(a3, viaServers, null, false) - it.countDown() - } + val a3 = session.createRoom(CreateRoomParams().apply { name = "A3" }) + spaceA!!.addChildren(a3, viaServers, null, false) + it.countDown() } Thread.sleep(2_000) @@ -265,7 +244,6 @@ class SpaceHierarchyTest : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testBreakCycle() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -283,20 +261,16 @@ class SpaceHierarchyTest : InstrumentedTest { val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId) val viaServers = listOf(session.sessionParams.homeServerHost ?: "") commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) - session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) - it.countDown() - } + spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) + session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers) + it.countDown() } // add back A as subspace of C commonTestHelper.waitWithLatch { - GlobalScope.launch { - val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId) - spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true) - it.countDown() - } + val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId) + spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true) + it.countDown() } Thread.sleep(1000) @@ -313,7 +287,6 @@ class SpaceHierarchyTest : InstrumentedTest { } @Test - @Suppress("EXPERIMENTAL_API_USAGE") fun testLiveFlatChildren() { val session = commonTestHelper.createAccount("John", SessionTestParams(true)) @@ -336,12 +309,14 @@ class SpaceHierarchyTest : InstrumentedTest { session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers) } - val flatAChildren = runBlocking(Dispatchers.Main) { - session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) - } + val spaceCInfo = createPublicSpace(session, "SpaceC", listOf( + Triple("C1", true /*auto-join*/, true/*canonical*/), + Triple("C2", true, true) + )) commonTestHelper.waitWithLatch { latch -> + val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) val childObserver = object : Observer> { override fun onChanged(children: List?) { // Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}") @@ -354,20 +329,13 @@ class SpaceHierarchyTest : InstrumentedTest { } } - val spaceCInfo = createPublicSpace(session, "SpaceC", listOf( - Triple("C1", true /*auto-join*/, true/*canonical*/), - Triple("C2", true, true) - )) - // add C as subspace of B - runBlocking { - val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId) - spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) - } + val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId) + spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true) // C1 and C2 should be in flatten child of A now - GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) } + flatAChildren.observeForever(childObserver) } // Test part one of the rooms @@ -376,7 +344,7 @@ class SpaceHierarchyTest : InstrumentedTest { val bRoom = session.getRoom(bRoomId) commonTestHelper.waitWithLatch { latch -> - + val flatAChildren = session.getFlattenRoomSummaryChildrenOfLive(spaceAInfo.spaceId) val childObserver = object : Observer> { override fun onChanged(children: List?) { System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}") @@ -389,13 +357,10 @@ class SpaceHierarchyTest : InstrumentedTest { } // part from b room - runBlocking { - bRoom!!.leave(null) - } + bRoom!!.leave(null) // The room should have disapear from flat children - GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) } + flatAChildren.observeForever(childObserver) } - commonTestHelper.signOutAndClose(session) } @@ -404,94 +369,66 @@ class SpaceHierarchyTest : InstrumentedTest { val roomIds: List ) - @Suppress("EXPERIMENTAL_API_USAGE") private fun createPublicSpace(session: Session, spaceName: String, childInfo: List> /** Name, auto-join, canonical*/ ): TestSpaceCreationResult { var spaceId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true) - it.countDown() + var roomIds: List = emptyList() + commonTestHelper.waitWithLatch { latch -> + spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true) + val syncedSpace = session.spaceService().getSpace(spaceId) + val viaServers = listOf(session.sessionParams.homeServerHost ?: "") + + roomIds = childInfo.map { entry -> + session.createRoom(CreateRoomParams().apply { name = entry.first }) } - } - - val syncedSpace = session.spaceService().getSpace(spaceId) - val viaServers = listOf(session.sessionParams.homeServerHost ?: "") - - val roomIds = - childInfo.map { entry -> - var roomId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - roomId = session.createRoom(CreateRoomParams().apply { name = entry.first }) - it.countDown() - } - } - roomId - } - - roomIds.forEachIndexed { index, roomId -> - runBlocking { + roomIds.forEachIndexed { index, roomId -> syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) val canonical = childInfo[index].third if (canonical != null) { session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) } } + latch.countDown() } return TestSpaceCreationResult(spaceId, roomIds) } - @Suppress("EXPERIMENTAL_API_USAGE") private fun createPrivateSpace(session: Session, spaceName: String, childInfo: List> /** Name, auto-join, canonical*/ ): TestSpaceCreationResult { var spaceId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false) - it.countDown() - } - } - - val syncedSpace = session.spaceService().getSpace(spaceId) - val viaServers = listOf(session.sessionParams.homeServerHost ?: "") - - val roomIds = - childInfo.map { entry -> - var roomId = "" - commonTestHelper.waitWithLatch { - GlobalScope.launch { - val homeServerCapabilities = session - .getHomeServerCapabilities() - roomId = session.createRoom(CreateRoomParams().apply { - name = entry.first - this.featurePreset = RestrictedRoomPreset( - homeServerCapabilities, - listOf( - RoomJoinRulesAllowEntry.restrictedToRoom(spaceId) - ) - ) - }) - it.countDown() - } + var roomIds: List = emptyList() + commonTestHelper.waitWithLatch { latch -> + spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false) + val syncedSpace = session.spaceService().getSpace(spaceId) + val viaServers = listOf(session.sessionParams.homeServerHost ?: "") + roomIds = + childInfo.map { entry -> + val homeServerCapabilities = session + .getHomeServerCapabilities() + session.createRoom(CreateRoomParams().apply { + name = entry.first + this.featurePreset = RestrictedRoomPreset( + homeServerCapabilities, + listOf( + RoomJoinRulesAllowEntry.restrictedToRoom(spaceId) + ) + ) + }) } - roomId - } - - roomIds.forEachIndexed { index, roomId -> - runBlocking { + roomIds.forEachIndexed { index, roomId -> syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second) val canonical = childInfo[index].third if (canonical != null) { session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers) } } + latch.countDown() } return TestSpaceCreationResult(spaceId, roomIds) } @@ -559,11 +496,9 @@ class SpaceHierarchyTest : InstrumentedTest { var bobRoomId = "" commonTestHelper.waitWithLatch { - GlobalScope.launch { - bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) - bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) - it.countDown() - } + bobRoomId = bobSession.createRoom(CreateRoomParams().apply { name = "A Bob Room" }) + bobSession.getRoom(bobRoomId)!!.invite(aliceSession.myUserId) + it.countDown() } commonTestHelper.runBlockingTest { @@ -577,10 +512,8 @@ class SpaceHierarchyTest : InstrumentedTest { } commonTestHelper.waitWithLatch { - GlobalScope.launch { - bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) - it.countDown() - } + bobSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) + it.countDown() } commonTestHelper.waitWithLatch { latch -> @@ -600,19 +533,17 @@ class SpaceHierarchyTest : InstrumentedTest { // Let's now try to make alice admin of the room commonTestHelper.waitWithLatch { - GlobalScope.launch { - val room = bobSession.getRoom(bobRoomId)!! - val currentPLContent = room - .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) - ?.let { it.content.toModel() } + val room = bobSession.getRoom(bobRoomId)!! + val currentPLContent = room + .getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + ?.let { it.content.toModel() } - val newPowerLevelsContent = currentPLContent - ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) - ?.toContent() + val newPowerLevelsContent = currentPLContent + ?.setUserPowerLevel(aliceSession.myUserId, Role.Admin.value) + ?.toContent() - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) - it.countDown() - } + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent!!) + it.countDown() } commonTestHelper.waitWithLatch { latch -> @@ -627,10 +558,8 @@ class SpaceHierarchyTest : InstrumentedTest { } commonTestHelper.waitWithLatch { - GlobalScope.launch { - aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) - it.countDown() - } + aliceSession.spaceService().setSpaceParent(bobRoomId, spaceAInfo.spaceId, false, listOf(bobSession.sessionParams.homeServerHost ?: "")) + it.countDown() } commonTestHelper.waitWithLatch { latch -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt index 8a4526a5e1..901ba75d16 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt @@ -20,6 +20,7 @@ import android.content.Context import androidx.lifecycle.ProcessLifecycleOwner import androidx.work.Configuration import androidx.work.WorkManager +import androidx.work.WorkerFactory import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.api.auth.AuthenticationService @@ -33,6 +34,7 @@ import org.matrix.android.sdk.internal.di.DaggerMatrixComponent import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.UserAgentHolder import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import org.matrix.olm.OlmManager import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicBoolean @@ -53,12 +55,17 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo @Inject internal lateinit var sessionManager: SessionManager @Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService @Inject internal lateinit var apiInterceptor: ApiInterceptor + @Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory init { Monarchy.init(context) DaggerMatrixComponent.factory().create(context, matrixConfiguration).inject(this) if (context.applicationContext !is Configuration.Provider) { - WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) + val configuration = Configuration.Builder() + .setExecutor(Executors.newCachedThreadPool()) + .setWorkerFactory(matrixWorkerFactory) + .build() + WorkManager.initialize(context, configuration) } ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) } @@ -77,6 +84,8 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo return legacySessionImporter } + fun workerFactory(): WorkerFactory = matrixWorkerFactory + fun registerApiInterceptorListener(path: ApiPath, listener: ApiInterceptorListener) { apiInterceptor.addListener(path, listener) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt index c11d00278b..3a5f8e7668 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried 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.internal.SessionManager import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.ShareRequestCancellation import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -34,9 +35,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject -internal class CancelGossipRequestWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class CancelGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt index b2ba189b65..3129ccae3b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried 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.internal.SessionManager import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest @@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import timber.log.Timber import javax.inject.Inject -internal class SendGossipRequestWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendGossipRequestWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt index b96943e4ae..ff206a3c96 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.failure.shouldBeRetried 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.internal.SessionManager import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap @@ -37,9 +38,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import timber.log.Timber import javax.inject.Inject -internal class SendGossipWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendGossipWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt index 3326d3707a..5cd647ff6f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt @@ -25,6 +25,7 @@ import io.realm.kotlin.where import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity @@ -50,9 +51,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import timber.log.Timber import javax.inject.Inject -internal class UpdateTrustWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class UpdateTrustWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt index 481ce85f70..a763c05e07 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt @@ -20,6 +20,7 @@ import androidx.work.Data import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.shouldBeRetried +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker @@ -33,9 +34,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class SendVerificationMessageWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendVerificationMessageWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt index 81a067f2c0..d9a4f1bde1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt @@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.session.TestInterceptor import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver import org.matrix.android.sdk.internal.util.system.SystemModule +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import org.matrix.olm.OlmManager import java.io.File @@ -86,6 +87,8 @@ internal interface MatrixComponent { fun sessionManager(): SessionManager + fun matrixWorkerFactory(): MatrixWorkerFactory + fun inject(matrix: Matrix) @Component.Factory diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt index 210eadeff7..b136041f79 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NoOpTestModule.kt @@ -20,6 +20,8 @@ import dagger.Module import dagger.Provides import org.matrix.android.sdk.internal.session.MockHttpInterceptor import org.matrix.android.sdk.internal.session.TestInterceptor +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.util.DefaultBackgroundDetectionObserver @Module internal object NoOpTestModule { @@ -28,4 +30,11 @@ internal object NoOpTestModule { @JvmStatic @MockHttpInterceptor fun providesTestInterceptor(): TestInterceptor? = null + + @Provides + @JvmStatic + @MatrixScope + fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver { + return DefaultBackgroundDetectionObserver() + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt index bafffdf852..7d004bc5c0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/WorkManagerProvider.kt @@ -17,24 +17,38 @@ package org.matrix.android.sdk.internal.di import android.content.Context +import androidx.lifecycle.Observer import androidx.work.Constraints import androidx.work.ListenableWorker import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo import androidx.work.WorkManager import androidx.work.WorkRequest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers +import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory import java.util.concurrent.TimeUnit import javax.inject.Inject +@SessionScope internal class WorkManagerProvider @Inject constructor( context: Context, - @SessionId private val sessionId: String + @SessionId private val sessionId: String, + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val sessionScope: CoroutineScope ) { private val tag = MATRIX_SDK_TAG_PREFIX + sessionId val workManager = WorkManager.getInstance(context) + init { + checkIfWorkerFactoryIsSetup() + } + /** * Create a OneTimeWorkRequestBuilder, with the Matrix SDK tag */ @@ -60,6 +74,27 @@ internal class WorkManagerProvider @Inject constructor( } } + private fun checkIfWorkerFactoryIsSetup() { + sessionScope.launch(coroutineDispatchers.main) { + val checkWorkerRequest = OneTimeWorkRequestBuilder().build() + workManager.enqueue(checkWorkerRequest) + val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id) + val observer = object : Observer { + override fun onChanged(workInfo: WorkInfo) { + if (workInfo.state.isFinished) { + checkWorkerLiveState.removeObserver(this) + if (workInfo.state == WorkInfo.State.FAILED) { + throw RuntimeException("MatrixWorkerFactory is not being set on your worker configuration.\n" + + "Makes sure to add it to a DelegatingWorkerFactory if you have your own factory or use it directly.\n" + + "You can grab the instance through the Matrix class.") + } + } + } + } + checkWorkerLiveState.observeForever(observer) + } + } + companion object { private const val MATRIX_SDK_TAG_PREFIX = "MatrixSDK-" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt index 9fc84e6fe5..a89713870a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/model/MxCallImpl.kt @@ -203,8 +203,11 @@ internal class MxCallImpl( override fun selectAnswer() { Timber.tag(loggerTag.value).v("select answer $callId") - if (isOutgoing) return - state = CallState.Answering + if (!isOutgoing) return + // This is an outgoing call, select the remote client that answered. + if (state != CallState.Dialing && state !is CallState.Connected) { + Timber.tag(loggerTag.value).w("Expected state is CallState.Dialing or CallState.Connected got $state.") + } CallSelectAnswerContent( callId = callId, partyId = ourPartyId, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index 7f35c91010..52dee0ee55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.util.MimeTypes +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import org.matrix.android.sdk.internal.database.mapper.ContentMapper @@ -63,8 +64,8 @@ private data class NewAttachmentAttributes( * Possible previous worker: None * Possible next worker : Always [MultipleEventSendingDispatcherWorker] */ -internal class UploadContentWorker(val context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class UploadContentWorker(val context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt index 338f43bdbb..716859f195 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.group import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams @@ -28,8 +29,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class GetGroupDataWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt index 4df42b2cfb..ce29efaaac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt @@ -19,13 +19,14 @@ import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject -internal class AddPusherWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class AddPusherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt index 16a9eba363..f44c255f1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt @@ -21,6 +21,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.content.UploadContentWorker @@ -38,8 +39,8 @@ import javax.inject.Inject * Possible previous worker: Always [UploadContentWorker] * Possible next worker : None, but it will post new work to send events, encrypted or not */ -internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt index b4436bfcbf..c03d1fa81e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt @@ -19,6 +19,7 @@ import android.content.Context import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.SessionComponent @@ -32,8 +33,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class RedactEventWorker(context: Context, params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class RedactEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index 8b7fe4b907..7f24688ece 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -23,6 +23,7 @@ import io.realm.RealmConfiguration import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.crypto.tasks.SendEventTask import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.SessionComponent @@ -38,9 +39,8 @@ import javax.inject.Inject * Possible previous worker: [EncryptEventWorker] or first worker * Possible next worker : None */ -internal class SendEventWorker(context: Context, - params: WorkerParameters) : - SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class SendEventWorker(context: Context, params: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, params, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt index 35e561a106..869a4d425a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt @@ -25,9 +25,12 @@ import javax.inject.Inject internal class SyncTokenStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) { fun getLastToken(): String? { - return Realm.getInstance(monarchy.realmConfiguration).use { + val token = Realm.getInstance(monarchy.realmConfiguration).use { + // Makes sure realm is up-to-date as it's used for querying internally on non looper thread. + it.refresh() it.where(SyncEntity::class.java).findFirst()?.nextBatch } + return token } fun saveToken(realm: Realm, token: String?) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt index 41bb1a44a6..763cd55714 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt @@ -21,8 +21,8 @@ import androidx.work.ExistingWorkPolicy import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.isTokenError +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.di.WorkManagerProvider -import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.sync.SyncPresence import org.matrix.android.sdk.internal.session.sync.SyncTask @@ -41,9 +41,8 @@ private const val DEFAULT_DELAY_TIMEOUT = 30_000L * Possible previous worker: None * Possible next worker : None */ -internal class SyncWorker(context: Context, - workerParameters: WorkerParameters -) : SessionSafeCoroutineWorker(context, workerParameters, Params::class.java) { +internal class SyncWorker(context: Context, workerParameters: WorkerParameters, sessionManager: SessionManager) : + SessionSafeCoroutineWorker(context, workerParameters, sessionManager, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( @@ -56,7 +55,6 @@ internal class SyncWorker(context: Context, @Inject lateinit var syncTask: SyncTask @Inject lateinit var taskExecutor: TaskExecutor - @Inject lateinit var networkConnectivityChecker: NetworkConnectivityChecker @Inject lateinit var workManagerProvider: WorkManagerProvider override fun injectWith(injector: SessionComponent) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt index 3e977b31fb..9c8b36a3ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt @@ -18,26 +18,32 @@ package org.matrix.android.sdk.internal.util import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import org.matrix.android.sdk.internal.di.MatrixScope import timber.log.Timber -import javax.inject.Inject -/** - * To be attached to ProcessLifecycleOwner lifecycle - */ -@MatrixScope -internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecycleObserver { +interface BackgroundDetectionObserver : DefaultLifecycleObserver { + val isInBackground: Boolean - var isInBackground: Boolean = true + fun register(listener: Listener) + fun unregister(listener: Listener) + + interface Listener { + fun onMoveToForeground() + fun onMoveToBackground() + } +} + +internal class DefaultBackgroundDetectionObserver : BackgroundDetectionObserver { + + override var isInBackground: Boolean = true private set - private val listeners = LinkedHashSet() + private val listeners = LinkedHashSet() - fun register(listener: Listener) { + override fun register(listener: BackgroundDetectionObserver.Listener) { listeners.add(listener) } - fun unregister(listener: Listener) { + override fun unregister(listener: BackgroundDetectionObserver.Listener) { listeners.remove(listener) } @@ -52,9 +58,4 @@ internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecy isInBackground = true listeners.forEach { it.onMoveToBackground() } } - - interface Listener { - fun onMoveToForeground() - fun onMoveToBackground() - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt index b58cab99b5..0b451e9c34 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/MatrixWorkerFactory.kt @@ -17,16 +17,33 @@ package org.matrix.android.sdk.internal.worker import android.content.Context +import androidx.work.CoroutineWorker import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters +import org.matrix.android.sdk.internal.SessionManager +import org.matrix.android.sdk.internal.crypto.CancelGossipRequestWorker +import org.matrix.android.sdk.internal.crypto.SendGossipRequestWorker +import org.matrix.android.sdk.internal.crypto.SendGossipWorker +import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker +import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker +import org.matrix.android.sdk.internal.di.MatrixScope +import org.matrix.android.sdk.internal.session.content.UploadContentWorker +import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker +import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker +import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker +import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker +import org.matrix.android.sdk.internal.session.room.send.SendEventWorker +import org.matrix.android.sdk.internal.session.sync.job.SyncWorker import timber.log.Timber import javax.inject.Inject -import javax.inject.Provider -class MatrixWorkerFactory @Inject constructor( - private val workerFactories: Map, @JvmSuppressWildcards Provider> -) : WorkerFactory() { +/** + * This factory is responsible of creating Workers by giving the session manager. + * This is not the cleanest way but getting SessionComponent is dependant of args type. + */ +@MatrixScope +internal class MatrixWorkerFactory @Inject constructor(private val sessionManager: SessionManager) : WorkerFactory() { override fun createWorker( appContext: Context, @@ -34,11 +51,61 @@ class MatrixWorkerFactory @Inject constructor( workerParameters: WorkerParameters ): ListenableWorker? { Timber.d("MatrixWorkerFactory.createWorker for $workerClassName") + return when (workerClassName) { + CheckFactoryWorker::class.java.name -> + CheckFactoryWorker(appContext, workerParameters, true) + AddPusherWorker::class.java.name -> + AddPusherWorker(appContext, workerParameters, sessionManager) + CancelGossipRequestWorker::class.java.name -> + CancelGossipRequestWorker(appContext, workerParameters, sessionManager) + GetGroupDataWorker::class.java.name -> + GetGroupDataWorker(appContext, workerParameters, sessionManager) + MultipleEventSendingDispatcherWorker::class.java.name -> + MultipleEventSendingDispatcherWorker(appContext, workerParameters, sessionManager) + RedactEventWorker::class.java.name -> + RedactEventWorker(appContext, workerParameters, sessionManager) + SendEventWorker::class.java.name -> + SendEventWorker(appContext, workerParameters, sessionManager) + SendGossipRequestWorker::class.java.name -> + SendGossipRequestWorker(appContext, workerParameters, sessionManager) + SendGossipWorker::class.java.name -> + SendGossipWorker(appContext, workerParameters, sessionManager) + SendVerificationMessageWorker::class.java.name -> + SendVerificationMessageWorker(appContext, workerParameters, sessionManager) + SyncWorker::class.java.name -> + SyncWorker(appContext, workerParameters, sessionManager) + UpdateTrustWorker::class.java.name -> + UpdateTrustWorker(appContext, workerParameters, sessionManager) + UploadContentWorker::class.java.name -> + UploadContentWorker(appContext, workerParameters, sessionManager) + else -> { + Timber.w("No worker defined on MatrixWorkerFactory for $workerClassName will delegate to default.") + // Return null to delegate to the default WorkerFactory. + null + } + } + } - val foundEntry = - workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) } - val factoryProvider = foundEntry?.value - ?: throw IllegalArgumentException("unknown worker class name: $workerClassName") - return factoryProvider.get().create(appContext, workerParameters) + /** + * This worker is launched by the factory with the isCreatedByMatrixWorkerFactory flag to true. + * If the MatrixWorkerFactory is not set up, it will default to the other constructor and it will throw + */ + class CheckFactoryWorker(context: Context, + workerParameters: WorkerParameters, + private val isCreatedByMatrixWorkerFactory: Boolean) : + CoroutineWorker(context, workerParameters) { + + // Called by WorkManager if there is no MatrixWorkerFactory + constructor(context: Context, workerParameters: WorkerParameters) : this(context, + workerParameters, + isCreatedByMatrixWorkerFactory = false) + + override suspend fun doWork(): Result { + return if (!isCreatedByMatrixWorkerFactory) { + Result.failure() + } else { + Result.success() + } + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt index d4179e2272..334c4580e9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/SessionSafeCoroutineWorker.kt @@ -22,6 +22,7 @@ import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.session.SessionComponent import timber.log.Timber @@ -33,6 +34,7 @@ import timber.log.Timber internal abstract class SessionSafeCoroutineWorker( context: Context, workerParameters: WorkerParameters, + private val sessionManager: SessionManager, private val paramClass: Class ) : CoroutineWorker(context, workerParameters) { @@ -48,7 +50,7 @@ internal abstract class SessionSafeCoroutineWorker( .also { Timber.e("Unable to parse work parameters") } return try { - val sessionComponent = getSessionComponent(params.sessionId) + val sessionComponent = sessionManager.getSessionComponent(params.sessionId) ?: return buildErrorResult(params, "No session") // Make sure to inject before handling error as you may need some dependencies to process them. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/Worker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/Worker.kt deleted file mode 100644 index 24035355f7..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/Worker.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 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.worker - -import androidx.work.ListenableWorker -import org.matrix.android.sdk.api.Matrix -import org.matrix.android.sdk.internal.session.SessionComponent - -internal fun ListenableWorker.getSessionComponent(sessionId: String): SessionComponent? { - return Matrix.getInstance(applicationContext).sessionManager.getSessionComponent(sessionId) -} diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index b135954f63..bbd6105b15 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -160,7 +160,7 @@ Formatter\.formatShortFileSize===1 # android\.text\.TextUtils ### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt -enum class===108 +enum class===110 ### Do not import temporary legacy classes import org.matrix.android.sdk.internal.legacy.riot===3 diff --git a/tools/check/forbidden_strings_in_layout.txt b/tools/check/forbidden_strings_in_layout.txt index 09f0d01f69..545983f844 100644 --- a/tools/check/forbidden_strings_in_layout.txt +++ b/tools/check/forbidden_strings_in_layout.txt @@ -25,3 +25,8 @@ ### Use style="@style/Widget.Vector.TextView.*" instead of textSize attribute android:textSize===9 + +### Use `@id` and not `@+id` when referencing ids in layouts +layout_(.*)="@\+id +accessibilityTraversal(.*)="@\+id +toolbarId="@\+id diff --git a/tools/compressVideo.sh b/tools/compressVideo.sh new file mode 100755 index 0000000000..5f9de9820b --- /dev/null +++ b/tools/compressVideo.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# +# Copyright (c) 2021 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e +echo "Converting file $1" +file=$(echo $1 | sed 's/\.[^.]*$//') +ffmpeg -i $1 -filter_complex "[0:v] fps=12,scale=480:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse" $file-tmp.gif +echo "Converting to Gif" +gifsicle -O3 --lossy=80 -o $file.gif $file-tmp.gif +rm $file-tmp.gif +echo "Done, $file.gif has been generated" diff --git a/tools/templates/ElementFeature/template.xml b/tools/templates/ElementFeature/template.xml index 14c718c993..71cf74f19b 100644 --- a/tools/templates/ElementFeature/template.xml +++ b/tools/templates/ElementFeature/template.xml @@ -105,8 +105,6 @@ suggest="${underscoreToCamelCase(classToResource(fragmentClass))}ViewEvents" default="MainViewEvents" help="The name of the view events to create" /> - - { + clickOn(R.string.settings_notification_advanced) + pressBack() + } + VectorFeatures.NotificationSettingsVersion.V2 -> { + clickOn(R.string.settings_notification_default) + pressBack() + clickOn(R.string.settings_notification_mentions_and_keywords) + // TODO Test adding a keyword? + pressBack() + clickOn(R.string.settings_notification_other) + pressBack() + } } + /* clickOn(R.string.settings_noisy_notifications_preferences) TODO Cannot go back diff --git a/vector/src/debug/res/layout/demo_theme_sample.xml b/vector/src/debug/res/layout/demo_theme_sample.xml index b9102f1b29..1d2820ea7b 100644 --- a/vector/src/debug/res/layout/demo_theme_sample.xml +++ b/vector/src/debug/res/layout/demo_theme_sample.xml @@ -1,9 +1,7 @@ - - - - - + + + + + + + + + + + + + - - - + android:windowSoftInputMode="adjustResize" /> - - - - - - - - - - - - - - - - - - + android:windowSoftInputMode="adjustResize" /> : AppCompatActivity(), Maver protected fun View.debouncedClicks(onClicked: () -> Unit) { clicks() - .throttleFirst(300) .onEach { onClicked() } .launchIn(lifecycleScope) } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index cbbcd48310..69c525dbde 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -36,7 +36,6 @@ import dagger.hilt.android.EntryPointAccessors import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.toMvRxBundle -import im.vector.app.core.flow.throttleFirst import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.analytics.VectorAnalytics import kotlinx.coroutines.flow.launchIn @@ -174,7 +173,6 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe protected fun View.debouncedClicks(onClicked: () -> Unit) { clicks() - .throttleFirst(300) .onEach { onClicked() } .launchIn(viewLifecycleOwner.lifecycleScope) } diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index 929ea536ca..eab1101064 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -42,7 +42,6 @@ import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.toMvRxBundle -import im.vector.app.core.flow.throttleFirst import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.navigation.Navigator import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog @@ -242,7 +241,6 @@ abstract class VectorBaseFragment : Fragment(), MavericksView protected fun View.debouncedClicks(onClicked: () -> Unit) { clicks() - .throttleFirst(300) .onEach { onClicked() } .launchIn(viewLifecycleOwner.lifecycleScope) } diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index acd2a81123..5f9c5433fe 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -42,8 +42,6 @@ import im.vector.app.features.pin.UnlockedActivity import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.signout.hard.SignedOutActivity -import im.vector.app.features.signout.soft.SoftLogoutActivity -import im.vector.app.features.signout.soft.SoftLogoutActivity2 import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.ui.UiStateRepository import kotlinx.coroutines.Dispatchers @@ -226,9 +224,11 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity navigator.openLogin(this, null) null } - args.isSoftLogout -> + args.isSoftLogout -> { // The homeserver has invalidated the token, with a soft logout - getSoftLogoutActivityIntent() + navigator.softLogout(this) + null + } args.isUserLoggedOut -> // the homeserver has invalidated the token (password changed, device deleted, other security reasons) SignedOutActivity.newIntent(this) @@ -239,7 +239,8 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity HomeActivity.newIntent(this) } else { // The token is still invalid - getSoftLogoutActivityIntent() + navigator.softLogout(this) + null } else -> { // First start, or no active session @@ -250,12 +251,4 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity intent?.let { startActivity(it) } finish() } - - private fun getSoftLogoutActivityIntent(): Intent { - return if (resources.getBoolean(R.bool.useLoginV2)) { - SoftLogoutActivity2.newIntent(this) - } else { - SoftLogoutActivity.newIntent(this) - } - } } diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt new file mode 100644 index 0000000000..b40d2d02f2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features + +import im.vector.app.BuildConfig + +interface VectorFeatures { + + fun loginVersion(): LoginVersion + fun notificationSettingsVersion(): NotificationSettingsVersion + + enum class LoginVersion { + V1, + V2 + } + + enum class NotificationSettingsVersion { + V1, + V2 + } +} + +class DefaultVectorFeatures : VectorFeatures { + override fun loginVersion(): VectorFeatures.LoginVersion = BuildConfig.LOGIN_VERSION + override fun notificationSettingsVersion(): VectorFeatures.NotificationSettingsVersion = BuildConfig.NOTIFICATION_SETTINGS_VERSION +} diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 0e46cb2c78..5b9c3f7fb4 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -79,7 +79,7 @@ class AttachmentsPreviewFragment @Inject constructor( applyInsets() setupRecyclerViews() setupToolbar(views.attachmentPreviewerToolbar) - views.attachmentPreviewerSendButton.setOnClickListener { + views.attachmentPreviewerSendButton.debouncedClicks { setResultAndFinish() } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 2406cbc625..995dc3d5e8 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -35,7 +35,6 @@ import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.view.isInvisible import androidx.core.view.isVisible -import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel @@ -62,8 +61,6 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs import io.github.hyuwah.draggableviewlib.DraggableView import io.github.hyuwah.draggableviewlib.setupDraggable -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.logger.LoggerTag @@ -142,12 +139,9 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } } - callViewModel.viewEvents - .stream() - .onEach { - handleViewEvents(it) - } - .launchIn(lifecycleScope) + callViewModel.observeViewEvents { + handleViewEvents(it) + } callViewModel.onEach(VectorCallViewState::callId, VectorCallViewState::isVideoCall) { _, isVideoCall -> if (isVideoCall) { diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index c1bccf855d..5310fccb3a 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -77,7 +77,7 @@ class ContactsBookFragment @Inject constructor( } private fun setupConsentView() { - views.phoneBookSearchForMatrixContacts.setOnClickListener { + views.phoneBookSearchForMatrixContacts.debouncedClicks { contactsBookViewModel.handle(ContactsBookAction.UserConsentRequest) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt index 435ca6e608..40ad1372fb 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -58,8 +58,8 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() : views.keyInputLayout.error = newValue } - views.keysRestoreButton.setOnClickListener { onRestoreFromKey() } - views.keysBackupImport.setOnClickListener { onImport() } + views.keysRestoreButton.debouncedClicks { onRestoreFromKey() } + views.keysBackupImport.debouncedClicks { onImport() } views.keyTextEdit.doOnTextChanged { text, _, _, _ -> onRestoreKeyTextEditChange(text) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index c023e66e44..631bc9ff4f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -58,8 +58,8 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase return@setOnEditorActionListener false } - views.helperTextWithLink.setOnClickListener { onUseRecoveryKey() } - views.keysBackupRestoreWithPassphraseSubmit.setOnClickListener { onRestoreBackup() } + views.helperTextWithLink.debouncedClicks { onUseRecoveryKey() } + views.keysBackupRestoreWithPassphraseSubmit.debouncedClicks { onRestoreBackup() } views.keysBackupPassphraseEnterEdittext.doOnTextChanged { text, _, _, _ -> onPassphraseTextEditChange(text) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt index 66a7df14c3..c4663fd3bc 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt @@ -52,7 +52,7 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen views.successText.text = context?.getString(R.string.keys_backup_restore_success_title_already_up_to_date) views.successDetailsText.isVisible = false } - views.keysBackupSetupDoneButton.setOnClickListener { onDone() } + views.keysBackupSetupDoneButton.debouncedClicks { onDone() } } private fun onDone() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt index 08496c490f..7d8feba942 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -45,8 +45,8 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment onPassphraseChanged() } views.keysBackupSetupStep2PassphraseConfirmEditText.doOnTextChanged { _, _, _, _ -> onConfirmPassphraseChanged() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index 2befc4e79d..c1cd87b4c8 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -85,9 +85,9 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment(R.id.keys_backup_setup_save)?.setOnClickListener { + dialog.findViewById(R.id.keys_backup_setup_save)?.debouncedClicks { val userId = viewModel.userId val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( @@ -139,7 +139,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment(R.id.keys_backup_setup_share)?.setOnClickListener { + dialog.findViewById(R.id.keys_backup_setup_share)?.debouncedClicks { startSharePlainTextIntent( fragment = this, activityResultLauncher = null, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 8335c635e4..de8e0dfa9a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -898,7 +898,7 @@ class RoomDetailFragment @Inject constructor( } private fun setupJumpToReadMarkerView() { - views.jumpToReadMarkerView.setOnClickListener { + views.jumpToReadMarkerView.debouncedClicks { onJumpToReadMarkerClicked() } views.jumpToReadMarkerView.setOnCloseIconClickListener { @@ -954,7 +954,7 @@ class RoomDetailFragment @Inject constructor( super.onCreateOptionsMenu(menu, inflater) // We use a custom layout for this menu item, so we need to set a ClickListener menu.findItem(R.id.open_matrix_apps)?.let { menuItem -> - menuItem.actionView.setOnClickListener { + menuItem.actionView.debouncedClicks { onOptionsItemSelected(menuItem) } } @@ -1463,7 +1463,7 @@ class RoomDetailFragment @Inject constructor( callback = this@RoomDetailFragment isVisible = true render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState) - setOnClickListener { } + setOnClickListener(null) } Unit } else if (mainState.asyncInviter.complete) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt index c6dce5a9ce..747183bce6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt @@ -25,6 +25,7 @@ import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.onClick +import im.vector.app.core.epoxy.onLongClickIgnoringLinks import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever @@ -94,10 +95,9 @@ abstract class MessageTextItem : AbsMessageItem() { } super.bind(holder) holder.messageView.movementMethod = movementMethod - renderSendState(holder.messageView, holder.messageView) holder.messageView.onClick(attributes.itemClickListener) - holder.messageView.setOnLongClickListener(attributes.itemLongClickListener) + holder.messageView.onLongClickIgnoringLinks(attributes.itemLongClickListener) if (canUseTextFuture) { holder.messageView.setTextFuture(textFuture) @@ -133,6 +133,7 @@ abstract class MessageTextItem : AbsMessageItem() { previewUrlView?.render(state, safeImageContentRenderer) } } + companion object { private const val STUB_ID = R.id.messageContentTextStub } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 7e39156b18..a2d10cf818 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -17,7 +17,6 @@ package im.vector.app.features.home.room.list.actions import androidx.annotation.StringRes import com.airbnb.epoxy.TypedEpoxyController -import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem @@ -25,6 +24,7 @@ import im.vector.app.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem import im.vector.app.core.epoxy.profiles.notifications.radioButtonItem import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider +import im.vector.app.features.VectorFeatures import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.notifications.notificationOptions import im.vector.app.features.roomprofile.notifications.notificationStateMapped @@ -38,7 +38,8 @@ import javax.inject.Inject class RoomListQuickActionsEpoxyController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val colorProvider: ColorProvider, - private val stringProvider: StringProvider + private val stringProvider: StringProvider, + private val features: VectorFeatures ) : TypedEpoxyController() { var listener: Listener? = null @@ -47,7 +48,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor( val notificationViewState = state.notificationSettingsViewState val roomSummary = notificationViewState.roomSummary() ?: return val host = this - val isV2 = BuildConfig.USE_NOTIFICATION_SETTINGS_V2 + val isV2 = features.notificationSettingsVersion() == VectorFeatures.NotificationSettingsVersion.V2 // V2 always shows full details as we no longer display the sheet from RoomProfile > Notifications val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL || isV2 @@ -73,14 +74,14 @@ class RoomListQuickActionsEpoxyController @Inject constructor( } if (isV2) { - notificationViewState.notificationOptions.forEach { notificationState -> + notificationViewState.notificationOptions.forEach { notificationState -> val title = titleForNotificationState(notificationState) radioButtonItem { id(notificationState.name) titleRes(title) selected(notificationViewState.notificationStateMapped() == notificationState) listener { - host.listener?.didSelectRoomNotificationState(notificationState) + host.listener?.didSelectRoomNotificationState(notificationState) } } } @@ -102,8 +103,9 @@ class RoomListQuickActionsEpoxyController @Inject constructor( RoomNotificationState.ALL_MESSAGES_NOISY -> R.string.room_settings_all_messages RoomNotificationState.MENTIONS_ONLY -> R.string.room_settings_mention_and_keyword_only RoomNotificationState.MUTE -> R.string.room_settings_none - else -> null + else -> null } + private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) { val host = this@RoomListQuickActionsEpoxyController val selected = when (this) { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index a357caf8fa..bbad40ce78 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -18,6 +18,7 @@ package im.vector.app.features.login import android.content.Context import android.content.Intent +import android.net.Uri import android.view.View import android.view.ViewGroup import androidx.core.view.ViewCompat @@ -363,5 +364,11 @@ open class LoginActivity : VectorBaseActivity(), ToolbarCo putExtra(EXTRA_CONFIG, loginConfig) } } + + fun redirectIntent(context: Context, data: Uri?): Intent { + return Intent(context, LoginActivity::class.java).apply { + setData(data) + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt index 0978621f28..9ca8a1dbec 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt @@ -81,7 +81,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment(), ToolbarC putExtra(EXTRA_CONFIG, loginConfig) } } + + fun redirectIntent(context: Context, data: Uri?): Intent { + return Intent(context, LoginActivity2::class.java).apply { + setData(data) + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt index efa4bd29c6..94784b0605 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt @@ -85,11 +85,11 @@ class AccountCreatedFragment @Inject constructor( } private fun setupClickListener() { - views.loginAccountCreatedMessage.setOnClickListener { + views.loginAccountCreatedMessage.debouncedClicks { // Update display name displayDialog() } - views.loginAccountCreatedAvatar.setOnClickListener { + views.loginAccountCreatedAvatar.debouncedClicks { galleryOrCameraDialogHelper.show() } } @@ -120,8 +120,8 @@ class AccountCreatedFragment @Inject constructor( } private fun setupSubmitButton() { - views.loginAccountCreatedLater.setOnClickListener { terminate() } - views.loginAccountCreatedDone.setOnClickListener { terminate() } + views.loginAccountCreatedLater.debouncedClicks { terminate() } + views.loginAccountCreatedDone.debouncedClicks { terminate() } } private fun terminate() { diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 631c7b8395..6b035e7d49 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -19,6 +19,7 @@ package im.vector.app.features.navigation import android.app.Activity import android.content.Context import android.content.Intent +import android.net.Uri import android.os.Build import android.view.View import android.view.Window @@ -36,6 +37,7 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.fatalError import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.toast +import im.vector.app.features.VectorFeatures import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity import im.vector.app.features.call.conference.JitsiCallViewModel import im.vector.app.features.call.conference.VectorJitsiActivity @@ -78,6 +80,8 @@ import im.vector.app.features.roomprofile.RoomProfileActivity import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData +import im.vector.app.features.signout.soft.SoftLogoutActivity +import im.vector.app.features.signout.soft.SoftLogoutActivity2 import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.SpaceExploreActivity import im.vector.app.features.spaces.SpacePreviewActivity @@ -103,19 +107,35 @@ class DefaultNavigator @Inject constructor( private val vectorPreferences: VectorPreferences, private val widgetArgsBuilder: WidgetArgsBuilder, private val appStateHandler: AppStateHandler, - private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider + private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, + private val features: VectorFeatures ) : Navigator { override fun openLogin(context: Context, loginConfig: LoginConfig?, flags: Int) { - val intent = if (context.resources.getBoolean(R.bool.useLoginV2)) { - LoginActivity2.newIntent(context, loginConfig) - } else { - LoginActivity.newIntent(context, loginConfig) + val intent = when (features.loginVersion()) { + VectorFeatures.LoginVersion.V1 -> LoginActivity.newIntent(context, loginConfig) + VectorFeatures.LoginVersion.V2 -> LoginActivity2.newIntent(context, loginConfig) } intent.addFlags(flags) context.startActivity(intent) } + override fun loginSSORedirect(context: Context, data: Uri?) { + val intent = when (features.loginVersion()) { + VectorFeatures.LoginVersion.V1 -> LoginActivity.redirectIntent(context, data) + VectorFeatures.LoginVersion.V2 -> LoginActivity2.redirectIntent(context, data) + } + context.startActivity(intent) + } + + override fun softLogout(context: Context) { + val intent = when (features.loginVersion()) { + VectorFeatures.LoginVersion.V1 -> SoftLogoutActivity.newIntent(context) + VectorFeatures.LoginVersion.V2 -> SoftLogoutActivity2.newIntent(context) + } + context.startActivity(intent) + } + override fun openRoom(context: Context, roomId: String, eventId: String?, buildTask: Boolean) { if (sessionHolder.getSafeActiveSession()?.getRoom(roomId) == null) { fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast()) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index c828a80322..6778c39a22 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -19,6 +19,7 @@ package im.vector.app.features.navigation import android.app.Activity import android.content.Context import android.content.Intent +import android.net.Uri import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair @@ -41,6 +42,10 @@ interface Navigator { fun openLogin(context: Context, loginConfig: LoginConfig? = null, flags: Int = 0) + fun loginSSORedirect(context: Context, data: Uri?) + + fun softLogout(context: Context) + fun openRoom(context: Context, roomId: String, eventId: String? = null, buildTask: Boolean = false) sealed class PostSwitchSpaceAction { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index e1a5cae907..2091fe04a1 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -31,7 +31,6 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder -import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.animations.AppBarStateChangeListener import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener @@ -45,6 +44,7 @@ import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.ViewStubRoomProfileHeaderBinding +import im.vector.app.features.VectorFeatures import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailPendingAction import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore @@ -70,6 +70,7 @@ class RoomProfileFragment @Inject constructor( private val roomProfileController: RoomProfileController, private val avatarRenderer: AvatarRenderer, private val roomDetailPendingActionStore: RoomDetailPendingActionStore, + private val features: VectorFeatures ) : VectorBaseFragment(), RoomProfileController.Callback { @@ -145,12 +146,12 @@ class RoomProfileFragment @Inject constructor( headerViews.roomProfileNameView, views.matrixProfileToolbarTitleView ).forEach { - it.setOnClickListener { + it.debouncedClicks { roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomSettings) } } // Shortcut to room alias - headerViews.roomProfileAliasView.setOnClickListener { + headerViews.roomProfileAliasView.debouncedClicks { roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAliasesSettings) } // Open Avatar @@ -158,7 +159,7 @@ class RoomProfileFragment @Inject constructor( headerViews.roomProfileAvatarView, views.matrixProfileToolbarAvatarImageView ).forEach { view -> - view.setOnClickListener { onAvatarClicked(view) } + view.debouncedClicks { onAvatarClicked(view) } } } @@ -258,12 +259,15 @@ class RoomProfileFragment @Inject constructor( } override fun onNotificationsClicked() { - if (BuildConfig.USE_NOTIFICATION_SETTINGS_V2) { - roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings) - } else { - RoomListQuickActionsBottomSheet - .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) - .show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS") + when (features.notificationSettingsVersion()) { + VectorFeatures.NotificationSettingsVersion.V1 -> { + RoomListQuickActionsBottomSheet + .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) + .show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS") + } + VectorFeatures.NotificationSettingsVersion.V2 -> { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings) + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 6887a4f623..18b23b826e 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -47,6 +47,9 @@ class VectorPreferences @Inject constructor(private val context: Context) { const val SETTINGS_DISCOVERY_PREFERENCE_KEY = "SETTINGS_DISCOVERY_PREFERENCE_KEY" const val SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY" + const val SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY" + const val SETTINGS_NOTIFICATION_KEYWORD_AND_MENTIONS_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_KEYWORD_AND_MENTIONS_PREFERENCE_KEY" + const val SETTINGS_NOTIFICATION_OTHER_PREFERENCE_KEY = "SETTINGS_NOTIFICATION_OTHER_PREFERENCE_KEY" const val SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY" const val SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY = "SETTINGS_OTHER_THIRD_PARTY_NOTICES_PREFERENCE_KEY" const val SETTINGS_COPYRIGHT_PREFERENCE_KEY = "SETTINGS_COPYRIGHT_PREFERENCE_KEY" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt index 2187765599..08d67067ec 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt @@ -20,6 +20,7 @@ import android.content.Context import android.os.Bundle import android.view.View import androidx.annotation.CallSuper +import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceFragmentCompat import com.airbnb.mvrx.MavericksView import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -29,7 +30,10 @@ import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.toast import im.vector.app.features.analytics.VectorAnalytics +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.session.Session +import reactivecircus.flowbinding.android.view.clicks import timber.log.Timber abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), MavericksView { @@ -45,6 +49,16 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick protected lateinit var errorFormatter: ErrorFormatter protected lateinit var analytics: VectorAnalytics + /* ========================================================================================== + * Views + * ========================================================================================== */ + + protected fun View.debouncedClicks(onClicked: () -> Unit) { + clicks() + .onEach { onClicked() } + .launchIn(viewLifecycleOwner.lifecycleScope) + } + abstract val preferenceXmlRes: Int @CallSuper diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index 8d950b4e32..27548dc756 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -418,7 +418,7 @@ class VectorSettingsGeneralFragment @Inject constructor( } } - updateButton.setOnClickListener { + updateButton.debouncedClicks { // Hide passwords during processing views.changePasswordOldPwdText.hidePassword() views.changePasswordNewPwdText.hidePassword() diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt index 2a3ea799a5..dbea253649 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt @@ -197,7 +197,7 @@ class VectorSettingsPreferencesFragment @Inject constructor( .forEachIndexed { i, v -> v.isChecked = i == index - v.setOnClickListener { + v.debouncedClicks { dialog.dismiss() FontScale.updateFontScale(activity, i) vectorConfiguration.applyToApplicationContext() diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index c1c7feb7df..a8038e2224 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -481,7 +481,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( val importDialog = builder.show() - views.dialogE2eKeysImportButton.setOnClickListener { + views.dialogE2eKeysImportButton.debouncedClicks { val password = views.dialogE2eKeysPassphraseEditText.text.toString() displayLoadingView() diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 4199bd1753..3004d30913 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -40,6 +40,7 @@ import im.vector.app.core.pushers.PushersManager import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.requestDisablingBatteryOptimization +import im.vector.app.features.VectorFeatures import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.BackgroundSyncModeChooserDialog @@ -63,7 +64,8 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( private val pushManager: PushersManager, private val activeSessionHolder: ActiveSessionHolder, private val vectorPreferences: VectorPreferences, - private val guardServiceStarter: GuardServiceStarter + private val guardServiceStarter: GuardServiceStarter, + private val features: VectorFeatures ) : VectorSettingsBaseFragment(), BackgroundSyncModeChooserDialog.InteractionListener { @@ -145,6 +147,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( refreshBackgroundSyncPrefs() handleSystemPreference() + handleVersionedSettings() } private fun bindEmailNotifications() { @@ -309,6 +312,15 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( } } + private fun handleVersionedSettings() { + val isNotificationSettingsV2Enabled = features.notificationSettingsVersion() == VectorFeatures.NotificationSettingsVersion.V2 + + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY)?.isVisible = !isNotificationSettingsV2Enabled + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_DEFAULT_PREFERENCE_KEY)?.isVisible = isNotificationSettingsV2Enabled + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_KEYWORD_AND_MENTIONS_PREFERENCE_KEY)?.isVisible = isNotificationSettingsV2Enabled + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_OTHER_PREFERENCE_KEY)?.isVisible = isNotificationSettingsV2Enabled + } + override fun onResume() { super.onResume() activeSessionHolder.getSafeActiveSession()?.refreshPushers() diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index d5fd3050e9..e1efef4d5a 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -118,7 +118,7 @@ class IncomingShareFragment @Inject constructor( return true } }) - views.sendShareButton.setOnClickListener { + views.sendShareButton.debouncedClicks { handleSendShare() } } diff --git a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt index ee7557b402..87b8c33aa3 100644 --- a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt @@ -42,7 +42,7 @@ class SignedOutActivity : VectorBaseActivity() { } private fun setupViews() { - views.signedOutSubmit.setOnClickListener { submit() } + views.signedOutSubmit.debouncedClicks { submit() } } private fun submit() { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index a31edfcb02..f8d817acd5 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -40,7 +40,6 @@ class WidgetActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { - private const val WIDGET_FRAGMENT_TAG = "WIDGET_FRAGMENT_TAG" private const val WIDGET_PERMISSION_FRAGMENT_TAG = "WIDGET_PERMISSION_FRAGMENT_TAG" private const val EXTRA_RESULT = "EXTRA_RESULT" @@ -56,7 +55,7 @@ class WidgetActivity : VectorBaseActivity(), return intent.extras?.getSerializable(EXTRA_RESULT) as? Content } - fun createResultIntent(content: Content): Intent { + private fun createResultIntent(content: Content): Intent { return Intent().apply { putExtra(EXTRA_RESULT, content as Serializable) } diff --git a/vector/src/main/res/drawable/bg_attachment_type_selector.xml b/vector/src/main/res/drawable/bg_attachment_type_selector.xml index 40c4337a28..2cbad6a855 100644 --- a/vector/src/main/res/drawable/bg_attachment_type_selector.xml +++ b/vector/src/main/res/drawable/bg_attachment_type_selector.xml @@ -20,5 +20,4 @@ - \ No newline at end of file diff --git a/vector/src/main/res/drawable/bg_rounded_button.xml b/vector/src/main/res/drawable/bg_rounded_button.xml index 4b5503a92f..a316195792 100644 --- a/vector/src/main/res/drawable/bg_rounded_button.xml +++ b/vector/src/main/res/drawable/bg_rounded_button.xml @@ -1,5 +1,4 @@ - diff --git a/vector/src/main/res/drawable/bg_tombstone_predecessor.xml b/vector/src/main/res/drawable/bg_tombstone_predecessor.xml index 65c214d2cd..4b7a8d2d4f 100644 --- a/vector/src/main/res/drawable/bg_tombstone_predecessor.xml +++ b/vector/src/main/res/drawable/bg_tombstone_predecessor.xml @@ -18,5 +18,4 @@ - \ No newline at end of file diff --git a/vector/src/main/res/drawable/bg_unread_notification.xml b/vector/src/main/res/drawable/bg_unread_notification.xml index 2b6c1051ca..7c9ea18eec 100644 --- a/vector/src/main/res/drawable/bg_unread_notification.xml +++ b/vector/src/main/res/drawable/bg_unread_notification.xml @@ -1,5 +1,4 @@ - diff --git a/vector/src/main/res/drawable/highlighted_message_background.xml b/vector/src/main/res/drawable/highlighted_message_background.xml index 10f745fb4d..9eeded4853 100644 --- a/vector/src/main/res/drawable/highlighted_message_background.xml +++ b/vector/src/main/res/drawable/highlighted_message_background.xml @@ -31,5 +31,4 @@ - \ No newline at end of file diff --git a/vector/src/main/res/layout/activity_big_image_viewer.xml b/vector/src/main/res/layout/activity_big_image_viewer.xml index 3865e26261..8f1cf88aff 100644 --- a/vector/src/main/res/layout/activity_big_image_viewer.xml +++ b/vector/src/main/res/layout/activity_big_image_viewer.xml @@ -25,7 +25,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/appBarLayout" + app:layout_constraintTop_toBottomOf="@id/appBarLayout" app:optimizeDisplay="true" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/activity_call.xml b/vector/src/main/res/layout/activity_call.xml index 342ae11562..2238f96c9f 100644 --- a/vector/src/main/res/layout/activity_call.xml +++ b/vector/src/main/res/layout/activity_call.xml @@ -1,8 +1,6 @@ - - + app:layout_constraintTop_toBottomOf="@id/callToolbar"> - @@ -65,7 +65,7 @@ android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginEnd="8dp" - android:layout_toStartOf="@+id/callTransferConnectAction" + android:layout_toStartOf="@id/callTransferConnectAction" android:layout_toEndOf="@id/callTransferConsultCheckBox" android:ellipsize="end" android:text="@string/call_transfer_consult_first" /> diff --git a/vector/src/main/res/layout/activity_filtered_rooms.xml b/vector/src/main/res/layout/activity_filtered_rooms.xml index 270de8f514..07a2bc7a2e 100644 --- a/vector/src/main/res/layout/activity_filtered_rooms.xml +++ b/vector/src/main/res/layout/activity_filtered_rooms.xml @@ -39,7 +39,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/appBarLayout" /> + app:layout_constraintTop_toBottomOf="@id/appBarLayout" /> diff --git a/vector/src/main/res/layout/activity_home.xml b/vector/src/main/res/layout/activity_home.xml index a41256fb84..9899c15aa6 100644 --- a/vector/src/main/res/layout/activity_home.xml +++ b/vector/src/main/res/layout/activity_home.xml @@ -22,12 +22,10 @@ - - \ No newline at end of file diff --git a/vector/src/main/res/layout/activity_incoming_share.xml b/vector/src/main/res/layout/activity_incoming_share.xml index f0e0c0a485..0de417f57b 100644 --- a/vector/src/main/res/layout/activity_incoming_share.xml +++ b/vector/src/main/res/layout/activity_incoming_share.xml @@ -39,7 +39,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/appBarLayout" /> + app:layout_constraintTop_toBottomOf="@id/appBarLayout" /> diff --git a/vector/src/main/res/layout/activity_main.xml b/vector/src/main/res/layout/activity_main.xml index f341a710fa..c7bca50acb 100644 --- a/vector/src/main/res/layout/activity_main.xml +++ b/vector/src/main/res/layout/activity_main.xml @@ -1,5 +1,4 @@ - - + app:layout_constraintTop_toBottomOf="@id/appBarLayout" /> diff --git a/vector/src/main/res/layout/activity_vector_web_view.xml b/vector/src/main/res/layout/activity_vector_web_view.xml index d691f4de5d..a8d1521159 100644 --- a/vector/src/main/res/layout/activity_vector_web_view.xml +++ b/vector/src/main/res/layout/activity_vector_web_view.xml @@ -25,7 +25,7 @@ android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@+id/appBarLayout" /> + app:layout_constraintTop_toBottomOf="@id/appBarLayout" /> @@ -48,7 +48,7 @@ android:maxLines="1" android:textColor="?vctr_content_secondary" app:drawableTint="?vctr_content_secondary" - app:layout_constraintEnd_toStartOf="@+id/incomingCallRejectView" + app:layout_constraintEnd_toStartOf="@id/incomingCallRejectView" app:layout_constraintStart_toStartOf="@id/incomingCallNameView" app:layout_constraintTop_toBottomOf="@id/incomingCallNameView" tools:drawableStart="@drawable/ic_call_audio_small" diff --git a/vector/src/main/res/layout/alerter_verification_layout.xml b/vector/src/main/res/layout/alerter_verification_layout.xml index 86dcd41340..d532a10dc2 100644 --- a/vector/src/main/res/layout/alerter_verification_layout.xml +++ b/vector/src/main/res/layout/alerter_verification_layout.xml @@ -14,7 +14,7 @@ android:layout_height="40dp" android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/alerter_texts" + app:layout_constraintEnd_toStartOf="@id/alerter_texts" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:src="@sample/user_round_avatars" /> @@ -26,7 +26,7 @@ android:layout_height="24dp" android:importantForAccessibility="no" android:src="@drawable/ic_shield_warning" - app:layout_constraintCircle="@+id/ivUserAvatar" + app:layout_constraintCircle="@id/ivUserAvatar" app:layout_constraintCircleAngle="135" app:layout_constraintCircleRadius="20dp" tools:ignore="MissingConstraints" diff --git a/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml b/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml index e80a55bc16..25c2d1c3e5 100644 --- a/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml +++ b/vector/src/main/res/layout/bottom_sheet_add_rooms_or_spaces_to_space.xml @@ -41,5 +41,4 @@ app:titleTextColor="?vctr_content_primary" tools:actionDescription="" /> - diff --git a/vector/src/main/res/layout/bottom_sheet_bootstrap.xml b/vector/src/main/res/layout/bottom_sheet_bootstrap.xml index 52784bc6af..c3fa9d2931 100644 --- a/vector/src/main/res/layout/bottom_sheet_bootstrap.xml +++ b/vector/src/main/res/layout/bottom_sheet_bootstrap.xml @@ -41,7 +41,7 @@ android:textStyle="bold" app:layout_constraintBottom_toBottomOf="@id/bootstrapIcon" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toEndOf="@+id/bootstrapIcon" + app:layout_constraintStart_toEndOf="@id/bootstrapIcon" app:layout_constraintTop_toTopOf="@id/bootstrapIcon" tools:text="@string/bottom_sheet_setup_secure_backup_title" /> diff --git a/vector/src/main/res/layout/bottom_sheet_call_dialer_choice.xml b/vector/src/main/res/layout/bottom_sheet_call_dialer_choice.xml index f07e6fff8c..0539c648b4 100644 --- a/vector/src/main/res/layout/bottom_sheet_call_dialer_choice.xml +++ b/vector/src/main/res/layout/bottom_sheet_call_dialer_choice.xml @@ -24,5 +24,4 @@ app:tint="?vctr_content_primary" app:titleTextColor="?vctr_content_primary" /> - diff --git a/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml b/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml index 5686c8879e..a1ecf80dd5 100644 --- a/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml +++ b/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml @@ -33,7 +33,6 @@ app:layout_constraintTop_toTopOf="parent" tools:src="@sample/user_round_avatars" /> - - -