From 216138394f5767f5b1886c887f4387eb730f0a39 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Sep 2020 09:32:35 +0200 Subject: [PATCH 01/64] Version++ --- CHANGES.md | 24 ++++++++++++++++++++++++ vector/build.gradle | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 638e7f586c..fae13abeed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,27 @@ +Changes in Element 1.0.8 (2020-XX-XX) +=================================================== + +Features ✨: + - + +Improvements 🙌: + - + +Bugfix 🐛: + - + +Translations 🗣: + - + +SDK API changes ⚠️: + - + +Build 🧱: + - + +Other changes: + - + Changes in Element 1.0.7 (2020-09-17) =================================================== diff --git a/vector/build.gradle b/vector/build.gradle index e0f401aaf6..e8ea267748 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -17,7 +17,7 @@ androidExtensions { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 0 -ext.versionPatch = 7 +ext.versionPatch = 8 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From d29d1ead9b45c5ef4f329c2f1a5a6e2181472c9d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 17 Sep 2020 16:58:43 +0200 Subject: [PATCH 02/64] Update issue templates --- .github/ISSUE_TEMPLATE/matrix-sdk.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/matrix-sdk.md diff --git a/.github/ISSUE_TEMPLATE/matrix-sdk.md b/.github/ISSUE_TEMPLATE/matrix-sdk.md new file mode 100644 index 0000000000..30f705a575 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/matrix-sdk.md @@ -0,0 +1,10 @@ +--- +name: Matrix SDK +about: Report issue or ask for a feature regarding the Android Matrix SDK +title: "[SDK] " +labels: matrix-sdk +assignees: '' + +--- + + From 144d0e56cc997adfc9a4c3a6a233a07d691e4825 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 17 Sep 2020 18:31:47 +0200 Subject: [PATCH 03/64] Realm transaction, use semaphore as suggested by Dominaezzz --- .../sdk/internal/database/AsyncTransaction.kt | 59 +++++++++++++------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt index c633dc5860..eec4fa2f88 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/AsyncTransaction.kt @@ -16,31 +16,52 @@ */ package org.matrix.android.sdk.internal.database +import com.zhuinden.monarchy.Monarchy import io.realm.Realm import io.realm.RealmConfiguration +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.withContext import timber.log.Timber -suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T) = withContext(Dispatchers.Default) { - Realm.getInstance(config).use { bgRealm -> - bgRealm.beginTransaction() - val result: T - try { - val start = System.currentTimeMillis() - result = transaction(bgRealm) - if (isActive) { - bgRealm.commitTransaction() - val end = System.currentTimeMillis() - val time = end - start - Timber.v("Execute transaction in $time millis") - } - } finally { - if (bgRealm.isInTransaction) { - bgRealm.cancelTransaction() - } - } - result +internal fun CoroutineScope.asyncTransaction(monarchy: Monarchy, transaction: suspend (realm: Realm) -> T) { + asyncTransaction(monarchy.realmConfiguration, transaction) +} + +internal fun CoroutineScope.asyncTransaction(realmConfiguration: RealmConfiguration, transaction: suspend (realm: Realm) -> T) { + launch { + awaitTransaction(realmConfiguration, transaction) + } +} + +private val realmSemaphore = Semaphore(1) + +suspend fun awaitTransaction(config: RealmConfiguration, transaction: suspend (realm: Realm) -> T): T { + return realmSemaphore.withPermit { + withContext(Dispatchers.IO) { + Realm.getInstance(config).use { bgRealm -> + bgRealm.beginTransaction() + val result: T + try { + val start = System.currentTimeMillis() + result = transaction(bgRealm) + if (isActive) { + bgRealm.commitTransaction() + val end = System.currentTimeMillis() + val time = end - start + Timber.v("Execute transaction in $time millis") + } + } finally { + if (bgRealm.isInTransaction) { + bgRealm.cancelTransaction() + } + } + result + } + } } } From b227dc3e5c99d76eac2cca2dd72aa7abf47d2be0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 17 Sep 2020 18:37:33 +0200 Subject: [PATCH 04/64] Sending: remove events from Worker params by fetching in db instead --- .../crypto/store/db/RealmCryptoStore.kt | 12 ++-- .../SendVerificationMessageWorker.kt | 15 ++-- .../VerificationTransportRoomMessage.kt | 14 ++-- .../session/content/UploadContentWorker.kt | 70 ++++++++++--------- .../session/group/GetGroupDataWorker.kt | 2 - .../profile/RefreshUserThreePidsTask.kt | 3 +- .../room/relation/DefaultRelationService.kt | 4 +- .../room/relation/SendRelationWorker.kt | 9 +-- .../session/room/send/DefaultSendService.kt | 7 +- .../session/room/send/EncryptEventWorker.kt | 34 +++++---- .../session/room/send/LocalEchoIdentifiers.kt | 25 +++++++ .../session/room/send/LocalEchoRepository.kt | 49 ++++++++----- .../MultipleEventSendingDispatcherWorker.kt | 39 +++++------ .../session/room/send/RedactEventWorker.kt | 1 - .../session/room/send/RoomEventSender.kt | 4 +- .../session/room/send/SendEventWorker.kt | 17 ++--- .../android/sdk/internal/util/Monarchy.kt | 2 +- .../internal/worker/WorkerParamsFactory.kt | 3 +- 18 files changed, 181 insertions(+), 129 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoIdentifiers.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index a7b9503a84..4f3f06beac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -20,6 +20,10 @@ package org.matrix.android.sdk.internal.crypto.store.db import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import io.realm.RealmConfiguration +import io.realm.Sort +import io.realm.kotlin.where import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.events.model.Event @@ -85,10 +89,6 @@ import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.di.CryptoDatabase import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.session.SessionScope -import io.realm.Realm -import io.realm.RealmConfiguration -import io.realm.Sort -import io.realm.kotlin.where import org.matrix.olm.OlmAccount import org.matrix.olm.OlmException import timber.log.Timber @@ -541,7 +541,7 @@ internal class RealmCryptoStore @Inject constructor( deviceId = it.deviceId ) } - monarchy.writeAsync { realm -> + doRealmTransactionAsync(realmConfiguration) { realm -> realm.where().findAll().deleteAllFromRealm() entities.forEach { realm.insertOrUpdate(it) @@ -1191,7 +1191,7 @@ internal class RealmCryptoStore @Inject constructor( .findAll() .mapNotNull { entity -> when (entity.type) { - GossipRequestType.KEY -> { + GossipRequestType.KEY -> { IncomingRoomKeyRequest( userId = entity.otherUserId, deviceId = entity.otherDeviceId, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt index 2b7d26e76b..2080f04c42 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt @@ -23,8 +23,8 @@ import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.crypto.CryptoService -import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.internal.crypto.tasks.SendVerificationMessageTask +import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import org.matrix.android.sdk.internal.worker.getSessionComponent @@ -42,13 +42,16 @@ internal class SendVerificationMessageWorker(context: Context, @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, - val event: Event, + val eventId: String, override val lastFailureMessage: String? = null ) : SessionWorkerParams @Inject lateinit var sendVerificationMessageTask: SendVerificationMessageTask + @Inject + lateinit var localEchoRepository: LocalEchoRepository + @Inject lateinit var cryptoService: CryptoService @@ -63,16 +66,18 @@ internal class SendVerificationMessageWorker(context: Context, Timber.e("Unknown Session, cannot send message, sessionId: ${params.sessionId}") } sessionComponent.inject(this) - val localId = params.event.eventId ?: "" + + val localEvent = localEchoRepository.getUpToDateEcho(params.eventId) ?: return Result.success(errorOutputData) + return try { val eventId = sendVerificationMessageTask.execute( SendVerificationMessageTask.Params( - event = params.event, + event = localEvent, cryptoService = cryptoService ) ) - Result.success(Data.Builder().putString(localId, eventId).build()) + Result.success(Data.Builder().putString(params.eventId, eventId).build()) } catch (exception: Throwable) { if (exception.shouldBeRetried()) { Result.retry() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt index 69f00ce359..e00a4c854c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt @@ -22,6 +22,9 @@ import androidx.work.Data import androidx.work.ExistingWorkPolicy import androidx.work.Operation import androidx.work.WorkInfo +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.R import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest @@ -52,9 +55,6 @@ import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.StringProvider import org.matrix.android.sdk.internal.worker.WorkerParamsFactory -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import timber.log.Timber import java.util.UUID import java.util.concurrent.TimeUnit @@ -87,7 +87,7 @@ internal class VerificationTransportRoomMessage( val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params( sessionId = sessionId, - event = event + eventId = event.eventId?:"" )) val enqueueInfo = enqueueSendWork(workerParams) @@ -174,7 +174,7 @@ internal class VerificationTransportRoomMessage( val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params( sessionId = sessionId, - event = event + eventId = event.eventId ?: "" )) val workRequest = workManagerProvider.matrixOneTimeWorkRequestBuilder() @@ -229,7 +229,7 @@ internal class VerificationTransportRoomMessage( ) val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params( sessionId = sessionId, - event = event + eventId = event.eventId ?: "" )) enqueueSendWork(workerParams) } @@ -249,7 +249,7 @@ internal class VerificationTransportRoomMessage( ) val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params( sessionId = sessionId, - event = event + eventId = event.eventId ?: "" )) val enqueueInfo = enqueueSendWork(workerParams) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index 6e70906d13..385e32ab15 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -24,7 +24,6 @@ import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.extensions.tryThis import org.matrix.android.sdk.api.session.content.ContentAttachmentData -import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent @@ -34,9 +33,13 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo +import org.matrix.android.sdk.internal.database.mapper.ContentMapper +import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.network.ProgressRequestBody import org.matrix.android.sdk.internal.session.DefaultFileService import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker +import org.matrix.android.sdk.internal.session.room.send.LocalEchoIdentifiers +import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory @@ -61,7 +64,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, - val events: List, + val localEchoIds: List, val attachment: ContentAttachmentData, val isEncrypted: Boolean, val compressBeforeSending: Boolean, @@ -73,6 +76,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter @Inject lateinit var fileService: DefaultFileService @Inject lateinit var cancelSendTracker: CancelSendTracker @Inject lateinit var imageCompressor: ImageCompressor + @Inject lateinit var localEchoRepository: LocalEchoRepository override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) @@ -100,7 +104,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) - val allCancelled = params.events.all { cancelSendTracker.isCancelRequestedFor(it.eventId, it.roomId) } + val allCancelled = params.localEchoIds.all { cancelSendTracker.isCancelRequestedFor(it.eventId, it.roomId) } if (allCancelled) { // there is no point in uploading the image! return Result.success(inputData) @@ -289,46 +293,48 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter ) } - private fun handleSuccess(params: Params, - attachmentUrl: String, - encryptedFileInfo: EncryptedFileInfo?, - thumbnailUrl: String?, - thumbnailEncryptedFileInfo: EncryptedFileInfo?, - newImageAttributes: NewImageAttributes?): Result { + private suspend fun handleSuccess(params: Params, + attachmentUrl: String, + encryptedFileInfo: EncryptedFileInfo?, + thumbnailUrl: String?, + thumbnailEncryptedFileInfo: EncryptedFileInfo?, + newImageAttributes: NewImageAttributes?): Result { notifyTracker(params) { contentUploadStateTracker.setSuccess(it) } + params.localEchoIds.forEach { + updateEvent(it.eventId, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newImageAttributes) + } - val updatedEvents = params.events - .map { - updateEvent(it, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newImageAttributes) - } - - val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isEncrypted) + val sendParams = MultipleEventSendingDispatcherWorker.Params( + sessionId = params.sessionId, + localEchoIds = params.localEchoIds, + isEncrypted = params.isEncrypted + ) return Result.success(WorkerParamsFactory.toData(sendParams)).also { Timber.v("## handleSuccess $attachmentUrl, work is stopped $isStopped") } } - private fun updateEvent(event: Event, - url: String, - encryptedFileInfo: EncryptedFileInfo?, - thumbnailUrl: String? = null, - thumbnailEncryptedFileInfo: EncryptedFileInfo?, - newImageAttributes: NewImageAttributes?): Event { - val messageContent: MessageContent = event.content.toModel() ?: return event - val updatedContent = when (messageContent) { - is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newImageAttributes) - is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo) - is MessageFileContent -> messageContent.update(url, encryptedFileInfo) - is MessageAudioContent -> messageContent.update(url, encryptedFileInfo) - else -> messageContent + private suspend fun updateEvent(eventId: String, + url: String, + encryptedFileInfo: EncryptedFileInfo?, + thumbnailUrl: String? = null, + thumbnailEncryptedFileInfo: EncryptedFileInfo?, + newImageAttributes: NewImageAttributes?) { + localEchoRepository.updateEcho(eventId) { _, event -> + val messageContent: MessageContent? = event.asDomain().content.toModel() + val updatedContent = when (messageContent) { + is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newImageAttributes) + is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo) + is MessageFileContent -> messageContent.update(url, encryptedFileInfo) + is MessageAudioContent -> messageContent.update(url, encryptedFileInfo) + else -> messageContent + } + event.content = ContentMapper.map(updatedContent.toContent()) } - return event.copy(content = updatedContent.toContent()) } private fun notifyTracker(params: Params, function: (String) -> Unit) { - params.events - .mapNotNull { it.eventId } - .forEach { eventId -> function.invoke(eventId) } + params.localEchoIds.forEach { function.invoke(it.eventId) } } private fun MessageImageContent.update(url: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt index 7a04f076e9..63ac3dc87c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt @@ -24,7 +24,6 @@ import com.squareup.moshi.JsonClass import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import org.matrix.android.sdk.internal.worker.getSessionComponent -import timber.log.Timber import javax.inject.Inject /** @@ -44,7 +43,6 @@ internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() - .also { Timber.e("Unable to parse work parameters") } val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt index dcc0db8ad1..bf09e52284 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/RefreshUserThreePidsTask.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.internal.util.awaitTransaction import timber.log.Timber import javax.inject.Inject @@ -39,7 +40,7 @@ internal class DefaultRefreshUserThreePidsTask @Inject constructor(private val p Timber.d("Get ${accountThreePidsResponse.threePids?.size} threePids") // Store the list in DB - monarchy.writeAsync { realm -> + monarchy.awaitTransaction { realm -> realm.where(UserThreePidEntity::class.java).findAll().deleteAllFromRealm() accountThreePidsResponse.threePids?.forEach { val entity = UserThreePidEntity( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt index 111551d66d..204a7f8aba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt @@ -202,13 +202,13 @@ internal class DefaultRelationService @AssistedInject constructor( private fun createEncryptEventWork(event: Event, keepKeys: List?): OneTimeWorkRequest { // Same parameter - val params = EncryptEventWorker.Params(sessionId, event, keepKeys) + val params = EncryptEventWorker.Params(sessionId, event.eventId!!, keepKeys) val sendWorkData = WorkerParamsFactory.toData(params) return timeLineSendEventWorkCommon.createWork(sendWorkData, true) } private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event) + val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, eventId = event.eventId!!) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return timeLineSendEventWorkCommon.createWork(sendWorkData, startChain) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt index fc7f712629..e5522323dd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt @@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent import org.matrix.android.sdk.api.session.room.model.relation.ReactionInfo import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository import org.matrix.android.sdk.internal.session.room.send.SendResponse import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory @@ -42,18 +43,18 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) : internal data class Params( override val sessionId: String, val roomId: String, - val event: Event, + val eventId: String, val relationType: String? = null, override val lastFailureMessage: String? = null ) : SessionWorkerParams @Inject lateinit var roomAPI: RoomAPI @Inject lateinit var eventBus: EventBus + @Inject lateinit var localEchoRepository: LocalEchoRepository override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() - .also { Timber.e("Unable to parse work parameters") } if (params.lastFailureMessage != null) { // Transmit the error @@ -64,8 +65,8 @@ internal class SendRelationWorker(context: Context, params: WorkerParameters) : val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) - val localEvent = params.event - if (localEvent.eventId == null) { + val localEvent = localEchoRepository.getUpToDateEcho(params.eventId) + if (localEvent?.eventId == null) { return Result.failure() } val relationContent = localEvent.content.toModel() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index 95cd1c699c..98b793923c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -336,7 +336,7 @@ internal class DefaultSendService @AssistedInject constructor( private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { // Same parameter - return EncryptEventWorker.Params(sessionId, event) + return EncryptEventWorker.Params(sessionId, event.eventId ?: "") .let { WorkerParamsFactory.toData(it) } .let { workManagerProvider.matrixOneTimeWorkRequestBuilder() @@ -360,7 +360,10 @@ internal class DefaultSendService @AssistedInject constructor( attachment: ContentAttachmentData, isRoomEncrypted: Boolean, compressBeforeSending: Boolean): OneTimeWorkRequest { - val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, allLocalEchos, attachment, isRoomEncrypted, compressBeforeSending) + val localEchoIds = allLocalEchos.map { + LocalEchoIdentifiers(it.roomId!!, it.eventId!!) + } + val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, localEchoIds, attachment, isRoomEncrypted, compressBeforeSending) val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) return workManagerProvider.matrixOneTimeWorkRequestBuilder() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt index 6b2a2ab115..d918f27e68 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/EncryptEventWorker.kt @@ -24,11 +24,13 @@ import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import org.matrix.android.sdk.internal.crypto.model.MXEncryptEventContentResult +import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.WorkerParamsFactory @@ -46,7 +48,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, - val event: Event, + val eventId: String, /** Do not encrypt these keys, keep them as is in encrypted content (e.g. m.relates_to) */ val keepKeys: List? = null, override val lastFailureMessage: String? = null @@ -60,20 +62,19 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) Timber.v("Start Encrypt work") val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() - .also { Timber.e("Unable to parse work parameters") } - Timber.v("## SendEvent: Start Encrypt work for event ${params.event.eventId}") if (params.lastFailureMessage != null) { // Transmit the error return Result.success(inputData) .also { Timber.e("Work cancelled due to input error from parent") } } + Timber.v("## SendEvent: Start Encrypt work for event ${params.eventId}") val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) - val localEvent = params.event - if (localEvent.eventId == null) { + val localEvent = localEchoRepository.getUpToDateEcho(params.eventId) + if (localEvent?.eventId == null) { return Result.success() } @@ -106,15 +107,10 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) modifiedContent[toKeep] = it } } - val safeResult = result.copy(eventContent = modifiedContent) - val encryptedEvent = localEvent.copy( - type = safeResult.eventType, - content = safeResult.eventContent - ) // Better handling of local echo, to avoid decrypting transition on remote echo // Should I only do it for text messages? - if (result.eventContent["algorithm"] == MXCRYPTO_ALGORITHM_MEGOLM) { - val decryptionLocalEcho = MXEventDecryptionResult( + val decryptionLocalEcho = if (result.eventContent["algorithm"] == MXCRYPTO_ALGORITHM_MEGOLM) { + MXEventDecryptionResult( clearEvent = Event( type = localEvent.type, content = localEvent.content, @@ -124,10 +120,18 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) senderCurve25519Key = result.eventContent["sender_key"] as? String, claimedEd25519Key = crypto.getMyDevice().fingerprint() ) - localEchoRepository.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho) + } else { + null + } + localEchoRepository.updateEcho(localEvent.eventId) { _, localEcho -> + localEcho.type = EventType.ENCRYPTED + localEcho.content = ContentMapper.map(modifiedContent) + decryptionLocalEcho?.also { + localEcho.setDecryptionResult(it) + } } - val nextWorkerParams = SendEventWorker.Params(sessionId = params.sessionId, event = encryptedEvent) + val nextWorkerParams = SendEventWorker.Params(sessionId = params.sessionId, eventId = params.eventId) return Result.success(WorkerParamsFactory.toData(nextWorkerParams)) } else { val sendState = when (error) { @@ -138,7 +142,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters) // always return success, or the chain will be stuck for ever! val nextWorkerParams = SendEventWorker.Params( sessionId = params.sessionId, - event = localEvent, + eventId = localEvent.eventId, lastFailureMessage = error?.localizedMessage ?: "Error" ) return Result.success(WorkerParamsFactory.toData(nextWorkerParams)) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoIdentifiers.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoIdentifiers.kt new file mode 100644 index 0000000000..9b987773dc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoIdentifiers.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.session.room.send + +import com.squareup.moshi.JsonClass + +/** + * This is used as a holder to pass necessary data to some workers params. + */ +@JsonClass(generateAdapter = true) +data class LocalEchoIdentifiers(val roomId: String, val eventId: String) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt index 00c624a20d..dc0f7ec5bd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt @@ -18,8 +18,8 @@ package org.matrix.android.sdk.internal.session.room.send import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.greenrobot.eventbus.EventBus -import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel @@ -27,11 +27,11 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import org.matrix.android.sdk.internal.database.RealmSessionProvider +import org.matrix.android.sdk.internal.database.asyncTransaction import org.matrix.android.sdk.internal.database.helper.nextId -import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper +import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.toEntity import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventInsertEntity @@ -44,11 +44,13 @@ import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline +import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.awaitTransaction import timber.log.Timber import javax.inject.Inject internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor, private val realmSessionProvider: RealmSessionProvider, private val roomSummaryUpdater: RoomSummaryUpdater, private val eventBus: EventBus, @@ -76,12 +78,12 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private } val timelineEvent = timelineEventMapper.map(timelineEventEntity) eventBus.post(DefaultTimeline.OnLocalEchoCreated(roomId = roomId, timelineEvent = timelineEvent)) - monarchy.writeAsync { realm -> + taskExecutor.executorScope.asyncTransaction(monarchy) { realm -> val eventInsertEntity = EventInsertEntity(event.eventId, event.type).apply { this.insertType = EventInsertType.LOCAL_ECHO } realm.insert(eventInsertEntity) - val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return@writeAsync + val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return@asyncTransaction roomEntity.sendingTimelineEvents.add(0, timelineEventEntity) roomSummaryUpdater.updateSendingInformation(realm, roomId) } @@ -89,30 +91,41 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private fun updateSendState(eventId: String, sendState: SendState) { Timber.v("## SendEvent: [${System.currentTimeMillis()}] Update local state of $eventId to ${sendState.name}") - monarchy.writeAsync { realm -> + updateEchoAsync(eventId) { realm, sendingEventEntity -> + if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) { + // If already synced, do not put as sent + } else { + sendingEventEntity.sendState = sendState + } + roomSummaryUpdater.updateSendingInformation(realm, sendingEventEntity.roomId) + } + } + + suspend fun updateEcho(eventId: String, block: (realm: Realm, eventEntity: EventEntity) -> Unit) { + monarchy.awaitTransaction { realm -> val sendingEventEntity = EventEntity.where(realm, eventId).findFirst() if (sendingEventEntity != null) { - if (sendState == SendState.SENT && sendingEventEntity.sendState == SendState.SYNCED) { - // If already synced, do not put as sent - } else { - sendingEventEntity.sendState = sendState - } - roomSummaryUpdater.updateSendingInformation(realm, sendingEventEntity.roomId) + block(realm, sendingEventEntity) } } } - fun updateEncryptedEcho(eventId: String, encryptedContent: Content, mxEventDecryptionResult: MXEventDecryptionResult) { - monarchy.writeAsync { realm -> + fun updateEchoAsync(eventId: String, block: (realm: Realm, eventEntity: EventEntity) -> Unit) { + taskExecutor.executorScope.asyncTransaction(monarchy) { realm -> val sendingEventEntity = EventEntity.where(realm, eventId).findFirst() if (sendingEventEntity != null) { - sendingEventEntity.type = EventType.ENCRYPTED - sendingEventEntity.content = ContentMapper.map(encryptedContent) - sendingEventEntity.setDecryptionResult(mxEventDecryptionResult) + block(realm, sendingEventEntity) } } } + suspend fun getUpToDateEcho(eventId: String): Event? { + // We are using awaitTransaction here to make sure this executes after other transactions + return monarchy.awaitTransaction { realm -> + EventEntity.where(realm, eventId).findFirst()?.asDomain() + } + } + suspend fun deleteFailedEcho(roomId: String, localEcho: TimelineEvent) { deleteFailedEcho(roomId, localEcho.eventId) } @@ -150,7 +163,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private return getAllEventsWithStates(roomId, SendState.HAS_FAILED_STATES) } - fun getAllEventsWithStates(roomId: String, states : List): List { + fun getAllEventsWithStates(roomId: String, states: List): List { return realmSessionProvider.withRealm { realm -> TimelineEventEntity .findAllInRoomWithSendStates(realm, roomId, states) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt index 8e8d24c227..a00c87fed2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt @@ -23,7 +23,6 @@ import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequest import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.session.content.UploadContentWorker @@ -48,7 +47,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo @JsonClass(generateAdapter = true) internal data class Params( override val sessionId: String, - val events: List, + val localEchoIds: List, val isEncrypted: Boolean, override val lastFailureMessage: String? = null ) : SessionWorkerParams @@ -61,42 +60,42 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo Timber.v("## SendEvent: Start dispatch sending multiple event work") val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() - .also { Timber.e("Unable to parse work parameters") } val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) if (params.lastFailureMessage != null) { - params.events.forEach { event -> - event.eventId?.let { localEchoRepository.updateSendState(it, SendState.UNDELIVERED) } + params.localEchoIds.forEach { localEchoIds -> + localEchoRepository.updateSendState(localEchoIds.eventId, SendState.UNDELIVERED) } // Transmit the error if needed? return Result.success(inputData) .also { Timber.e("## SendEvent: Work cancelled due to input error from parent ${params.lastFailureMessage}") } } - // Create a work for every event - params.events.forEach { event -> + params.localEchoIds.forEach { localEchoIds -> + val roomId = localEchoIds.roomId + val eventId = localEchoIds.eventId if (params.isEncrypted) { - localEchoRepository.updateSendState(event.eventId ?: "", SendState.ENCRYPTING) - Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule encrypt and send event ${event.eventId}") - val encryptWork = createEncryptEventWork(params.sessionId, event, true) + localEchoRepository.updateSendState(eventId, SendState.ENCRYPTING) + Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule encrypt and send event $eventId") + val encryptWork = createEncryptEventWork(params.sessionId, eventId, true) // Note that event will be replaced by the result of the previous work - val sendWork = createSendEventWork(params.sessionId, event, false) - timelineSendEventWorkCommon.postSequentialWorks(event.roomId!!, encryptWork, sendWork) + val sendWork = createSendEventWork(params.sessionId, eventId, false) + timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork) } else { - localEchoRepository.updateSendState(event.eventId ?: "", SendState.SENDING) - Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule send event ${event.eventId}") - val sendWork = createSendEventWork(params.sessionId, event, true) - timelineSendEventWorkCommon.postWork(event.roomId!!, sendWork) + localEchoRepository.updateSendState(eventId, SendState.SENDING) + Timber.v("## SendEvent: [${System.currentTimeMillis()}] Schedule send event $eventId") + val sendWork = createSendEventWork(params.sessionId, eventId, true) + timelineSendEventWorkCommon.postWork(roomId, sendWork) } } return Result.success() } - private fun createEncryptEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest { - val params = EncryptEventWorker.Params(sessionId, event) + private fun createEncryptEventWork(sessionId: String, eventId: String, startChain: Boolean): OneTimeWorkRequest { + val params = EncryptEventWorker.Params(sessionId, eventId) val sendWorkData = WorkerParamsFactory.toData(params) return workManagerProvider.matrixOneTimeWorkRequestBuilder() @@ -107,8 +106,8 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo .build() } - private fun createSendEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event) + private fun createSendEventWork(sessionId: String, eventId: String, startChain: Boolean): OneTimeWorkRequest { + val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, eventId = eventId) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return timelineSendEventWorkCommon.createWork(sendWorkData, startChain) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt index e1e780c35a..7978f15bcb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt @@ -52,7 +52,6 @@ internal class RedactEventWorker(context: Context, params: WorkerParameters) : C override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() - .also { Timber.e("Unable to parse work parameters") } if (params.lastFailureMessage != null) { // Transmit the error diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt index 6085459a08..4ac8d87550 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RoomEventSender.kt @@ -56,7 +56,7 @@ internal class RoomEventSender @Inject constructor( private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { // Same parameter - val params = EncryptEventWorker.Params(sessionId, event) + val params = EncryptEventWorker.Params(sessionId, event.eventId!!) val sendWorkData = WorkerParamsFactory.toData(params) return workManagerProvider.matrixOneTimeWorkRequestBuilder() @@ -68,7 +68,7 @@ internal class RoomEventSender @Inject constructor( } private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, event = event) + val sendContentWorkerParams = SendEventWorker.Params(sessionId = sessionId, eventId = event.eventId!!) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) return timelineSendEventWorkCommon.createWork(sendWorkData, startChain) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index 16acde7d16..ebc2f8dbfb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -21,11 +21,12 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass +import io.realm.RealmConfiguration import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.api.failure.shouldBeRetried import org.matrix.android.sdk.api.session.events.model.Content -import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.worker.SessionWorkerParams @@ -48,29 +49,25 @@ internal class SendEventWorker(context: Context, internal data class Params( override val sessionId: String, override val lastFailureMessage: String? = null, - val event: Event? = null, - // Keep for compat at the moment, will be removed later - val eventId: String? = null + val eventId: String ) : SessionWorkerParams @Inject lateinit var localEchoRepository: LocalEchoRepository @Inject lateinit var roomAPI: RoomAPI @Inject lateinit var eventBus: EventBus @Inject lateinit var cancelSendTracker: CancelSendTracker + @SessionDatabase @Inject lateinit var realmConfiguration: RealmConfiguration override suspend fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) ?: return Result.success() - .also { Timber.e("## SendEvent: Unable to parse work parameters") } + val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success() sessionComponent.inject(this) - val event = params.event + val event = localEchoRepository.getUpToDateEcho(params.eventId) if (event?.eventId == null || event.roomId == null) { - // Old way of sending - if (params.eventId != null) { - localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED) - } + localEchoRepository.updateSendState(params.eventId, SendState.UNDELIVERED) return Result.success() .also { Timber.e("Work cancelled due to bad input data") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Monarchy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Monarchy.kt index 81f5af9ac6..91127790ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Monarchy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Monarchy.kt @@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.util import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.internal.database.awaitTransaction import io.realm.Realm import io.realm.RealmModel +import org.matrix.android.sdk.internal.database.awaitTransaction import java.util.concurrent.atomic.AtomicReference internal suspend fun Monarchy.awaitTransaction(transaction: suspend (realm: Realm) -> T): T { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt index b162566403..84a42840f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/WorkerParamsFactory.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.worker import androidx.work.Data +import org.matrix.android.sdk.api.extensions.tryThis import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.network.parsing.CheckNumberType @@ -41,7 +42,7 @@ internal object WorkerParamsFactory { return Data.Builder().putString(KEY, json).build() } - inline fun fromData(data: Data): T? { + inline fun fromData(data: Data): T? = tryThis("Unable to parse work parameters") { val json = data.getString(KEY) return if (json == null) { null From 72f8c8ef72aa8c93421f457b8c686cb63c2cc720 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 18 Sep 2020 12:25:29 +0200 Subject: [PATCH 05/64] Clean and update CHANGES --- CHANGES.md | 2 +- .../crypto/verification/VerificationTransportRoomMessage.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fae13abeed..d5caaf5f8d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ Improvements 🙌: - Bugfix 🐛: - - + - Long message cannot be sent/takes infinite time & blocks other messages #1397 Translations 🗣: - diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt index e00a4c854c..9480c0bc04 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt @@ -87,7 +87,7 @@ internal class VerificationTransportRoomMessage( val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params( sessionId = sessionId, - eventId = event.eventId?:"" + eventId = event.eventId ?: "" )) val enqueueInfo = enqueueSendWork(workerParams) From 0e85a3d59f76fd3559d01e1618f6689fc94ccfa8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 18 Sep 2020 12:49:14 +0200 Subject: [PATCH 06/64] Add an advanced action to reset an account data entry (there is no API to completely delete it) --- CHANGES.md | 2 +- .../app/core/ui/list/GenericItemWithValue.kt | 4 +++ .../settings/devtools/AccountDataAction.kt | 23 +++++++++++++++++ .../devtools/AccountDataEpoxyController.kt | 7 +++++- .../settings/devtools/AccountDataFragment.kt | 19 ++++++++++++-- .../settings/devtools/AccountDataViewModel.kt | 25 +++++++++++++++---- vector/src/main/res/values/strings.xml | 2 ++ 7 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataAction.kt diff --git a/CHANGES.md b/CHANGES.md index fae13abeed..c2620d63ef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,7 +20,7 @@ Build 🧱: - Other changes: - - + - Add an advanced action to reset an account data entry Changes in Element 1.0.7 (2020-09-17) =================================================== diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericItemWithValue.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericItemWithValue.kt index f9d32a9f1e..0e719a1cd2 100644 --- a/vector/src/main/java/im/vector/app/core/ui/list/GenericItemWithValue.kt +++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericItemWithValue.kt @@ -56,6 +56,9 @@ abstract class GenericItemWithValue : VectorEpoxyModel + viewModel.handle(AccountDataAction.DeleteAccountData(data.type)) + } + .show() + .withColoredButton(DialogInterface.BUTTON_POSITIVE) + } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index 4cac55cb84..af8d162f15 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devtools +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxState @@ -24,11 +25,13 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent +import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx data class AccountDataViewState( @@ -37,7 +40,7 @@ data class AccountDataViewState( class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: AccountDataViewState, private val session: Session) - : VectorViewModel(initialState) { + : VectorViewModel(initialState) { init { session.rx().liveAccountData(emptySet()) @@ -46,7 +49,19 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A } } - override fun handle(action: EmptyAction) {} + override fun handle(action: AccountDataAction) { + when (action) { + is AccountDataAction.DeleteAccountData -> handleDeleteAccountData(action) + }.exhaustive + } + + private fun handleDeleteAccountData(action: AccountDataAction.DeleteAccountData) { + viewModelScope.launch { + awaitCallback { + session.updateAccountData(action.type, emptyMap(), it) + } + } + } @AssistedInject.Factory interface Factory { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 167655f800..a32f3f6a4e 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2226,6 +2226,8 @@ Dev Tools Account Data + Delete the account data of type %1$s?\n\nUse with caution, it may lead to unexpected behavior. + %d vote %d votes From 4b66ffcd5b143310511f1f5315e50ac669d08cda Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 18 Sep 2020 15:00:36 +0200 Subject: [PATCH 07/64] Add "show password" in import Megolm keys dialog --- CHANGES.md | 2 +- .../VectorSettingsSecurityPrivacyFragment.kt | 11 ++++++ .../res/layout/dialog_import_e2e_keys.xml | 36 +++++++++++++++---- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c2620d63ef..e6e0b540ca 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ Features ✨: - Improvements 🙌: - - + - Add "show password" in import Megolm keys dialog Bugfix 🐛: - diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 034fa617f0..ff824733ae 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -24,6 +24,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import android.widget.Button +import android.widget.ImageView import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat @@ -38,6 +39,7 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.showPassword import im.vector.app.core.intent.ExternalIntentData import im.vector.app.core.intent.analyseIntent import im.vector.app.core.intent.getFilenameFromUri @@ -458,6 +460,15 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( val passPhraseEditText = dialogLayout.findViewById(R.id.dialog_e2e_keys_passphrase_edit_text) val importButton = dialogLayout.findViewById