diff --git a/CHANGES.md b/CHANGES.md index 6a11606c058b7439815bf94722fcdf07b87cee64..a4694b33ceb2b71963484b6485b9c1069c1e3ee2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,17 @@ Please also refer to the Changelog of Element Android: https://github.com/vector-im/element-android/blob/main/CHANGES.md +Changes in Matrix-SDK 1.4.4 (2022-03-16) +=================================================== + +Bugfixes 🛠+---------- +- Overflowing media size ([#5394](https://github.com/vector-im/element-android/issues/5394)) +- Updating avatar failing due to wrong thread on some devices ([#5402](https://github.com/vector-im/element-android/issues/5402)) + +SDK API changes âš ï¸ +------------------ +- Change name of getTimeLineEvent and getTimeLineEventLive methods to getTimelineEvent and getTimelineEventLive. ([#5330](https://github.com/vector-im/element-android/issues/5330)) + Changes in Matrix-SDK 1.4.2 (2022-02-28) =================================================== diff --git a/dependencies.gradle b/dependencies.gradle index 77d072e7c7f9c0a8dffb42f7d35c267fd8b4680e..87b8e3c12ffabfac2fa80507b8587ef76d1cb181 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -71,8 +71,7 @@ ext.libs = [ 'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso" ], google : [ - // TODO There is 1.6.0? - 'material' : "com.google.android.material:material:1.4.0" + 'material' : "com.google.android.material:material:1.5.0" ], dagger : [ 'dagger' : "com.google.dagger:dagger:$dagger", diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle index 7de8100469ba5238322de827938b302b31932eab..45883f506d4326389bd26e24fb69856ea533e605 100644 --- a/dependencies_groups.gradle +++ b/dependencies_groups.gradle @@ -29,9 +29,10 @@ ext.groups = [ 'com\\.google\\.android\\..*', ], group: [ - 'com.google.firebase', 'com.android', 'com.android.tools', + 'com.google.firebase', + 'com.google.testing.platform', ] ], mavenCentral: [ @@ -63,6 +64,8 @@ ext.groups = [ 'com.github.piasy', 'com.github.shyiko.klob', 'com.google', + 'com.google.android', + 'com.google.api.grpc', 'com.google.auto.service', 'com.google.auto.value', 'com.google.code.findbugs', @@ -111,10 +114,13 @@ ext.groups = [ 'io.arrow-kt', 'io.github.detekt.sarif4k', 'io.github.reactivecircus.flowbinding', + 'io.grpc', 'io.jsonwebtoken', 'io.kindedj', 'io.mockk', + 'io.netty', 'io.noties.markwon', + 'io.opencensus', 'io.reactivex.rxjava2', 'io.realm', 'it.unimi.dsi', @@ -150,6 +156,7 @@ ext.groups = [ 'org.ec4j.core', 'org.glassfish.jaxb', 'org.hamcrest', + 'org.jacoco', 'org.jetbrains', 'org.jetbrains.intellij.deps', 'org.jetbrains.kotlin', @@ -175,6 +182,7 @@ ext.groups = [ 'org.sonatype.oss', 'org.testng', 'org.threeten', + 'org.webjars', 'ru.noties', 'xerces', 'xml-apis', diff --git a/gradle.properties b/gradle.properties index fe7693929ea82af1313670b391c5de56f98fbbce..917eb10cf076969580a8add91f1db9dfba67eb69 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,7 +26,7 @@ vector.httpLogLevel=NONE # Ref: https://github.com/vanniktech/gradle-maven-publish-plugin GROUP=org.matrix.android POM_ARTIFACT_ID=matrix-android-sdk2 -VERSION_NAME=1.4.2 +VERSION_NAME=1.4.4 POM_PACKAGING=aar diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index c7a8d9f1960761a249819f4ec5f8aded956a1d4f..6605acd29384c4104fd1e77cddbff46d6aafa9c6 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -170,7 +170,7 @@ dependencies { implementation libs.apache.commonsImaging // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.43' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.44' testImplementation libs.tests.junit testImplementation 'org.robolectric:robolectric:4.7.3' 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 031d0a8bcf8d1fab82eec03ef840fe43449c642b..ac4ccf56d109768ba72e978dff23c5710d005522 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 @@ -71,7 +71,7 @@ class CommonTestHelper(context: Context) { ) ) } - matrix = TestMatrix.getInstance(context) + matrix = TestMatrix.getInstance() } fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt index e92232a7c5f0f984f675e184e34b9b14b46b072a..fa44167a8f37f1403a321a03ebdb669a6a11d7d0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt @@ -105,16 +105,9 @@ internal class TestMatrix constructor(context: Context, matrixConfiguration: Mat } } - 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 = TestMatrix(appContext, matrixConfiguration) - } else { - throw IllegalStateException("Matrix is not initialized properly." + - " You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.") - } + fun getInstance(): TestMatrix { + if (isInit.compareAndSet(false, false)) { + throw IllegalStateException("Matrix is not initialized properly. You should call TestMatrix.initialize first") } return instance } 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 c95cc6b4ca352fb0accd97297448151b49d20d7f..a7a81bacf50bd5d4775288d3b1e42130ba944625 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 @@ -95,7 +95,7 @@ class PreShareKeysTest : InstrumentedTest { assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session") testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE + bobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE } } 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 e0605db0b830024dd6eb982043b013b56ea5c523..82aee454eb361657f26b6f68748290b7272cfca4 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 @@ -92,7 +92,7 @@ class KeyShareTests : InstrumentedTest { val roomSecondSessionPOV = aliceSession2.getRoom(roomId) - val receivedEvent = roomSecondSessionPOV?.getTimeLineEvent(sentEventId) + val receivedEvent = roomSecondSessionPOV?.getTimelineEvent(sentEventId) assertNotNull(receivedEvent) assert(receivedEvent!!.isEncrypted()) @@ -382,7 +382,7 @@ class KeyShareTests : InstrumentedTest { commonTestHelper.sendTextMessage(roomAlicePov, "After", 1) val roomRoomBobPov = aliceSession.getRoom(roomId) - val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId) + val beforeJoin = roomRoomBobPov!!.getTimelineEvent(secondEventId) var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") } 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 586d96b0074087e7898c57d1be9bcbd037595072..9fda21763a023c94f0d01f55cad1956ef9599d4a 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 @@ -80,11 +80,11 @@ class WithHeldTests : InstrumentedTest { // await for bob unverified session to get the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null + bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(timelineEvent.eventId) != null } } - val eventBobPOV = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId)!! + val eventBobPOV = bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(timelineEvent.eventId)!! // ============================= // ASSERT @@ -109,7 +109,7 @@ class WithHeldTests : InstrumentedTest { testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId) + val ev = bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(secondEvent.eventId) // wait until it's decrypted ev?.root?.getClearType() == EventType.MESSAGE } @@ -157,12 +157,12 @@ class WithHeldTests : InstrumentedTest { // await for bob session to get the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null + bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId) != null } } // Previous message should still be undecryptable (partially withheld session) - val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) + val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId) try { // .. might need to wait a bit for stability? bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "") @@ -190,7 +190,7 @@ class WithHeldTests : InstrumentedTest { // await for bob SecondSession session to get the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null + bobSecondSession.getRoom(testData.roomId)?.getTimelineEvent(secondMessageId) != null } } @@ -231,7 +231,7 @@ class WithHeldTests : InstrumentedTest { // await for bob SecondSession session to get the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also { + val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)?.also { // try to decrypt and force key request tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt index 63e74603d046b4ad92814b322a1599dfc57ca113..d035fe5fdea677c056ffcae700542ef97a5b1b8a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt @@ -22,6 +22,7 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -31,6 +32,7 @@ import org.matrix.android.sdk.common.assertByteArrayNotEqual import org.matrix.olm.OlmManager import org.matrix.olm.OlmPkDecryption +@Ignore("Ignored in order to speed up test run time") @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) class KeysBackupPasswordTest : InstrumentedTest { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/threads/ThreadMessagingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/threads/ThreadMessagingTest.kt index 6aa4f4cc32b3284427d692ed029e8827eba1b022..dcb181f0c1e498fbec597b0c7f0e51346234dcbb 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/threads/ThreadMessagingTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/threads/ThreadMessagingTest.kt @@ -22,6 +22,7 @@ import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeNull import org.amshove.kluent.shouldBeTrue import org.junit.FixMethodOrder +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -38,6 +39,7 @@ import java.util.concurrent.CountDownLatch @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) +@Ignore("Remaining Integration tests are unstable if run with this test. Issue #5439") class ThreadMessagingTest : InstrumentedTest { @Test diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt index 23dc1e0ba8906a8596e8d5172bce5b3d7cdacebe..f76e4be4401647ab6d452c838c4551a3fa42f3eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt @@ -117,5 +117,5 @@ interface FileService { /** * Get size of cached files */ - fun getCacheSize(): Int + fun getCacheSize(): Long } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt index 3c021384e13a6773465939996ce146aa414b9aa9..61520696445913f8dac2b508b3773e0357b5f5fe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt @@ -41,7 +41,7 @@ interface TimelineService { * At the opposite of getTimeLineEventLive which will be updated when local echo event is synced, it will return null in this case. * @param eventId the eventId to get the TimelineEvent */ - fun getTimeLineEvent(eventId: String): TimelineEvent? + fun getTimelineEvent(eventId: String): TimelineEvent? /** * Creates a LiveData of Optional TimelineEvent event with eventId. @@ -49,7 +49,7 @@ interface TimelineService { * In this case, makes sure to use the new synced eventId from the TimelineEvent class if you want to interact, as the local echo is removed from the SDK. * @param eventId the eventId to listen for TimelineEvent */ - fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> + fun getTimelineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> /** * Returns a snapshot list of TimelineEvent with EventType.MESSAGE and MessageType.MSGTYPE_IMAGE or MessageType.MSGTYPE_VIDEO. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt index f3770e4afee96aedb217207b66d4010dadf2a142..2be4510b6fbe25af67bb9a4df8e20e0dad5d9b2e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt @@ -16,8 +16,11 @@ package org.matrix.android.sdk.internal.database.mapper +import io.realm.Realm +import io.realm.RealmList import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.internal.database.RealmSessionProvider +import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.query.where @@ -32,14 +35,22 @@ internal class ReadReceiptsSummaryMapper @Inject constructor( return emptyList() } val readReceipts = readReceiptsSummaryEntity.readReceipts - - return realmSessionProvider.withRealm { realm -> - readReceipts - .mapNotNull { - val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).findFirst() - ?: return@mapNotNull null - ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong()) - } + // Avoid opening a new realm if we already have one opened + return if (readReceiptsSummaryEntity.isManaged) { + map(readReceipts, readReceiptsSummaryEntity.realm) + } else { + realmSessionProvider.withRealm { realm -> + map(readReceipts, realm) + } } } + + private fun map(readReceipts: RealmList<ReadReceiptEntity>, realm: Realm): List<ReadReceipt> { + return readReceipts + .mapNotNull { + val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).findFirst() + ?: return@mapNotNull null + ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong()) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt index 14dfc097cfc2300447f6e329c5a8827d7a867c14..08651764c2f7ba95d1e6135cc1933e46a17ad3a9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt @@ -323,13 +323,13 @@ internal class DefaultFileService @Inject constructor( return FileProvider.getUriForFile(context, authority, targetFile) } - override fun getCacheSize(): Int { + override fun getCacheSize(): Long { return downloadFolder.walkTopDown() .onEnter { Timber.v("Get size of ${it.absolutePath}") true } - .sumOf { it.length().toInt() } + .sumOf { it.length() } } override fun clearCache() { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt index b988f2253cdd92e82f5fd1a87e7d99fad6a64129..e9cb4238933d26d429b12ca51a820cde5a557fad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt @@ -30,6 +30,7 @@ import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okio.BufferedSink import okio.source +import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError @@ -53,6 +54,7 @@ internal class FileUploader @Inject constructor( private val homeServerCapabilitiesService: HomeServerCapabilitiesService, private val context: Context, private val temporaryFileCreator: TemporaryFileCreator, + private val coroutineDispatchers: MatrixCoroutineDispatchers, contentUrlResolver: ContentUrlResolver, moshi: Moshi ) { @@ -146,14 +148,16 @@ internal class FileUploader @Inject constructor( .post(requestBody) .build() - return okHttpClient.newCall(request).awaitResponse().use { response -> - if (!response.isSuccessful) { - throw response.toFailure(globalErrorReceiver) - } else { - response.body?.source()?.let { - responseAdapter.fromJson(it) + return withContext(coroutineDispatchers.io) { + okHttpClient.newCall(request).awaitResponse().use { response -> + if (!response.isSuccessful) { + throw response.toFailure(globalErrorReceiver) + } else { + response.body?.source()?.let { + responseAdapter.fromJson(it) + } + ?: throw IOException() } - ?: throw IOException() } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt index caf415865795baf9d8813a9488451f961842a987..6f99577ac27c48d7ee8b0eb66b4d6b093ad90499 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/DefaultProfileService.kt @@ -68,11 +68,9 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto } override suspend fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String) { - withContext(coroutineDispatchers.io) { - val response = fileUploader.uploadFromUri(newAvatarUri, fileName, MimeTypes.Jpeg) - setAvatarUrlTask.execute(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri)) - userStore.updateAvatar(userId, response.contentUri) - } + val response = fileUploader.uploadFromUri(newAvatarUri, fileName, MimeTypes.Jpeg) + setAvatarUrlTask.execute(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri)) + userStore.updateAvatar(userId, response.contentUri) } override suspend fun getAvatarUrl(userId: String): Optional<String> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index acceaf6e24c34bd78d0d9ff3736e371a6a2573ed..1e0eb8b49787921d29a5a94bbb9bbe5a04d01fac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -513,7 +513,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( private fun getPollEvent(roomId: String, eventId: String): TimelineEvent? { val session = sessionManager.getSessionComponent(sessionId)?.session() - return session?.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return null.also { + return session?.getRoom(roomId)?.getTimelineEvent(eventId) ?: return null.also { Timber.v("## POLL target poll event $eventId not found in room $roomId") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt index 84261e6ebff09e253f141db66ca06539fc290816..c9914449c3c7871c1d469c81dcda8e47899eded6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt @@ -112,19 +112,18 @@ internal class CreateRoomBodyBuilder @Inject constructor( private suspend fun buildAvatarEvent(params: CreateRoomParams): Event? { return params.avatarUri?.let { avatarUri -> // First upload the image, ignoring any error - tryOrNull { + tryOrNull("Failed to upload image") { fileUploader.uploadFromUri( uri = avatarUri, filename = UUID.randomUUID().toString(), mimeType = MimeTypes.Jpeg) } - ?.let { response -> - Event( - type = EventType.STATE_ROOM_AVATAR, - stateKey = "", - content = mapOf("url" to response.contentUri) - ) - } + }?.let { response -> + Event( + type = EventType.STATE_ROOM_AVATAR, + stateKey = "", + content = mapOf("url" to response.contentUri) + ) } } @@ -180,19 +179,19 @@ internal class CreateRoomBodyBuilder @Inject constructor( params.invite3pids.isEmpty() && params.invitedUserIds.isNotEmpty() && params.invitedUserIds.let { userIds -> - val keys = deviceListManager.downloadKeys(userIds, forceDownload = false) - - userIds.all { userId -> - keys.map[userId].let { deviceMap -> - if (deviceMap.isNullOrEmpty()) { - // A user has no device, so do not enable encryption - false - } else { - // Check that every user's device have at least one key - deviceMap.values.all { !it.keys.isNullOrEmpty() } + val keys = deviceListManager.downloadKeys(userIds, forceDownload = false) + + userIds.all { userId -> + keys.map[userId].let { deviceMap -> + if (deviceMap.isNullOrEmpty()) { + // A user has no device, so do not enable encryption + false + } else { + // Check that every user's device have at least one key + deviceMap.values.all { !it.keys.isNullOrEmpty() } + } + } } } - } - } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index 848e14ff57bee63375f404f5c4202bd89f52601c..d5019aea7b842448b91636324db1720b9333511e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -30,15 +30,14 @@ import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity -import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor +import org.matrix.android.sdk.internal.session.room.timeline.TimelineEventDataSource import org.matrix.android.sdk.internal.util.fetchCopyMap import timber.log.Timber @@ -50,7 +49,7 @@ internal class DefaultRelationService @AssistedInject constructor( private val findReactionEventForUndoTask: FindReactionEventForUndoTask, private val fetchEditHistoryTask: FetchEditHistoryTask, private val fetchThreadTimelineTask: FetchThreadTimelineTask, - private val timelineEventMapper: TimelineEventMapper, + private val timelineEventDataSource: TimelineEventDataSource, @SessionDatabase private val monarchy: Monarchy ) : RelationService { @@ -60,14 +59,8 @@ internal class DefaultRelationService @AssistedInject constructor( } override fun sendReaction(targetEventId: String, reaction: String): Cancelable { - return if (monarchy - .fetchCopyMap( - { realm -> - TimelineEventEntity.where(realm, roomId, targetEventId).findFirst() - }, - { entity, _ -> - timelineEventMapper.map(entity) - }) + val targetTimelineEvent = timelineEventDataSource.getTimelineEvent(roomId, targetEventId) + return if (targetTimelineEvent ?.annotations ?.reactionsSummary .orEmpty() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index d7d61f0b478594c49073b857d4d46d648f10848d..8094fee504ff535d9b78aa152e8e7b2c0eb5c50d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -21,36 +21,23 @@ import com.zhuinden.monarchy.Monarchy import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import io.realm.Sort -import io.realm.kotlin.where import org.matrix.android.sdk.api.MatrixCoroutineDispatchers -import org.matrix.android.sdk.api.session.events.model.isImageMessage -import org.matrix.android.sdk.api.session.events.model.isVideoMessage 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.TimelineService import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper -import org.matrix.android.sdk.internal.database.model.TimelineEventEntity -import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields -import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler -import org.matrix.android.sdk.internal.task.TaskExecutor internal class DefaultTimelineService @AssistedInject constructor( @Assisted private val roomId: String, - @UserId private val userId: String, @SessionDatabase private val monarchy: Monarchy, - private val realmSessionProvider: RealmSessionProvider, private val timelineInput: TimelineInput, - private val taskExecutor: TaskExecutor, private val contextOfEventTask: GetContextOfEventTask, private val eventDecryptor: TimelineEventDecryptor, private val paginationTask: PaginationTask, @@ -60,7 +47,8 @@ internal class DefaultTimelineService @AssistedInject constructor( private val threadsAwarenessHandler: ThreadsAwarenessHandler, private val lightweightSettingsStorage: LightweightSettingsStorage, private val readReceiptHandler: ReadReceiptHandler, - private val coroutineDispatchers: MatrixCoroutineDispatchers + private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val timelineEventDataSource: TimelineEventDataSource ) : TimelineService { @AssistedFactory @@ -88,27 +76,15 @@ internal class DefaultTimelineService @AssistedInject constructor( ) } - override fun getTimeLineEvent(eventId: String): TimelineEvent? { - return realmSessionProvider.withRealm { realm -> - TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let { - timelineEventMapper.map(it) - } - } + override fun getTimelineEvent(eventId: String): TimelineEvent? { + return timelineEventDataSource.getTimelineEvent(roomId, eventId) } - override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> { - return LiveTimelineEvent(monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId) + override fun getTimelineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> { + return timelineEventDataSource.getTimelineEventLive(roomId, eventId) } override fun getAttachmentMessages(): List<TimelineEvent> { - // TODO pretty bad query.. maybe we should denormalize clear type in base? - return realmSessionProvider.withRealm { realm -> - realm.where<TimelineEventEntity>() - .equalTo(TimelineEventEntityFields.ROOM_ID, roomId) - .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) - .findAll() - ?.mapNotNull { timelineEventMapper.map(it).takeIf { it.root.isImageMessage() || it.root.isVideoMessage() } } - .orEmpty() - } + return timelineEventDataSource.getAttachmentMessages(roomId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/SendingEventsDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/SendingEventsDataSource.kt index a98de1c595ad3df8508e5e15d6f8adfeec323cbd..1262c09d974b1667baaa1c0291e59596857a5b3c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/SendingEventsDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/SendingEventsDataSource.kt @@ -46,7 +46,7 @@ internal class RealmSendingEventsDataSource( private val sendingTimelineEventsListener = RealmChangeListener<RealmList<TimelineEventEntity>> { events -> uiEchoManager.onSentEventsInDatabase(events.map { it.eventId }) - frozenSendingTimelineEvents = sendingTimelineEvents?.freeze() + updateFrozenResults(events) onEventsUpdated(false) } @@ -59,21 +59,32 @@ internal class RealmSendingEventsDataSource( override fun stop() { sendingTimelineEvents?.removeChangeListener(sendingTimelineEventsListener) + updateFrozenResults(null) sendingTimelineEvents = null roomEntity = null } + private fun updateFrozenResults(sendingEvents: RealmList<TimelineEventEntity>?) { + // Makes sure to close the previous frozen realm + if (frozenSendingTimelineEvents?.isValid == true) { + frozenSendingTimelineEvents?.realm?.close() + } + frozenSendingTimelineEvents = sendingEvents?.freeze() + } + override fun buildSendingEvents(): List<TimelineEvent> { val builtSendingEvents = mutableListOf<TimelineEvent>() uiEchoManager.getInMemorySendingEvents() .addWithUiEcho(builtSendingEvents) - frozenSendingTimelineEvents - ?.filter { timelineEvent -> - builtSendingEvents.none { it.eventId == timelineEvent.eventId } - } - ?.map { - timelineEventMapper.map(it) - }?.addWithUiEcho(builtSendingEvents) + if (frozenSendingTimelineEvents?.isValid == true) { + frozenSendingTimelineEvents + ?.filter { timelineEvent -> + builtSendingEvents.none { it.eventId == timelineEvent.eventId } + } + ?.map { + timelineEventMapper.map(it) + }?.addWithUiEcho(builtSendingEvents) + } return builtSendingEvents } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt new file mode 100644 index 0000000000000000000000000000000000000000..638866a46efbf7865c45387301dbc5dc1fd7efec --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.timeline + +import androidx.lifecycle.LiveData +import com.zhuinden.monarchy.Monarchy +import io.realm.Sort +import io.realm.kotlin.where +import org.matrix.android.sdk.api.session.events.model.isImageMessage +import org.matrix.android.sdk.api.session.events.model.isVideoMessage +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.database.RealmSessionProvider +import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper +import org.matrix.android.sdk.internal.database.model.TimelineEventEntity +import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields +import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.task.TaskExecutor +import javax.inject.Inject + +internal class TimelineEventDataSource @Inject constructor(private val realmSessionProvider: RealmSessionProvider, + private val timelineEventMapper: TimelineEventMapper, + private val taskExecutor: TaskExecutor, + @SessionDatabase private val monarchy: Monarchy) { + + fun getTimelineEvent(roomId: String, eventId: String): TimelineEvent? { + return realmSessionProvider.withRealm { realm -> + TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let { + timelineEventMapper.map(it) + } + } + } + + fun getTimelineEventLive(roomId: String, eventId: String): LiveData<Optional<TimelineEvent>> { + return LiveTimelineEvent(monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId) + } + + fun getAttachmentMessages(roomId: String): List<TimelineEvent> { + // TODO pretty bad query.. maybe we should denormalize clear type in base? + return realmSessionProvider.withRealm { realm -> + realm.where<TimelineEventEntity>() + .equalTo(TimelineEventEntityFields.ROOM_ID, roomId) + .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) + .findAll() + ?.mapNotNull { timelineEventMapper.map(it).takeIf { it.root.isImageMessage() || it.root.isVideoMessage() } } + .orEmpty() + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt index 7ac34e80e994a06d664bc6603daaca7f255c9670..e5213c4696f60653756e3ac76ceeca9188604125 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.signout import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.cleanup.CleanupSession @@ -65,7 +66,13 @@ internal class DefaultSignOutTask @Inject constructor( // Logout from identity server if any runCatching { identityDisconnectTask.execute(Unit) } - .onFailure { Timber.w(it, "Unable to disconnect identity server") } + .onFailure { + if (it is IdentityServiceError.NoIdentityServerConfigured) { + Timber.i("No identity server configured to disconnect") + } else { + Timber.w(it, "Unable to disconnect identity server") + } + } Timber.d("SignOut: cleanup session...") cleanupSession.cleanup() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index 1ee62ad774eccaa983f57ce49be68236ea26d2e6..b4da1a02cd918066ac3d32bcb73b18da45ad9bba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -147,7 +147,7 @@ internal class DefaultSyncTask @Inject constructor( } defaultSyncStatusService.endAll() } else { - Timber.tag(loggerTag.value).d("Start incremental sync request") + Timber.tag(loggerTag.value).d("Start incremental sync request with since token $token") defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncIdle) val syncResponse = try { executeRequest(globalErrorReceiver) { @@ -163,7 +163,10 @@ internal class DefaultSyncTask @Inject constructor( } val nbRooms = syncResponse.rooms?.invite.orEmpty().size + syncResponse.rooms?.join.orEmpty().size + syncResponse.rooms?.leave.orEmpty().size val nbToDevice = syncResponse.toDevice?.events.orEmpty().size - Timber.tag(loggerTag.value).d("Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s)") + val nextBatch = syncResponse.nextBatch + Timber.tag(loggerTag.value).d( + "Incremental sync request parsing, $nbRooms room(s) $nbToDevice toDevice(s). Got nextBatch: $nextBatch" + ) defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncParsing( rooms = nbRooms, toDevice = nbToDevice 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 423a4e553f5236be42df039a3db3f81c246b9083..c67c0e350e09ee2e8bc975b517f6ee2c4df9b82c 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 @@ -27,7 +27,6 @@ import org.matrix.android.sdk.internal.di.WorkManagerProvider 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 -import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory @@ -58,7 +57,6 @@ internal class SyncWorker(context: Context, workerParameters: WorkerParameters, ) : SessionWorkerParams @Inject lateinit var syncTask: SyncTask - @Inject lateinit var taskExecutor: TaskExecutor @Inject lateinit var workManagerProvider: WorkManagerProvider override fun injectWith(injector: SessionComponent) { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushrulesConditionTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRulesConditionTest.kt similarity index 100% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushrulesConditionTest.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRulesConditionTest.kt