diff --git a/CHANGES.md b/CHANGES.md index a85c2ff6..08566926 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,8 @@ Improvements: - UI for pending edits (#193) - UX image preview screen transition (#393) - Basic support for resending failed messages (retry/remove) + - Enable proper cancellation of suspending functions (including db transaction) + - Enhances network connectivity checks in SDK Other changes: - @@ -17,6 +19,7 @@ Bugfix: - Close detail room screen when the room is left with another client (#256) - Clear notification for a room left on another client - Fix messages with empty `in_reply_to` not rendering (#447) + - Fix clear cache (#408) and Logout (#205 ) Translations: - diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt index 5c6e7498..58c015df 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackCompletable.kt @@ -28,7 +28,7 @@ internal class MatrixCallbackCompletable(private val completableEmitter: Comp } override fun onFailure(failure: Throwable) { - completableEmitter.onError(failure) + completableEmitter.tryOnError(failure) } } diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt index 05756d49..8d554df2 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/MatrixCallbackSingle.kt @@ -27,7 +27,7 @@ internal class MatrixCallbackSingle(private val singleEmitter: SingleEmitter< } override fun onFailure(failure: Throwable) { - singleEmitter.onError(failure) + singleEmitter.tryOnError(failure) } } diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index e62b7df5..201622b3 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -41,7 +41,7 @@ class RxRoom(private val room: Room) { return room.liveTimeLineEvent(eventId).asObservable() } - fun loadRoomMembersIfNeeded(): Single = Single.create { + fun loadRoomMembersIfNeeded(): Single = Single.create { room.loadRoomMembersIfNeeded(MatrixCallbackSingle(it)).toSingle(it) } diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index b62b3fea..1755fb50 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -110,7 +110,7 @@ dependencies { implementation 'com.squareup.retrofit2:converter-moshi:2.4.0' implementation 'com.squareup.okhttp3:okhttp:3.14.1' implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' - implementation 'com.novoda:merlin:1.1.6' + implementation 'com.novoda:merlin:1.2.0' implementation "com.squareup.moshi:moshi-adapters:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" @@ -126,9 +126,6 @@ dependencies { // FP implementation "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-instances-core:$arrow_version" - implementation "io.arrow-kt:arrow-effects:$arrow_version" - implementation "io.arrow-kt:arrow-effects-instances:$arrow_version" - implementation "io.arrow-kt:arrow-integration-retrofit-adapter:$arrow_version" // olm lib is now hosted by jitpack: https://jitpack.io/#org.matrix.gitlab.matrix-org/olm implementation 'org.matrix.gitlab.matrix-org:olm:3.1.2' diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/LiveDataTestObserver.java b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/LiveDataTestObserver.java index 512d9d0e..10d25135 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/LiveDataTestObserver.java +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/LiveDataTestObserver.java @@ -16,10 +16,10 @@ package im.vector.matrix.android; +import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; -import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt index be351fb0..c6da3c46 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/ChunkEntityTest.kt @@ -19,11 +19,7 @@ package im.vector.matrix.android.session.room.timeline import androidx.test.ext.junit.runners.AndroidJUnit4 import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.InstrumentedTest -import im.vector.matrix.android.internal.database.helper.add -import im.vector.matrix.android.internal.database.helper.addAll -import im.vector.matrix.android.internal.database.helper.isUnlinked -import im.vector.matrix.android.internal.database.helper.lastStateIndex -import im.vector.matrix.android.internal.database.helper.merge +import im.vector.matrix.android.internal.database.helper.* import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import im.vector.matrix.android.session.room.timeline.RoomDataHelper.createFakeListOfEvents diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt index f77c9b1a..48f22392 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakeGetContextOfEventTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor @@ -24,7 +23,7 @@ import kotlin.random.Random internal class FakeGetContextOfEventTask constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : GetContextOfEventTask { - override suspend fun execute(params: GetContextOfEventTask.Params): Try { + override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result { val fakeEvents = RoomDataHelper.createFakeListOfEvents(30) val tokenChunkEvent = FakeTokenChunkEvent( Random.nextLong(System.currentTimeMillis()).toString(), diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt index bf163401..2f7f63d7 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/FakePaginationTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import javax.inject.Inject @@ -24,7 +23,7 @@ import kotlin.random.Random internal class FakePaginationTask @Inject constructor(private val tokenChunkEventPersistor: TokenChunkEventPersistor) : PaginationTask { - override suspend fun execute(params: PaginationTask.Params): Try { + override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result { val fakeEvents = RoomDataHelper.createFakeListOfEvents(30) val tokenChunkEvent = FakeTokenChunkEvent(params.from, Random.nextLong(System.currentTimeMillis()).toString(), fakeEvents) return tokenChunkEventPersistor.insertInDb(tokenChunkEvent, params.roomId, params.direction) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt index bbef1d36..58cd76a0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/MatrixPermalinkSpan.kt @@ -18,6 +18,7 @@ package im.vector.matrix.android.api.permalinks import android.text.style.ClickableSpan import android.view.View +import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan.Callback /** * This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt index 8e933426..0397b514 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt @@ -26,14 +26,12 @@ import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.NewSessionListener -import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody -import java.io.File interface CryptoService { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt index 8b56406c..9dba26cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/members/MembershipService.kt @@ -30,7 +30,7 @@ interface MembershipService { * This methods load all room members if it was done yet. * @return a [Cancelable] */ - fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable + fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable /** * Return the roomMember with userId or null. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt index ef2769d9..acab75df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.isReply -import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt index f5689b4d..145ddb66 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/CancelableBag.kt @@ -19,5 +19,6 @@ package im.vector.matrix.android.api.util class CancelableBag : Cancelable, MutableList by ArrayList() { override fun cancel() { forEach { it.cancel() } + clear() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt index 81e64f5b..adea7c89 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/DefaultAuthenticator.kt @@ -68,7 +68,9 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated callback: MatrixCallback): Cancelable { val job = GlobalScope.launch(coroutineDispatchers.main) { - val sessionOrFailure = authenticate(homeServerConnectionConfig, login, password) + val sessionOrFailure = runCatching { + authenticate(homeServerConnectionConfig, login, password) + } sessionOrFailure.foldToCallback(callback) } return CancelableCoroutine(job) @@ -85,16 +87,12 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated } else { PasswordLoginParams.userIdentifier(login, password, "Mobile") } - executeRequest { + val credentials = executeRequest { apiCall = authAPI.login(loginParams) - }.map { - val sessionParams = SessionParams(it, homeServerConnectionConfig) - sessionParamsStore.save(sessionParams) - sessionParams - }.map { - sessionManager.getOrCreateSession(it) } - + val sessionParams = SessionParams(credentials, homeServerConnectionConfig) + sessionParamsStore.save(sessionParams) + sessionManager.getOrCreateSession(sessionParams) } private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt index 64303ea0..36a03ab1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt @@ -20,7 +20,6 @@ import com.squareup.moshi.Moshi import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.SessionParams -import im.vector.matrix.android.internal.di.MatrixScope import javax.inject.Inject internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt index 3fadb09b..96d337bd 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/CryptoManager.kt @@ -72,7 +72,6 @@ import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.task.toConfigurableTask import im.vector.matrix.android.internal.util.JsonCanonicalizer import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.fetchCopied @@ -167,22 +166,25 @@ internal class CryptoManager @Inject constructor( override fun setDeviceName(deviceId: String, deviceName: String, callback: MatrixCallback) { setDeviceNameTask - .configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) - .dispatchTo(callback) + .configureWith(SetDeviceNameTask.Params(deviceId, deviceName)) { + this.callback = callback + } .executeBy(taskExecutor) } override fun deleteDevice(deviceId: String, callback: MatrixCallback) { deleteDeviceTask - .configureWith(DeleteDeviceTask.Params(deviceId)) - .dispatchTo(callback) + .configureWith(DeleteDeviceTask.Params(deviceId)) { + this.callback = callback + } .executeBy(taskExecutor) } override fun deleteDeviceWithUserPassword(deviceId: String, authSession: String?, password: String, callback: MatrixCallback) { deleteDeviceWithUserPasswordTask - .configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) - .dispatchTo(callback) + .configureWith(DeleteDeviceWithUserPasswordTask.Params(deviceId, authSession, password)) { + this.callback = callback + } .executeBy(taskExecutor) } @@ -196,8 +198,9 @@ internal class CryptoManager @Inject constructor( override fun getDevicesList(callback: MatrixCallback) { getDevicesTask - .toConfigurableTask() - .dispatchTo(callback) + .configureWith { + this.callback = callback + } .executeBy(taskExecutor) } @@ -254,35 +257,36 @@ internal class CryptoManager @Inject constructor( private suspend fun internalStart(isInitialSync: Boolean) { // Open the store cryptoStore.open() - uploadDeviceKeys() - .flatMap { oneTimeKeysUploader.maybeUploadOneTimeKeys() } - .fold( - { - Timber.e("Start failed: $it") - delay(1000) - isStarting.set(false) - internalStart(isInitialSync) - }, - { - outgoingRoomKeyRequestManager.start() - keysBackup.checkAndStartKeysBackup() - if (isInitialSync) { - // refresh the devices list for each known room members - deviceListManager.invalidateAllDeviceLists() - deviceListManager.refreshOutdatedDeviceLists() - } else { - incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() - } - isStarting.set(false) - isStarted.set(true) - } - ) + runCatching { + uploadDeviceKeys() + oneTimeKeysUploader.maybeUploadOneTimeKeys() + outgoingRoomKeyRequestManager.start() + keysBackup.checkAndStartKeysBackup() + if (isInitialSync) { + // refresh the devices list for each known room members + deviceListManager.invalidateAllDeviceLists() + deviceListManager.refreshOutdatedDeviceLists() + } else { + incomingRoomKeyRequestManager.processReceivedRoomKeyRequests() + } + }.fold( + { + isStarting.set(false) + isStarted.set(true) + }, + { + Timber.e("Start failed: $it") + delay(1000) + isStarting.set(false) + internalStart(isInitialSync) + } + ) } /** * Close the crypto */ - fun close() { + fun close() = runBlocking(coroutineDispatchers.crypto) { olmDevice.release() cryptoStore.close() outgoingRoomKeyRequestManager.stop() @@ -556,13 +560,16 @@ internal class CryptoManager @Inject constructor( if (safeAlgorithm != null) { val t0 = System.currentTimeMillis() Timber.v("## encryptEventContent() starts") - safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) + runCatching { + safeAlgorithm.encryptEventContent(eventContent, eventType, userIds) + } .fold( - { callback.onFailure(it) }, { Timber.v("## encryptEventContent() : succeeds after ${System.currentTimeMillis() - t0} ms") callback.onSuccess(MXEncryptEventContentResult(it, EventType.ENCRYPTED)) - } + }, + { callback.onFailure(it) } + ) } else { val algorithm = getEncryptionAlgorithm(roomId) @@ -584,10 +591,7 @@ internal class CryptoManager @Inject constructor( @Throws(MXCryptoError::class) override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { return runBlocking { - internalDecryptEvent(event, timeline).fold( - { throw it }, - { it } - ) + internalDecryptEvent(event, timeline) } } @@ -600,8 +604,10 @@ internal class CryptoManager @Inject constructor( */ override fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback) { GlobalScope.launch { - val result = withContext(coroutineDispatchers.crypto) { - internalDecryptEvent(event, timeline) + val result = runCatching { + withContext(coroutineDispatchers.crypto) { + internalDecryptEvent(event, timeline) + } } result.foldToCallback(callback) } @@ -612,22 +618,22 @@ internal class CryptoManager @Inject constructor( * * @param event the raw event. * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. - * @return the MXEventDecryptionResult data, or null in case of error wrapped into [Try] + * @return the MXEventDecryptionResult data, or null in case of error */ - private suspend fun internalDecryptEvent(event: Event, timeline: String): Try { + private suspend fun internalDecryptEvent(event: Event, timeline: String): MXEventDecryptionResult { val eventContent = event.content - return if (eventContent == null) { + if (eventContent == null) { Timber.e("## decryptEvent : empty event content") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) } else { val algorithm = eventContent["algorithm"]?.toString() val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm) if (alg == null) { val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm) Timber.e("## decryptEvent() : $reason") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason) } else { - alg.decryptEvent(event, timeline) + return alg.decryptEvent(event, timeline) } } } @@ -689,12 +695,13 @@ internal class CryptoManager @Inject constructor( private fun onRoomEncryptionEvent(roomId: String, event: Event) { GlobalScope.launch(coroutineDispatchers.crypto) { val params = LoadRoomMembersTask.Params(roomId) - loadRoomMembersTask - .execute(params) - .map { _ -> - val userIds = getRoomUserIds(roomId) - setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds) - } + try { + loadRoomMembersTask.execute(params) + val userIds = getRoomUserIds(roomId) + setEncryptionInRoom(roomId, event.content?.get("algorithm")?.toString(), true, userIds) + } catch (throwable: Throwable) { + Timber.e(throwable) + } } } @@ -761,7 +768,7 @@ internal class CryptoManager @Inject constructor( /** * Upload my user's device keys. */ - private suspend fun uploadDeviceKeys(): Try { + private suspend fun uploadDeviceKeys(): KeysUploadResponse { // Prepare the device keys data to send // Sign it val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, getMyDevice().signalableJSONDictionary()) @@ -868,10 +875,8 @@ internal class CryptoManager @Inject constructor( fun checkUnknownDevices(userIds: List, callback: MatrixCallback) { // force the refresh to ensure that the devices list is up-to-date GlobalScope.launch(coroutineDispatchers.crypto) { - deviceListManager - .downloadKeys(userIds, true) + runCatching { deviceListManager.downloadKeys(userIds, true) } .fold( - { callback.onFailure(it) }, { val unknownDevices = getUnknownDevices(it) if (unknownDevices.map.isEmpty()) { @@ -880,7 +885,8 @@ internal class CryptoManager @Inject constructor( // trigger an an unknown devices exception callback.onFailure(Failure.CryptoError(MXCryptoError.UnknownDevice(unknownDevices))) } - } + }, + { callback.onFailure(it) } ) } } @@ -1035,16 +1041,17 @@ internal class CryptoManager @Inject constructor( override fun downloadKeys(userIds: List, forceDownload: Boolean, callback: MatrixCallback>) { GlobalScope.launch(coroutineDispatchers.crypto) { - deviceListManager - .downloadKeys(userIds, forceDownload) - .foldToCallback(callback) + runCatching { + deviceListManager.downloadKeys(userIds, forceDownload) + }.foldToCallback(callback) } } override fun clearCryptoCache(callback: MatrixCallback) { clearCryptoDataTask - .toConfigurableTask() - .dispatchTo(callback) + .configureWith { + this.callback = callback + } .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt index e8f65b9d..e7cfdfe1 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DeviceListManager.kt @@ -18,14 +18,12 @@ package im.vector.matrix.android.internal.crypto import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.DownloadKeysForUsersTask -import im.vector.matrix.android.internal.extensions.onError import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.sync.SyncTokenStore import timber.log.Timber @@ -237,7 +235,7 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * @param forceDownload Always download the keys even if cached. * @param callback the asynchronous callback */ - suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): Try> { + suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): MXUsersDevicesMap { Timber.v("## downloadKeys() : forceDownload $forceDownload : $userIds") // Map from userId -> deviceId -> MXDeviceInfo val stored = MXUsersDevicesMap() @@ -268,16 +266,15 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } return if (downloadUsers.isEmpty()) { Timber.v("## downloadKeys() : no new user device") - Try.just(stored) + stored } else { Timber.v("## downloadKeys() : starts") val t0 = System.currentTimeMillis() - doKeyDownloadForUsers(downloadUsers) - .map { - Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms") - it.addEntriesFromMap(stored) - it - } + val result = doKeyDownloadForUsers(downloadUsers) + Timber.v("## downloadKeys() : doKeyDownloadForUsers succeeds after " + (System.currentTimeMillis() - t0) + " ms") + result.also { + it.addEntriesFromMap(stored) + } } } @@ -286,60 +283,60 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM * * @param downloadUsers the user ids list */ - private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList): Try> { + private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList): MXUsersDevicesMap { Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers") // get the user ids which did not already trigger a keys download val filteredUsers = downloadUsers.filter { MatrixPatterns.isUserId(it) } if (filteredUsers.isEmpty()) { // trigger nothing - return Try.just(MXUsersDevicesMap()) + return MXUsersDevicesMap() } val params = DownloadKeysForUsersTask.Params(filteredUsers, syncTokenStore.getLastToken()) - return downloadKeysForUsersTask.execute(params) - .map { response -> - Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users") - for (userId in filteredUsers) { - val devices = response.deviceKeys?.get(userId) - Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices") - if (devices != null) { - val mutableDevices = HashMap(devices) - val deviceIds = ArrayList(mutableDevices.keys) - for (deviceId in deviceIds) { - // Get the potential previously store device keys for this device - val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId) - val deviceInfo = mutableDevices[deviceId] + val response = try { + downloadKeysForUsersTask.execute(params) + } catch (throwable: Throwable) { + Timber.e(throwable, "##doKeyDownloadForUsers(): error") + onKeysDownloadFailed(filteredUsers) + throw throwable + } + Timber.v("## doKeyDownloadForUsers() : Got keys for " + filteredUsers.size + " users") + for (userId in filteredUsers) { + val devices = response.deviceKeys?.get(userId) + Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $devices") + if (devices != null) { + val mutableDevices = HashMap(devices) + val deviceIds = ArrayList(mutableDevices.keys) + for (deviceId in deviceIds) { + // Get the potential previously store device keys for this device + val previouslyStoredDeviceKeys = cryptoStore.getUserDevice(deviceId, userId) + val deviceInfo = mutableDevices[deviceId] - // in some race conditions (like unit tests) - // the self device must be seen as verified - if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) { - deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED - } - // Validate received keys - if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) { - // New device keys are not valid. Do not store them - mutableDevices.remove(deviceId) - if (null != previouslyStoredDeviceKeys) { - // But keep old validated ones if any - mutableDevices[deviceId] = previouslyStoredDeviceKeys - } - } else if (null != previouslyStoredDeviceKeys) { - // The verified status is not sync'ed with hs. - // This is a client side information, valid only for this client. - // So, transfer its previous value - mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified - } - } - // Update the store - // Note that devices which aren't in the response will be removed from the stores - cryptoStore.storeUserDevices(userId, mutableDevices) - } + // in some race conditions (like unit tests) + // the self device must be seen as verified + if (TextUtils.equals(deviceInfo!!.deviceId, credentials.deviceId) && TextUtils.equals(userId, credentials.userId)) { + deviceInfo.verified = MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED + } + // Validate received keys + if (!validateDeviceKeys(deviceInfo, userId, deviceId, previouslyStoredDeviceKeys)) { + // New device keys are not valid. Do not store them + mutableDevices.remove(deviceId) + if (null != previouslyStoredDeviceKeys) { + // But keep old validated ones if any + mutableDevices[deviceId] = previouslyStoredDeviceKeys + } + } else if (null != previouslyStoredDeviceKeys) { + // The verified status is not sync'ed with hs. + // This is a client side information, valid only for this client. + // So, transfer its previous value + mutableDevices[deviceId]!!.verified = previouslyStoredDeviceKeys.verified } - onKeysDownloadSucceed(filteredUsers, response.failures) - } - .onError { - Timber.e(it, "##doKeyDownloadForUsers(): error") - onKeysDownloadFailed(filteredUsers) } + // Update the store + // Note that devices which aren't in the response will be removed from the stores + cryptoStore.storeUserDevices(userId, mutableDevices) + } + } + return onKeysDownloadSucceed(filteredUsers, response.failures) } /** @@ -465,15 +462,16 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses) - doKeyDownloadForUsers(users) - .fold( - { - Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users") - }, - { - Timber.v("## refreshOutdatedDeviceLists() : done") - } - ) + runCatching { + doKeyDownloadForUsers(users) + }.fold( + { + Timber.v("## refreshOutdatedDeviceLists() : done") + }, + { + Timber.e(it, "## refreshOutdatedDeviceLists() : ERROR updating device keys for users $users") + } + ) } companion object { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt index 8f0bfcd4..d9387ad3 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/MXOlmDevice.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.util.JSON_DICT_PARAMETERIZED_TYPE import im.vector.matrix.android.api.util.JsonDict @@ -506,25 +505,25 @@ internal class MXOlmDevice @Inject constructor( keysClaimed: Map, exportFormat: Boolean): Boolean { val session = OlmInboundGroupSessionWrapper(sessionKey, exportFormat) + runCatching { getInboundGroupSession(sessionId, senderKey, roomId) } + .fold( + { + // If we already have this session, consider updating it + Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") - getInboundGroupSession(sessionId, senderKey, roomId).fold( - { - // Nothing to do in case of error - }, - { - // If we already have this session, consider updating it - Timber.e("## addInboundGroupSession() : Update for megolm session $senderKey/$sessionId") + val existingFirstKnown = it.firstKnownIndex!! + val newKnownFirstIndex = session.firstKnownIndex - val existingFirstKnown = it.firstKnownIndex!! - val newKnownFirstIndex = session.firstKnownIndex - - //If our existing session is better we keep it - if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { - session.olmInboundGroupSession?.releaseSession() - return false - } - } - ) + //If our existing session is better we keep it + if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { + session.olmInboundGroupSession?.releaseSession() + return false + } + }, + { + // Nothing to do in case of error + } + ) // sanity check if (null == session.olmInboundGroupSession) { @@ -595,12 +594,8 @@ internal class MXOlmDevice @Inject constructor( continue } - getInboundGroupSession(sessionId, senderKey, roomId) + runCatching { getInboundGroupSession(sessionId, senderKey, roomId) } .fold( - { - // Session does not already exist, add it - sessions.add(session) - }, { // If we already have this session, consider updating it Timber.e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") @@ -613,7 +608,12 @@ internal class MXOlmDevice @Inject constructor( sessions.add(session) } Unit + }, + { + // Session does not already exist, add it + sessions.add(session) } + ) } @@ -648,61 +648,57 @@ internal class MXOlmDevice @Inject constructor( roomId: String, timeline: String?, sessionId: String, - senderKey: String): Try { - return getInboundGroupSession(sessionId, senderKey, roomId) - .flatMap { session -> - // Check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (roomId == session.roomId) { - var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null - try { - decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) - } catch (e: OlmException) { - Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") - return@flatMap Try.Failure(MXCryptoError.OlmError(e)) - } + senderKey: String): OlmDecryptionResult { + val session = getInboundGroupSession(sessionId, senderKey, roomId) + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (roomId == session.roomId) { + var decryptResult: OlmInboundGroupSession.DecryptMessageResult? = null + try { + decryptResult = session.olmInboundGroupSession!!.decryptMessage(body) + } catch (e: OlmException) { + Timber.e(e, "## decryptGroupMessage () : decryptMessage failed") + throw MXCryptoError.OlmError(e) + } - if (null != timeline) { - if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { - inboundGroupSessionMessageIndexes[timeline] = HashMap() - } - - val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex - - if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { - val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) - Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)) - } - - inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) - } - - store.storeInboundGroupSessions(listOf(session)) - val payload = try { - val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) - val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) - adapter.fromJson(payloadString) - } catch (e: Exception) { - Timber.e("## decryptGroupMessage() : fails to parse the payload") - return@flatMap Try.Failure( - MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) - } - - return@flatMap Try.just( - OlmDecryptionResult( - payload, - session.keysClaimed, - senderKey, - session.forwardingCurve25519KeyChain - ) - ) - } else { - val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) - Timber.e("## decryptGroupMessage() : $reason") - return@flatMap Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)) - } + if (null != timeline) { + if (!inboundGroupSessionMessageIndexes.containsKey(timeline)) { + inboundGroupSessionMessageIndexes[timeline] = HashMap() } + + val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex + + if (inboundGroupSessionMessageIndexes[timeline]?.get(messageIndexKey) != null) { + val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex) + Timber.e("## decryptGroupMessage() : $reason") + throw MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason) + } + + inboundGroupSessionMessageIndexes[timeline]!!.put(messageIndexKey, true) + } + + store.storeInboundGroupSessions(listOf(session)) + val payload = try { + val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) + val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) + adapter.fromJson(payloadString) + } catch (e: Exception) { + Timber.e("## decryptGroupMessage() : fails to parse the payload") + throw + MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) + } + + return OlmDecryptionResult( + payload, + session.keysClaimed, + senderKey, + session.forwardingCurve25519KeyChain + ) + } else { + val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) + Timber.e("## decryptGroupMessage() : $reason") + throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason) + } } /** @@ -766,26 +762,26 @@ internal class MXOlmDevice @Inject constructor( * @param senderKey the base64-encoded curve25519 key of the sender. * @return the inbound group session. */ - fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): Try { + fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): OlmInboundGroupSessionWrapper { if (sessionId.isNullOrBlank() || senderKey.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON) } val session = store.getInboundGroupSession(sessionId, senderKey) - return if (null != session) { + if (session != null) { // Check that the room id matches the original one for the session. This stops // the HS pretending a message was targeting a different room. if (!TextUtils.equals(roomId, session.roomId)) { val errorDescription = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, session.roomId) Timber.e("## getInboundGroupSession() : $errorDescription") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, errorDescription) } else { - Try.just(session) + return session } } else { Timber.e("## getInboundGroupSession() : Cannot retrieve inbound group session $sessionId") - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID, MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_REASON) } } @@ -798,6 +794,6 @@ internal class MXOlmDevice @Inject constructor( * @return true if the unbound session keys are known. */ fun hasInboundSessionKeys(roomId: String, senderKey: String, sessionId: String): Boolean { - return getInboundGroupSession(sessionId, senderKey, roomId).isSuccess() + return runCatching { getInboundGroupSession(sessionId, senderKey, roomId) }.isSuccess } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt index 7b8e0199..29e20290 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/ObjectSigner.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.internal.session.SessionScope import java.util.* import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt index a81cbfcf..f0d7662f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OneTimeKeysUploader.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.crypto -import arrow.core.Try -import arrow.instances.`try`.applicativeError.handleError import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse @@ -59,13 +57,13 @@ internal class OneTimeKeysUploader @Inject constructor( /** * Check if the OTK must be uploaded. */ - suspend fun maybeUploadOneTimeKeys(): Try { + suspend fun maybeUploadOneTimeKeys() { if (oneTimeKeyCheckInProgress) { - return Try.just(Unit) + return } if (System.currentTimeMillis() - lastOneTimeKeyCheck < ONE_TIME_KEY_UPLOAD_PERIOD) { // we've done a key upload recently. - return Try.just(Unit) + return } lastOneTimeKeyCheck = System.currentTimeMillis() @@ -81,41 +79,31 @@ internal class OneTimeKeysUploader @Inject constructor( // discard the oldest private keys first. This will eventually clean // out stale private keys that won't receive a message. val keyLimit = Math.floor(maxOneTimeKeys / 2.0).toInt() - val result = if (oneTimeKeyCount != null) { + if (oneTimeKeyCount != null) { uploadOTK(oneTimeKeyCount!!, keyLimit) } else { // ask the server how many keys we have val uploadKeysParams = UploadKeysTask.Params(null, null, credentials.deviceId!!) - uploadKeysTask.execute(uploadKeysParams) - .flatMap { - // We need to keep a pool of one time public keys on the server so that - // other devices can start conversations with us. But we can only store - // a finite number of private keys in the olm Account object. - // To complicate things further then can be a delay between a device - // claiming a public one time key from the server and it sending us a - // message. We need to keep the corresponding private key locally until - // we receive the message. - // But that message might never arrive leaving us stuck with duff - // private keys clogging up our local storage. - // So we need some kind of engineering compromise to balance all of - // these factors. - // TODO Why we do not set oneTimeKeyCount here? - // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) - val keyCount = it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) - uploadOTK(keyCount, keyLimit) - } + val response = uploadKeysTask.execute(uploadKeysParams) + // We need to keep a pool of one time public keys on the server so that + // other devices can start conversations with us. But we can only store + // a finite number of private keys in the olm Account object. + // To complicate things further then can be a delay between a device + // claiming a public one time key from the server and it sending us a + // message. We need to keep the corresponding private key locally until + // we receive the message. + // But that message might never arrive leaving us stuck with duff + // private keys clogging up our local storage. + // So we need some kind of engineering compromise to balance all of + // these factors. + // TODO Why we do not set oneTimeKeyCount here? + // TODO This is not needed anymore, see https://github.com/matrix-org/matrix-js-sdk/pull/493 (TODO on iOS also) + val keyCount = response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE) + uploadOTK(keyCount, keyLimit) } - return result - .map { - Timber.v("## uploadKeys() : success") - oneTimeKeyCount = null - oneTimeKeyCheckInProgress = false - } - .handleError { - Timber.e(it, "## uploadKeys() : failed") - oneTimeKeyCount = null - oneTimeKeyCheckInProgress = false - } + Timber.v("## uploadKeys() : success") + oneTimeKeyCount = null + oneTimeKeyCheckInProgress = false } /** @@ -124,29 +112,26 @@ internal class OneTimeKeysUploader @Inject constructor( * @param keyCount the key count * @param keyLimit the limit */ - private suspend fun uploadOTK(keyCount: Int, keyLimit: Int): Try { + private suspend fun uploadOTK(keyCount: Int, keyLimit: Int) { if (keyLimit <= keyCount) { // If we don't need to generate any more keys then we are done. - return Try.just(Unit) + return } - val keysThisLoop = Math.min(keyLimit - keyCount, ONE_TIME_KEY_GENERATION_MAX_NUMBER) olmDevice.generateOneTimeKeys(keysThisLoop) - return uploadOneTimeKeys() - .flatMap { - if (it.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { - uploadOTK(it.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) - } else { - Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") - Try.raise(Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519")) - } - } + val response = uploadOneTimeKeys() + if (response.hasOneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE)) { + uploadOTK(response.oneTimeKeyCountsForAlgorithm(MXKey.KEY_SIGNED_CURVE_25519_TYPE), keyLimit) + } else { + Timber.e("## uploadLoop() : response for uploading keys does not contain one_time_key_counts.signed_curve25519") + throw Exception("response for uploading keys does not contain one_time_key_counts.signed_curve25519") + } } /** * Upload my user's one time keys. */ - private suspend fun uploadOneTimeKeys(): Try { + private suspend fun uploadOneTimeKeys(): KeysUploadResponse { val oneTimeKeys = olmDevice.getOneTimeKeys() val oneTimeJson = HashMap() @@ -169,13 +154,10 @@ internal class OneTimeKeysUploader @Inject constructor( // For now, we set the device id explicitly, as we may not be using the // same one as used in login. val uploadParams = UploadKeysTask.Params(null, oneTimeJson, credentials.deviceId!!) - return uploadKeysTask - .execute(uploadParams) - .map { - lastPublishedOneTimeKeys = oneTimeKeys - olmDevice.markKeysAsPublished() - it - } + val response = uploadKeysTask.execute(uploadParams) + lastPublishedOneTimeKeys = oneTimeKeys + olmDevice.markKeysAsPublished() + return response } companion object { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt index a13ae75b..c0702f70 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/OutgoingRoomKeyRequestManager.kt @@ -299,10 +299,12 @@ internal class OutgoingRoomKeyRequestManager @Inject constructor( // TODO Change this two hard coded key to something better contentMap.setObject(recipient["userId"], recipient["deviceId"], message) } - sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) - .dispatchTo(callback) - .executeOn(TaskThread.CALLER) - .callbackOn(TaskThread.CALLER) + sendToDeviceTask + .configureWith(SendToDeviceTask.Params(EventType.ROOM_KEY_REQUEST, contentMap, transactionId)) { + this.callback = callback + this.callbackThread = TaskThread.CALLER + this.executionThread = TaskThread.CALLER + } .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 4c4ee106..7a56f46b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.actions import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXKey @@ -32,7 +31,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val private val oneTimeKeysForUsersDeviceTask: ClaimOneTimeKeysForUsersDeviceTask) { - suspend fun handle(devicesByUser: Map>): Try> { + suspend fun handle(devicesByUser: Map>): MXUsersDevicesMap { val devicesWithoutSession = ArrayList() val results = MXUsersDevicesMap() @@ -58,7 +57,7 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val } if (devicesWithoutSession.size == 0) { - return Try.just(results) + return results } // Prepare the request for claiming one-time keys @@ -79,39 +78,36 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(private val Timber.v("## claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim") val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim) - return oneTimeKeysForUsersDeviceTask - .execute(claimParams) - .map { - Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $it") - for (userId in userIds) { - val deviceInfos = devicesByUser[userId] - for (deviceInfo in deviceInfos!!) { - var oneTimeKey: MXKey? = null - val deviceIds = it.getUserDeviceIds(userId) - if (null != deviceIds) { - for (deviceId in deviceIds) { - val olmSessionResult = results.getObject(userId, deviceId) - if (olmSessionResult!!.sessionId != null) { - // We already have a result for this device - continue - } - val key = it.getObject(userId, deviceId) - if (key?.type == oneTimeKeyAlgorithm) { - oneTimeKey = key - } - if (oneTimeKey == null) { - Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId) - continue - } - // Update the result for this device in results - olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) - } - } + val oneTimeKeys = oneTimeKeysForUsersDeviceTask.execute(claimParams) + Timber.v("## claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys") + for (userId in userIds) { + val deviceInfos = devicesByUser[userId] + for (deviceInfo in deviceInfos!!) { + var oneTimeKey: MXKey? = null + val deviceIds = oneTimeKeys.getUserDeviceIds(userId) + if (null != deviceIds) { + for (deviceId in deviceIds) { + val olmSessionResult = results.getObject(userId, deviceId) + if (olmSessionResult!!.sessionId != null) { + // We already have a result for this device + continue } + val key = oneTimeKeys.getObject(userId, deviceId) + if (key?.type == oneTimeKeyAlgorithm) { + oneTimeKey = key + } + if (oneTimeKey == null) { + Timber.v("## ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + + " for device " + userId + " : " + deviceId) + continue + } + // Update the result for this device in results + olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo) } - results } + } + } + return results } private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: MXDeviceInfo): String? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index f6817e14..840a66c5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -17,14 +17,11 @@ package im.vector.matrix.android.internal.crypto.actions import android.text.TextUtils -import arrow.core.Try -import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import java.util.* import javax.inject.Inject @@ -37,7 +34,7 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o * Try to make sure we have established olm sessions for the given users. * @param users a list of user ids. */ - suspend fun handle(users: List) : Try> { + suspend fun handle(users: List) : MXUsersDevicesMap { Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users") val devicesByUser = HashMap>() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt index 918bdd73..9d345dfb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -26,7 +26,6 @@ import im.vector.matrix.android.internal.crypto.RoomDecryptorProvider import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt index dfc54ffe..7fc39312 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.actions import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.session.SessionScope import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt index 8714e156..f63eaa93 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXDecrypting.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.algorithms -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult @@ -35,7 +34,7 @@ internal interface IMXDecrypting { * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. * @return the decryption information, or an error */ - suspend fun decryptEvent(event: Event, timeline: String): Try + suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult /** * Handle a key event. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt index 544bbe60..555ce9df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/IMXEncrypting.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.algorithms -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Content /** @@ -31,7 +30,7 @@ internal interface IMXEncrypting { * @param eventContent the content of the event. * @param eventType the type of the event. * @param userIds the room members the event will be sent to. - * @return the encrypted content wrapped by [Try] + * @return the encrypted content */ - suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Try + suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index b30176b2..47bf9956 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event @@ -40,7 +39,6 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber -import kotlin.collections.HashMap internal class MXMegolmDecryption(private val credentials: Credentials, private val olmDevice: MXOlmDevice, @@ -61,30 +59,46 @@ internal class MXMegolmDecryption(private val credentials: Credentials, */ private var pendingEvents: MutableMap>> = HashMap() - override suspend fun decryptEvent(event: Event, timeline: String): Try { + override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { return decryptEvent(event, timeline, true) } - private fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): Try { + private suspend fun decryptEvent(event: Event, timeline: String, requestKeysOnFail: Boolean): MXEventDecryptionResult { if (event.roomId.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) } val encryptedEventContent = event.content.toModel() - ?: return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + ?: throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) if (encryptedEventContent.senderKey.isNullOrBlank() || encryptedEventContent.sessionId.isNullOrBlank() || encryptedEventContent.ciphertext.isNullOrBlank()) { - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) } - return olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext, - event.roomId, - timeline, - encryptedEventContent.sessionId, - encryptedEventContent.senderKey) + return runCatching { + olmDevice.decryptGroupMessage(encryptedEventContent.ciphertext, + event.roomId, + timeline, + encryptedEventContent.sessionId, + encryptedEventContent.senderKey) + } .fold( + { olmDecryptionResult -> + // the decryption succeeds + if (olmDecryptionResult.payload != null) { + MXEventDecryptionResult( + clearEvent = olmDecryptionResult.payload, + senderCurve25519Key = olmDecryptionResult.senderKey, + claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"), + forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain + ?: emptyList() + ) + } else { + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) + } + }, { throwable -> if (throwable is MXCryptoError.OlmError) { // TODO Check the value of .message @@ -98,10 +112,10 @@ internal class MXMegolmDecryption(private val credentials: Credentials, val reason = String.format(MXCryptoError.OLM_REASON, throwable.olmException.message) val detailedReason = String.format(MXCryptoError.DETAILED_OLM_REASON, encryptedEventContent.ciphertext, reason) - Try.Failure(MXCryptoError.Base( + throw MXCryptoError.Base( MXCryptoError.ErrorType.OLM, reason, - detailedReason)) + detailedReason) } if (throwable is MXCryptoError.Base) { if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) { @@ -111,23 +125,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } } } - - Try.Failure(throwable) - }, - { olmDecryptionResult -> - // the decryption succeeds - if (olmDecryptionResult.payload != null) { - Try.just( - MXEventDecryptionResult( - clearEvent = olmDecryptionResult.payload, - senderCurve25519Key = olmDecryptionResult.senderKey, - claimedEd25519Key = olmDecryptionResult.keysClaimed?.get("ed25519"), - forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain ?: emptyList() - ) - ) - } else { - Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)) - } + throw throwable } ) } @@ -311,51 +309,48 @@ internal class MXMegolmDecryption(private val credentials: Credentials, } val userId = request.userId ?: return GlobalScope.launch(coroutineDispatchers.crypto) { - deviceListManager - .downloadKeys(listOf(userId), false) - .flatMap { + runCatching { deviceListManager.downloadKeys(listOf(userId), false) } + .mapCatching { val deviceId = request.deviceId val deviceInfo = cryptoStore.getUserDevice(deviceId ?: "", userId) if (deviceInfo == null) { throw RuntimeException() } else { val devicesByUser = mapOf(userId to listOf(deviceInfo)) - ensureOlmSessionsForDevicesAction - .handle(devicesByUser) - .flatMap { - val body = request.requestBody - val olmSessionResult = it.getObject(userId, deviceId) - if (olmSessionResult?.sessionId == null) { - // no session with this device, probably because there - // were no one-time keys. - Try.just(Unit) - } - Timber.v("## shareKeysWithDevice() : sharing keys for session" + - " ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId") + val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) + val body = request.requestBody + val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) + if (olmSessionResult?.sessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + return@mapCatching + } + Timber.v("## shareKeysWithDevice() : sharing keys for session" + + " ${body?.senderKey}|${body?.sessionId} with device $userId:$deviceId") - val payloadJson = mutableMapOf("type" to EventType.FORWARDED_ROOM_KEY) + val payloadJson = mutableMapOf("type" to EventType.FORWARDED_ROOM_KEY) + runCatching { olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) } + .fold( + { + // TODO + payloadJson["content"] = it.exportKeys() + ?: "" + }, + { + // TODO + } - olmDevice.getInboundGroupSession(body?.sessionId, body?.senderKey, body?.roomId) - .fold( - { - // TODO - }, - { - // TODO - payloadJson["content"] = it.exportKeys() ?: "" - } - ) + ) - val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) - val sendToDeviceMap = MXUsersDevicesMap() - sendToDeviceMap.setObject(userId, deviceId, encodedPayload) - Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId") - val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) - sendToDeviceTask.execute(sendToDeviceParams) - } + val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(userId, deviceId, encodedPayload) + Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) + sendToDeviceTask.execute(sendToDeviceParams) } } } } - } + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt index 43c485df..eb8df7b9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -24,7 +24,6 @@ import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevi import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 165fc736..b465cb6e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Content @@ -69,12 +68,10 @@ internal class MXMegolmEncryption( override suspend fun encryptEventContent(eventContent: Content, eventType: String, - userIds: List): Try { - return getDevicesInRoom(userIds) - .flatMap { ensureOutboundSession(it) } - .flatMap { - encryptContent(it, eventType, eventContent) - } + userIds: List): Content { + val devices = getDevicesInRoom(userIds) + val outboundSession = ensureOutboundSession(devices) + return encryptContent(outboundSession, eventType, eventContent) } /** @@ -101,7 +98,7 @@ internal class MXMegolmEncryption( * * @param devicesInRoom the devices list */ - private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): Try { + private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): MXOutboundSessionInfo { var session = outboundSession if (session == null // Need to make a brand new session? @@ -126,7 +123,8 @@ internal class MXMegolmEncryption( } } } - return shareKey(safeSession, shareMap).map { safeSession!! } + shareKey(safeSession, shareMap) + return safeSession } /** @@ -136,11 +134,11 @@ internal class MXMegolmEncryption( * @param devicesByUsers the devices map */ private suspend fun shareKey(session: MXOutboundSessionInfo, - devicesByUsers: Map>): Try { + devicesByUsers: Map>) { // nothing to send, the task is done if (devicesByUsers.isEmpty()) { Timber.v("## shareKey() : nothing more to do") - return Try.just(Unit) + return } // reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user) val subMap = HashMap>() @@ -157,11 +155,9 @@ internal class MXMegolmEncryption( } } Timber.v("## shareKey() ; userId $userIds") - return shareUserDevicesKey(session, subMap) - .flatMap { - val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() } - shareKey(session, remainingDevices) - } + shareUserDevicesKey(session, subMap) + val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() } + shareKey(session, remainingDevices) } /** @@ -172,7 +168,7 @@ internal class MXMegolmEncryption( * @param callback the asynchronous callback */ private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo, - devicesByUser: Map>): Try { + devicesByUser: Map>) { val sessionKey = olmDevice.getSessionKey(session.sessionId) val chainIndex = olmDevice.getMessageIndex(session.sessionId) @@ -190,94 +186,86 @@ internal class MXMegolmEncryption( var t0 = System.currentTimeMillis() Timber.v("## shareUserDevicesKey() : starts") - return ensureOlmSessionsForDevicesAction.handle(devicesByUser) - .flatMap { - Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " - + (System.currentTimeMillis() - t0) + " ms") - val contentMap = MXUsersDevicesMap() - var haveTargets = false - val userIds = it.userIds - for (userId in userIds) { - val devicesToShareWith = devicesByUser[userId] - for ((deviceID) in devicesToShareWith!!) { - val sessionResult = it.getObject(userId, deviceID) - if (sessionResult?.sessionId == null) { - // no session with this device, probably because there - // were no one-time keys. - // - // we could send them a to_device message anyway, as a - // signal that they have missed out on the key sharing - // message because of the lack of keys, but there's not - // much point in that really; it will mostly serve to clog - // up to_device inboxes. - // - // ensureOlmSessionsForUsers has already done the logging, - // so just skip it. - continue - } - Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") - //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument - contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo))) - haveTargets = true - } - } - if (haveTargets) { - t0 = System.currentTimeMillis() - Timber.v("## shareUserDevicesKey() : has target") - val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) - sendToDeviceTask.execute(sendToDeviceParams) - .map { - Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " - + (System.currentTimeMillis() - t0) + " ms") - - // Add the devices we have shared with to session.sharedWithDevices. - // we deliberately iterate over devicesByUser (ie, the devices we - // attempted to share with) rather than the contentMap (those we did - // share with), because we don't want to try to claim a one-time-key - // for dead devices on every message. - for (userId in devicesByUser.keys) { - val devicesToShareWith = devicesByUser[userId] - for ((deviceId) in devicesToShareWith!!) { - session.sharedWithDevices.setObject(userId, deviceId, chainIndex) - } - } - Unit - } - } else { - Timber.v("## shareUserDevicesKey() : no need to sharekey") - Try.just(Unit) - } + val results = ensureOlmSessionsForDevicesAction.handle(devicesByUser) + Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + val contentMap = MXUsersDevicesMap() + var haveTargets = false + val userIds = results.userIds + for (userId in userIds) { + val devicesToShareWith = devicesByUser[userId] + for ((deviceID) in devicesToShareWith!!) { + val sessionResult = results.getObject(userId, deviceID) + if (sessionResult?.sessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + // + // we could send them a to_device message anyway, as a + // signal that they have missed out on the key sharing + // message because of the lack of keys, but there's not + // much point in that really; it will mostly serve to clog + // up to_device inboxes. + // + // ensureOlmSessionsForUsers has already done the logging, + // so just skip it. + continue } + Timber.v("## shareUserDevicesKey() : Sharing keys with device $userId:$deviceID") + //noinspection ArraysAsListWithZeroOrOneArgument,ArraysAsListWithZeroOrOneArgument + contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, Arrays.asList(sessionResult.deviceInfo))) + haveTargets = true + } + } + if (haveTargets) { + t0 = System.currentTimeMillis() + Timber.v("## shareUserDevicesKey() : has target") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) + sendToDeviceTask.execute(sendToDeviceParams) + Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " + + (System.currentTimeMillis() - t0) + " ms") + + // Add the devices we have shared with to session.sharedWithDevices. + // we deliberately iterate over devicesByUser (ie, the devices we + // attempted to share with) rather than the contentMap (those we did + // share with), because we don't want to try to claim a one-time-key + // for dead devices on every message. + for (userId in devicesByUser.keys) { + val devicesToShareWith = devicesByUser[userId] + for ((deviceId) in devicesToShareWith!!) { + session.sharedWithDevices.setObject(userId, deviceId, chainIndex) + } + } + } else { + Timber.v("## shareUserDevicesKey() : no need to sharekey") + } } /** * process the pending encryptions */ - private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Try { - return Try { - // Everything is in place, encrypt all pending events - val payloadJson = HashMap() - payloadJson["room_id"] = roomId - payloadJson["type"] = eventType - payloadJson["content"] = eventContent + private fun encryptContent(session: MXOutboundSessionInfo, eventType: String, eventContent: Content): Content { + // Everything is in place, encrypt all pending events + val payloadJson = HashMap() + payloadJson["room_id"] = roomId + payloadJson["type"] = eventType + payloadJson["content"] = eventContent - // Get canonical Json from + // Get canonical Json from - val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson)) - val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) + val payloadString = convertToUTF8(JsonCanonicalizer.getCanonicalJson(Map::class.java, payloadJson)) + val ciphertext = olmDevice.encryptGroupMessage(session.sessionId, payloadString!!) - val map = HashMap() - map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM - map["sender_key"] = olmDevice.deviceCurve25519Key!! - map["ciphertext"] = ciphertext!! - map["session_id"] = session.sessionId + val map = HashMap() + map["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM + map["sender_key"] = olmDevice.deviceCurve25519Key!! + map["ciphertext"] = ciphertext!! + map["session_id"] = session.sessionId - // Include our device ID so that recipients can send us a - // m.new_device message if they don't have our session key. - map["device_id"] = credentials.deviceId!! - session.useCount++ - map - } + // Include our device ID so that recipients can send us a + // m.new_device message if they don't have our session key. + map["device_id"] = credentials.deviceId!! + session.useCount++ + return map } /** @@ -287,50 +275,47 @@ internal class MXMegolmEncryption( * @param userIds the user ids whose devices must be checked. * @param callback the asynchronous callback */ - private suspend fun getDevicesInRoom(userIds: List): Try> { + private suspend fun getDevicesInRoom(userIds: List): MXUsersDevicesMap { // We are happy to use a cached version here: we assume that if we already // have a list of the user's devices, then we already share an e2e room // with them, which means that they will have announced any new devices via // an m.new_device. - return deviceListManager - .downloadKeys(userIds, false) - .flatMap { - val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() - || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) + val keys = deviceListManager.downloadKeys(userIds, false) + val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() + || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) - val devicesInRoom = MXUsersDevicesMap() - val unknownDevices = MXUsersDevicesMap() + val devicesInRoom = MXUsersDevicesMap() + val unknownDevices = MXUsersDevicesMap() - for (userId in it.userIds) { - val deviceIds = it.getUserDeviceIds(userId) ?: continue - for (deviceId in deviceIds) { - val deviceInfo = it.getObject(userId, deviceId) ?: continue - if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) { - // The device is not yet known by the user - unknownDevices.setObject(userId, deviceId, deviceInfo) - continue - } - if (deviceInfo.isBlocked) { - // Remove any blocked devices - continue - } - - if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { - continue - } - - if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { - // Don't bother sending to ourself - continue - } - devicesInRoom.setObject(userId, deviceId, deviceInfo) - } - } - if (unknownDevices.isEmpty) { - Try.just(devicesInRoom) - } else { - Try.Failure(MXCryptoError.UnknownDevice(unknownDevices)) - } + for (userId in keys.userIds) { + val deviceIds = keys.getUserDeviceIds(userId) ?: continue + for (deviceId in deviceIds) { + val deviceInfo = keys.getObject(userId, deviceId) ?: continue + if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) { + // The device is not yet known by the user + unknownDevices.setObject(userId, deviceId, deviceInfo) + continue } + if (deviceInfo.isBlocked) { + // Remove any blocked devices + continue + } + + if (!deviceInfo.isVerified && encryptToVerifiedDevicesOnly) { + continue + } + + if (TextUtils.equals(deviceInfo.identityKey(), olmDevice.deviceCurve25519Key)) { + // Don't bother sending to ourself + continue + } + devicesInRoom.setObject(userId, deviceId, deviceInfo) + } + } + if (unknownDevices.isEmpty) { + return devicesInRoom + } else { + throw MXCryptoError.UnknownDevice(unknownDevices) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 7b5a8cd4..b57486c1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event @@ -41,29 +40,28 @@ internal class MXOlmDecryption( private val credentials: Credentials) : IMXDecrypting { - override suspend fun decryptEvent(event: Event, timeline: String): Try { + override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { val olmEventContent = event.content.toModel() ?: run { Timber.e("## decryptEvent() : bad event format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT, - MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_EVENT_FORMAT, + MXCryptoError.BAD_EVENT_FORMAT_TEXT_REASON) } val cipherText = olmEventContent.ciphertext ?: run { Timber.e("## decryptEvent() : missing cipher text") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT, - MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_CIPHER_TEXT, + MXCryptoError.MISSING_CIPHER_TEXT_REASON) } val senderKey = olmEventContent.senderKey ?: run { Timber.e("## decryptEvent() : missing sender key") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, - MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, + MXCryptoError.MISSING_SENDER_KEY_TEXT_REASON) } val messageAny = cipherText[olmDevice.deviceCurve25519Key] ?: run { Timber.e("## decryptEvent() : our device ${olmDevice.deviceCurve25519Key} is not included in recipients") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, - MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.NOT_INCLUDE_IN_RECIPIENTS, MXCryptoError.NOT_INCLUDED_IN_RECIPIENT_REASON) } // The message for myUser @@ -73,14 +71,12 @@ internal class MXOlmDecryption( if (decryptedPayload == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, - MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) } val payloadString = convertFromUTF8(decryptedPayload) if (payloadString == null) { Timber.e("## decryptEvent() Failed to decrypt Olm event (id= ${event.eventId} from $senderKey") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, - MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON) } val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) @@ -88,73 +84,70 @@ internal class MXOlmDecryption( if (payload == null) { Timber.e("## decryptEvent failed : null payload") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, - MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, MXCryptoError.MISSING_CIPHER_TEXT_REASON) } val olmPayloadContent = OlmPayloadContent.fromJsonString(payloadString) ?: run { Timber.e("## decryptEvent() : bad olmPayloadContent format") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, - MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) } if (olmPayloadContent.recipient.isNullOrBlank()) { val reason = String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient") Timber.e("## decryptEvent() : $reason") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, - reason)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, reason) } if (olmPayloadContent.recipient != credentials.userId) { Timber.e("## decryptEvent() : Event ${event.eventId}:" + " Intended recipient ${olmPayloadContent.recipient} does not match our id ${credentials.userId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT, - String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT, + String.format(MXCryptoError.BAD_RECIPIENT_REASON, olmPayloadContent.recipient)) } val recipientKeys = olmPayloadContent.recipient_keys ?: run { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'recipient_keys' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, - String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys"))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, + String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "recipient_keys")) } val ed25519 = recipientKeys["ed25519"] if (ed25519 != olmDevice.deviceEd25519Key) { Timber.e("## decryptEvent() : Event ${event.eventId}: Intended recipient ed25519 key $ed25519 did not match ours") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY, - MXCryptoError.BAD_RECIPIENT_KEY_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_RECIPIENT_KEY, + MXCryptoError.BAD_RECIPIENT_KEY_REASON) } if (olmPayloadContent.sender.isNullOrBlank()) { Timber.e("## decryptEvent() : Olm event (id=${event.eventId}) contains no 'sender' property; cannot prevent unknown-key attack") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, - String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender"))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_PROPERTY, + String.format(MXCryptoError.ERROR_MISSING_PROPERTY_REASON, "sender")) } if (olmPayloadContent.sender != event.senderId) { Timber.e("Event ${event.eventId}: original sender ${olmPayloadContent.sender} does not match reported sender ${event.senderId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE, - String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.FORWARDED_MESSAGE, + String.format(MXCryptoError.FORWARDED_MESSAGE_REASON, olmPayloadContent.sender)) } if (olmPayloadContent.room_id != event.roomId) { Timber.e("## decryptEvent() : Event ${event.eventId}: original room ${olmPayloadContent.room_id} does not match reported room ${event.roomId}") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM, - String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id))) + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ROOM, + String.format(MXCryptoError.BAD_ROOM_REASON, olmPayloadContent.room_id)) } val keys = olmPayloadContent.keys ?: run { Timber.e("## decryptEvent failed : null keys") - return Try.Failure(MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, - MXCryptoError.MISSING_CIPHER_TEXT_REASON)) + throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, + MXCryptoError.MISSING_CIPHER_TEXT_REASON) } - return Try.just(MXEventDecryptionResult( + return MXEventDecryptionResult( clearEvent = payload, senderCurve25519Key = senderKey, claimedEd25519Key = keys["ed25519"] - )) + ) } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt index a8b4b1a0..afe4b36e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmDecryptionFactory.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.crypto.MXOlmDevice -import im.vector.matrix.android.internal.session.SessionScope import javax.inject.Inject internal class MXOlmDecryptionFactory @Inject constructor(private val olmDevice: MXOlmDevice, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt index ad0680a4..10d481dc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.crypto.algorithms.olm import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.internal.crypto.DeviceListManager @@ -40,37 +39,35 @@ internal class MXOlmEncryption( private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) : IMXEncrypting { - override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Try { + override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content { // pick the list of recipients based on the membership list. // // TODO: there is a race condition here! What if a new user turns up - return ensureSession(userIds) - .map { - val deviceInfos = ArrayList() - for (userId in userIds) { - val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() - for (device in devices) { - val key = device.identityKey() - if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { - // Don't bother setting up session to ourself - continue - } - if (device.isBlocked) { - // Don't bother setting up sessions with blocked users - continue - } - deviceInfos.add(device) - } - } - - val messageMap = HashMap() - messageMap["room_id"] = roomId - messageMap["type"] = eventType - messageMap["content"] = eventContent - - messageEncrypter.encryptMessage(messageMap, deviceInfos) - messageMap.toContent()!! + ensureSession(userIds) + val deviceInfos = ArrayList() + for (userId in userIds) { + val devices = cryptoStore.getUserDevices(userId)?.values ?: emptyList() + for (device in devices) { + val key = device.identityKey() + if (TextUtils.equals(key, olmDevice.deviceCurve25519Key)) { + // Don't bother setting up session to ourself + continue } + if (device.isBlocked) { + // Don't bother setting up sessions with blocked users + continue + } + deviceInfos.add(device) + } + } + + val messageMap = HashMap() + messageMap["room_id"] = roomId + messageMap["type"] = eventType + messageMap["content"] = eventContent + + messageEncrypter.encryptMessage(messageMap, deviceInfos) + return messageMap.toContent()!! } @@ -78,13 +75,9 @@ internal class MXOlmEncryption( * Ensure that the session * * @param users the user ids list - * @param callback the asynchronous callback */ - private suspend fun ensureSession(users: List): Try { - return deviceListManager - .downloadKeys(users, false) - .flatMap { ensureOlmSessionsForUsersAction.handle(users) } - .map { Unit } - + private suspend fun ensureSession(users: List) { + deviceListManager.downloadKeys(users, false) + ensureOlmSessionsForUsersAction.handle(users) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt index 99ab8eda..eff833cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/algorithms/olm/MXOlmEncryptionFactory.kt @@ -21,7 +21,6 @@ import im.vector.matrix.android.internal.crypto.MXOlmDevice import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForUsersAction import im.vector.matrix.android.internal.crypto.actions.MessageEncrypter import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt index 23e5b466..263cfef2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/api/CryptoApi.kt @@ -18,8 +18,6 @@ package im.vector.matrix.android.internal.crypto.api import im.vector.matrix.android.internal.crypto.model.rest.* -import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody -import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody import im.vector.matrix.android.internal.network.NetworkConstants import retrofit2.Call import retrofit2.http.* diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt index 88d9aec9..a69a7325 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackup.kt @@ -51,7 +51,6 @@ import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEnt import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.session.SessionScope -import im.vector.matrix.android.internal.task.* import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskThread @@ -204,31 +203,32 @@ internal class KeysBackup @Inject constructor( keysBackupStateManager.state = KeysBackupState.Enabling createKeysBackupVersionTask - .configureWith(createKeysBackupVersionBody) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(info: KeysVersion) { - // Reset backup markers. - cryptoStore.resetBackupMarkers() + .configureWith(createKeysBackupVersionBody) { + this.callback = object : MatrixCallback { + override fun onSuccess(info: KeysVersion) { + // Reset backup markers. + cryptoStore.resetBackupMarkers() - val keyBackupVersion = KeysVersionResult() - keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm - keyBackupVersion.authData = createKeysBackupVersionBody.authData - keyBackupVersion.version = info.version + val keyBackupVersion = KeysVersionResult() + keyBackupVersion.algorithm = createKeysBackupVersionBody.algorithm + keyBackupVersion.authData = createKeysBackupVersionBody.authData + keyBackupVersion.version = info.version - // We can consider that the server does not have keys yet - keyBackupVersion.count = 0 - keyBackupVersion.hash = null + // We can consider that the server does not have keys yet + keyBackupVersion.count = 0 + keyBackupVersion.hash = null - enableKeysBackup(keyBackupVersion) + enableKeysBackup(keyBackupVersion) - callback.onSuccess(info) + callback.onSuccess(info) + } + + override fun onFailure(failure: Throwable) { + keysBackupStateManager.state = KeysBackupState.Disabled + callback.onFailure(failure) + } } - - override fun onFailure(failure: Throwable) { - keysBackupStateManager.state = KeysBackupState.Disabled - callback.onFailure(failure) - } - }) + } .executeBy(taskExecutor) } @@ -243,27 +243,29 @@ internal class KeysBackup @Inject constructor( keysBackupStateManager.state = KeysBackupState.Unknown } - deleteBackupTask.configureWith(DeleteBackupTask.Params(version)) - .dispatchTo(object : MatrixCallback { - private fun eventuallyRestartBackup() { - // Do not stay in KeysBackupState.Unknown but check what is available on the homeserver - if (state == KeysBackupState.Unknown) { - checkAndStartKeysBackup() + deleteBackupTask + .configureWith(DeleteBackupTask.Params(version)) { + this.callback = object : MatrixCallback { + private fun eventuallyRestartBackup() { + // Do not stay in KeysBackupState.Unknown but check what is available on the homeserver + if (state == KeysBackupState.Unknown) { + checkAndStartKeysBackup() + } + } + + override fun onSuccess(data: Unit) { + eventuallyRestartBackup() + + uiHandler.post { callback?.onSuccess(Unit) } + } + + override fun onFailure(failure: Throwable) { + eventuallyRestartBackup() + + uiHandler.post { callback?.onFailure(failure) } } } - - override fun onSuccess(data: Unit) { - eventuallyRestartBackup() - - uiHandler.post { callback?.onSuccess(Unit) } - } - - override fun onFailure(failure: Throwable) { - eventuallyRestartBackup() - - uiHandler.post { callback?.onFailure(failure) } - } - }) + } .executeBy(taskExecutor) } } @@ -355,15 +357,14 @@ internal class KeysBackup @Inject constructor( callback: MatrixCallback) { // TODO Validate with François that this is correct object : Task { - override suspend fun execute(params: KeysVersionResult): Try { - return Try { - getKeysBackupTrustBg(params) - } + override suspend fun execute(params: KeysVersionResult): KeysBackupVersionTrust { + return getKeysBackupTrustBg(params) } } - .configureWith(keysBackupVersion) - .dispatchTo(callback) - .executeOn(TaskThread.COMPUTATION) + .configureWith(keysBackupVersion) { + this.callback = callback + this.executionThread = TaskThread.COMPUTATION + } .executeBy(taskExecutor) } @@ -454,7 +455,8 @@ internal class KeysBackup @Inject constructor( val myUserId = credentials.userId // Get current signatures, or create an empty set - val myUserSignatures = authData.signatures?.get(myUserId)?.toMutableMap() ?: HashMap() + val myUserSignatures = authData.signatures?.get(myUserId)?.toMutableMap() + ?: HashMap() if (trust) { // Add current device signature @@ -492,27 +494,28 @@ internal class KeysBackup @Inject constructor( // And send it to the homeserver updateKeysBackupVersionTask - .configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - // Relaunch the state machine on this updated backup version - val newKeysBackupVersion = KeysVersionResult() + .configureWith(UpdateKeysBackupVersionTask.Params(keysBackupVersion.version!!, updateKeysBackupVersionBody)) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: Unit) { + // Relaunch the state machine on this updated backup version + val newKeysBackupVersion = KeysVersionResult() - newKeysBackupVersion.version = keysBackupVersion.version - newKeysBackupVersion.algorithm = keysBackupVersion.algorithm - newKeysBackupVersion.count = keysBackupVersion.count - newKeysBackupVersion.hash = keysBackupVersion.hash - newKeysBackupVersion.authData = updateKeysBackupVersionBody.authData + newKeysBackupVersion.version = keysBackupVersion.version + newKeysBackupVersion.algorithm = keysBackupVersion.algorithm + newKeysBackupVersion.count = keysBackupVersion.count + newKeysBackupVersion.hash = keysBackupVersion.hash + newKeysBackupVersion.authData = updateKeysBackupVersionBody.authData - checkAndStartWithKeysBackupVersion(newKeysBackupVersion) + checkAndStartWithKeysBackupVersion(newKeysBackupVersion) - callback.onSuccess(data) + callback.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } } - - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) - } - }) + } .executeBy(taskExecutor) } } @@ -758,49 +761,52 @@ internal class KeysBackup @Inject constructor( if (roomId != null && sessionId != null) { // Get key for the room and for the session getRoomSessionDataTask - .configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: KeyBackupData) { - // Convert to KeysBackupData - val keysBackupData = KeysBackupData() - keysBackupData.roomIdToRoomKeysBackupData = HashMap() - val roomKeysBackupData = RoomKeysBackupData() - roomKeysBackupData.sessionIdToKeyBackupData = HashMap() - roomKeysBackupData.sessionIdToKeyBackupData[sessionId] = data - keysBackupData.roomIdToRoomKeysBackupData[roomId] = roomKeysBackupData + .configureWith(GetRoomSessionDataTask.Params(roomId, sessionId, version)) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: KeyBackupData) { + // Convert to KeysBackupData + val keysBackupData = KeysBackupData() + keysBackupData.roomIdToRoomKeysBackupData = HashMap() + val roomKeysBackupData = RoomKeysBackupData() + roomKeysBackupData.sessionIdToKeyBackupData = HashMap() + roomKeysBackupData.sessionIdToKeyBackupData[sessionId] = data + keysBackupData.roomIdToRoomKeysBackupData[roomId] = roomKeysBackupData - callback.onSuccess(keysBackupData) - } + callback.onSuccess(keysBackupData) + } - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } } - }) + } .executeBy(taskExecutor) } else if (roomId != null) { // Get all keys for the room getRoomSessionsDataTask - .configureWith(GetRoomSessionsDataTask.Params(roomId, version)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: RoomKeysBackupData) { - // Convert to KeysBackupData - val keysBackupData = KeysBackupData() - keysBackupData.roomIdToRoomKeysBackupData = HashMap() - keysBackupData.roomIdToRoomKeysBackupData[roomId] = data + .configureWith(GetRoomSessionsDataTask.Params(roomId, version)) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: RoomKeysBackupData) { + // Convert to KeysBackupData + val keysBackupData = KeysBackupData() + keysBackupData.roomIdToRoomKeysBackupData = HashMap() + keysBackupData.roomIdToRoomKeysBackupData[roomId] = data - callback.onSuccess(keysBackupData) - } + callback.onSuccess(keysBackupData) + } - override fun onFailure(failure: Throwable) { - callback.onFailure(failure) + override fun onFailure(failure: Throwable) { + callback.onFailure(failure) + } } - }) + } .executeBy(taskExecutor) } else { // Get all keys getSessionsDataTask - .configureWith(GetSessionsDataTask.Params(version)) - .dispatchTo(callback) + .configureWith(GetSessionsDataTask.Params(version)) { + this.callback = callback + } .executeBy(taskExecutor) } } @@ -855,45 +861,47 @@ internal class KeysBackup @Inject constructor( override fun getVersion(version: String, callback: MatrixCallback) { getKeysBackupVersionTask - .configureWith(version) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: KeysVersionResult) { - callback.onSuccess(data) - } + .configureWith(version) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult) { + callback.onSuccess(data) + } - override fun onFailure(failure: Throwable) { - if (failure is Failure.ServerError - && failure.error.code == MatrixError.NOT_FOUND) { - // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup - callback.onSuccess(null) - } else { - // Transmit the error - callback.onFailure(failure) + override fun onFailure(failure: Throwable) { + if (failure is Failure.ServerError + && failure.error.code == MatrixError.NOT_FOUND) { + // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup + callback.onSuccess(null) + } else { + // Transmit the error + callback.onFailure(failure) + } } } - }) + } .executeBy(taskExecutor) } override fun getCurrentVersion(callback: MatrixCallback) { getKeysBackupLastVersionTask - .toConfigurableTask() - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: KeysVersionResult) { - callback.onSuccess(data) - } + .configureWith { + this.callback = object : MatrixCallback { + override fun onSuccess(data: KeysVersionResult) { + callback.onSuccess(data) + } - override fun onFailure(failure: Throwable) { - if (failure is Failure.ServerError - && failure.error.code == MatrixError.NOT_FOUND) { - // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup - callback.onSuccess(null) - } else { - // Transmit the error - callback.onFailure(failure) + override fun onFailure(failure: Throwable) { + if (failure is Failure.ServerError + && failure.error.code == MatrixError.NOT_FOUND) { + // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup + callback.onSuccess(null) + } else { + // Transmit the error + callback.onFailure(failure) + } } } - }) + } .executeBy(taskExecutor) } @@ -1236,69 +1244,72 @@ internal class KeysBackup @Inject constructor( Timber.v("backupKeys: 4 - Sending request") - // Make the request - storeSessionDataTask - .configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: BackupKeysResult) { - uiHandler.post { - Timber.v("backupKeys: 5a - Request complete") + val sendingRequestCallback = object : MatrixCallback { + override fun onSuccess(data: BackupKeysResult) { + uiHandler.post { + Timber.v("backupKeys: 5a - Request complete") - // Mark keys as backed up - cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers) + // Mark keys as backed up + cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers) - if (olmInboundGroupSessionWrappers.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) { - Timber.v("backupKeys: All keys have been backed up") - onServerDataRetrieved(data.count, data.hash) + if (olmInboundGroupSessionWrappers.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) { + Timber.v("backupKeys: All keys have been backed up") + onServerDataRetrieved(data.count, data.hash) - // Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess() - keysBackupStateManager.state = KeysBackupState.ReadyToBackUp - } else { - Timber.v("backupKeys: Continue to back up keys") - keysBackupStateManager.state = KeysBackupState.WillBackUp + // Note: Changing state will trigger the call to backupAllGroupSessionsCallback.onSuccess() + keysBackupStateManager.state = KeysBackupState.ReadyToBackUp + } else { + Timber.v("backupKeys: Continue to back up keys") + keysBackupStateManager.state = KeysBackupState.WillBackUp - backupKeys() - } - } + backupKeys() } + } + } - override fun onFailure(failure: Throwable) { - if (failure is Failure.ServerError) { - uiHandler.post { - Timber.e(failure, "backupKeys: backupKeys failed.") + override fun onFailure(failure: Throwable) { + if (failure is Failure.ServerError) { + uiHandler.post { + Timber.e(failure, "backupKeys: backupKeys failed.") - when (failure.error.code) { - MatrixError.NOT_FOUND, - MatrixError.WRONG_ROOM_KEYS_VERSION -> { - // Backup has been deleted on the server, or we are not using the last backup version - keysBackupStateManager.state = KeysBackupState.WrongBackUpVersion - backupAllGroupSessionsCallback?.onFailure(failure) - resetBackupAllGroupSessionsListeners() - resetKeysBackupData() - keysBackupVersion = null - - // Do not stay in KeysBackupState.WrongBackUpVersion but check what is available on the homeserver - checkAndStartKeysBackup() - } - else -> - // Come back to the ready state so that we will retry on the next received key - keysBackupStateManager.state = KeysBackupState.ReadyToBackUp - } - } - } else { - uiHandler.post { + when (failure.error.code) { + MatrixError.NOT_FOUND, + MatrixError.WRONG_ROOM_KEYS_VERSION -> { + // Backup has been deleted on the server, or we are not using the last backup version + keysBackupStateManager.state = KeysBackupState.WrongBackUpVersion backupAllGroupSessionsCallback?.onFailure(failure) resetBackupAllGroupSessionsListeners() + resetKeysBackupData() + keysBackupVersion = null - Timber.e("backupKeys: backupKeys failed.") - - // Retry a bit later - keysBackupStateManager.state = KeysBackupState.ReadyToBackUp - maybeBackupKeys() + // Do not stay in KeysBackupState.WrongBackUpVersion but check what is available on the homeserver + checkAndStartKeysBackup() } + else -> + // Come back to the ready state so that we will retry on the next received key + keysBackupStateManager.state = KeysBackupState.ReadyToBackUp } } - }) + } else { + uiHandler.post { + backupAllGroupSessionsCallback?.onFailure(failure) + resetBackupAllGroupSessionsListeners() + + Timber.e("backupKeys: backupKeys failed.") + + // Retry a bit later + keysBackupStateManager.state = KeysBackupState.ReadyToBackUp + maybeBackupKeys() + } + } + } + } + + // Make the request + storeSessionDataTask + .configureWith(StoreSessionsDataTask.Params(keysBackupVersion!!.version!!, keysBackupData)){ + this.callback = sendingRequestCallback + } .executeBy(taskExecutor) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt index f0c5037f..5aaaaea5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/CreateKeysBackupVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion @@ -30,7 +29,7 @@ internal class DefaultCreateKeysBackupVersionTask @Inject constructor(private va : CreateKeysBackupVersionTask { - override suspend fun execute(params: CreateKeysBackupVersionBody): Try { + override suspend fun execute(params: CreateKeysBackupVersionBody): KeysVersion { return executeRequest { apiCall = roomKeysApi.createKeysBackupVersion(params) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt index a1ad84ab..6c966dd2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteBackupTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,10 +31,9 @@ internal interface DeleteBackupTask : Task { internal class DefaultDeleteBackupTask @Inject constructor(private val roomKeysApi: RoomKeysApi) : DeleteBackupTask { - override suspend fun execute(params: DeleteBackupTask.Params): Try { + override suspend fun execute(params: DeleteBackupTask.Params) { return executeRequest { - apiCall = roomKeysApi.deleteBackup( - params.version) + apiCall = roomKeysApi.deleteBackup(params.version) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt index c24d314c..ccb3645e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionDataTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -34,7 +32,7 @@ internal interface DeleteRoomSessionDataTask : Task { + override suspend fun execute(params: DeleteRoomSessionDataTask.Params) { return executeRequest { apiCall = roomKeysApi.deleteRoomSessionData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt index 7476e2d2..98836654 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteRoomSessionsDataTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,7 +31,7 @@ internal interface DeleteRoomSessionsDataTask : Task { + override suspend fun execute(params: DeleteRoomSessionsDataTask.Params) { return executeRequest { apiCall = roomKeysApi.deleteRoomSessionsData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt index 14030775..12fc1357 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/DeleteSessionsDataTask.kt @@ -16,10 +16,8 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -32,10 +30,9 @@ internal interface DeleteSessionsDataTask : Task { + override suspend fun execute(params: DeleteSessionsDataTask.Params) { return executeRequest { - apiCall = roomKeysApi.deleteSessionsData( - params.version) + apiCall = roomKeysApi.deleteSessionsData(params.version) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt index 48f405a0..30076226 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.network.executeRequest @@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupLastVersionTask @Inject constructor(private v : GetKeysBackupLastVersionTask { - override suspend fun execute(params: Unit): Try { + override suspend fun execute(params: Unit): KeysVersionResult { return executeRequest { apiCall = roomKeysApi.getKeysBackupLastVersion() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt index dfaeafa1..c36f039c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetKeysBackupVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult import im.vector.matrix.android.internal.network.executeRequest @@ -29,7 +28,7 @@ internal class DefaultGetKeysBackupVersionTask @Inject constructor(private val r : GetKeysBackupVersionTask { - override suspend fun execute(params: String): Try { + override suspend fun execute(params: String): KeysVersionResult { return executeRequest { apiCall = roomKeysApi.getKeysBackupVersion(params) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt index 253a2a5a..a36850ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionDataTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -35,7 +33,7 @@ internal interface GetRoomSessionDataTask : Task { + override suspend fun execute(params: GetRoomSessionDataTask.Params): KeyBackupData { return executeRequest { apiCall = roomKeysApi.getRoomSessionData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt index c5a6d19a..e8888f25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetRoomSessionsDataTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -35,7 +33,7 @@ internal interface GetRoomSessionsDataTask : Task { + override suspend fun execute(params: GetRoomSessionsDataTask.Params): RoomKeysBackupData { return executeRequest { apiCall = roomKeysApi.getRoomSessionsData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt index 837e3eb8..d78159d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/GetSessionsDataTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,10 +31,9 @@ internal interface GetSessionsDataTask : Task { + override suspend fun execute(params: GetSessionsDataTask.Params): KeysBackupData { return executeRequest { - apiCall = roomKeysApi.getSessionsData( - params.version) + apiCall = roomKeysApi.getSessionsData(params.version) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt index 748dd106..b25c327e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionDataTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi -import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeyBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -37,7 +35,7 @@ internal interface StoreRoomSessionDataTask : Task { + override suspend fun execute(params: StoreRoomSessionDataTask.Params): BackupKeysResult { return executeRequest { apiCall = roomKeysApi.storeRoomSessionData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt index 1799df33..8290b2af 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreRoomSessionsDataTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi -import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -36,7 +34,7 @@ internal interface StoreRoomSessionsDataTask : Task { + override suspend fun execute(params: StoreRoomSessionsDataTask.Params): BackupKeysResult { return executeRequest { apiCall = roomKeysApi.storeRoomSessionsData( params.roomId, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt index 977f8114..f3467edb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/StoreSessionsDataTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi -import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.BackupKeysResult +import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysBackupData import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -35,7 +33,7 @@ internal interface StoreSessionsDataTask : Task { + override suspend fun execute(params: StoreSessionsDataTask.Params): BackupKeysResult { return executeRequest { apiCall = roomKeysApi.storeSessionsData( params.version, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt index 74c39485..28aedaf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/keysbackup/tasks/UpdateKeysBackupVersionTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.keysbackup.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.keysbackup.api.RoomKeysApi import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody import im.vector.matrix.android.internal.network.executeRequest @@ -34,7 +33,7 @@ internal class DefaultUpdateKeysBackupVersionTask @Inject constructor(private va : UpdateKeysBackupVersionTask { - override suspend fun execute(params: UpdateKeysBackupVersionTask.Params): Try { + override suspend fun execute(params: UpdateKeysBackupVersionTask.Params) { return executeRequest { apiCall = roomKeysApi.updateKeysBackupVersion(params.version, params.keysBackupVersionBody) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt index 96d69b7b..ff2f6cc4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStoreModule.kt @@ -16,8 +16,8 @@ package im.vector.matrix.android.internal.crypto.store.db -import io.realm.annotations.RealmModule import im.vector.matrix.android.internal.crypto.store.db.model.* +import io.realm.annotations.RealmModule /** * Realm module for Crypto store classes diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt index 19cc06fa..2c96bb25 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/CryptoMetadataEntity.kt @@ -16,10 +16,10 @@ package im.vector.matrix.android.internal.crypto.store.db.model -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey import org.matrix.olm.OlmAccount internal open class CryptoMetadataEntity( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt index d690073a..2c321cc5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/DeviceInfoEntity.kt @@ -16,9 +16,9 @@ package im.vector.matrix.android.internal.crypto.store.db.model +import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm -import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.LinkingObjects diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt index d446f42a..4835300e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt @@ -16,11 +16,11 @@ package im.vector.matrix.android.internal.crypto.store.db.model -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey +import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm -import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt index 4425cf33..dbc860ef 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OlmSessionEntity.kt @@ -16,10 +16,10 @@ package im.vector.matrix.android.internal.crypto.store.db.model -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey import org.matrix.olm.OlmSession internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt index cbbc2028..d4682858 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/model/OutgoingRoomKeyRequestEntity.kt @@ -17,9 +17,9 @@ package im.vector.matrix.android.internal.crypto.store.db.model import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest +import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.store.db.deserializeFromRealm import im.vector.matrix.android.internal.crypto.store.db.serializeForRealm -import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import io.realm.RealmObject import io.realm.annotations.PrimaryKey diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt index 2aea3cd2..706815ff 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt @@ -16,11 +16,11 @@ package im.vector.matrix.android.internal.crypto.store.db.query -import io.realm.Realm -import io.realm.kotlin.where import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntity import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityFields import im.vector.matrix.android.internal.crypto.store.db.model.createPrimaryKey +import io.realm.Realm +import io.realm.kotlin.where /** * Get or create a device info diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt index dd5278ce..3860d8d8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/query/UserEntitiesQueries.kt @@ -16,10 +16,10 @@ package im.vector.matrix.android.internal.crypto.store.db.query -import io.realm.Realm -import io.realm.kotlin.where import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields +import io.realm.Realm +import io.realm.kotlin.where /** * Get or create a user diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt index d60105e0..23b0f958 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/ClaimOneTimeKeysForUsersDeviceTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.MXKey import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap @@ -37,35 +36,30 @@ internal interface ClaimOneTimeKeysForUsersDeviceTask : Task> { + override suspend fun execute(params: ClaimOneTimeKeysForUsersDeviceTask.Params): MXUsersDevicesMap { val body = KeysClaimBody(oneTimeKeys = params.usersDevicesKeyTypesMap.map) - return executeRequest { + val keysClaimResponse = executeRequest { apiCall = cryptoApi.claimOneTimeKeysForUsersDevices(body) - }.flatMap { keysClaimResponse -> - Try { - val map = MXUsersDevicesMap() + } + val map = MXUsersDevicesMap() + keysClaimResponse.oneTimeKeys?.let { oneTimeKeys -> + for (userId in oneTimeKeys.keys) { + val mapByUserId = oneTimeKeys[userId] - keysClaimResponse.oneTimeKeys?.let { oneTimeKeys -> - for (userId in oneTimeKeys.keys) { - val mapByUserId = oneTimeKeys[userId] + if (mapByUserId != null) { + for (deviceId in mapByUserId.keys) { + val mxKey = MXKey.from(mapByUserId[deviceId]) - if (mapByUserId != null) { - for (deviceId in mapByUserId.keys) { - val mxKey = MXKey.from(mapByUserId[deviceId]) - - if (mxKey != null) { - map.setObject(userId, deviceId, mxKey) - } else { - Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") - } - } + if (mxKey != null) { + map.setObject(userId, deviceId, mxKey) + } else { + Timber.e("## claimOneTimeKeysForUsersDevices : fail to create a MXKey") } } } - - map } } + return map } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt index 0f93e213..3e6e4d8c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceTask.kt @@ -16,16 +16,12 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try -import arrow.core.failure -import arrow.core.recoverWith import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.DeleteDeviceParams import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -38,10 +34,12 @@ internal interface DeleteDeviceTask : Task { internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi: CryptoApi) : DeleteDeviceTask { - override suspend fun execute(params: DeleteDeviceTask.Params): Try { - return executeRequest { - apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()) - }.recoverWith { throwable -> + override suspend fun execute(params: DeleteDeviceTask.Params) { + try { + executeRequest { + apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams()) + } + } catch (throwable: Throwable) { if (throwable is Failure.OtherServerError && throwable.httpCode == 401) { // Parse to get a RegistrationFlowResponse val registrationFlowResponse = try { @@ -51,17 +49,16 @@ internal class DefaultDeleteDeviceTask @Inject constructor(private val cryptoApi } catch (e: Exception) { null } - // check if the server response can be casted if (registrationFlowResponse != null) { - Failure.RegistrationFlowError(registrationFlowResponse).failure() + throw Failure.RegistrationFlowError(registrationFlowResponse) } else { - throwable.failure() + throw throwable } } else { // Other error - throwable.failure() + throw throwable } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt index 2476f242..060b57ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DeleteDeviceWithUserPasswordTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.auth.data.LoginFlowTypes import im.vector.matrix.android.internal.crypto.api.CryptoApi @@ -38,7 +37,7 @@ internal class DefaultDeleteDeviceWithUserPasswordTask @Inject constructor(priva private val credentials: Credentials) : DeleteDeviceWithUserPasswordTask { - override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params): Try { + override suspend fun execute(params: DeleteDeviceWithUserPasswordTask.Params) { return executeRequest { apiCall = cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams() .apply { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt index 00ea7178..92908071 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/DownloadKeysForUsersTask.kt @@ -17,12 +17,10 @@ package im.vector.matrix.android.internal.crypto.tasks import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryBody import im.vector.matrix.android.internal.crypto.model.rest.KeysQueryResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import java.util.* import javax.inject.Inject @@ -38,7 +36,7 @@ internal interface DownloadKeysForUsersTask : Task { + override suspend fun execute(params: DownloadKeysForUsersTask.Params): KeysQueryResponse { val downloadQuery = HashMap>() if (null != params.userIds) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt index 3779a102..d6e82adb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetDevicesTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -29,7 +27,7 @@ internal interface GetDevicesTask : Task internal class DefaultGetDevicesTask @Inject constructor(private val cryptoApi: CryptoApi) : GetDevicesTask { - override suspend fun execute(params: Unit): Try { + override suspend fun execute(params: Unit): DevicesListResponse { return executeRequest { apiCall = cryptoApi.getDevices() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt index 21eabc22..42c36bd1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/GetKeyChangesTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.KeyChangesResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -36,10 +34,9 @@ internal interface GetKeyChangesTask : Task { + override suspend fun execute(params: GetKeyChangesTask.Params): KeyChangesResponse { return executeRequest { - apiCall = cryptoApi.getKeyChanges(params.from, - params.to) + apiCall = cryptoApi.getKeyChanges(params.from, params.to) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt index 27559f74..1865c633 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SendToDeviceTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceBody import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import java.util.* import javax.inject.Inject @@ -40,7 +38,7 @@ internal interface SendToDeviceTask : Task { internal class DefaultSendToDeviceTask @Inject constructor(private val cryptoApi: CryptoApi) : SendToDeviceTask { - override suspend fun execute(params: SendToDeviceTask.Params): Try { + override suspend fun execute(params: SendToDeviceTask.Params) { val sendToDeviceBody = SendToDeviceBody() sendToDeviceBody.messages = params.contentMap.map diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt index 04613ddd..1abef576 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/SetDeviceNameTask.kt @@ -17,11 +17,9 @@ package im.vector.matrix.android.internal.crypto.tasks import android.text.TextUtils -import arrow.core.Try import im.vector.matrix.android.internal.crypto.api.CryptoApi import im.vector.matrix.android.internal.crypto.model.rest.UpdateDeviceInfoBody import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -37,11 +35,10 @@ internal interface SetDeviceNameTask : Task { internal class DefaultSetDeviceNameTask @Inject constructor(private val cryptoApi: CryptoApi) : SetDeviceNameTask { - override suspend fun execute(params: SetDeviceNameTask.Params): Try { + override suspend fun execute(params: SetDeviceNameTask.Params) { val body = UpdateDeviceInfoBody( displayName = if (TextUtils.isEmpty(params.deviceName)) "" else params.deviceName ) - return executeRequest { apiCall = cryptoApi.updateDeviceInfo(params.deviceId, body) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt index 96c3a558..fd7a5e8e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/tasks/UploadKeysTask.kt @@ -16,14 +16,12 @@ package im.vector.matrix.android.internal.crypto.tasks -import arrow.core.Try import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.crypto.api.CryptoApi -import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.DeviceKeys import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadBody +import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.convertToUTF8 import javax.inject.Inject @@ -41,7 +39,7 @@ internal interface UploadKeysTask : Task { + override suspend fun execute(params: UploadKeysTask.Params): KeysUploadResponse { val encodedDeviceId = convertToUTF8(params.deviceId) val body = KeysUploadBody() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt index 54a4e14d..6eea9ab5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -37,6 +37,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.* import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.task.TaskConstraints import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers @@ -219,17 +220,20 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre startReq: KeyVerificationStart, success: (MXUsersDevicesMap) -> Unit, error: () -> Unit) { - deviceListManager.downloadKeys(listOf(otherUserId), true) - .fold( - { error() }, - { - if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) { - success(it) - } else { - error() - } - } - ) + runCatching { + deviceListManager.downloadKeys(listOf(otherUserId), true) + }.fold( + { + if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) { + success(it) + } else { + error() + } + }, + { + error() + } + ) } private suspend fun onCancelReceived(event: Event) { @@ -412,16 +416,18 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre val contentMap = MXUsersDevicesMap() contentMap.setObject(userId, userDevice, cancelMessage) - sendToDeviceTask.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}") - } + sendToDeviceTask + .configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: Unit) { + Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}") + } - override fun onFailure(failure: Throwable) { - Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.") + override fun onFailure(failure: Throwable) { + Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.") + } } - }) + } .executeBy(taskExecutor) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt index 8b179c1d..de4b9974 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt @@ -287,23 +287,25 @@ internal abstract class SASVerificationTransaction( val contentMap = MXUsersDevicesMap() contentMap.setObject(otherUserId, otherDeviceId, keyToDevice) - sendToDeviceTask.configureWith(SendToDeviceTask.Params(type, contentMap, transactionId)) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - Timber.v("## SAS verification [$transactionId] toDevice type '$type' success.") - if (onDone != null) { - onDone() - } else { - state = nextState + sendToDeviceTask + .configureWith(SendToDeviceTask.Params(type, contentMap, transactionId)) { + this.callback = object : MatrixCallback { + override fun onSuccess(data: Unit) { + Timber.v("## SAS verification [$transactionId] toDevice type '$type' success.") + if (onDone != null) { + onDone() + } else { + state = nextState + } + } + + override fun onFailure(failure: Throwable) { + Timber.e("## SAS verification [$transactionId] failed to send toDevice in state : $state") + + cancel(onErrorReason) } } - - override fun onFailure(failure: Throwable) { - Timber.e("## SAS verification [$transactionId] failed to send toDevice in state : $state") - - cancel(onErrorReason) - } - }) + } .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt new file mode 100644 index 00000000..e1beefc2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/AsyncTransaction.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2019 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.matrix.android.internal.database + +import io.realm.Realm +import io.realm.RealmConfiguration +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.isActive +import kotlinx.coroutines.withContext + + +suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> Unit) = withContext(Dispatchers.IO) { + Realm.getInstance(config).use { bgRealm -> + bgRealm.beginTransaction() + try { + transaction(bgRealm) + if (isActive) { + bgRealm.commitTransaction() + } + } finally { + if (bgRealm.isInTransaction) { + bgRealm.cancelTransaction() + } + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt index af842c67..e78078f9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmLiveEntityObserver.kt @@ -18,12 +18,7 @@ package im.vector.matrix.android.internal.database import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.util.createBackgroundHandler -import io.realm.OrderedCollectionChangeSet -import io.realm.OrderedRealmCollectionChangeListener -import io.realm.Realm -import io.realm.RealmConfiguration -import io.realm.RealmObject -import io.realm.RealmResults +import io.realm.* import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt index d700fdf3..f6bf258b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/TimelineEventEntityHelper.kt @@ -20,11 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.internal.database.mapper.ContentMapper -import im.vector.matrix.android.internal.database.model.EventEntity -import im.vector.matrix.android.internal.database.model.EventEntityFields -import im.vector.matrix.android.internal.database.model.RoomEntity -import im.vector.matrix.android.internal.database.model.TimelineEventEntity -import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields +import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.query.next import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 0d76b548..4375a19d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -20,13 +20,10 @@ import com.squareup.moshi.JsonDataException import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.UnsignedData -import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult -import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmDecryption import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.di.MoshiProvider import timber.log.Timber -import java.util.* internal object EventMapper { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt index fa067999..61d5a601 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.database.mapper import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.internal.database.model.TimelineEventEntity diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index ab10297f..4401d394 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -24,8 +24,6 @@ import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.Index import io.realm.annotations.LinkingObjects -import io.realm.annotations.PrimaryKey -import java.util.* internal open class EventEntity(@Index var eventId: String = "", @Index var roomId: String = "", diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt index cfa56914..5a48e022 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt @@ -16,12 +16,8 @@ package im.vector.matrix.android.internal.database.query -import im.vector.matrix.android.internal.database.model.ChunkEntity -import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.model.EventEntity.LinkFilterMode.* -import im.vector.matrix.android.internal.database.model.RoomEntity -import im.vector.matrix.android.internal.database.model.TimelineEventEntity -import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields import io.realm.Realm import io.realm.RealmList import io.realm.RealmQuery diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt index ad03bbca..1627266c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixComponent.kt @@ -27,13 +27,11 @@ import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.auth.AuthModule import im.vector.matrix.android.internal.auth.SessionParamsStore import im.vector.matrix.android.internal.network.NetworkConnectivityChecker -import im.vector.matrix.android.internal.network.RetrofitFactory import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import okhttp3.OkHttpClient import org.matrix.olm.OlmManager -import retrofit2.Retrofit @Component(modules = [MatrixModule::class, NetworkModule::class, AuthModule::class]) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Result.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Result.kt new file mode 100644 index 00000000..7d4ba7aa --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Result.kt @@ -0,0 +1,25 @@ +/* + + * Copyright 2019 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.matrix.android.internal.extensions + +import im.vector.matrix.android.api.MatrixCallback + +fun Result.foldToCallback(callback: MatrixCallback): Unit = fold( + { callback.onSuccess(it) }, + { callback.onFailure(it) } +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt index 9502cb28..412cb73c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/NetworkConnectivityChecker.kt @@ -19,35 +19,64 @@ package im.vector.matrix.android.internal.network import android.content.Context import com.novoda.merlin.Merlin import com.novoda.merlin.MerlinsBeard -import com.novoda.merlin.registerable.connection.Connectable import im.vector.matrix.android.internal.di.MatrixScope +import timber.log.Timber +import java.util.* import javax.inject.Inject +import kotlin.collections.ArrayList +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine @MatrixScope internal class NetworkConnectivityChecker @Inject constructor(context: Context) { - private val merlin = Merlin.Builder().withConnectableCallbacks().build(context) - private val merlinsBeard = MerlinsBeard.from(context) + private val merlin = Merlin.Builder() + .withConnectableCallbacks() + .withDisconnectableCallbacks() + .build(context) - private val listeners = ArrayList() + private val merlinsBeard = MerlinsBeard.Builder().build(context) + private val listeners = Collections.synchronizedList(ArrayList()) - fun register(listener: Listener) { - if (listeners.isEmpty()) { - merlin.bind() - } - listeners.add(listener) - val connectable = Connectable { - if (listeners.contains(listener)) { - listener.onConnect() + init { + merlin.bind() + merlin.registerDisconnectable { + Timber.v("On Disconnect") + val localListeners = listeners.toList() + localListeners.forEach { + it.onDisconnect() } } - merlin.registerConnectable(connectable) + merlin.registerConnectable { + Timber.v("On Connect") + val localListeners = listeners.toList() + localListeners.forEach { + it.onConnect() + } + } + } + + suspend fun waitUntilConnected() { + if (isConnected()) { + return + } else { + suspendCoroutine { continuation -> + register(object : Listener { + override fun onConnect() { + unregister(this) + continuation.resume(Unit) + } + }) + } + } + } + + fun register(listener: Listener) { + listeners.add(listener) } fun unregister(listener: Listener) { - if (listeners.remove(listener) && listeners.isEmpty()) { - merlin.unbind() - } + listeners.remove(listener) } fun isConnected(): Boolean { @@ -55,7 +84,13 @@ internal class NetworkConnectivityChecker @Inject constructor(context: Context) } interface Listener { - fun onConnect() + fun onConnect() { + + } + + fun onDisconnect() { + + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/ProgressRequestBody.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/ProgressRequestBody.kt index fabd9763..49655ab4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/ProgressRequestBody.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/ProgressRequestBody.kt @@ -18,11 +18,7 @@ package im.vector.matrix.android.internal.network import okhttp3.MediaType import okhttp3.RequestBody -import okio.Buffer -import okio.BufferedSink -import okio.ForwardingSink -import okio.Okio -import okio.Sink +import okio.* import java.io.IOException internal class ProgressRequestBody(private val delegate: RequestBody, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt index 4dfc5810..3d1e433b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt @@ -16,9 +16,6 @@ package im.vector.matrix.android.internal.network -import arrow.core.Try -import arrow.core.failure -import arrow.core.recoverWith import com.squareup.moshi.JsonDataException import com.squareup.moshi.Moshi import im.vector.matrix.android.api.failure.Failure @@ -36,8 +33,8 @@ internal class Request { private val moshi: Moshi = MoshiProvider.providesMoshi() lateinit var apiCall: Call - suspend fun execute(): Try { - return Try { + suspend fun execute(): DATA { + return try { val response = apiCall.awaitResponse() if (response.isSuccessful) { response.body() @@ -45,13 +42,13 @@ internal class Request { } else { throw manageFailure(response.errorBody(), response.code()) } - }.recoverWith { - when (it) { - is IOException -> Failure.NetworkConnection(it) + } catch (exception: Throwable) { + throw when (exception) { + is IOException -> Failure.NetworkConnection(exception) is Failure.ServerError, - is Failure.OtherServerError -> it - else -> Failure.Unknown(it) - }.failure() + is Failure.OtherServerError -> exception + else -> Failure.Unknown(exception) + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitExtensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitExtensions.kt index 7528dee2..824d74b3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitExtensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitExtensions.kt @@ -19,7 +19,9 @@ package im.vector.matrix.android.internal.network import kotlinx.coroutines.suspendCancellableCoroutine -import retrofit2.* +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt index 4e9ff6f9..70143f7c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/RetrofitFactory.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.network import com.squareup.moshi.Moshi -import okhttp3.Interceptor import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 09baebb2..2922b7d6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -47,6 +47,7 @@ import im.vector.matrix.android.internal.session.sync.job.SyncWorker import im.vector.matrix.android.internal.worker.WorkManagerUtil import timber.log.Timber import javax.inject.Inject +import javax.inject.Provider @SessionScope internal class DefaultSession @Inject constructor(override val sessionParams: SessionParams, @@ -64,7 +65,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private val pushersService: Lazy, private val cryptoService: Lazy, private val fileService: Lazy, - private val syncThread: SyncThread, + private val syncThreadProvider: Provider, private val contentUrlResolver: ContentUrlResolver, private val contentUploadProgressTracker: ContentUploadStateTracker, private val initialSyncProgressService: Lazy) @@ -84,6 +85,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private var isOpen = false + private var syncThread: SyncThread? = null @MainThread override fun open() { @@ -105,21 +107,23 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se SyncWorker.stopAnyBackgroundSync(context) } - override fun startSync(fromForeground : Boolean) { + override fun startSync(fromForeground: Boolean) { Timber.i("Starting sync thread") assert(isOpen) - syncThread.setInitialForeground(fromForeground) - if (!syncThread.isAlive) { - syncThread.start() + val localSyncThread = getSyncThread() + localSyncThread.setInitialForeground(fromForeground) + if (!localSyncThread.isAlive) { + localSyncThread.start() } else { - syncThread.restart() + localSyncThread.restart() Timber.w("Attempt to start an already started thread") } } override fun stopSync() { assert(isOpen) - syncThread.kill() + syncThread?.kill() + syncThread = null } override fun close() { @@ -131,7 +135,13 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se } override fun syncState(): LiveData { - return syncThread.liveState() + return getSyncThread().liveState() + } + + private fun getSyncThread(): SyncThread { + return syncThread ?: syncThreadProvider.get().also { + syncThread = it + } } @MainThread @@ -139,21 +149,20 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se Timber.w("SIGN_OUT: start") assert(isOpen) - //Timber.w("SIGN_OUT: kill sync thread") - //syncThread.kill() Timber.w("SIGN_OUT: call webservice") return signOutService.get().signOut(object : MatrixCallback { override fun onSuccess(data: Unit) { Timber.w("SIGN_OUT: call webservice -> SUCCESS: clear cache") - + stopSync() + stopAnyBackgroundSync() // Clear the cache cacheService.get().clearCache(object : MatrixCallback { override fun onSuccess(data: Unit) { Timber.w("SIGN_OUT: clear cache -> SUCCESS: clear crypto cache") cryptoService.get().clearCryptoCache(MatrixCallbackDelegate(callback)) - WorkManagerUtil.cancelAllWorks(context) + callback.onSuccess(Unit) } override fun onFailure(failure: Throwable) { @@ -172,6 +181,22 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se }) } + override fun clearCache(callback: MatrixCallback) { + stopSync() + stopAnyBackgroundSync() + cacheService.get().clearCache(object : MatrixCallback { + override fun onSuccess(data: Unit) { + startSync(true) + callback.onSuccess(data) + } + + override fun onFailure(failure: Throwable) { + startSync(true) + callback.onFailure(failure) + } + }) + } + override fun contentUrlResolver() = contentUrlResolver override fun contentUploadProgressTracker() = contentUploadProgressTracker diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt index 36f42895..26f43be0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionComponent.kt @@ -23,7 +23,6 @@ import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.internal.crypto.CryptoModule import im.vector.matrix.android.internal.di.MatrixComponent import im.vector.matrix.android.internal.network.NetworkConnectivityChecker -import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule import im.vector.matrix.android.internal.session.cache.CacheModule import im.vector.matrix.android.internal.session.content.ContentModule import im.vector.matrix.android.internal.session.content.UploadContentWorker @@ -43,6 +42,7 @@ import im.vector.matrix.android.internal.session.sync.SyncTask import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.sync.job.SyncWorker import im.vector.matrix.android.internal.session.user.UserModule +import im.vector.matrix.android.internal.session.user.accountdata.AccountDataModule import im.vector.matrix.android.internal.task.TaskExecutor @Component(dependencies = [MatrixComponent::class], diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index ca8461f1..38637fb7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -38,8 +38,8 @@ import im.vector.matrix.android.internal.network.RetrofitFactory import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver -import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver import im.vector.matrix.android.internal.session.room.prune.EventsPruner +import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver import im.vector.matrix.android.internal.util.md5 import io.realm.RealmConfiguration import okhttp3.OkHttpClient diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt index b9449d0c..96f6fbd8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/CacheModule.kt @@ -21,7 +21,6 @@ import dagger.Module import dagger.Provides import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.internal.di.SessionDatabase -import im.vector.matrix.android.internal.session.SessionScope import io.realm.RealmConfiguration @Module diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt index 0dd39c10..5af4ee74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/ClearCacheTask.kt @@ -16,27 +16,18 @@ package im.vector.matrix.android.internal.session.cache -import arrow.core.Try -import im.vector.matrix.android.internal.session.SessionScope +import im.vector.matrix.android.internal.database.awaitTransaction import im.vector.matrix.android.internal.task.Task -import io.realm.Realm import io.realm.RealmConfiguration import javax.inject.Inject -import javax.inject.Named internal interface ClearCacheTask : Task internal class RealmClearCacheTask @Inject constructor(private val realmConfiguration: RealmConfiguration) : ClearCacheTask { - override suspend fun execute(params: Unit): Try { - return Try { - val realm = Realm.getInstance(realmConfiguration) - - realm.executeTransaction { - it.deleteAll() - } - - realm.close() + override suspend fun execute(params: Unit) { + awaitTransaction(realmConfiguration) { + it.deleteAll() } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt index c23c9eea..12684965 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/cache/DefaultCacheService.kt @@ -20,16 +20,19 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.cache.CacheService import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.toConfigurableTask +import im.vector.matrix.android.internal.task.configureWith import javax.inject.Inject -internal class DefaultCacheService @Inject constructor(@SessionDatabase private val clearCacheTask: ClearCacheTask, +internal class DefaultCacheService @Inject constructor(@SessionDatabase + private val clearCacheTask: ClearCacheTask, private val taskExecutor: TaskExecutor) : CacheService { override fun clearCache(callback: MatrixCallback) { + taskExecutor.cancelAll() clearCacheTask - .toConfigurableTask() - .dispatchTo(callback) + .configureWith { + this.callback = callback + } .executeBy(taskExecutor) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt index 5fa5d0a2..a5da026a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/filter/DefaultSaveFilterTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.filter -import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task @@ -39,15 +38,12 @@ internal class DefaultSaveFilterTask @Inject constructor(private val sessionPara private val filterRepository: FilterRepository ) : SaveFilterTask { - override suspend fun execute(params: SaveFilterTask.Params): Try { - return executeRequest { + override suspend fun execute(params: SaveFilterTask.Params) { + val filterResponse = executeRequest { // TODO auto retry apiCall = filterAPI.uploadFilter(sessionParams.credentials.userId, params.filter) - }.flatMap { filterResponse -> - Try { - filterRepository.storeFilterId(params.filter, filterResponse.filterId) - } } + filterRepository.storeFilterId(params.filter, filterResponse.filterId) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt index ed8c11bc..6964ccf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt @@ -16,10 +16,6 @@ package im.vector.matrix.android.internal.session.group -import arrow.core.Try -import arrow.core.fix -import arrow.instances.`try`.monad.monad -import arrow.typeclasses.binding import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.query.where @@ -28,8 +24,6 @@ import im.vector.matrix.android.internal.session.group.model.GroupRooms import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse import im.vector.matrix.android.internal.session.group.model.GroupUsers import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync -import io.realm.kotlin.createObject import javax.inject.Inject internal interface GetGroupDataTask : Task { @@ -43,7 +37,7 @@ internal class DefaultGetGroupDataTask @Inject constructor( private val monarchy: Monarchy ) : GetGroupDataTask { - override suspend fun execute(params: GetGroupDataTask.Params): Try { + override suspend fun execute(params: GetGroupDataTask.Params) { val groupId = params.groupId val groupSummary = executeRequest { apiCall = groupAPI.getSummary(groupId) @@ -54,20 +48,18 @@ internal class DefaultGetGroupDataTask @Inject constructor( val groupUsers = executeRequest { apiCall = groupAPI.getUsers(groupId) } - return Try.monad().binding { - insertInDb(groupSummary.bind(), groupRooms.bind(), groupUsers.bind(), groupId).bind() - }.fix() + insertInDb(groupSummary, groupRooms, groupUsers, groupId) } private fun insertInDb(groupSummary: GroupSummaryResponse, groupRooms: GroupRooms, groupUsers: GroupUsers, - groupId: String): Try { - return monarchy - .tryTransactionSync { realm -> + groupId: String) { + monarchy + .writeAsync { realm -> val groupSummaryEntity = GroupSummaryEntity.where(realm, groupId).findFirst() - ?: realm.createObject(groupId) + ?: realm.createObject(GroupSummaryEntity::class.java, groupId) groupSummaryEntity.avatarUrl = groupSummary.profile?.avatarUrl ?: "" val name = groupSummary.profile?.name @@ -82,7 +74,6 @@ internal class DefaultGetGroupDataTask @Inject constructor( val userIds = groupUsers.users.map { it.userId } groupSummaryEntity.userIds.clear() groupSummaryEntity.userIds.addAll(userIds) - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt index ed0552da..a574df7c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGroupService.kt @@ -25,7 +25,6 @@ import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.model.GroupSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import javax.inject.Inject internal class DefaultGroupService @Inject constructor(private val monarchy: Monarchy) : GroupService { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt index 081739cb..913a468b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.group import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import arrow.core.Try import com.squareup.moshi.JsonClass import im.vector.matrix.android.internal.worker.SessionWorkerParams import im.vector.matrix.android.internal.worker.WorkerParamsFactory @@ -43,16 +42,15 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() sessionComponent.inject(this) - val results = params.groupIds.map { groupId -> - fetchGroupData(groupId) + runCatching { fetchGroupData(groupId) } } - val isSuccessful = results.none { it.isFailure() } + val isSuccessful = results.none { it.isFailure } return if (isSuccessful) Result.success() else Result.retry() } - private suspend fun fetchGroupData(groupId: String): Try { - return getGroupDataTask.execute(GetGroupDataTask.Params(groupId)) + private suspend fun fetchGroupData(groupId: String) { + getGroupDataTask.execute(GetGroupDataTask.Params(groupId)) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt index fcfe9f0a..47905ecc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt @@ -22,20 +22,15 @@ import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.database.RealmLiveEntityObserver -import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.SessionDatabase -import im.vector.matrix.android.internal.session.room.prune.PruneEventTask -import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory import io.realm.OrderedCollectionChangeSet import io.realm.RealmConfiguration import io.realm.RealmResults -import timber.log.Timber import javax.inject.Inject private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt index 7cddebde..83b89701 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt @@ -83,9 +83,10 @@ internal class DefaultPushRuleService @Inject constructor( override fun updatePushRuleEnableStatus(kind: String, pushRule: PushRule, enabled: Boolean, callback: MatrixCallback): Cancelable { return updatePushRuleEnableStatusTask - .configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) + .configureWith(UpdatePushRuleEnableStatusTask.Params(kind, pushRule, enabled)) { + this.callback = callback + } // TODO Fetch the rules - .dispatchTo(callback) .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt index d196e563..a434c6e9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.notification -import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.api.session.events.model.Event @@ -41,48 +40,45 @@ internal class DefaultProcessEventForPushTask @Inject constructor( private val sessionParams: SessionParams ) : ProcessEventForPushTask { - - override suspend fun execute(params: ProcessEventForPushTask.Params): Try { - return Try { - // Handle left rooms - params.syncResponse.leave.keys.forEach { - defaultPushRuleService.dispatchRoomLeft(it) - } - val newJoinEvents = params.syncResponse.join - .map { entries -> - entries.value.timeline?.events?.map { it.copy(roomId = entries.key) } - } - .fold(emptyList(), { acc, next -> - acc + (next ?: emptyList()) - }) - val inviteEvents = params.syncResponse.invite - .map { entries -> - entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) } - } - .fold(emptyList(), { acc, next -> - acc + (next ?: emptyList()) - }) - val allEvents = (newJoinEvents + inviteEvents).filter { event -> - when (event.type) { - EventType.MESSAGE, - EventType.REDACTION, - EventType.ENCRYPTED, - EventType.STATE_ROOM_MEMBER -> true - else -> false - } - }.filter { - it.senderId != sessionParams.credentials.userId - } - Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" + - " to check for push rules with ${params.rules.size} rules") - allEvents.forEach { event -> - fulfilledBingRule(event, params.rules)?.let { - Timber.v("[PushRules] Rule $it match for event ${event.eventId}") - defaultPushRuleService.dispatchBing(event, it) - } - } - defaultPushRuleService.dispatchFinish() + override suspend fun execute(params: ProcessEventForPushTask.Params) { + // Handle left rooms + params.syncResponse.leave.keys.forEach { + defaultPushRuleService.dispatchRoomLeft(it) } + val newJoinEvents = params.syncResponse.join + .map { entries -> + entries.value.timeline?.events?.map { it.copy(roomId = entries.key) } + } + .fold(emptyList(), { acc, next -> + acc + (next ?: emptyList()) + }) + val inviteEvents = params.syncResponse.invite + .map { entries -> + entries.value.inviteState?.events?.map { it.copy(roomId = entries.key) } + } + .fold(emptyList(), { acc, next -> + acc + (next ?: emptyList()) + }) + val allEvents = (newJoinEvents + inviteEvents).filter { event -> + when (event.type) { + EventType.MESSAGE, + EventType.REDACTION, + EventType.ENCRYPTED, + EventType.STATE_ROOM_MEMBER -> true + else -> false + } + }.filter { + it.senderId != sessionParams.credentials.userId + } + Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" + + " to check for push rules with ${params.rules.size} rules") + allEvents.forEach { event -> + fulfilledBingRule(event, params.rules)?.let { + Timber.v("[PushRules] Rule $it match for event ${event.eventId}") + defaultPushRuleService.dispatchBing(event, it) + } + } + defaultPushRuleService.dispatchFinish() } private fun fulfilledBingRule(event: Event, rules: List): PushRule? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt index 6ac59607..bc587d91 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/AddHttpPusherWorker.kt @@ -26,6 +26,7 @@ import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.util.awaitTransaction import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.getSessionComponent import javax.inject.Inject @@ -55,15 +56,14 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters) if (pusher.pushKey.isBlank()) { return Result.failure() } - - val result = executeRequest { - apiCall = pushersAPI.setPusher(pusher) - } - return result.fold({ - when (it) { + return try { + setPusher(pusher, params.userId) + Result.success() + } catch (exception: Throwable) { + when (exception) { is Failure.NetworkConnection -> Result.retry() else -> { - monarchy.runTransactionSync { realm -> + monarchy.awaitTransaction { realm -> PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst()?.let { //update it it.state = PusherState.FAILED_TO_REGISTER @@ -73,28 +73,31 @@ internal class AddHttpPusherWorker(context: Context, params: WorkerParameters) Result.failure() } } - }, { - monarchy.runTransactionSync { realm -> - val echo = PusherEntity.where(realm, params.userId, pusher.pushKey).findFirst() - if (echo != null) { - //update it - echo.appDisplayName = pusher.appDisplayName - echo.appId = pusher.appId - echo.kind = pusher.kind - echo.lang = pusher.lang - echo.profileTag = pusher.profileTag - echo.data?.format = pusher.data?.format - echo.data?.url = pusher.data?.url - echo.state = PusherState.REGISTERED - } else { - pusher.toEntity(params.userId).also { - it.state = PusherState.REGISTERED - realm.insertOrUpdate(it) - } + } + } + + private suspend fun setPusher(pusher: JsonPusher, userId: String) { + executeRequest { + apiCall = pushersAPI.setPusher(pusher) + } + monarchy.awaitTransaction { realm -> + val echo = PusherEntity.where(realm, userId, pusher.pushKey).findFirst() + if (echo != null) { + //update it + echo.appDisplayName = pusher.appDisplayName + echo.appId = pusher.appId + echo.kind = pusher.kind + echo.lang = pusher.lang + echo.profileTag = pusher.profileTag + echo.data?.format = pusher.data?.format + echo.data?.url = pusher.data?.url + echo.state = PusherState.REGISTERED + } else { + pusher.toEntity(userId).also { + it.state = PusherState.REGISTERED + realm.insertOrUpdate(it) } } - Result.success() - - }) + } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt index 5d9af0d3..1c03a4f7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/DefaultPusherService.kt @@ -29,7 +29,6 @@ import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.task.toConfigurableTask import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory @@ -50,7 +49,7 @@ internal class DefaultPusherService @Inject constructor( override fun refreshPushers() { getPusherTask - .toConfigurableTask() + .configureWith() .executeBy(taskExecutor) } @@ -85,8 +84,9 @@ internal class DefaultPusherService @Inject constructor( override fun removeHttpPusher(pushkey: String, appId: String, callback: MatrixCallback) { val params = RemovePusherTask.Params(sessionParam.credentials.userId, pushkey, appId) removePusherTask - .configureWith(params) - .dispatchTo(callback) + .configureWith(params) { + this.callback = callback + } //.enableRetry() ?? .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt index cbd8dcac..627c6f89 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushRulesTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse @@ -24,7 +23,7 @@ import im.vector.matrix.android.internal.database.model.PushRulesEntity import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import javax.inject.Inject @@ -39,58 +38,57 @@ internal class DefaultGetPushRulesTask @Inject constructor(private val pushRules private val monarchy: Monarchy, private val sessionParams: SessionParams) : GetPushRulesTask { - override suspend fun execute(params: GetPushRulesTask.Params): Try { - return executeRequest { + override suspend fun execute(params: GetPushRulesTask.Params) { + val response = executeRequest { apiCall = pushRulesApi.getAllRules() - }.flatMap { response -> - val scope = params.scope - return monarchy.tryTransactionSync { realm -> - //clear existings? - //TODO - realm.where(PushRulesEntity::class.java) - .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) - .findAll().deleteAllFromRealm() + } + val scope = params.scope + monarchy.awaitTransaction { realm -> + //clear existings? + //TODO + realm.where(PushRulesEntity::class.java) + .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) + .findAll().deleteAllFromRealm() - val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content") - response.global.content?.forEach { rule -> - PushRulesMapper.map(rule).also { - content.pushRules.add(it) - } + val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content") + response.global.content?.forEach { rule -> + PushRulesMapper.map(rule).also { + content.pushRules.add(it) } - realm.insertOrUpdate(content) - - val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override") - response.global.override?.forEach { rule -> - PushRulesMapper.map(rule).also { - override.pushRules.add(it) - } - } - realm.insertOrUpdate(override) - - val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room") - response.global.room?.forEach { rule -> - PushRulesMapper.map(rule).also { - rooms.pushRules.add(it) - } - } - realm.insertOrUpdate(rooms) - - val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender") - response.global.sender?.forEach { rule -> - PushRulesMapper.map(rule).also { - senders.pushRules.add(it) - } - } - realm.insertOrUpdate(senders) - - val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride") - response.global.underride?.forEach { rule -> - PushRulesMapper.map(rule).also { - underrides.pushRules.add(it) - } - } - realm.insertOrUpdate(underrides) } + realm.insertOrUpdate(content) + + val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override") + response.global.override?.forEach { rule -> + PushRulesMapper.map(rule).also { + override.pushRules.add(it) + } + } + realm.insertOrUpdate(override) + + val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room") + response.global.room?.forEach { rule -> + PushRulesMapper.map(rule).also { + rooms.pushRules.add(it) + } + } + realm.insertOrUpdate(rooms) + + val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender") + response.global.sender?.forEach { rule -> + PushRulesMapper.map(rule).also { + senders.pushRules.add(it) + } + } + realm.insertOrUpdate(senders) + + val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride") + response.global.underride?.forEach { rule -> + PushRulesMapper.map(rule).also { + underrides.pushRules.add(it) + } + } + realm.insertOrUpdate(underrides) } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt index 57ea8a38..b3199ea3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/GetPushersTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.pushers.PusherState @@ -24,7 +23,7 @@ import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.model.PusherEntityFields import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import javax.inject.Inject internal interface GetPushersTask : Task @@ -33,20 +32,19 @@ internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: private val monarchy: Monarchy, private val sessionParams: SessionParams) : GetPushersTask { - override suspend fun execute(params: Unit): Try { - return executeRequest { + override suspend fun execute(params: Unit) { + val response = executeRequest { apiCall = pushersAPI.getPushers() - }.flatMap { response -> - monarchy.tryTransactionSync { realm -> - //clear existings? - realm.where(PusherEntity::class.java) - .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) - .findAll().deleteAllFromRealm() - response.pushers?.forEach { jsonPusher -> - jsonPusher.toEntity(sessionParams.credentials.userId).also { - it.state = PusherState.REGISTERED - realm.insertOrUpdate(it) - } + } + monarchy.awaitTransaction { realm -> + //clear existings? + realm.where(PusherEntity::class.java) + .equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId) + .findAll().deleteAllFromRealm() + response.pushers?.forEach { jsonPusher -> + jsonPusher.toEntity(sessionParams.credentials.userId).also { + it.state = PusherState.REGISTERED + realm.insertOrUpdate(it) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt index 92f62fe0..0ed7175e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/RemovePusherTask.kt @@ -16,16 +16,15 @@ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.session.pushers.Pusher import im.vector.matrix.android.api.session.pushers.PusherState import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.PusherEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction +import io.realm.Realm import javax.inject.Inject internal interface RemovePusherTask : Task { @@ -39,42 +38,33 @@ internal class DefaultRemovePusherTask @Inject constructor( private val monarchy: Monarchy ) : RemovePusherTask { - override suspend fun execute(params: RemovePusherTask.Params): Try { - return Try { - var existing: Pusher? = null - monarchy.runTransactionSync { - val existingEntity = PusherEntity.where(it, params.userId, params.pushKey).findFirst() - existingEntity?.state == PusherState.UNREGISTERING - existing = existingEntity?.asDomain() - } - if (existing == null) { - throw Exception("No existing pusher") - } else { - existing!! - } - }.flatMap { - executeRequest { - val deleteBody = JsonPusher( - pushKey = params.pushKey, - appId = params.pushAppId, - // kind null deletes the pusher - kind = null, - appDisplayName = it.appDisplayName ?: "", - deviceDisplayName = it.deviceDisplayName ?: "", - profileTag = it.profileTag ?: "", - lang = it.lang, - data = JsonPusherData(it.data.url, it.data.format), - append = false - ) - apiCall = pushersAPI.setPusher(deleteBody) - } - }.flatMap { - monarchy.tryTransactionSync { - val existing = PusherEntity.where(it, params.userId, params.pushKey).findFirst() - existing?.deleteFromRealm() - } + override suspend fun execute(params: RemovePusherTask.Params) { + monarchy.awaitTransaction { realm -> + val existingEntity = PusherEntity.where(realm, params.userId, params.pushKey).findFirst() + existingEntity?.state = PusherState.UNREGISTERING + } + + val existing = Realm.getInstance(monarchy.realmConfiguration).use { realm -> + PusherEntity.where(realm, params.userId, params.pushKey).findFirst()?.asDomain() + } ?: throw Exception("No existing pusher") + + val deleteBody = JsonPusher( + pushKey = params.pushKey, + appId = params.pushAppId, + // kind null deletes the pusher + kind = null, + appDisplayName = existing.appDisplayName ?: "", + deviceDisplayName = existing.deviceDisplayName ?: "", + profileTag = existing.profileTag ?: "", + lang = existing.lang, + data = JsonPusherData(existing.data.url, existing.data.format), + append = false + ) + executeRequest { + apiCall = pushersAPI.setPusher(deleteBody) + } + monarchy.awaitTransaction { + PusherEntity.where(it, params.userId, params.pushKey).findFirst()?.deleteFromRealm() } } - - } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt index 9af67a6b..828d9e71 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/pushers/UpdatePushRuleEnableStatusTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.pushers -import arrow.core.Try import im.vector.matrix.android.api.pushrules.rest.PushRule import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.task.Task @@ -31,7 +30,7 @@ internal interface UpdatePushRuleEnableStatusTask : Task { + override suspend fun execute(params: UpdatePushRuleEnableStatusTask.Params) { return executeRequest { apiCall = pushRulesApi.updateEnableRuleStatus(params.kind, params.pushRule.ruleId, params.enabled) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt index 158802f8..4aeace70 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomDirectoryService.kt @@ -27,7 +27,6 @@ import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyPro import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.task.toConfigurableTask import javax.inject.Inject internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask, @@ -39,22 +38,25 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu publicRoomsParams: PublicRoomsParams, callback: MatrixCallback): Cancelable { return getPublicRoomTask - .configureWith(GetPublicRoomTask.Params(server, publicRoomsParams)) - .dispatchTo(callback) + .configureWith(GetPublicRoomTask.Params(server, publicRoomsParams)) { + this.callback = callback + } .executeBy(taskExecutor) } override fun joinRoom(roomId: String, callback: MatrixCallback): Cancelable { return joinRoomTask - .configureWith(JoinRoomTask.Params(roomId)) - .dispatchTo(callback) + .configureWith(JoinRoomTask.Params(roomId)) { + this.callback = callback + } .executeBy(taskExecutor) } override fun getThirdPartyProtocol(callback: MatrixCallback>): Cancelable { return getThirdPartyProtocolsTask - .toConfigurableTask() - .dispatchTo(callback) + .configureWith { + this.callback = callback + } .executeBy(taskExecutor) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index 5a7f82b0..bd5462b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -34,7 +34,7 @@ import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith -import im.vector.matrix.android.internal.util.fetchManaged +import io.realm.Realm import javax.inject.Inject internal class DefaultRoomService @Inject constructor(private val monarchy: Monarchy, @@ -46,14 +46,20 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback): Cancelable { return createRoomTask - .configureWith(createRoomParams) - .dispatchTo(callback) + .configureWith(createRoomParams) { + this.callback = callback + } .executeBy(taskExecutor) } override fun getRoom(roomId: String): Room? { - monarchy.fetchManaged { RoomEntity.where(it, roomId).findFirst() } ?: return null - return roomFactory.create(roomId) + return Realm.getInstance(monarchy.realmConfiguration).use { + if (RoomEntity.where(it, roomId).findFirst() != null) { + roomFactory.create(roomId) + } else { + null + } + } } override fun liveRoomSummaries(): LiveData> { @@ -69,8 +75,9 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona override fun joinRoom(roomId: String, viaServers: List, callback: MatrixCallback): Cancelable { return joinRoomTask - .configureWith(JoinRoomTask.Params(roomId, viaServers)) - .dispatchTo(callback) + .configureWith(JoinRoomTask.Params(roomId, viaServers)) { + this.callback = callback + } .executeBy(taskExecutor) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt index 2e1a4273..786ba168 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationsAggregationTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.room -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.MXCryptoError @@ -31,7 +30,7 @@ import im.vector.matrix.android.internal.database.query.create import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm import timber.log.Timber import javax.inject.Inject @@ -54,10 +53,10 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor( //OPT OUT serer aggregation until API mature enough private val SHOULD_HANDLE_SERVER_AGREGGATION = false - override suspend fun execute(params: EventRelationsAggregationTask.Params): Try { + override suspend fun execute(params: EventRelationsAggregationTask.Params) { val events = params.events val userId = params.userId - return monarchy.tryTransactionSync { realm -> + monarchy.awaitTransaction { realm -> Timber.v(">>> DefaultEventRelationsAggregationTask[${params.hashCode()}] called with ${events.size} events") update(realm, events, userId) Timber.v("<<< DefaultEventRelationsAggregationTask[${params.hashCode()}] finished") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt index a310cc31..1c64c91b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper -import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask @@ -41,7 +40,6 @@ import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineSe import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.task.TaskExecutor -import io.realm.RealmConfiguration import javax.inject.Inject internal class RoomFactory @Inject constructor(private val context: Context, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt index 04982c9a..93c2eb0b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.session.room.create -import arrow.core.Try -import arrow.core.recoverWith import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams @@ -34,7 +32,7 @@ import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.RealmConfiguration import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -46,60 +44,49 @@ internal class DefaultCreateRoomTask @Inject constructor(private val roomAPI: Ro private val directChatsHelper: DirectChatsHelper, private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val readMarkersTask: SetReadMarkersTask, - @SessionDatabase private val realmConfiguration: RealmConfiguration) : CreateRoomTask { + @SessionDatabase + private val realmConfiguration: RealmConfiguration) : CreateRoomTask { - override suspend fun execute(params: CreateRoomParams): Try { - return executeRequest { + override suspend fun execute(params: CreateRoomParams): String { + val createRoomResponse = executeRequest { apiCall = roomAPI.createRoom(params) - }.flatMap { createRoomResponse -> - val roomId = createRoomResponse.roomId!! - // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) - val rql = RealmQueryLatch(realmConfiguration) { realm -> - realm.where(RoomEntity::class.java) - .equalTo(RoomEntityFields.ROOM_ID, roomId) - } - try { - rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) - Try.just(roomId) - } catch (exception: Exception) { - Try.raise(CreateRoomFailure.CreatedWithTimeout) - } - }.flatMap { roomId -> - if (params.isDirect()) { - handleDirectChatCreation(params, roomId) - } else { - Try.just(roomId) - } - }.flatMap { roomId -> - setReadMarkers(roomId) } + val roomId = createRoomResponse.roomId!! + // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + val rql = RealmQueryLatch(realmConfiguration) { realm -> + realm.where(RoomEntity::class.java) + .equalTo(RoomEntityFields.ROOM_ID, roomId) + } + try { + rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) + } catch (exception: Exception) { + throw CreateRoomFailure.CreatedWithTimeout + } + if (params.isDirect()) { + handleDirectChatCreation(params, roomId) + } + setReadMarkers(roomId) + return roomId } - private suspend fun handleDirectChatCreation(params: CreateRoomParams, roomId: String): Try { + private suspend fun handleDirectChatCreation(params: CreateRoomParams, roomId: String) { val otherUserId = params.getFirstInvitedUserId() - ?: return Try.raise(IllegalStateException("You can't create a direct room without an invitedUser")) + ?: throw IllegalStateException("You can't create a direct room without an invitedUser") - return monarchy.tryTransactionSync { realm -> + monarchy.awaitTransaction { realm -> RoomSummaryEntity.where(realm, roomId).findFirst()?.apply { this.directUserId = otherUserId this.isDirect = true } - }.flatMap { - val directChats = directChatsHelper.getLocalUserAccount() - updateUserAccountDataTask.execute(UpdateUserAccountDataTask.DirectChatParams(directMessages = directChats)) - }.flatMap { - Try.just(roomId) } + val directChats = directChatsHelper.getLocalUserAccount() + updateUserAccountDataTask.execute(UpdateUserAccountDataTask.DirectChatParams(directMessages = directChats)) } - private suspend fun setReadMarkers(roomId: String): Try { + private suspend fun setReadMarkers(roomId: String) { val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true) - return readMarkersTask - .execute(setReadMarkerParams) - .flatMap { - Try.just(roomId) - } + return readMarkersTask.execute(setReadMarkerParams) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/RoomCreateEventLiveObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/RoomCreateEventLiveObserver.kt index 9b652fe3..1bc6b965 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/RoomCreateEventLiveObserver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/RoomCreateEventLiveObserver.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.VersioningState import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent -import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt index b2cc1a74..a24765e0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetPublicRoomTask.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.session.room.directory -import arrow.core.Try import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -34,7 +32,7 @@ internal interface GetPublicRoomTask : Task { + override suspend fun execute(params: GetPublicRoomTask.Params): PublicRoomsResponse { return executeRequest { apiCall = roomAPI.publicRooms(params.server, params.publicRoomsParams) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt index cd53c92d..4d3bc8fd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/directory/GetThirdPartyProtocolsTask.kt @@ -16,19 +16,17 @@ package im.vector.matrix.android.internal.session.room.directory -import arrow.core.Try import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject internal interface GetThirdPartyProtocolsTask : Task> -internal class DefaultGetThirdPartyProtocolsTask @Inject constructor (private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask { +internal class DefaultGetThirdPartyProtocolsTask @Inject constructor(private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask { - override suspend fun execute(params: Unit): Try> { + override suspend fun execute(params: Unit): Map { return executeRequest { apiCall = roomAPI.thirdPartyProtocols() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt index f5d46b9b..7fca1e42 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/DefaultMembershipService.kt @@ -42,10 +42,12 @@ internal class DefaultMembershipService @Inject constructor(private val roomId: private val leaveRoomTask: LeaveRoomTask ) : MembershipService { - override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable { + override fun loadRoomMembersIfNeeded(matrixCallback: MatrixCallback): Cancelable { val params = LoadRoomMembersTask.Params(roomId, Membership.LEAVE) - return loadRoomMembersTask.configureWith(params) - .dispatchTo(matrixCallback) + return loadRoomMembersTask + .configureWith(params) { + this.callback = matrixCallback + } .executeBy(taskExecutor) } @@ -77,22 +79,28 @@ internal class DefaultMembershipService @Inject constructor(private val roomId: override fun invite(userId: String, callback: MatrixCallback): Cancelable { val params = InviteTask.Params(roomId, userId) - return inviteTask.configureWith(params) - .dispatchTo(callback) + return inviteTask + .configureWith(params) { + this.callback = callback + } .executeBy(taskExecutor) } override fun join(viaServers: List, callback: MatrixCallback): Cancelable { val params = JoinRoomTask.Params(roomId, viaServers) - return joinTask.configureWith(params) - .dispatchTo(callback) + return joinTask + .configureWith(params) { + this.callback = callback + } .executeBy(taskExecutor) } override fun leave(callback: MatrixCallback): Cancelable { val params = LeaveRoomTask.Params(roomId) - return leaveRoomTask.configureWith(params) - .dispatchTo(callback) + return leaveRoomTask + .configureWith(params) { + this.callback = callback + } .executeBy(taskExecutor) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt index a3090605..709e8da9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/LoadRoomMembersTask.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.session.room.membership -import arrow.core.Try -import com.squareup.moshi.JsonReader import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.internal.database.helper.addStateEvent @@ -30,14 +28,12 @@ import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.user.UserEntityFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm import io.realm.kotlin.createObject -import okhttp3.ResponseBody -import okio.Okio import javax.inject.Inject -internal interface LoadRoomMembersTask : Task { +internal interface LoadRoomMembersTask : Task { data class Params( val roomId: String, @@ -51,39 +47,36 @@ internal class DefaultLoadRoomMembersTask @Inject constructor(private val roomAP private val roomSummaryUpdater: RoomSummaryUpdater ) : LoadRoomMembersTask { - override suspend fun execute(params: LoadRoomMembersTask.Params): Try { - return if (areAllMembersAlreadyLoaded(params.roomId)) { - Try.just(true) - } else { - val lastToken = syncTokenStore.getLastToken() - executeRequest { - apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value) - }.flatMap { response -> - insertInDb(response, params.roomId) - }.map { true } + override suspend fun execute(params: LoadRoomMembersTask.Params) { + if (areAllMembersAlreadyLoaded(params.roomId)) { + return } + val lastToken = syncTokenStore.getLastToken() + val response = executeRequest { + apiCall = roomAPI.getMembers(params.roomId, lastToken, null, params.excludeMembership?.value) + } + insertInDb(response, params.roomId) } - private fun insertInDb(response: RoomMembersResponse, roomId: String): Try { - return monarchy - .tryTransactionSync { realm -> - // We ignore all the already known members - val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + private suspend fun insertInDb(response: RoomMembersResponse, roomId: String) { + monarchy.awaitTransaction { realm -> + // We ignore all the already known members + val roomEntity = RoomEntity.where(realm, roomId).findFirst() + ?: realm.createObject(roomId) - for (roomMemberEvent in response.roomMemberEvents) { - roomEntity.addStateEvent(roomMemberEvent) - UserEntityFactory.createOrNull(roomMemberEvent)?.also { - realm.insertOrUpdate(it) - } - } - roomEntity.chunks.flatMap { it.timelineEvents }.forEach { - it.updateSenderData() - } - roomEntity.areAllMembersLoaded = true - roomSummaryUpdater.update(realm, roomId) + for (roomMemberEvent in response.roomMemberEvents) { + roomEntity.addStateEvent(roomMemberEvent) + UserEntityFactory.createOrNull(roomMemberEvent)?.also { + realm.insertOrUpdate(it) } + } + roomEntity.chunks.flatMap { it.timelineEvents }.forEach { + it.updateSenderData() + } + roomEntity.areAllMembersLoaded = true + roomSummaryUpdater.update(realm, roomId) + } } private fun areAllMembersAlreadyLoaded(roomId: String): Boolean { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt index 53e9e55a..815fc96e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -22,11 +22,7 @@ import im.vector.matrix.android.R import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel -import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.RoomAliasesContent -import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent -import im.vector.matrix.android.api.session.room.model.RoomMember -import im.vector.matrix.android.api.session.room.model.RoomNameContent +import im.vector.matrix.android.api.session.room.model.* import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntityFields diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt index 5f656c01..68d72c6b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/InviteTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.membership.joining -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -33,7 +31,7 @@ internal interface InviteTask : Task { internal class DefaultInviteTask @Inject constructor(private val roomAPI: RoomAPI) : InviteTask { - override suspend fun execute(params: InviteTask.Params): Try { + override suspend fun execute(params: InviteTask.Params) { return executeRequest { val body = InviteBody(params.userId) apiCall = roomAPI.invite(params.roomId, body) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt index bc76a211..5ca23a0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/joining/JoinRoomTask.kt @@ -16,8 +16,6 @@ package im.vector.matrix.android.internal.session.room.membership.joining -import arrow.core.Try -import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure import im.vector.matrix.android.api.session.room.failure.JoinRoomFailure import im.vector.matrix.android.internal.database.RealmQueryLatch import im.vector.matrix.android.internal.database.model.RoomEntity @@ -40,32 +38,30 @@ internal interface JoinRoomTask : Task { internal class DefaultJoinRoomTask @Inject constructor(private val roomAPI: RoomAPI, private val readMarkersTask: SetReadMarkersTask, - @SessionDatabase private val realmConfiguration: RealmConfiguration) : JoinRoomTask { + @SessionDatabase + private val realmConfiguration: RealmConfiguration) : JoinRoomTask { - override suspend fun execute(params: JoinRoomTask.Params): Try { - return executeRequest { + override suspend fun execute(params: JoinRoomTask.Params) { + executeRequest { apiCall = roomAPI.join(params.roomId, params.viaServers, HashMap()) - }.flatMap { - val roomId = params.roomId - // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) - val rql = RealmQueryLatch(realmConfiguration) { realm -> - realm.where(RoomEntity::class.java) - .equalTo(RoomEntityFields.ROOM_ID, roomId) - } - try { - rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) - Try.just(roomId) - } catch (exception: Exception) { - Try.raise(JoinRoomFailure.JoinedWithTimeout) - } - }.flatMap { roomId -> - setReadMarkers(roomId) } + val roomId = params.roomId + // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + val rql = RealmQueryLatch(realmConfiguration) { realm -> + realm.where(RoomEntity::class.java) + .equalTo(RoomEntityFields.ROOM_ID, roomId) + } + try { + rql.await(timeout = 1L, timeUnit = TimeUnit.MINUTES) + } catch (exception: Exception) { + throw JoinRoomFailure.JoinedWithTimeout + } + setReadMarkers(roomId) } - private suspend fun setReadMarkers(roomId: String): Try { + private suspend fun setReadMarkers(roomId: String) { val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true) - return readMarkersTask.execute(setReadMarkerParams) + readMarkersTask.execute(setReadMarkerParams) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt index fe055e99..d4ff169c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/leaving/LeaveRoomTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.membership.leaving -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -31,7 +29,7 @@ internal interface LeaveRoomTask : Task { internal class DefaultLeaveRoomTask @Inject constructor(private val roomAPI: RoomAPI) : LeaveRoomTask { - override suspend fun execute(params: LeaveRoomTask.Params): Try { + override suspend fun execute(params: LeaveRoomTask.Params) { return executeRequest { apiCall = roomAPI.leave(params.roomId, HashMap()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt index 91d3c4e7..c63733f3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt @@ -23,10 +23,7 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.types -import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.SessionDatabase -import im.vector.matrix.android.internal.session.SessionScope -import im.vector.matrix.android.internal.session.room.EventRelationsAggregationTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import io.realm.OrderedCollectionChangeSet diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt index ab373a6e..35df20df 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventTask.kt @@ -15,12 +15,10 @@ */ package im.vector.matrix.android.internal.session.room.prune -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.UnsignedData -import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.database.helper.updateSenderData import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.mapper.EventMapper @@ -31,7 +29,7 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.Realm import timber.log.Timber import javax.inject.Inject @@ -48,8 +46,8 @@ internal interface PruneEventTask : Task { internal class DefaultPruneEventTask @Inject constructor(private val monarchy: Monarchy) : PruneEventTask { - override suspend fun execute(params: PruneEventTask.Params): Try { - return monarchy.tryTransactionSync { realm -> + override suspend fun execute(params: PruneEventTask.Params) { + monarchy.awaitTransaction { realm -> params.redactionEvents.forEach { event -> pruneEvent(realm, event, params.userId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt index 7830ce0e..2e30c12e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt @@ -37,17 +37,29 @@ internal class DefaultReadService @Inject constructor(private val roomId: String override fun markAllAsRead(callback: MatrixCallback) { val params = SetReadMarkersTask.Params(roomId, markAllAsRead = true) - setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor) + setReadMarkersTask + .configureWith(params) { + this.callback = callback + } + .executeBy(taskExecutor) } override fun setReadReceipt(eventId: String, callback: MatrixCallback) { val params = SetReadMarkersTask.Params(roomId, fullyReadEventId = null, readReceiptEventId = eventId) - setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor) + setReadMarkersTask + .configureWith(params) { + this.callback = callback + } + .executeBy(taskExecutor) } override fun setReadMarker(fullyReadEventId: String, callback: MatrixCallback) { val params = SetReadMarkersTask.Params(roomId, fullyReadEventId = fullyReadEventId, readReceiptEventId = null) - setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor) + setReadMarkersTask + .configureWith(params) { + this.callback = callback + } + .executeBy(taskExecutor) } @@ -55,13 +67,13 @@ internal class DefaultReadService @Inject constructor(private val roomId: String var isEventRead = false monarchy.doWithRealm { val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst() - ?: return@doWithRealm + ?: return@doWithRealm val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId) - ?: return@doWithRealm + ?: return@doWithRealm val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex - ?: Int.MIN_VALUE + ?: Int.MIN_VALUE val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex - ?: Int.MAX_VALUE + ?: Int.MAX_VALUE isEventRead = eventToCheckIndex <= readReceiptIndex } return isEventRead diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt index c32fc598..41c9cca5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.room.read -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -31,7 +30,6 @@ import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionAsync import io.realm.Realm import timber.log.Timber import javax.inject.Inject @@ -54,7 +52,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI private val monarchy: Monarchy ) : SetReadMarkersTask { - override suspend fun execute(params: SetReadMarkersTask.Params): Try { + override suspend fun execute(params: SetReadMarkersTask.Params) { val markers = HashMap() val fullyReadEventId: String? val readReceiptEventId: String? @@ -78,7 +76,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI } } if (readReceiptEventId != null - && !isEventRead(params.roomId, readReceiptEventId)) { + && !isEventRead(params.roomId, readReceiptEventId)) { if (LocalEchoEventFactory.isLocalEchoId(readReceiptEventId)) { Timber.w("Can't set read receipt for local event ${params.fullyReadEventId}") @@ -87,21 +85,20 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI markers[READ_RECEIPT] = readReceiptEventId } } - return if (markers.isEmpty()) { - Try.just(Unit) - } else { - executeRequest { - apiCall = roomAPI.sendReadMarker(params.roomId, markers) - } + if (markers.isEmpty()) { + return + } + executeRequest { + apiCall = roomAPI.sendReadMarker(params.roomId, markers) } } private fun updateNotificationCountIfNecessary(roomId: String, eventId: String) { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId == eventId if (isLatestReceived) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() - ?: return@tryTransactionAsync + ?: return@writeAsync roomSummary.notificationCount = 0 roomSummary.highlightCount = 0 } @@ -112,13 +109,13 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI var isEventRead = false monarchy.doWithRealm { val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst() - ?: return@doWithRealm + ?: return@doWithRealm val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId) - ?: return@doWithRealm + ?: return@doWithRealm val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex - ?: Int.MIN_VALUE + ?: Int.MIN_VALUE val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex - ?: Int.MAX_VALUE + ?: Int.MAX_VALUE isEventRead = eventToCheckIndex <= readReceiptIndex } return isEventRead diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index 183f2a6c..b5a0e50e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -43,7 +43,6 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEvent import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.CancelableWork -import im.vector.matrix.android.internal.util.tryTransactionAsync import im.vector.matrix.android.internal.worker.WorkerParamsFactory import timber.log.Timber import javax.inject.Inject @@ -81,28 +80,30 @@ internal class DefaultRelationService @Inject constructor(private val context: C reaction, myUserId ) - findReactionEventForUndoTask.configureWith(params) - .enableRetry() - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: FindReactionEventForUndoTask.Result) { - if (data.redactEventId == null) { - Timber.w("Cannot find reaction to undo (not yet synced?)") - //TODO? - } - data.redactEventId?.let { toRedact -> + val callback = object : MatrixCallback { + override fun onSuccess(data: FindReactionEventForUndoTask.Result) { + if (data.redactEventId == null) { + Timber.w("Cannot find reaction to undo (not yet synced?)") + //TODO? + } + data.redactEventId?.let { toRedact -> - val redactEvent = eventFactory.createRedactEvent(roomId, toRedact, null).also { - saveLocalEcho(it) - } - val redactWork = createRedactEventWork(redactEvent, toRedact, null) - - TimelineSendEventWorkCommon.postWork(context, roomId, redactWork) - - } + val redactEvent = eventFactory.createRedactEvent(roomId, toRedact, null).also { + saveLocalEcho(it) } - }) - .executeBy(taskExecutor) + val redactWork = createRedactEventWork(redactEvent, toRedact, null) + TimelineSendEventWorkCommon.postWork(context, roomId, redactWork) + + } + } + } + findReactionEventForUndoTask + .configureWith(params) { + this.retryCount = Int.MAX_VALUE + this.callback = callback + } + .executeBy(taskExecutor) } //TODO duplicate with send service? @@ -168,8 +169,10 @@ internal class DefaultRelationService @Inject constructor(private val context: C override fun fetchEditHistory(eventId: String, callback: MatrixCallback>) { val params = FetchEditHistoryTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), eventId) - fetchEditHistoryTask.configureWith(params) - .dispatchTo(callback) + fetchEditHistoryTask + .configureWith(params) { + this.callback = callback + } .executeBy(taskExecutor) } @@ -223,9 +226,9 @@ internal class DefaultRelationService @Inject constructor(private val context: C * the same transaction id is received (in unsigned data) */ private fun saveLocalEcho(event: Event) { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() - ?: return@tryTransactionAsync + ?: return@writeAsync roomEntity.addSendingEvent(event) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt index 7afbe288..792df3b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FetchEditHistoryTask.kt @@ -15,7 +15,6 @@ */ package im.vector.matrix.android.internal.session.room.relation -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.RelationType @@ -39,16 +38,16 @@ internal class DefaultFetchEditHistoryTask @Inject constructor( private val roomAPI: RoomAPI ) : FetchEditHistoryTask { - override suspend fun execute(params: FetchEditHistoryTask.Params): Try> { - return executeRequest { + override suspend fun execute(params: FetchEditHistoryTask.Params): List { + val response = executeRequest { apiCall = roomAPI.getRelations(params.roomId, params.eventId, RelationType.REPLACE, if (params.isRoomEncrypted) EventType.ENCRYPTED else EventType.MESSAGE) - }.map { resp -> - val events = resp.chunks.toMutableList() - resp.originalEvent?.let { events.add(it) } - events } + + val events = response.chunks.toMutableList() + response.originalEvent?.let { events.add(it) } + return events } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt index 8841819e..f74a7ebb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/FindReactionEventForUndoTask.kt @@ -15,13 +15,11 @@ */ package im.vector.matrix.android.internal.session.room.relation -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import io.realm.Realm import javax.inject.Inject @@ -44,14 +42,11 @@ internal interface FindReactionEventForUndoTask : Task { - return Try { - var eventId: String? = null - monarchy.doWithRealm { realm -> - eventId = getReactionToRedact(realm, params.reaction, params.eventId, params.myUserId)?.eventId - } - FindReactionEventForUndoTask.Result(eventId) + override suspend fun execute(params: FindReactionEventForUndoTask.Params): FindReactionEventForUndoTask.Result { + val eventId = Realm.getInstance(monarchy.realmConfiguration).use { realm -> + getReactionToRedact(realm, params.reaction, params.eventId, params.myUserId)?.eventId } + return FindReactionEventForUndoTask.Result(eventId) } private fun getReactionToRedact(realm: Realm, reaction: String, eventId: String, userId: String): EventEntity? { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt index 81a888ee..5df995d9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/SendRelationWorker.kt @@ -66,18 +66,11 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) : val relatedEventId = relationContent.relatesTo?.eventId ?: return Result.failure() val relationType = (relationContent.relatesTo as? ReactionInfo)?.type ?: params.relationType ?: return Result.failure() - - val result = executeRequest { - apiCall = roomAPI.sendRelation( - roomId = params.roomId, - parent_id = relatedEventId, - relationType = relationType, - eventType = localEvent.type, - content = localEvent.content - ) - } - return result.fold({ - when (it) { + return try { + sendRelation(params.roomId, relationType, relatedEventId, localEvent) + Result.success() + } catch (exception: Throwable) { + when (exception) { is Failure.NetworkConnection -> Result.retry() else -> { //TODO mark as failed to send? @@ -85,7 +78,19 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) : Result.success() } } - }, { Result.success() }) + } + } + + private suspend fun sendRelation(roomId: String, relationType: String, relatedEventId: String, localEvent: Event) { + executeRequest { + apiCall = roomAPI.sendRelation( + roomId = roomId, + parent_id = relatedEventId, + relationType = relationType, + eventType = localEvent.type, + content = localEvent.content + ) + } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt index 66e02ce9..fd081fbe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/UpdateQuickReactionTask.kt @@ -15,13 +15,11 @@ */ package im.vector.matrix.android.internal.session.room.relation -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.task.Task import io.realm.Realm import javax.inject.Inject @@ -44,14 +42,13 @@ internal interface UpdateQuickReactionTask : Task { - return Try { - var res: Pair?>? = null - monarchy.doWithRealm { realm -> - res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId, params.myUserId) - } - UpdateQuickReactionTask.Result(res?.first, res?.second ?: emptyList()) + + override suspend fun execute(params: UpdateQuickReactionTask.Params): UpdateQuickReactionTask.Result { + var res: Pair?>? = null + monarchy.doWithRealm { realm -> + res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId, params.myUserId) } + return UpdateQuickReactionTask.Result(res?.first, res?.second ?: emptyList()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 8adb45dd..2d585ad6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -41,7 +41,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.content.UploadContentWorker import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon import im.vector.matrix.android.internal.util.CancelableWork -import im.vector.matrix.android.internal.util.tryTransactionAsync import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory @@ -149,7 +148,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun deleteFailedEcho(localEcho: TimelineEvent) { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> TimelineEventEntity.where(realm, eventId = localEcho.root.eventId ?: "").findFirst()?.let { it.deleteFromRealm() @@ -175,7 +174,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte .enqueue() } - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> RoomEntity.where(realm, roomId).findFirst()?.let { room -> room.sendingTimelineEvents.forEach { it.root?.sendState = SendState.UNDELIVERED @@ -186,7 +185,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun resendAllFailedMessages() { - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> RoomEntity.where(realm, roomId).findFirst()?.let { room -> room.sendingTimelineEvents.filter { it.root?.sendState?.hasFailed() ?: false diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt index e83181b8..031ceb16 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EncryptEventWorker.kt @@ -17,24 +17,23 @@ package im.vector.matrix.android.internal.session.room.send import android.content.Context -import androidx.work.Worker +import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult +import im.vector.matrix.android.internal.util.awaitCallback import im.vector.matrix.android.internal.worker.SessionWorkerParams import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.getSessionComponent import timber.log.Timber -import java.util.concurrent.CountDownLatch import javax.inject.Inject internal class EncryptEventWorker(context: Context, params: WorkerParameters) - : Worker(context, params) { + : CoroutineWorker(context, params) { @JsonClass(generateAdapter = true) internal data class Params( @@ -49,7 +48,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) @Inject lateinit var crypto: CryptoService @Inject lateinit var localEchoUpdater: LocalEchoUpdater - override fun doWork(): Result { + override suspend fun doWork(): Result { Timber.v("Start Encrypt work") val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success().also { @@ -71,44 +70,30 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) } localEchoUpdater.updateSendState(localEvent.eventId, SendState.ENCRYPTING) - // TODO Better async handling - val latch = CountDownLatch(1) - - var result: MXEncryptEventContentResult? = null - var error: Throwable? = null val localMutableContent = HashMap(localEvent.content) params.keepKeys?.forEach { localMutableContent.remove(it) } + var error: Throwable? = null + var result: MXEncryptEventContentResult? = null try { - crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, object : MatrixCallback { - override fun onSuccess(data: MXEncryptEventContentResult) { - result = data - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - error = failure - latch.countDown() - } - }) - } catch (e: Throwable) { - error = e - latch.countDown() + result = awaitCallback { + crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, it) + } + } catch (throwable: Throwable) { + error = throwable } - latch.await() - if (result != null) { - val modifiedContent = HashMap(result?.eventContent) + val modifiedContent = HashMap(result.eventContent) params.keepKeys?.forEach { toKeep -> localEvent.content?.get(toKeep)?.let { //put it back in the encrypted thing modifiedContent[toKeep] = it } } - val safeResult = result!!.copy(eventContent = modifiedContent) + val safeResult = result.copy(eventContent = modifiedContent) val encryptedEvent = localEvent.copy( type = safeResult.eventType, content = safeResult.eventContent @@ -122,7 +107,8 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) } localEchoUpdater.updateSendState(localEvent.eventId, sendState) //always return success, or the chain will be stuck for ever! - val nextWorkerParams = SendEventWorker.Params(params.userId, params.roomId, localEvent, error?.localizedMessage ?: "Error") + val nextWorkerParams = SendEventWorker.Params(params.userId, params.roomId, localEvent, error?.localizedMessage + ?: "Error") return Result.success(WorkerParamsFactory.toData(nextWorkerParams)) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index df79d62e..6d97da84 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -36,7 +36,6 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.content.ThumbnailExtractor import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.util.StringProvider -import im.vector.matrix.android.internal.util.tryTransactionAsync import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import java.util.* @@ -380,9 +379,9 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials fun saveLocalEcho(monarchy: Monarchy, event: Event) { if (event.roomId == null) throw IllegalStateException("Your event should have a roomId") - monarchy.tryTransactionAsync { realm -> + monarchy.writeAsync { realm -> val roomEntity = RoomEntity.where(realm, roomId = event.roomId).findFirst() - ?: return@tryTransactionAsync + ?: return@writeAsync roomEntity.addSendingEvent(event) roomSummaryUpdater.update(realm, event.roomId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt index 9d41979b..c4242239 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoUpdater.kt @@ -20,15 +20,15 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.util.tryTransactionAsync +import im.vector.matrix.android.internal.util.awaitTransaction import timber.log.Timber import javax.inject.Inject internal class LocalEchoUpdater @Inject constructor(private val monarchy: Monarchy) { - fun updateSendState(eventId: String, sendState: SendState) { + suspend fun updateSendState(eventId: String, sendState: SendState) { Timber.v("Update local state of $eventId to ${sendState.name}") - monarchy.tryTransactionAsync { realm -> + monarchy.awaitTransaction { realm -> val sendingEventEntity = EventEntity.where(realm, eventId).findFirst() if (sendingEventEntity != null) { if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt index 5acc16e1..bac71cf7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/RedactEventWorker.kt @@ -54,28 +54,32 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters) : C sessionComponent.inject(this) val eventId = params.eventId - val result = executeRequest { - apiCall = roomAPI.redactEvent( - params.txID, - params.roomId, - eventId, - if (params.reason == null) emptyMap() else mapOf("reason" to params.reason) - ) - } - return result.fold({ - when (it) { - is Failure.NetworkConnection -> Result.retry() - else -> { - //TODO mark as failed to send? - //always return success, or the chain will be stuck for ever! - Result.success(WorkerParamsFactory.toData(params.copy( - lastFailureMessage = it.localizedMessage - ))) - } + return runCatching { + executeRequest { + apiCall = roomAPI.redactEvent( + params.txID, + params.roomId, + eventId, + if (params.reason == null) emptyMap() else mapOf("reason" to params.reason) + ) } - }, { - Result.success() - }) + }.fold( + { + Result.success() + }, + { + when (it) { + is Failure.NetworkConnection -> Result.retry() + else -> { + //TODO mark as failed to send? + //always return success, or the chain will be stuck for ever! + Result.success(WorkerParamsFactory.toData(params.copy( + lastFailureMessage = it.localizedMessage + ))) + } + } + } + ) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt index ddc18e87..442c6084 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt @@ -21,6 +21,8 @@ import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.failure.Failure +import im.vector.matrix.android.api.failure.MatrixError +import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.network.executeRequest @@ -59,33 +61,39 @@ internal class SendEventWorker constructor(context: Context, params: WorkerParam if (params.lastFailureMessage != null) { localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED) - // Transmit the error return Result.success(inputData) } + return try { + sendEvent(event.eventId, event.type, event.content, params.roomId) + Result.success() + } catch (exception: Throwable) { + if (exception.shouldBeRetried()) { + Result.retry() + } else { + localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED) + //always return success, or the chain will be stuck for ever! + Result.success() + } + } + } - localEchoUpdater.updateSendState(event.eventId, SendState.SENDING) - val result = executeRequest { + private fun Throwable.shouldBeRetried(): Boolean { + return this is Failure.NetworkConnection + || (this is Failure.ServerError && this.error.code == MatrixError.LIMIT_EXCEEDED) + } + + private suspend fun sendEvent(eventId: String, eventType: String, content: Content?, roomId: String) { + localEchoUpdater.updateSendState(eventId, SendState.SENDING) + executeRequest { apiCall = roomAPI.send( - event.eventId, - params.roomId, - event.type, - event.content + eventId, + roomId, + eventType, + content ) } - return result.fold({ - when (it) { - is Failure.NetworkConnection -> Result.retry() - else -> { - localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED) - //always return success, or the chain will be stuck for ever! - Result.success() - } - } - }, { - localEchoUpdater.updateSendState(event.eventId, SendState.SENT) - Result.success() - }) + localEchoUpdater.updateSendState(eventId, SendState.SENT) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt index 03beb8e7..60999b61 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/DefaultStateService.kt @@ -51,8 +51,10 @@ internal class DefaultStateService @Inject constructor(private val roomId: Strin )) - sendStateTask.configureWith(params) - .dispatchTo(callback) + sendStateTask + .configureWith(params) { + this.callback = callback + } .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt index 085340e6..39d606f5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/state/SendStateTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.state -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task import javax.inject.Inject @@ -32,7 +30,7 @@ internal interface SendStateTask : Task { } internal class DefaultSendStateTask @Inject constructor(private val roomAPI: RoomAPI) : SendStateTask { - override suspend fun execute(params: SendStateTask.Params): Try { + override suspend fun execute(params: SendStateTask.Params) { return executeRequest { apiCall = roomAPI.sendStateEvent(params.roomId, params.eventType, params.body) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt index a47bab62..e4f48d35 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task @@ -38,13 +36,12 @@ internal class DefaultGetContextOfEventTask @Inject constructor(private val room private val tokenChunkEventPersistor: TokenChunkEventPersistor ) : GetContextOfEventTask { - override suspend fun execute(params: GetContextOfEventTask.Params): Try { + override suspend fun execute(params: GetContextOfEventTask.Params): TokenChunkEventPersistor.Result { val filter = filterRepository.getRoomFilter() - return executeRequest { + val response = executeRequest { apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter) - }.flatMap { response -> - tokenChunkEventPersistor.insertInDb(response, params.roomId, PaginationDirection.BACKWARDS) } + return tokenChunkEventPersistor.insertInDb(response, params.roomId, PaginationDirection.BACKWARDS) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt index bc8a93b6..57efcdae 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.task.Task @@ -41,14 +39,12 @@ internal class DefaultPaginationTask @Inject constructor(private val roomAPI: Ro private val tokenChunkEventPersistor: TokenChunkEventPersistor ) : PaginationTask { - override suspend fun execute(params: PaginationTask.Params): Try { + override suspend fun execute(params: PaginationTask.Params): TokenChunkEventPersistor.Result { val filter = filterRepository.getRoomFilter() - return executeRequest { + val chunk = executeRequest { apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter) - }.flatMap { chunk -> - tokenChunkEventPersistor - .insertInDb(chunk, params.roomId, params.direction) } + return tokenChunkEventPersistor.insertInDb(chunk, params.roomId, params.direction) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index 70a5b12d..42595051 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -29,6 +29,7 @@ import im.vector.matrix.android.internal.database.query.findIncludingEvent import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.whereInRoom +import im.vector.matrix.android.internal.task.TaskConstraints import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.Debouncer @@ -133,7 +134,7 @@ internal class DefaultTimeline( builtEventsIdMap[eventId]?.let { builtIndex -> //Update the relation of existing event builtEvents[builtIndex]?.let { te -> - builtEvents[builtIndex] = eventEntity.asDomain() + builtEvents[builtIndex] = eventEntity.asDomain() hasChanged = true } } @@ -209,7 +210,7 @@ internal class DefaultTimeline( override fun pendingEventCount(): Int { var count = 0 Realm.getInstance(realmConfiguration).use { - count = RoomEntity.where(it,roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0 + count = RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0 } return count } @@ -217,7 +218,7 @@ internal class DefaultTimeline( override fun failedToDeliverEventCount(): Int { var count = 0 Realm.getInstance(realmConfiguration).use { - count = RoomEntity.where(it,roomId).findFirst()?.sendingTimelineEvents?.filter { + count = RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.filter { it.root?.sendState?.hasFailed() ?: false }?.count() ?: 0 } @@ -405,24 +406,27 @@ internal class DefaultTimeline( limit = limit) Timber.v("Should fetch $limit items $direction") - cancelableBag += paginationTask.configureWith(params) - .enableRetry() - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: TokenChunkEventPersistor.Result) { - if (data == TokenChunkEventPersistor.Result.SUCCESS) { - Timber.v("Success fetching $limit items $direction from pagination request") - } else { - // Database won't be updated, so we force pagination request - BACKGROUND_HANDLER.post { - executePaginationTask(direction, limit) + cancelableBag += paginationTask + .configureWith(params) { + this.retryCount = Int.MAX_VALUE + this.constraints = TaskConstraints(connectedToNetwork = true) + this.callback = object : MatrixCallback { + override fun onSuccess(data: TokenChunkEventPersistor.Result) { + if (data == TokenChunkEventPersistor.Result.SUCCESS) { + Timber.v("Success fetching $limit items $direction from pagination request") + } else { + // Database won't be updated, so we force pagination request + BACKGROUND_HANDLER.post { + executePaginationTask(direction, limit) + } } } - } - override fun onFailure(failure: Throwable) { - Timber.v("Failure fetching $limit items $direction from pagination request") + override fun onFailure(failure: Throwable) { + Timber.v("Failure fetching $limit items $direction from pagination request") + } } - }) + } .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt index 5ce58e02..5fdee1b5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt @@ -16,12 +16,10 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.task.Task import javax.inject.Inject internal class GetEventTask @Inject constructor(private val roomAPI: RoomAPI @@ -32,7 +30,7 @@ internal class GetEventTask @Inject constructor(private val roomAPI: RoomAPI val eventId: String ) - override suspend fun execute(params: Params): Try { + override suspend fun execute(params: Params): Event { return executeRequest { apiCall = roomAPI.getEvent(params.roomId, params.eventId) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt index fb8b6271..af845040 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.room.timeline -import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.helper.* import im.vector.matrix.android.internal.database.model.ChunkEntity @@ -26,7 +25,7 @@ import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.user.UserEntityFactory -import im.vector.matrix.android.internal.util.tryTransactionSync +import im.vector.matrix.android.internal.util.awaitTransaction import io.realm.kotlin.createObject import timber.log.Timber import javax.inject.Inject @@ -104,12 +103,12 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy SUCCESS } - fun insertInDb(receivedChunk: TokenChunkEvent, - roomId: String, - direction: PaginationDirection): Try { + suspend fun insertInDb(receivedChunk: TokenChunkEvent, + roomId: String, + direction: PaginationDirection): Result { - return monarchy - .tryTransactionSync { realm -> + monarchy + .awaitTransaction { realm -> Timber.v("Start persisting ${receivedChunk.events.size} events in $roomId towards $direction") val roomEntity = RoomEntity.where(realm, roomId).findFirst() @@ -127,7 +126,7 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy if (ChunkEntity.find(realm, roomId, nextToken = nextToken) != null || ChunkEntity.find(realm, roomId, prevToken = prevToken) != null) { Timber.v("Already inserted - SKIP") - return@tryTransactionSync + return@awaitTransaction } val prevChunk = ChunkEntity.find(realm, roomId, nextToken = prevToken) @@ -181,13 +180,11 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy currentChunk.updateSenderDataFor(eventIds) } } - .map { - if (receivedChunk.events.isEmpty() && receivedChunk.stateEvents.isEmpty() && receivedChunk.start != receivedChunk.end) { - Result.SHOULD_FETCH_MORE - } else { - Result.SUCCESS - } - } + return if (receivedChunk.events.isEmpty() && receivedChunk.stateEvents.isEmpty() && receivedChunk.start != receivedChunk.end) { + Result.SHOULD_FETCH_MORE + } else { + Result.SUCCESS + } } private fun handleMerge(roomEntity: RoomEntity, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt index fff75d14..2463a5ad 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/DefaultSignOutService.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.internal.session.signout import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.signout.SignOutService import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.toConfigurableTask +import im.vector.matrix.android.internal.task.configureWith import javax.inject.Inject internal class DefaultSignOutService @Inject constructor(private val signOutTask: SignOutTask, @@ -27,8 +27,9 @@ internal class DefaultSignOutService @Inject constructor(private val signOutTask override fun signOut(callback: MatrixCallback) { signOutTask - .toConfigurableTask() - .dispatchTo(callback) + .configureWith { + this.callback = callback + } .executeBy(taskExecutor) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt index 903763e4..6f4441b1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/signout/SignOutTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.signout -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.auth.SessionParamsStore @@ -31,15 +30,11 @@ internal class DefaultSignOutTask @Inject constructor(private val credentials: C private val sessionManager: SessionManager, private val sessionParamsStore: SessionParamsStore) : SignOutTask { - override suspend fun execute(params: Unit): Try { - return executeRequest { + override suspend fun execute(params: Unit) { + executeRequest { apiCall = signOutAPI.signOut() - }.flatMap { - sessionParamsStore.delete(credentials.userId) - }.flatMap { - Try { - sessionManager.releaseSession(credentials.userId) - } } + sessionParamsStore.delete(credentials.userId) + sessionManager.releaseSession(credentials.userId) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt index cb102d5d..013fc3ca 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/CryptoSyncHandler.kt @@ -27,7 +27,6 @@ import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService -import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.session.sync.model.ToDeviceSyncResponse import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt index 930f20f2..9ada6e71 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt @@ -17,7 +17,6 @@ package im.vector.matrix.android.internal.session.sync import im.vector.matrix.android.internal.database.model.ReadReceiptEntity -import im.vector.matrix.android.internal.session.SessionScope import io.realm.Realm import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index ae20fa68..9da3db76 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -18,20 +18,13 @@ package im.vector.matrix.android.internal.session.sync import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.R -import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.database.helper.add -import im.vector.matrix.android.internal.database.helper.addOrUpdate -import im.vector.matrix.android.internal.database.helper.addStateEvent -import im.vector.matrix.android.internal.database.helper.lastStateIndex -import im.vector.matrix.android.internal.database.helper.updateSenderDataFor -import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.helper.* import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.RoomEntity @@ -43,13 +36,8 @@ import im.vector.matrix.android.internal.session.mapWithProgress import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService import im.vector.matrix.android.internal.session.notification.ProcessEventForPushTask import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater -import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection -import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync -import im.vector.matrix.android.internal.session.sync.model.RoomSync -import im.vector.matrix.android.internal.session.sync.model.RoomSyncAccountData -import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral -import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse +import im.vector.matrix.android.internal.session.sync.model.* import im.vector.matrix.android.internal.session.user.UserEntityFactory import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith @@ -66,7 +54,6 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch private val tokenStore: SyncTokenStore, private val pushRuleService: DefaultPushRuleService, private val processForPushTask: ProcessEventForPushTask, - private val credentials: Credentials, private val taskExecutor: TaskExecutor) { sealed class HandlingStrategy { @@ -130,7 +117,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch Timber.v("Handle join sync for room $roomId") val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + ?: realm.createObject(roomId) if (roomEntity.membership == Membership.INVITE) { roomEntity.chunks.deleteAllFromRealm() @@ -140,7 +127,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch // State event if (roomSync.state != null && roomSync.state.events.isNotEmpty()) { val minStateIndex = roomEntity.untimelinedStateEvents.where().min(EventEntityFields.STATE_INDEX)?.toInt() - ?: Int.MIN_VALUE + ?: Int.MIN_VALUE val untimelinedStateIndex = minStateIndex + 1 roomSync.state.events.forEach { event -> roomEntity.addStateEvent(event, filterDuplicates = true, stateIndex = untimelinedStateIndex) @@ -180,7 +167,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch InvitedRoomSync): RoomEntity { Timber.v("Handle invited sync for room $roomId") val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + ?: realm.createObject(roomId) roomEntity.membership = Membership.INVITE if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) { val chunkEntity = handleTimelineEvents(realm, roomEntity, roomSync.inviteState.events) @@ -194,7 +181,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch roomId: String, roomSync: RoomSync): RoomEntity { val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: realm.createObject(roomId) + ?: realm.createObject(roomId) roomEntity.membership = Membership.LEAVE roomEntity.chunks.deleteAllFromRealm() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt index 49038eed..a3c00e43 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomTagHandler.kt @@ -20,7 +20,6 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomTagEntity import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope import io.realm.Realm import java.util.* import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt index b49b787f..fafa758c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncResponseHandler.kt @@ -17,23 +17,11 @@ package im.vector.matrix.android.internal.session.sync import arrow.core.Try -import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.R -import im.vector.matrix.android.api.auth.data.Credentials -import im.vector.matrix.android.api.session.events.model.toModel -import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.RoomSummaryEntity -import im.vector.matrix.android.internal.database.query.isDirect import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService import im.vector.matrix.android.internal.session.reportSubtask -import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.sync.model.SyncResponse -import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper -import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask -import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.configureWith import timber.log.Timber import javax.inject.Inject import kotlin.system.measureTimeMillis diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt index 84490404..ea4efa47 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt @@ -16,9 +16,6 @@ package im.vector.matrix.android.internal.session.sync -import arrow.core.Try -import arrow.core.failure -import arrow.core.recoverWith import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.R import im.vector.matrix.android.api.auth.data.Credentials @@ -30,7 +27,6 @@ import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressServi import im.vector.matrix.android.internal.session.filter.FilterRepository import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.task.Task -import im.vector.matrix.android.internal.util.tryTransactionAsync import javax.inject.Inject internal interface SyncTask : Task { @@ -50,7 +46,7 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI, ) : SyncTask { - override suspend fun execute(params: SyncTask.Params): Try { + override suspend fun execute(params: SyncTask.Params) { val requestParams = HashMap() var timeout = 0L val token = syncTokenStore.getLastToken() @@ -66,27 +62,22 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI, initialSyncProgressService.endAll() initialSyncProgressService.startTask(R.string.initial_sync_start_importing_account, 100) } - return executeRequest { - apiCall = syncAPI.sync(requestParams) - }.recoverWith { throwable -> + val syncResponse = try { + executeRequest { + apiCall = syncAPI.sync(requestParams) + } + } catch (throwable: Throwable) { // Intercept 401 if (throwable is Failure.ServerError && throwable.error.code == MatrixError.UNKNOWN_TOKEN) { sessionParamsStore.delete(credentials.userId) } - - // Transmit the throwable - throwable.failure() - }.flatMap { syncResponse -> - syncResponseHandler.handleResponse(syncResponse, token, false).also { - if (isInitialSync) { - monarchy.tryTransactionAsync { - initialSyncProgressService.endAll() - } - } - } - }.map { - syncTokenStore.saveToken(it.nextBatch) + throw throwable + } + syncResponseHandler.handleResponse(syncResponse, token, false) + syncTokenStore.saveToken(syncResponse.nextBatch) + if (isInitialSync) { + initialSyncProgressService.endAll() } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt index 1af5688b..148e25b3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncService.kt @@ -104,58 +104,57 @@ open class SyncService : Service() { } else { Timber.v("Execute sync request with timeout 0") val params = SyncTask.Params(TIME_OUT) - cancelableTask = syncTask.configureWith(params) - .callbackOn(TaskThread.SYNC) - .executeOn(TaskThread.SYNC) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - cancelableTask = null - if (!once) { - timer.schedule(object : TimerTask() { - override fun run() { - doSync() - } - }, NEXT_BATCH_DELAY) - } else { - //stop - stopMe() + cancelableTask = syncTask + .configureWith(params) { + callbackThread = TaskThread.SYNC + executionThread = TaskThread.SYNC + callback = object : MatrixCallback { + override fun onSuccess(data: Unit) { + cancelableTask = null + if (!once) { + timer.schedule(object : TimerTask() { + override fun run() { + doSync() + } + }, NEXT_BATCH_DELAY) + } else { + //stop + stopMe() + } + } + + override fun onFailure(failure: Throwable) { + Timber.e(failure) + cancelableTask = null + if (failure is Failure.NetworkConnection + && failure.cause is SocketTimeoutException) { + // Timeout are not critical + timer.schedule(object : TimerTask() { + override fun run() { + doSync() + } + }, 5_000L) + } + + if (failure !is Failure.NetworkConnection + || failure.cause is JsonEncodingException) { + // Wait 10s before retrying + timer.schedule(object : TimerTask() { + override fun run() { + doSync() + } + }, 5_000L) + } + + if (failure is Failure.ServerError + && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) { + // No token or invalid token, stop the thread + stopSelf() + } } } - - override fun onFailure(failure: Throwable) { - Timber.e(failure) - cancelableTask = null - if (failure is Failure.NetworkConnection - && failure.cause is SocketTimeoutException) { - // Timeout are not critical - timer.schedule(object : TimerTask() { - override fun run() { - doSync() - } - }, 5_000L) - } - - if (failure !is Failure.NetworkConnection - || failure.cause is JsonEncodingException) { - // Wait 10s before retrying - timer.schedule(object : TimerTask() { - override fun run() { - doSync() - } - }, 5_000L) - } - - if (failure is Failure.ServerError - && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) { - // No token or invalid token, stop the thread - stopSelf() - } - - } - - }) + } .executeBy(taskExecutor) - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt index c08f6101..0b9365dc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt @@ -102,39 +102,40 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, Timber.v("[$this] Execute sync request with timeout $DEFAULT_LONG_POOL_TIMEOUT") val latch = CountDownLatch(1) val params = SyncTask.Params(DEFAULT_LONG_POOL_TIMEOUT) - cancelableTask = syncTask.configureWith(params) - .callbackOn(TaskThread.SYNC) - .executeOn(TaskThread.SYNC) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - latch.countDown() + + cancelableTask = syncTask.configureWith(params) { + this.callbackThread = TaskThread.SYNC + this.executionThread = TaskThread.SYNC + this.callback = object : MatrixCallback { + + override fun onSuccess(data: Unit) { + latch.countDown() + } + + override fun onFailure(failure: Throwable) { + if (failure is Failure.NetworkConnection + && failure.cause is SocketTimeoutException) { + // Timeout are not critical + Timber.v("Timeout") + } else { + Timber.e(failure) } - override fun onFailure(failure: Throwable) { - if (failure is Failure.NetworkConnection - && failure.cause is SocketTimeoutException) { - // Timeout are not critical - Timber.v("Timeout") - } else { - Timber.e(failure) - } - - if (failure !is Failure.NetworkConnection - || failure.cause is JsonEncodingException) { - // Wait 10s before retrying - sleep(RETRY_WAIT_TIME_MS) - } - - if (failure is Failure.ServerError - && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) { - // No token or invalid token, stop the thread - updateStateTo(SyncState.KILLING) - } - - latch.countDown() + if (failure !is Failure.NetworkConnection + || failure.cause is JsonEncodingException) { + // Wait 10s before retrying + sleep(RETRY_WAIT_TIME_MS) } - }) + if (failure is Failure.ServerError + && (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) { + // No token or invalid token, stop the thread + updateStateTo(SyncState.KILLING) + } + latch.countDown() + } + } + } .executeBy(taskExecutor) latch.await() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt index 570aa9c4..b5d7118b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncWorker.kt @@ -18,18 +18,15 @@ package im.vector.matrix.android.internal.session.sync.job import android.content.Context import androidx.work.* import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.session.sync.SyncTask import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.internal.task.TaskThread -import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.getSessionComponent +import kotlinx.coroutines.withContext import timber.log.Timber -import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -47,45 +44,26 @@ internal class SyncWorker(context: Context, val automaticallyRetry: Boolean = false ) - @Inject - lateinit var syncTask: SyncTask - @Inject - lateinit var taskExecutor: TaskExecutor + @Inject lateinit var syncTask: SyncTask + @Inject lateinit var taskExecutor: TaskExecutor + @Inject lateinit var coroutineDispatchers: MatrixCoroutineDispatchers override suspend fun doWork(): Result { Timber.i("Sync work starting") val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() val sessionComponent = getSessionComponent(params.userId) ?: return Result.success() sessionComponent.inject(this) - - - val latch = CountDownLatch(1) - val taskParams = SyncTask.Params(0) - cancelableTask = syncTask.configureWith(taskParams) - .callbackOn(TaskThread.SYNC) - .executeOn(TaskThread.SYNC) - .dispatchTo(object : MatrixCallback { - override fun onSuccess(data: Unit) { - latch.countDown() - } - - override fun onFailure(failure: Throwable) { - Timber.e(failure) - latch.countDown() - } - - }) - .executeBy(taskExecutor) - - latch.await() + runCatching { + withContext(coroutineDispatchers.sync) { + val taskParams = SyncTask.Params(0) + syncTask.execute(taskParams) + } + } return Result.success() } companion object { - - private var cancelableTask: Cancelable? = null - fun requireBackgroundSync(context: Context, userId: String, serverTimeout: Long = 0) { val data = WorkerParamsFactory.toData(Params(userId, serverTimeout, false)) val workRequest = matrixOneTimeWorkRequestBuilder() @@ -107,7 +85,6 @@ internal class SyncWorker(context: Context, } fun stopAnyBackgroundSync(context: Context) { - cancelableTask?.cancel() WorkManager.getInstance(context).cancelUniqueWork("BG_SYNCP") } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt index 8d47d401..29259973 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt @@ -61,7 +61,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona override fun getUser(userId: String): User? { val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() } - ?: return null + ?: return null return userEntity.asDomain() } @@ -113,8 +113,9 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona callback: MatrixCallback>): Cancelable { val params = SearchUserTask.Params(limit, search, excludedUserIds) return searchUserTask - .configureWith(params) - .dispatchTo(callback) + .configureWith(params) { + this.callback = callback + } .executeBy(taskExecutor) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt index 46ae4e38..a31dc137 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt @@ -21,7 +21,6 @@ import dagger.Module import dagger.Provides import im.vector.matrix.android.api.session.user.UserService import im.vector.matrix.android.internal.session.SessionScope -import im.vector.matrix.android.internal.session.sync.SyncAPI import im.vector.matrix.android.internal.session.user.model.DefaultSearchUserTask import im.vector.matrix.android.internal.session.user.model.SearchUserTask import retrofit2.Retrofit diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt index 8d23a988..b4b14387 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/DirectChatsHelper.kt @@ -21,7 +21,6 @@ import im.vector.matrix.android.internal.database.query.getDirectRooms import im.vector.matrix.android.internal.di.SessionDatabase import io.realm.Realm import io.realm.RealmConfiguration -import timber.log.Timber import javax.inject.Inject internal class DirectChatsHelper @Inject constructor(@SessionDatabase diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt index 57ee9632..ddbdb973 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.user.accountdata -import arrow.core.Try import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.sync.model.UserAccountData @@ -45,8 +44,7 @@ internal interface UpdateUserAccountDataTask : Task { - + override suspend fun execute(params: UpdateUserAccountDataTask.Params) { return executeRequest { apiCall = accountDataApi.setAccountData(credentials.userId, params.type, params.getData()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt index 85264dba..4472bcf8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/model/SearchUserTask.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.session.user.model -import arrow.core.Try import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.user.SearchUserAPI @@ -34,13 +33,12 @@ internal interface SearchUserTask : Task> { internal class DefaultSearchUserTask @Inject constructor(private val searchUserAPI: SearchUserAPI) : SearchUserTask { - override suspend fun execute(params: SearchUserTask.Params): Try> { - return executeRequest { + override suspend fun execute(params: SearchUserTask.Params): List { + val response = executeRequest { apiCall = searchUserAPI.searchUsers(SearchUsersParams(params.search, params.limit)) - }.map { response -> - response.users.map { - User(it.userId, it.displayName, it.avatarUrl) - } + } + return response.users.map { + User(it.userId, it.displayName, it.avatarUrl) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt index 955ccc67..6896fe68 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/ConfigurableTask.kt @@ -16,49 +16,54 @@ package im.vector.matrix.android.internal.task -import arrow.core.Try import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable +import java.util.* -internal fun Task.configureWith(params: PARAMS): ConfigurableTask { - return ConfigurableTask(this, params) +internal fun Task.configureWith(params: PARAMS, + init: (ConfigurableTask.Builder.() -> Unit) = {} +): ConfigurableTask { + return ConfigurableTask.Builder(this, params).apply(init).build() } -/** - * Convert a Task to a ConfigurableTask without parameter - */ -internal fun Task.toConfigurableTask(): ConfigurableTask { - return ConfigurableTask(this, Unit) +internal fun Task.configureWith(init: (ConfigurableTask.Builder.() -> Unit) = {}): ConfigurableTask { + return configureWith(Unit, init) } internal data class ConfigurableTask( val task: Task, val params: PARAMS, - val callbackThread: TaskThread = TaskThread.MAIN, - val executionThread: TaskThread = TaskThread.IO, - val retryCount: Int = 0, - val callback: MatrixCallback = object : MatrixCallback {} -) : Task { + val id: UUID, + val callbackThread: TaskThread, + val executionThread: TaskThread, + val constraints: TaskConstraints, + val retryCount: Int, + val callback: MatrixCallback + +) : Task by task { - override suspend fun execute(params: PARAMS): Try { - return task.execute(params) - } + class Builder( + private val task: Task, + private val params: PARAMS, + var id: UUID = UUID.randomUUID(), + var callbackThread: TaskThread = TaskThread.MAIN, + var executionThread: TaskThread = TaskThread.IO, + var constraints: TaskConstraints = TaskConstraints(), + var retryCount: Int = 0, + var callback: MatrixCallback = object : MatrixCallback {} + ) { - fun callbackOn(thread: TaskThread): ConfigurableTask { - return copy(callbackThread = thread) - } - - fun executeOn(thread: TaskThread): ConfigurableTask { - return copy(executionThread = thread) - } - - fun dispatchTo(matrixCallback: MatrixCallback): ConfigurableTask { - return copy(callback = matrixCallback) - } - - fun enableRetry(retryCount: Int = Int.MAX_VALUE): ConfigurableTask { - return copy(retryCount = retryCount) + fun build() = ConfigurableTask( + task = task, + params = params, + id = id, + callbackThread = callbackThread, + executionThread = executionThread, + constraints = constraints, + retryCount = retryCount, + callback = callback + ) } fun executeBy(taskExecutor: TaskExecutor): Cancelable { @@ -66,7 +71,7 @@ internal data class ConfigurableTask( } override fun toString(): String { - return task.javaClass.name + return "${task.javaClass.name} with ID: $id" } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt index 81a2add8..be761fdb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/Task.kt @@ -16,11 +16,9 @@ package im.vector.matrix.android.internal.task -import arrow.core.Try - internal interface Task { - suspend fun execute(params: PARAMS): Try + suspend fun execute(params: PARAMS): RESULT } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskConstraints.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskConstraints.kt new file mode 100644 index 00000000..18733d6e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskConstraints.kt @@ -0,0 +1,22 @@ +/* + + * Copyright 2019 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.matrix.android.internal.task + +data class TaskConstraints( + val connectedToNetwork: Boolean = false +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt index 31de5ae7..c3f08b15 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/task/TaskExecutor.kt @@ -17,33 +17,41 @@ package im.vector.matrix.android.internal.task -import arrow.core.Try import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.di.MatrixScope import im.vector.matrix.android.internal.extensions.foldToCallback -import im.vector.matrix.android.internal.extensions.onError +import im.vector.matrix.android.internal.network.NetworkConnectivityChecker import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import timber.log.Timber import javax.inject.Inject import kotlin.coroutines.EmptyCoroutineContext -internal class TaskExecutor @Inject constructor(private val coroutineDispatchers: MatrixCoroutineDispatchers) { +@MatrixScope +internal class TaskExecutor @Inject constructor(private val coroutineDispatchers: MatrixCoroutineDispatchers, + private val networkConnectivityChecker: NetworkConnectivityChecker) { + + private val executorScope = CoroutineScope(SupervisorJob()) fun execute(task: ConfigurableTask): Cancelable { - val job = GlobalScope.launch(task.callbackThread.toDispatcher()) { - val resultOrFailure = withContext(task.executionThread.toDispatcher()) { - Timber.v("Executing $task on ${Thread.currentThread().name}") - retry(task.retryCount) { - task.execute(task.params) + val job = executorScope.launch(task.callbackThread.toDispatcher()) { + val resultOrFailure = runCatching { + withContext(task.executionThread.toDispatcher()) { + Timber.v("Enqueue task $task") + retry(task.retryCount) { + if (task.constraints.connectedToNetwork) { + Timber.v("Waiting network for $task") + networkConnectivityChecker.waitUntilConnected() + } + Timber.v("Execute task $task on ${Thread.currentThread().name}") + task.execute(task.params) + } } } resultOrFailure - .onError { + .onFailure { Timber.d(it, "Task failed") } .foldToCallback(task.callback) @@ -51,19 +59,22 @@ internal class TaskExecutor @Inject constructor(private val coroutineDispatchers return CancelableCoroutine(job) } + fun cancelAll() = executorScope.coroutineContext.cancelChildren() + + private suspend fun retry( times: Int = Int.MAX_VALUE, initialDelay: Long = 100, // 0.1 second maxDelay: Long = 10_000, // 10 second factor: Double = 2.0, - block: suspend () -> Try): Try { + block: suspend () -> T): T { var currentDelay = initialDelay repeat(times - 1) { - val blockResult = block() - if (blockResult.isSuccess()) { - return blockResult - } else { + try { + return block() + } catch (e: Exception) { + Timber.v("Retry task after $currentDelay ms") delay(currentDelay) currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt index 0ad14b91..97b8cd0e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableCoroutine.kt @@ -22,7 +22,9 @@ import kotlinx.coroutines.Job internal class CancelableCoroutine(private val job: Job) : Cancelable { override fun cancel() { - job.cancel() + if (!job.isCancelled) { + job.cancel() + } } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt index 6b547db1..fc999922 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Monarchy.kt @@ -16,30 +16,25 @@ package im.vector.matrix.android.internal.util -import arrow.core.Try import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.internal.database.awaitTransaction import io.realm.Realm import io.realm.RealmModel import java.util.concurrent.atomic.AtomicReference -internal fun Monarchy.tryTransactionSync(transaction: (realm: Realm) -> Unit): Try { - return Try { - this.runTransactionSync(transaction) - } -} - -internal fun Monarchy.tryTransactionAsync(transaction: (realm: Realm) -> Unit): Try { - return Try { - this.writeAsync(transaction) - } -} - -fun Monarchy.fetchManaged(query: (Realm) -> T?): T? { - return fetch(query, false) +internal suspend fun Monarchy.awaitTransaction(transaction: suspend (realm: Realm) -> Unit) { + awaitTransaction(realmConfiguration, transaction) } fun Monarchy.fetchCopied(query: (Realm) -> T?): T? { - return fetch(query, true) + val ref = AtomicReference() + doWithRealm { realm -> + val result = query.invoke(realm)?.let { + realm.copyFromRealm(it) + } + ref.set(result) + } + return ref.get() } fun Monarchy.fetchCopyMap(query: (Realm) -> T?, map: (T, realm: Realm) -> U): U? { @@ -52,18 +47,3 @@ fun Monarchy.fetchCopyMap(query: (Realm) -> T?, map: (T, rea } return ref.get() } - -private fun Monarchy.fetch(query: (Realm) -> T?, copyFromRealm: Boolean): T? { - val ref = AtomicReference() - doWithRealm { realm -> - val result = query.invoke(realm)?.let { - if (copyFromRealm) { - realm.copyFromRealm(it) - } else { - it - } - } - ref.set(result) - } - return ref.get() -} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt index 479412cf..a9053865 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringProvider.kt @@ -20,9 +20,7 @@ import android.content.res.Resources import androidx.annotation.NonNull import androidx.annotation.StringRes import dagger.Reusable -import im.vector.matrix.android.internal.di.MatrixScope import javax.inject.Inject -import javax.inject.Singleton @Reusable internal class StringProvider @Inject constructor(private val resources: Resources) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SuspendMatrixCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SuspendMatrixCallback.kt new file mode 100644 index 00000000..801578ac --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/SuspendMatrixCallback.kt @@ -0,0 +1,35 @@ +/* + + * Copyright 2019 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.matrix.android.internal.util + +import im.vector.matrix.android.api.MatrixCallback +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +suspend inline fun awaitCallback(crossinline callback: (MatrixCallback) -> Unit) = suspendCoroutine { cont -> + callback(object : MatrixCallback { + override fun onFailure(failure: Throwable) { + cont.resumeWithException(failure) + } + + override fun onSuccess(data: T) { + cont.resume(data) + } + }) +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt index ca0ddd04..3a676d30 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/worker/MatrixWorkerFactory.kt @@ -20,7 +20,6 @@ import android.content.Context import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters -import im.vector.matrix.android.internal.di.MatrixScope import javax.inject.Inject import javax.inject.Provider