diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt index 48e50eca..93540829 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixPatterns.kt @@ -149,7 +149,7 @@ object MatrixPatterns { return null } - val index = matrixId.lastIndexOf(":") + val index = matrixId.indexOf(":") return if (index == -1) { null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index 7dd0c6e2..902515a2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -82,8 +82,13 @@ data class Event( ) { + @Transient var mxDecryptionResult: OlmDecryptionResult? = null + + @Transient var mCryptoError: MXCryptoError.ErrorType? = null + + @Transient var sendState: SendState = SendState.UNKNOWN @@ -99,42 +104,6 @@ data class Event( // Crypto //============================================================================================================== -// /** -// * For encrypted events, the plaintext payload for the event. -// * This is a small MXEvent instance with typically value for `type` and 'content' fields. -// */ -// @Transient -// var mClearEvent: Event? = null -// private set -// -// /** -// * Curve25519 key which we believe belongs to the sender of the event. -// * See `senderKey` property. -// */ -// @Transient -// private var mSenderCurve25519Key: String? = null -// -// /** -// * Ed25519 key which the sender of this event (for olm) or the creator of the megolm session (for megolm) claims to own. -// * See `claimedEd25519Key` property. -// */ -// @Transient -// private var mClaimedEd25519Key: String? = null -// -// /** -// * Curve25519 keys of devices involved in telling us about the senderCurve25519Key and claimedEd25519Key. -// * See `forwardingCurve25519KeyChain` property. -// */ -// @Transient -// private var mForwardingCurve25519KeyChain: List = ArrayList() -// -// /** -// * Decryption error -// */ -// @Transient -// var mCryptoError: MXCryptoError? = null -// private set - /** * @return true if this event is encrypted. */ @@ -142,51 +111,11 @@ data class Event( return TextUtils.equals(type, EventType.ENCRYPTED) } - /** - * Update the clear data on this event. - * This is used after decrypting an event; it should not be used by applications. - * - * @param decryptionResult the decryption result, including the plaintext and some key info. - */ -// internal fun setClearData(decryptionResult: MXEventDecryptionResult?) { -// mClearEvent = null -// if (decryptionResult != null) { -// if (decryptionResult.clearEvent != null) { -// val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) -// mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent) -// -// if (mClearEvent != null) { -// mSenderCurve25519Key = decryptionResult.senderCurve25519Key -// mClaimedEd25519Key = decryptionResult.claimedEd25519Key -// mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain -// -// // For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back -// // in the clear event -// try { -// content?.get("m.relates_to")?.let { clearRelates -> -// mClearEvent = mClearEvent?.copy( -// content = HashMap(mClearEvent!!.content).apply { -// this["m.relates_to"] = clearRelates -// } -// ) -// } -// } catch (e: Exception) { -// Timber.e(e, "Unable to restore 'm.relates_to' the clear event") -// } -// } -// -// -// } -// } -// mCryptoError = null -// } - /** * @return The curve25519 key that sent this event. */ fun getSenderKey(): String? { return mxDecryptionResult?.senderKey - // return mClearEvent?.mSenderCurve25519Key ?: mSenderCurve25519Key } /** @@ -194,23 +123,13 @@ data class Event( */ fun getKeysClaimed(): Map { return mxDecryptionResult?.keysClaimed ?: HashMap() -// val res = HashMap() -// -// val claimedEd25519Key = if (null != mClearEvent) mClearEvent!!.mClaimedEd25519Key else mClaimedEd25519Key -// -// if (null != claimedEd25519Key) { -// res["ed25519"] = claimedEd25519Key -// } -// -// return res } -// + /** * @return the event type */ fun getClearType(): String { - return mxDecryptionResult?.payload?.get("type")?.toString() - ?: type//get("type")?.toString() ?: type + return mxDecryptionResult?.payload?.get("type")?.toString() ?: type } /** @@ -220,30 +139,8 @@ data class Event( return mxDecryptionResult?.payload?.get("content") as? Content ?: content } -// /** -// * @return the linked crypto error -// */ -// fun getCryptoError(): MXCryptoError? { -// return mCryptoError -// } -// -// /** -// * Update the linked crypto error -// * -// * @param error the new crypto error. -// */ -// fun setCryptoError(error: MXCryptoError?) { -// mCryptoError = error -// if (null != error) { -// mClearEvent = null -// } -// } - - fun toContentStringWithIndent(): String { - val contentMap = this.toContent()?.toMutableMap() ?: HashMap() - contentMap.remove("mxDecryptionResult") - contentMap.remove("mCryptoError") + val contentMap = toContent()?.toMutableMap() ?: HashMap() return JSONObject(contentMap).toString(4) } @@ -302,31 +199,19 @@ data class Event( fun Event.isTextMessage(): Boolean { - if (this.getClearType() == EventType.MESSAGE) { - return getClearContent()?.toModel()?.let { - when (it.type) { - MessageType.MSGTYPE_TEXT, - MessageType.MSGTYPE_EMOTE, - MessageType.MSGTYPE_NOTICE -> { - true - } - else -> false - } - } ?: false + return getClearType() == EventType.MESSAGE + && when (getClearContent()?.toModel()?.type) { + MessageType.MSGTYPE_TEXT, + MessageType.MSGTYPE_EMOTE, + MessageType.MSGTYPE_NOTICE -> true + else -> false } - return false } fun Event.isImageMessage(): Boolean { - if (this.getClearType() == EventType.MESSAGE) { - return getClearContent()?.toModel()?.let { - when (it.type) { - MessageType.MSGTYPE_IMAGE -> { - true - } - else -> false - } - } ?: false + return getClearType() == EventType.MESSAGE + && when (getClearContent()?.toModel()?.type) { + MessageType.MSGTYPE_IMAGE -> true + else -> false } - return false } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt index e7729d37..9067e818 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt @@ -27,7 +27,7 @@ internal interface SessionParamsStore { fun getAll(): List - fun save(sessionParams: SessionParams): Try + fun save(sessionParams: SessionParams): Try fun delete(userId: String): Try diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt index 1bb27d20..38771c91 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt @@ -62,7 +62,7 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S return sessionParams } - override fun save(sessionParams: SessionParams): Try { + override fun save(sessionParams: SessionParams): Try { return Try { val entity = mapper.map(sessionParams) if (entity != null) { @@ -72,7 +72,6 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S } realm.close() } - sessionParams } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Try.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Try.kt index 18ab0e47..78529404 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Try.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/extensions/Try.kt @@ -31,3 +31,11 @@ inline fun TryOf.onError(f: (Throwable) -> Unit): Try = fix() fun Try.foldToCallback(callback: MatrixCallback): Unit = fold( { callback.onFailure(it) }, { callback.onSuccess(it) }) + +/** + * Same as doOnNext for Observables + */ +inline fun Try.alsoDo(f: (A) -> Unit) = map { + f(it) + it +} \ No newline at end of file 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 b5a0e50e..cd31c978 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 @@ -115,7 +115,7 @@ internal class DefaultRelationService @Inject constructor(private val context: C eventId, reason) val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) - return TimelineSendEventWorkCommon.createWork(redactWorkData) + return TimelineSendEventWorkCommon.createWork(redactWorkData, true) } override fun editTextMessage(targetEventId: String, @@ -199,14 +199,13 @@ internal class DefaultRelationService @Inject constructor(private val context: C // Same parameter val params = EncryptEventWorker.Params(credentials.userId, roomId, event, keepKeys) val sendWorkData = WorkerParamsFactory.toData(params) - return TimelineSendEventWorkCommon.createWork(sendWorkData) + return TimelineSendEventWorkCommon.createWork(sendWorkData, true) } private fun createSendEventWork(event: Event): OneTimeWorkRequest { val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) - val workRequest = TimelineSendEventWorkCommon.createWork(sendWorkData) - return workRequest + return TimelineSendEventWorkCommon.createWork(sendWorkData, true) } override fun getEventSummaryLive(eventId: String): LiveData { 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 2d585ad6..8b65be24 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 @@ -22,10 +22,7 @@ import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.content.ContentAttachmentData 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.events.model.EventType -import im.vector.matrix.android.api.session.events.model.isTextMessage -import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.events.model.* import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.send.SendService @@ -41,9 +38,11 @@ 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.worker.AlwaysSuccessfulWorker 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.startChain import timber.log.Timber import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -82,11 +81,11 @@ internal class DefaultSendService @Inject constructor(private val context: Conte return if (cryptoService.isRoomEncrypted(roomId)) { Timber.v("Send event in encrypted room") val encryptWork = createEncryptEventWork(event, true) - val sendWork = createSendEventWork(event) + val sendWork = createSendEventWork(event, false) TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, sendWork) CancelableWork(context, encryptWork.id) } else { - val sendWork = createSendEventWork(event) + val sendWork = createSendEventWork(event, true) TimelineSendEventWorkCommon.postWork(context, roomId, sendWork) CancelableWork(context, sendWork.id) } @@ -106,7 +105,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? { - if (localEcho.root.isTextMessage()) { + if (localEcho.root.isTextMessage() && localEcho.root.sendState.hasFailed()) { return sendEvent(localEcho.root) } return null @@ -114,7 +113,8 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable? { - //TODO this need a refactoring of attachement sending + if (localEcho.root.isImageMessage() && localEcho.root.sendState.hasFailed()) { + //TODO this need a refactoring of attachement sending // val clearContent = localEcho.root.getClearContent() // val messageContent = clearContent?.toModel() ?: return null // when (messageContent.type) { @@ -143,8 +143,9 @@ internal class DefaultSendService @Inject constructor(private val context: Conte // } // } // } + return null + } return null - } override fun deleteFailedEcho(localEcho: TimelineEvent) { @@ -162,15 +163,16 @@ internal class DefaultSendService @Inject constructor(private val context: Conte override fun clearSendingQueue() { TimelineSendEventWorkCommon.cancelAllWorks(context, roomId) - WorkManager.getInstance(context).cancelUniqueWork(buildWorkIdentifier(UPLOAD_WORK)) + WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(UPLOAD_WORK)) - matrixOneTimeWorkRequestBuilder() + // Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied + matrixOneTimeWorkRequestBuilder() .build().let { TimelineSendEventWorkCommon.postWork(context, roomId, it, ExistingWorkPolicy.REPLACE) //need to clear also image sending queue WorkManager.getInstance(context) - .beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it) + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it) .enqueue() } @@ -244,26 +246,26 @@ internal class DefaultSendService @Inject constructor(private val context: Conte val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId) val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true) - val sendWork = createSendEventWork(localEcho) + val sendWork = createSendEventWork(localEcho, false) if (isRoomEncrypted) { val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/) val op: Operation = WorkManager.getInstance(context) - .beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) .then(encryptWork) .then(sendWork) .enqueue() op.result.addListener(Runnable { if (op.result.isCancelled) { - Timber.e("CHAINE WAS CANCELLED") + Timber.e("CHAIN WAS CANCELLED") } else if (op.state.value is Operation.State.FAILURE) { - Timber.e("CHAINE DID FAIL") + Timber.e("CHAIN DID FAIL") } }, workerFutureListenerExecutor) } else { WorkManager.getInstance(context) - .beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) .then(sendWork) .enqueue() } @@ -275,11 +277,11 @@ internal class DefaultSendService @Inject constructor(private val context: Conte localEchoEventFactory.saveLocalEcho(monarchy, event) } - private fun buildWorkIdentifier(identifier: String): String { + private fun buildWorkName(identifier: String): String { return "${roomId}_$identifier" } - private fun createEncryptEventWork(event: Event, startChain: Boolean = false): OneTimeWorkRequest { + private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { // Same parameter val params = EncryptEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(params) @@ -287,20 +289,16 @@ internal class DefaultSendService @Inject constructor(private val context: Conte return matrixOneTimeWorkRequestBuilder() .setConstraints(WorkManagerUtil.workConstraints) .setInputData(sendWorkData) - .apply { - if (startChain) { - setInputMerger(NoMerger::class.java) - } - } + .startChain(startChain) .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) .build() } - private fun createSendEventWork(event: Event): OneTimeWorkRequest { + private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event) val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) - return TimelineSendEventWorkCommon.createWork(sendWorkData) + return TimelineSendEventWorkCommon.createWork(sendWorkData, startChain) } private fun createRedactEventWork(event: Event, reason: String?): OneTimeWorkRequest { @@ -309,23 +307,19 @@ internal class DefaultSendService @Inject constructor(private val context: Conte } val sendContentWorkerParams = RedactEventWorker.Params(credentials.userId, redactEvent.eventId!!, roomId, event.eventId, reason) val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) - return TimelineSendEventWorkCommon.createWork(redactWorkData) + return TimelineSendEventWorkCommon.createWork(redactWorkData, true) } private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData, isRoomEncrypted: Boolean, - startChain: Boolean = false): OneTimeWorkRequest { + startChain: Boolean): OneTimeWorkRequest { val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment, isRoomEncrypted) val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) return matrixOneTimeWorkRequestBuilder() .setConstraints(WorkManagerUtil.workConstraints) - .apply { - if (startChain) { - setInputMerger(NoMerger::class.java) - } - } + .startChain(startChain) .setInputData(uploadWorkData) .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) .build() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/NoMerger.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/NoMerger.kt index 6938bc22..c41c4bc0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/NoMerger.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/NoMerger.kt @@ -18,7 +18,10 @@ package im.vector.matrix.android.internal.session.room.send import androidx.work.Data import androidx.work.InputMerger -class NoMerger : InputMerger() { +/** + * InputMerger which takes only the first input, to ensure an appended work will only have the specified parameters + */ +internal class NoMerger : InputMerger() { override fun merge(inputs: MutableList): Data { return inputs.first() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt index e75bb91b..575c0662 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineSendEventWorkCommon.kt @@ -19,6 +19,7 @@ import android.content.Context import androidx.work.* import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder +import im.vector.matrix.android.internal.worker.startChain import java.util.concurrent.TimeUnit @@ -41,7 +42,7 @@ internal object TimelineSendEventWorkCommon { else -> { val firstWork = workRequests.first() var continuation = WorkManager.getInstance(context) - .beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, firstWork) + .beginUniqueWork(buildWorkName(roomId), ExistingWorkPolicy.APPEND, firstWork) for (i in 1 until workRequests.size) { val workRequest = workRequests[i] continuation = continuation.then(workRequest) @@ -53,23 +54,24 @@ internal object TimelineSendEventWorkCommon { fun postWork(context: Context, roomId: String, workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND) { WorkManager.getInstance(context) - .beginUniqueWork(buildWorkIdentifier(roomId), policy, workRequest) + .beginUniqueWork(buildWorkName(roomId), policy, workRequest) .enqueue() } - inline fun createWork(data: Data): OneTimeWorkRequest { + inline fun createWork(data: Data, startChain: Boolean): OneTimeWorkRequest { return matrixOneTimeWorkRequestBuilder() .setConstraints(WorkManagerUtil.workConstraints) + .startChain(startChain) .setInputData(data) .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) .build() } - private fun buildWorkIdentifier(roomId: String): String { + private fun buildWorkName(roomId: String): String { return "${roomId}_$SEND_WORK" } fun cancelAllWorks(context: Context, roomId: String) { - WorkManager.getInstance(context).cancelUniqueWork(buildWorkIdentifier(roomId)) + WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(roomId)) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataModule.kt index e4b76ca1..850312d8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/AccountDataModule.kt @@ -36,6 +36,6 @@ internal abstract class AccountDataModule { } @Binds - abstract fun bindUpdateUserAccountDataTask(updateUserAccountDataTask: DefaultUpdateUserAcountDataTask): UpdateUserAccountDataTask + abstract fun bindUpdateUserAccountDataTask(updateUserAccountDataTask: DefaultUpdateUserAccountDataTask): UpdateUserAccountDataTask } \ No newline at end of file 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 ddbdb973..80fc4cc3 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 @@ -41,8 +41,8 @@ internal interface UpdateUserAccountDataTask : Task "" + null -> null is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network) is Failure.ServerError -> { throwable.error.message.takeIf { it.isNotEmpty() } - ?: throwable.error.code.takeIf { it.isNotEmpty() } - ?: stringProvider.getString(R.string.unknown_error) + ?: throwable.error.code.takeIf { it.isNotEmpty() } } else -> throwable.localizedMessage - ?: stringProvider.getString(R.string.unknown_error) } + ?: stringProvider.getString(R.string.unknown_error) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/EditText.kt b/vector/src/main/java/im/vector/riotx/core/extensions/EditText.kt index ace13754..cf64c9b3 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/EditText.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/EditText.kt @@ -18,25 +18,22 @@ package im.vector.riotx.core.extensions import android.text.Editable import android.text.InputType -import android.text.TextWatcher import android.view.MotionEvent import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.annotation.DrawableRes import im.vector.riotx.R +import im.vector.riotx.core.platform.SimpleTextWatcher fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_filter, @DrawableRes clearIconRes: Int = R.drawable.ic_x_green) { - addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(editable: Editable?) { - val clearIcon = if (editable?.isNotEmpty() == true) clearIconRes else 0 + addTextChangedListener(object : SimpleTextWatcher() { + override fun afterTextChanged(s: Editable) { + val clearIcon = if (s.isNotEmpty()) clearIconRes else 0 setCompoundDrawablesWithIntrinsicBounds(searchIconRes, 0, clearIcon, 0) } - - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit }) maxLines = 1 diff --git a/vector/src/main/java/im/vector/riotx/core/platform/ConfigurationViewModel.kt b/vector/src/main/java/im/vector/riotx/core/platform/ConfigurationViewModel.kt index bb2db5cb..dae8145f 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/ConfigurationViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/ConfigurationViewModel.kt @@ -19,6 +19,7 @@ package im.vector.riotx.core.platform import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.features.configuration.VectorConfiguration import timber.log.Timber @@ -46,7 +47,7 @@ class ConfigurationViewModel @Inject constructor( if (newHash != currentConfigurationValue) { Timber.v("Configuration: recreate the Activity") currentConfigurationValue = newHash - _activityRestarter.postValue(LiveEvent(Unit)) + _activityRestarter.postLiveEvent(Unit) } } } diff --git a/vector/src/main/java/im/vector/riotx/core/platform/SimpleTextWatcher.kt b/vector/src/main/java/im/vector/riotx/core/platform/SimpleTextWatcher.kt index e54a6d29..94680556 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/SimpleTextWatcher.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/SimpleTextWatcher.kt @@ -19,6 +19,7 @@ package im.vector.riotx.core.platform import android.text.Editable import android.text.TextWatcher + /** * TextWatcher with default no op implementation */ diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomActivity.kt index 8c40e569..b18892ac 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomActivity.kt @@ -24,11 +24,7 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.lifecycle.ViewModelProviders -import com.airbnb.mvrx.Async -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.Success -import com.airbnb.mvrx.viewModel +import com.airbnb.mvrx.* import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent @@ -39,7 +35,6 @@ import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.SimpleFragmentActivity import im.vector.riotx.core.platform.WaitingViewData import kotlinx.android.synthetic.main.activity.* -import timber.log.Timber import javax.inject.Inject class CreateDirectRoomActivity : SimpleFragmentActivity() { @@ -98,7 +93,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { } else AlertDialog.Builder(this) .setMessage(errorFormatter.toHumanReadable(error)) - .setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() } + .setPositiveButton(R.string.ok, null) .show() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt index b0fed9b8..a0d47dc0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt @@ -21,7 +21,9 @@ package im.vector.riotx.features.home.createdirect import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import arrow.core.Option -import com.airbnb.mvrx.* +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject @@ -33,10 +35,8 @@ import im.vector.matrix.rx.rx import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.utils.LiveEvent -import io.reactivex.Observable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.BiFunction import java.util.concurrent.TimeUnit private typealias KnowUsersFilter = String @@ -103,7 +103,6 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted .execute { copy(createAndInviteState = it) } - .disposeOnClear() } private fun handleRemoveSelectedUser(action: CreateDirectRoomActions.RemoveSelectedUser) = withState { state -> diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 5d10ab2c..a277f9a5 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -300,8 +300,9 @@ class RoomDetailFragment : composerLayout.collapse() } - private fun enterSpecialMode(event: TimelineEvent, @DrawableRes - iconRes: Int, useText: Boolean) { + private fun enterSpecialMode(event: TimelineEvent, + @DrawableRes iconRes: Int, + useText: Boolean) { commandAutocompletePolicy.enabled = false //switch to expanded bar composerLayout.composerRelatedMessageTitle.apply { @@ -820,106 +821,101 @@ class RoomDetailFragment : textComposerViewModel.process(TextComposerActions.QueryUsers(query)) } - private fun handleActions(actionData: ActionsHandler.ActionData) { - when (actionData.actionId) { - MessageMenuViewModel.ACTION_ADD_REACTION -> { - val eventId = actionData.data?.toString() ?: return - startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), eventId), REACTION_SELECT_REQUEST_CODE) + private fun handleActions(action: SimpleAction) { + when (action) { + is SimpleAction.AddReaction -> { + startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE) } - MessageMenuViewModel.ACTION_VIEW_REACTIONS -> { - val messageInformationData = actionData.data as? MessageInformationData - ?: return - ViewReactionBottomSheet.newInstance(roomDetailArgs.roomId, messageInformationData) + is SimpleAction.ViewReactions -> { + ViewReactionBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData) .show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS") } - MessageMenuViewModel.ACTION_COPY -> { + is SimpleAction.Copy -> { //I need info about the current selected message :/ - copyToClipboard(requireContext(), actionData.data?.toString() ?: "", false) + copyToClipboard(requireContext(), action.content, false) val msg = requireContext().getString(R.string.copied_to_clipboard) showSnackWithMessage(msg, Snackbar.LENGTH_SHORT) } - MessageMenuViewModel.ACTION_DELETE -> { - val eventId = actionData.data?.toString() ?: return - roomDetailViewModel.process(RoomDetailActions.RedactAction(eventId, context?.getString(R.string.event_redacted_by_user_reason))) + is SimpleAction.Delete -> { + roomDetailViewModel.process(RoomDetailActions.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason))) } - MessageMenuViewModel.ACTION_SHARE -> { + is SimpleAction.Share -> { //TODO current data communication is too limited //Need to now the media type - actionData.data?.toString()?.let { - //TODO bad, just POC - BigImageViewer.imageLoader().loadImage( - actionData.hashCode(), - Uri.parse(it), - object : ImageLoader.Callback { - override fun onFinish() {} - - override fun onSuccess(image: File?) { - if (image != null) - shareMedia(requireContext(), image, "image/*") - } - - override fun onFail(error: Exception?) {} - - override fun onCacheHit(imageType: Int, image: File?) {} - - override fun onCacheMiss(imageType: Int, image: File?) {} - - override fun onProgress(progress: Int) {} - - override fun onStart() {} + //TODO bad, just POC + BigImageViewer.imageLoader().loadImage( + action.hashCode(), + Uri.parse(action.imageUrl), + object : ImageLoader.Callback { + override fun onFinish() {} + override fun onSuccess(image: File?) { + if (image != null) + shareMedia(requireContext(), image, "image/*") } - ) - } + override fun onFail(error: Exception?) {} + + override fun onCacheHit(imageType: Int, image: File?) {} + + override fun onCacheMiss(imageType: Int, image: File?) {} + + override fun onProgress(progress: Int) {} + + override fun onStart() {} + + } + ) } - MessageMenuViewModel.VIEW_SOURCE, - MessageMenuViewModel.VIEW_DECRYPTED_SOURCE -> { + is SimpleAction.ViewSource -> { val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null) view.findViewById(R.id.event_content_text_view)?.let { - it.text = actionData.data?.toString() ?: "" + it.text = action.content } AlertDialog.Builder(requireActivity()) .setView(view) - .setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() } + .setPositiveButton(R.string.ok, null) .show() } - MessageMenuViewModel.ACTION_QUICK_REACT -> { - //eventId,ClickedOn,Add - (actionData.data as? Triple)?.let { (eventId, clickedOn, add) -> - roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, add)) + is SimpleAction.ViewDecryptedSource -> { + val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null) + view.findViewById(R.id.event_content_text_view)?.let { + it.text = action.content } + + AlertDialog.Builder(requireActivity()) + .setView(view) + .setPositiveButton(R.string.ok, null) + .show() } - MessageMenuViewModel.ACTION_EDIT -> { - val eventId = actionData.data.toString() - roomDetailViewModel.process(RoomDetailActions.EnterEditMode(eventId)) + is SimpleAction.QuickReact -> { + //eventId,ClickedOn,Add + roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) } - MessageMenuViewModel.ACTION_QUOTE -> { - val eventId = actionData.data.toString() - roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(eventId)) + is SimpleAction.Edit -> { + roomDetailViewModel.process(RoomDetailActions.EnterEditMode(action.eventId)) } - MessageMenuViewModel.ACTION_REPLY -> { - val eventId = actionData.data.toString() - roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(eventId)) + is SimpleAction.Quote -> { + roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(action.eventId)) } - MessageMenuViewModel.ACTION_COPY_PERMALINK -> { - val eventId = actionData.data.toString() - val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, eventId) + is SimpleAction.Reply -> { + roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(action.eventId)) + } + is SimpleAction.CopyPermalink -> { + val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId) copyToClipboard(requireContext(), permalink, false) showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) } - MessageMenuViewModel.ACTION_RESEND -> { - val eventId = actionData.data.toString() - roomDetailViewModel.process(RoomDetailActions.ResendMessage(eventId)) + is SimpleAction.Resend -> { + roomDetailViewModel.process(RoomDetailActions.ResendMessage(action.eventId)) } - MessageMenuViewModel.ACTION_REMOVE -> { - val eventId = actionData.data.toString() - roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(eventId)) + is SimpleAction.Remove -> { + roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId)) } - else -> { - Toast.makeText(context, "Action ${actionData.actionId} not implemented", Toast.LENGTH_LONG).show() + else -> { + Toast.makeText(context, "Action $action is not implemented yet", Toast.LENGTH_LONG).show() } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ActionsHandler.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ActionsHandler.kt index 6ad47bfe..ddc8a543 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ActionsHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ActionsHandler.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.home.room.detail.timeline.action import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.utils.LiveEvent import javax.inject.Inject @@ -25,15 +26,10 @@ import javax.inject.Inject */ class ActionsHandler @Inject constructor() : ViewModel() { - data class ActionData( - val actionId: String, - val data: Any? - ) + val actionCommandEvent = MutableLiveData>() - val actionCommandEvent = MutableLiveData>() - - fun fireAction(actionId: String, data: Any? = null) { - actionCommandEvent.value = LiveEvent(ActionData(actionId,data)) + fun fireAction(action: SimpleAction) { + actionCommandEvent.postLiveEvent(action) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index f3bec83a..a80a3454 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -89,7 +89,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() { } menuActionFragment.interactionListener = object : MessageMenuFragment.InteractionListener { override fun didSelectMenuAction(simpleAction: SimpleAction) { - actionHandlerModel.fireAction(simpleAction.uid, simpleAction.data) + actionHandlerModel.fireAction(simpleAction) dismiss() } } @@ -105,7 +105,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() { quickReactionFragment.interactionListener = object : QuickReactionFragment.InteractionListener { override fun didQuickReactWith(clickedOn: String, add: Boolean, eventId: String) { - actionHandlerModel.fireAction(MessageMenuViewModel.ACTION_QUICK_REACT, Triple(eventId, clickedOn, add)) + actionHandlerModel.fireAction(SimpleAction.QuickReact(eventId, clickedOn, add)) dismiss() } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt index 15e08831..0cc95af7 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt @@ -15,6 +15,8 @@ */ package im.vector.riotx.features.home.room.detail.timeline.action +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import com.airbnb.mvrx.* import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject @@ -36,7 +38,24 @@ import im.vector.riotx.core.utils.isSingleEmoji import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData -data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null) +sealed class SimpleAction(@StringRes val titleRes: Int, @DrawableRes val iconResId: Int) { + data class AddReaction(val eventId: String) : SimpleAction(R.string.message_add_reaction, R.drawable.ic_add_reaction) + data class Copy(val content: String) : SimpleAction(R.string.copy, R.drawable.ic_copy) + data class Edit(val eventId: String) : SimpleAction(R.string.edit, R.drawable.ic_edit) + data class Quote(val eventId: String) : SimpleAction(R.string.quote, R.drawable.ic_quote) + data class Reply(val eventId: String) : SimpleAction(R.string.reply, R.drawable.ic_reply) + data class Share(val imageUrl: String?) : SimpleAction(R.string.share, R.drawable.ic_share) + data class Resend(val eventId: String) : SimpleAction(R.string.global_retry, R.drawable.ic_refresh_cw) + data class Remove(val eventId: String) : SimpleAction(R.string.remove, R.drawable.ic_trash) + data class Delete(val eventId: String) : SimpleAction(R.string.delete, R.drawable.ic_delete) + data class Cancel(val eventId: String) : SimpleAction(R.string.cancel, R.drawable.ic_close_round) + data class ViewSource(val content: String) : SimpleAction(R.string.view_source, R.drawable.ic_view_source) + data class ViewDecryptedSource(val content: String) : SimpleAction(R.string.view_decrypted_source, R.drawable.ic_view_source) + data class CopyPermalink(val eventId: String) : SimpleAction(R.string.permalink, R.drawable.ic_permalink) + data class Flag(val eventId: String) : SimpleAction(R.string.report_content, R.drawable.ic_flag) + data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : SimpleAction(0, 0) + data class ViewReactions(val messageInformationData: MessageInformationData) : SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions) +} data class MessageMenuState( val roomId: String, @@ -68,24 +87,6 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M private val informationData: MessageInformationData = initialState.informationData companion object : MvRxViewModelFactory { - - const val ACTION_ADD_REACTION = "add_reaction" - const val ACTION_COPY = "copy" - const val ACTION_EDIT = "edit" - const val ACTION_QUOTE = "quote" - const val ACTION_REPLY = "reply" - const val ACTION_SHARE = "share" - const val ACTION_RESEND = "resend" - const val ACTION_REMOVE = "remove" - const val ACTION_DELETE = "delete" - const val ACTION_CANCEL = "cancel" - const val VIEW_SOURCE = "VIEW_SOURCE" - const val VIEW_DECRYPTED_SOURCE = "VIEW_DECRYPTED_SOURCE" - const val ACTION_COPY_PERMALINK = "ACTION_COPY_PERMALINK" - const val ACTION_FLAG = "ACTION_FLAG" - const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT" - const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS" - override fun create(viewModelContext: ViewModelContext, state: MessageMenuState): MessageMenuViewModel? { val fragment: MessageMenuFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.messageMenuViewModelFactory.create(state) @@ -99,75 +100,64 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M private fun observeEvent() { RxRoom(room) .liveTimelineEvent(eventId) - ?.map { + .map { actionsForEvent(it) } - ?.execute { + .execute { copy(actions = it) } } private fun actionsForEvent(event: TimelineEvent): List { - val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent.toModel() ?: event.root.getClearContent().toModel() val type = messageContent?.type - return if (event.root.sendState.hasFailed()) { - arrayListOf().apply { + return arrayListOf().apply { + if (event.root.sendState.hasFailed()) { if (canRetry(event)) { - this.add(SimpleAction(ACTION_RESEND, R.string.global_retry, R.drawable.ic_refresh_cw, eventId)) + add(SimpleAction.Resend(eventId)) } - this.add(SimpleAction(ACTION_REMOVE, R.string.remove, R.drawable.ic_trash, eventId)) - } - } else if (event.root.sendState.isSending()) { - //TODO is uploading attachment? - arrayListOf().apply { + add(SimpleAction.Remove(eventId)) + } else if (event.root.sendState.isSending()) { + //TODO is uploading attachment? if (canCancel(event)) { - this.add(SimpleAction(ACTION_CANCEL, R.string.cancel, R.drawable.ic_close_round, eventId)) + add(SimpleAction.Cancel(eventId)) } - } - } else { - arrayListOf().apply { - + } else { if (!event.root.isRedacted()) { - if (canReply(event, messageContent)) { - add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId)) + add(SimpleAction.Reply(eventId)) } if (canEdit(event, session.myUserId)) { - add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId)) + add(SimpleAction.Edit(eventId)) } if (canRedact(event, session.myUserId)) { - add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId)) + add(SimpleAction.Delete(eventId)) } if (canCopy(type)) { //TODO copy images? html? see ClipBoard - add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent!!.body)) + add(SimpleAction.Copy(messageContent!!.body)) } if (event.canReact()) { - add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, eventId)) + add(SimpleAction.AddReaction(eventId)) } if (canQuote(event, messageContent)) { - add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, eventId)) + add(SimpleAction.Quote(eventId)) } if (canViewReactions(event)) { - add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, informationData)) + add(SimpleAction.ViewReactions(informationData)) } if (canShare(type)) { if (messageContent is MessageImageContent) { - add( - SimpleAction(ACTION_SHARE, - R.string.share, R.drawable.ic_share, - session.contentUrlResolver().resolveFullSize(messageContent.url)) - ) + add(SimpleAction.Share(session.contentUrlResolver().resolveFullSize(messageContent.url))) } //TODO } @@ -181,17 +171,17 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M } } - add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, event.root.toContentStringWithIndent())) + add(SimpleAction.ViewSource(event.root.toContentStringWithIndent())) if (event.isEncrypted()) { val decryptedContent = event.root.toClearContentStringWithIndent() ?: stringProvider.getString(R.string.encryption_information_decryption_error) - add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, decryptedContent)) + add(SimpleAction.ViewDecryptedSource(decryptedContent)) } - add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, event.root.eventId)) + add(SimpleAction.CopyPermalink(eventId)) if (session.myUserId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) { //not sent by me - add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, event.root.eventId)) + add(SimpleAction.Flag(eventId)) } } } @@ -269,9 +259,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M MessageType.MSGTYPE_NOTICE, MessageType.MSGTYPE_EMOTE, MessageType.FORMAT_MATRIX_HTML, - MessageType.MSGTYPE_LOCATION -> { - true - } + MessageType.MSGTYPE_LOCATION -> true else -> false } } @@ -281,9 +269,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M return when (type) { MessageType.MSGTYPE_IMAGE, MessageType.MSGTYPE_AUDIO, - MessageType.MSGTYPE_VIDEO -> { - true - } + MessageType.MSGTYPE_VIDEO -> true else -> false } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt index fad25870..670cf471 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -166,11 +166,7 @@ abstract class AbsMessageItem : BaseEventItem() { root.isClickable = informationData.sendState.isSent() val state = if (informationData.hasPendingEdits) SendState.UNSENT else informationData.sendState textView?.setTextColor(colorProvider.getMessageTextColor(state)) - failureIndicator?.isVisible = when (informationData.sendState) { - SendState.UNDELIVERED, - SendState.FAILED_UNKNOWN_DEVICES -> true - else -> false - } + failureIndicator?.isVisible = informationData.sendState.hasFailed() } abstract class Holder(@IdRes stubId: Int) : BaseHolder(stubId) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt index 9ed9103c..6f713b17 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt @@ -53,10 +53,6 @@ abstract class MessageImageVideoItem : AbsMessageItem contentUrlResolver.resolveFullSize(data.url) - Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) - } - //Fallback to base url - ?: data.url - - GlideApp - .with(imageView) - .load(resolvedUrl) - } - - glideRequest + createGlideRequest(data, mode, imageView, width, height) .dontAnimate() .transform(RoundedCorners(dpToPx(8, imageView.context))) .thumbnail(0.3f) @@ -95,31 +76,11 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: } - fun renderFitTarget(data: Data, mode: Mode, imageView: ImageView, callback :((Boolean) -> Unit)? = null) { + fun renderFitTarget(data: Data, mode: Mode, imageView: ImageView, callback: ((Boolean) -> Unit)? = null) { val (width, height) = processSize(data, mode) - val glideRequest = if (data.elementToDecrypt != null) { - // Encrypted image - GlideApp - .with(imageView) - .load(data) - } else { - // Clear image - val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver() - val resolvedUrl = when (mode) { - Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url) - Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) - } - //Fallback to base url - ?: data.url - - GlideApp - .with(imageView) - .load(resolvedUrl) - } - - glideRequest - .listener(object: RequestListener { + createGlideRequest(data, mode, imageView, width, height) + .listener(object : RequestListener { override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, @@ -140,7 +101,28 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: }) .fitCenter() .into(imageView) + } + private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, width: Int, height: Int): GlideRequest { + return if (data.elementToDecrypt != null) { + // Encrypted image + GlideApp + .with(imageView) + .load(data) + } else { + // Clear image + val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver() + val resolvedUrl = when (mode) { + Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url) + Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE) + } + //Fallback to base url + ?: data.url + + GlideApp + .with(imageView) + .load(resolvedUrl) + } } fun render(data: Data, imageView: BigImageView) { diff --git a/vector/src/main/res/values-v21/theme_dark.xml b/vector/src/main/res/values-v21/theme_dark.xml index 54e2f589..285a3a2d 100644 --- a/vector/src/main/res/values-v21/theme_dark.xml +++ b/vector/src/main/res/values-v21/theme_dark.xml @@ -8,7 +8,6 @@ true - @transition/image_preview_transition @transition/image_preview_transition