From 47f47e40c438b291cf551f0fae54d04b640f8323 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 12 Feb 2020 15:06:22 +0100 Subject: [PATCH] code review and cleanup --- CHANGES.md | 2 +- .../api/session/events/model/RelationType.kt | 3 +- .../model/message/MessageOptionsContent.kt | 23 +- .../message/MessagePollResponseContent.kt | 33 +++ .../session/room/model/message/OptionItem.kt | 29 +++ .../api/session/room/send/SendService.kt | 16 +- ...llResponseAggregatedSummaryEntityMapper.kt | 5 +- .../PollResponseAggregatedSummaryEntity.kt | 2 +- .../query/TimelineEventEntityQueries.kt | 15 +- .../android/internal/di/MoshiProvider.kt | 2 +- .../session/room/send/DefaultSendService.kt | 244 +++++++++--------- .../room/send/LocalEchoEventFactory.kt | 16 +- .../riotx/features/command/CommandParser.kt | 8 +- .../home/room/detail/RoomDetailAction.kt | 3 +- .../home/room/detail/RoomDetailViewModel.kt | 14 +- .../timeline/factory/MessageItemFactory.kt | 44 ++-- .../timeline/item/MessageOptionsItem.kt | 3 +- .../detail/timeline/item/MessagePollItem.kt | 3 +- ...tem_timeline_event_option_buttons_stub.xml | 2 +- .../layout/item_timeline_event_poll_stub.xml | 38 ++- vector/src/main/res/layout/option_buttons.xml | 3 +- 21 files changed, 290 insertions(+), 218 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessagePollResponseContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/OptionItem.kt diff --git a/CHANGES.md b/CHANGES.md index ae44263bc2..bbdef263f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ Changes in RiotX 0.16.0 (2020-XX-XX) =================================================== Features ✨: - - Polls and Bot Buttons (MSC 2192) + - Polls and Bot Buttons (MSC 2192 matrix-org/matrix-doc#2192) Improvements 🙌: - Show confirmation dialog before deleting a message (#967) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt index 11e0f882c7..3be9bdb7cc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/RelationType.kt @@ -19,13 +19,12 @@ package im.vector.matrix.android.api.session.events.model * Constants defining known event relation types from Matrix specifications */ object RelationType { - /** Lets you define an event which annotates an existing event.*/ const val ANNOTATION = "m.annotation" /** Lets you define an event which replaces an existing event.*/ const val REPLACE = "m.replace" /** Lets you define an event which references an existing event.*/ const val REFERENCE = "m.reference" - /** Lets you define an event which references an existing event.*/ + /** Lets you define an event which adds a response to an existing event.*/ const val RESPONSE = "m.response" } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt index 23021f5c23..1dfad4ee4b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageOptionsContent.kt @@ -20,13 +20,13 @@ import com.squareup.moshi.JsonClass import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent -enum class OptionsType(val value: String) { - POLL("m.pool"), - BUTTONS("m.buttons"), -} +// Possible values for optionType +const val OPTION_TYPE_POLL = "m.pool" +const val OPTION_TYPE_BUTTONS = "m.buttons" /** * Polls and bot buttons are m.room.message events with a msgtype of m.options, + * Ref: https://github.com/matrix-org/matrix-doc/pull/2192 */ @JsonClass(generateAdapter = true) data class MessageOptionsContent( @@ -35,20 +35,7 @@ data class MessageOptionsContent( @Json(name = "body") override val body: String, @Json(name = "label") val label: String?, @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, - @Json(name = "options") val options: List? = null, + @Json(name = "options") val options: List? = null, @Json(name = "m.new_content") override val newContent: Content? = null ) : MessageContent -@JsonClass(generateAdapter = true) -data class OptionItems( - @Json(name = "label") val label: String?, - @Json(name = "value") val value: String? -) - -@JsonClass(generateAdapter = true) -data class MessagePollResponseContent( - @Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_RESPONSE, - @Json(name = "body") override val body: String, - @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, - @Json(name = "m.new_content") override val newContent: Content? = null -) : MessageContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessagePollResponseContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessagePollResponseContent.kt new file mode 100644 index 0000000000..dfd8059b9a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessagePollResponseContent.kt @@ -0,0 +1,33 @@ +/* + * 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 im.vector.matrix.android.api.session.room.model.message + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.events.model.Content +import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent + +/** + * Ref: https://github.com/matrix-org/matrix-doc/pull/2192 + */ +@JsonClass(generateAdapter = true) +data class MessagePollResponseContent( + @Json(name = "msgtype") override val msgType: String = MessageType.MSGTYPE_RESPONSE, + @Json(name = "body") override val body: String, + @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, + @Json(name = "m.new_content") override val newContent: Content? = null +) : MessageContent diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/OptionItem.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/OptionItem.kt new file mode 100644 index 0000000000..0ea9e246ba --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/OptionItem.kt @@ -0,0 +1,29 @@ +/* + * 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 im.vector.matrix.android.api.session.room.model.message + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Ref: https://github.com/matrix-org/matrix-doc/pull/2192 + */ +@JsonClass(generateAdapter = true) +data class OptionItem( + @Json(name = "label") val label: String?, + @Json(name = "value") val value: String? +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt index 33bbdc3072..e6c32193f4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.api.session.room.send import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.message.MessageType +import im.vector.matrix.android.api.session.room.model.message.OptionItem import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.util.Cancelable @@ -61,6 +62,14 @@ interface SendService { */ fun sendMedias(attachments: List): Cancelable + /** + * Send a poll to the room. + * @param question the question + * @param options list of (label, value) + * @return a [Cancelable] + */ + fun sendPoll(question: String, options: List): Cancelable + /** * Method to send a poll response. * @param pollEventId the poll currently replied to @@ -71,12 +80,7 @@ interface SendService { fun sendOptionsReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable /** - * @param options list of (label, value) - */ - fun sendPoll(question: String, options: List>) - - /** - * Redacts (delete) the given event. + * Redact (delete) the given event. * @param event The event to redact * @param reason Optional reason string */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt index 238d8c963a..1d34c660bc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt @@ -1,6 +1,5 @@ -package im.vector.matrix.android.internal.database.mapper /* - * Copyright 2019 New Vector Ltd + * Copyright 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. @@ -15,6 +14,8 @@ package im.vector.matrix.android.internal.database.mapper * limitations under the License. */ +package im.vector.matrix.android.internal.database.mapper + import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.PollResponseAggregatedSummary diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/PollResponseAggregatedSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/PollResponseAggregatedSummaryEntity.kt index b8eeeff96e..b17fbf07fa 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/PollResponseAggregatedSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/PollResponseAggregatedSummaryEntity.kt @@ -19,7 +19,7 @@ import io.realm.RealmList import io.realm.RealmObject /** - * Keep the latest state of edition of a message + * Keep the latest state of a poll */ internal open class PollResponseAggregatedSummaryEntity( // For now we persist this a JSON for greater flexibility diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt index 42f93d88e4..5168d0728e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt @@ -16,10 +16,16 @@ package im.vector.matrix.android.internal.database.query -import android.service.autofill.Validators.not import im.vector.matrix.android.api.session.room.send.SendState -import im.vector.matrix.android.internal.database.model.* -import io.realm.* +import im.vector.matrix.android.internal.database.model.ChunkEntity +import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields +import io.realm.Realm +import io.realm.RealmList +import io.realm.RealmQuery +import io.realm.RealmResults +import io.realm.Sort import io.realm.kotlin.where internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery { @@ -55,7 +61,8 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm, val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterTypes(filterTypes) val liveEvents = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes) if (filterContentRelation) { - liveEvents?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.EDIT_TYPE) + liveEvents + ?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.EDIT_TYPE) ?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.RESPONSE_TYPE) } val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt index fdf7c1b597..c19c686329 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt @@ -46,8 +46,8 @@ object MoshiProvider { .registerSubtype(MessageVideoContent::class.java, MessageType.MSGTYPE_VIDEO) .registerSubtype(MessageLocationContent::class.java, MessageType.MSGTYPE_LOCATION) .registerSubtype(MessageFileContent::class.java, MessageType.MSGTYPE_FILE) - .registerSubtype(MessageOptionsContent::class.java, MessageType.MSGTYPE_OPTIONS) .registerSubtype(MessageVerificationRequestContent::class.java, MessageType.MSGTYPE_VERIFICATION_REQUEST) + .registerSubtype(MessageOptionsContent::class.java, MessageType.MSGTYPE_OPTIONS) .registerSubtype(MessagePollResponseContent::class.java, MessageType.MSGTYPE_RESPONSE) ) .add(SerializeNulls.JSON_ADAPTER_FACTORY) 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 d56af77ab0..fbb80adf83 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 @@ -28,6 +28,7 @@ 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.isImageMessage import im.vector.matrix.android.api.session.events.model.isTextMessage +import im.vector.matrix.android.api.session.room.model.message.OptionItem import im.vector.matrix.android.api.session.room.send.SendService import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.timeline.TimelineEvent @@ -80,7 +81,13 @@ internal class DefaultSendService @AssistedInject constructor( val event = localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType).also { createLocalEcho(it) } + return sendEvent(event) + } + override fun sendPoll(question: String, options: List): Cancelable { + val event = localEchoEventFactory.createPollEvent(roomId, question, options).also { + createLocalEcho(it) + } return sendEvent(event) } @@ -91,13 +98,6 @@ internal class DefaultSendService @AssistedInject constructor( return sendEvent(event) } - override fun sendPoll(question: String, options: List>) { - localEchoEventFactory.createPollEvent(roomId, question, options).also { - createLocalEcho(it) - sendEvent(it) - } - } - private fun sendEvent(event: Event): Cancelable { // Encrypted room handling return if (cryptoService.isRoomEncrypted(roomId)) { @@ -172,123 +172,123 @@ internal class DefaultSendService @AssistedInject constructor( } } - override fun clearSendingQueue() { - timelineSendEventWorkCommon.cancelAllWorks(roomId) - workManagerProvider.workManager.cancelUniqueWork(buildWorkName(UPLOAD_WORK)) + override fun clearSendingQueue() { + timelineSendEventWorkCommon.cancelAllWorks(roomId) + workManagerProvider.workManager.cancelUniqueWork(buildWorkName(UPLOAD_WORK)) - // Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied - workManagerProvider.matrixOneTimeWorkRequestBuilder() - .build().let { - timelineSendEventWorkCommon.postWork(roomId, it, ExistingWorkPolicy.REPLACE) + // Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied + workManagerProvider.matrixOneTimeWorkRequestBuilder() + .build().let { + timelineSendEventWorkCommon.postWork(roomId, it, ExistingWorkPolicy.REPLACE) - // need to clear also image sending queue - workManagerProvider.workManager - .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it) - .enqueue() - } - taskExecutor.executorScope.launch { - localEchoRepository.clearSendingQueue(roomId) - } - } - - override fun resendAllFailedMessages() { - taskExecutor.executorScope.launch { - val eventsToResend = localEchoRepository.getAllFailedEventsToResend(roomId) - eventsToResend.forEach { - sendEvent(it) + // need to clear also image sending queue + workManagerProvider.workManager + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it) + .enqueue() } - localEchoRepository.updateSendState(roomId, eventsToResend.mapNotNull { it.eventId }, SendState.UNSENT) - } - } - - override fun sendMedia(attachment: ContentAttachmentData): Cancelable { - // Create an event with the media file path - val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also { - createLocalEcho(it) - } - return internalSendMedia(event, attachment) - } - - private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData): Cancelable { - val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId) - - val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true) - val sendWork = createSendEventWork(localEcho, false) - - if (isRoomEncrypted) { - val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/) - - val op: Operation = workManagerProvider.workManager - .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) - .then(encryptWork) - .then(sendWork) - .enqueue() - op.result.addListener(Runnable { - if (op.result.isCancelled) { - Timber.e("CHAIN WAS CANCELLED") - } else if (op.state.value is Operation.State.FAILURE) { - Timber.e("CHAIN DID FAIL") - } - }, workerFutureListenerExecutor) - } else { - workManagerProvider.workManager - .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) - .then(sendWork) - .enqueue() - } - - return CancelableWork(workManagerProvider.workManager, sendWork.id) - } - - private fun createLocalEcho(event: Event) { - localEchoEventFactory.createLocalEcho(event) - } - - private fun buildWorkName(identifier: String): String { - return "${roomId}_$identifier" - } - - private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { - // Same parameter - val params = EncryptEventWorker.Params(sessionId, roomId, event) - val sendWorkData = WorkerParamsFactory.toData(params) - - return workManagerProvider.matrixOneTimeWorkRequestBuilder() - .setConstraints(WorkManagerProvider.workConstraints) - .setInputData(sendWorkData) - .startChain(startChain) - .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) - .build() - } - - private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { - val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event) - val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) - - return timelineSendEventWorkCommon.createWork(sendWorkData, startChain) - } - - private fun createRedactEventWork(event: Event, reason: String?): OneTimeWorkRequest { - val redactEvent = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason).also { - createLocalEcho(it) - } - val sendContentWorkerParams = RedactEventWorker.Params(sessionId, redactEvent.eventId!!, roomId, event.eventId, reason) - val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) - return timelineSendEventWorkCommon.createWork(redactWorkData, true) - } - - private fun createUploadMediaWork(event: Event, - attachment: ContentAttachmentData, - isRoomEncrypted: Boolean, - startChain: Boolean): OneTimeWorkRequest { - val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted) - val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) - - return workManagerProvider.matrixOneTimeWorkRequestBuilder() - .setConstraints(WorkManagerProvider.workConstraints) - .startChain(startChain) - .setInputData(uploadWorkData) - .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) - .build() + taskExecutor.executorScope.launch { + localEchoRepository.clearSendingQueue(roomId) } } + + override fun resendAllFailedMessages() { + taskExecutor.executorScope.launch { + val eventsToResend = localEchoRepository.getAllFailedEventsToResend(roomId) + eventsToResend.forEach { + sendEvent(it) + } + localEchoRepository.updateSendState(roomId, eventsToResend.mapNotNull { it.eventId }, SendState.UNSENT) + } + } + + override fun sendMedia(attachment: ContentAttachmentData): Cancelable { + // Create an event with the media file path + val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also { + createLocalEcho(it) + } + return internalSendMedia(event, attachment) + } + + private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData): Cancelable { + val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId) + + val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true) + val sendWork = createSendEventWork(localEcho, false) + + if (isRoomEncrypted) { + val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/) + + val op: Operation = workManagerProvider.workManager + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) + .then(encryptWork) + .then(sendWork) + .enqueue() + op.result.addListener(Runnable { + if (op.result.isCancelled) { + Timber.e("CHAIN WAS CANCELLED") + } else if (op.state.value is Operation.State.FAILURE) { + Timber.e("CHAIN DID FAIL") + } + }, workerFutureListenerExecutor) + } else { + workManagerProvider.workManager + .beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork) + .then(sendWork) + .enqueue() + } + + return CancelableWork(workManagerProvider.workManager, sendWork.id) + } + + private fun createLocalEcho(event: Event) { + localEchoEventFactory.createLocalEcho(event) + } + + private fun buildWorkName(identifier: String): String { + return "${roomId}_$identifier" + } + + private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { + // Same parameter + val params = EncryptEventWorker.Params(sessionId, roomId, event) + val sendWorkData = WorkerParamsFactory.toData(params) + + return workManagerProvider.matrixOneTimeWorkRequestBuilder() + .setConstraints(WorkManagerProvider.workConstraints) + .setInputData(sendWorkData) + .startChain(startChain) + .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) + .build() + } + + private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest { + val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event) + val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) + + return timelineSendEventWorkCommon.createWork(sendWorkData, startChain) + } + + private fun createRedactEventWork(event: Event, reason: String?): OneTimeWorkRequest { + val redactEvent = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason).also { + createLocalEcho(it) + } + val sendContentWorkerParams = RedactEventWorker.Params(sessionId, redactEvent.eventId!!, roomId, event.eventId, reason) + val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams) + return timelineSendEventWorkCommon.createWork(redactWorkData, true) + } + + private fun createUploadMediaWork(event: Event, + attachment: ContentAttachmentData, + isRoomEncrypted: Boolean, + startChain: Boolean): OneTimeWorkRequest { + val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted) + val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) + + return workManagerProvider.matrixOneTimeWorkRequestBuilder() + .setConstraints(WorkManagerProvider.workConstraints) + .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/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index a445c96acf..f77f4b7f3a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -42,8 +42,8 @@ import im.vector.matrix.android.api.session.room.model.message.MessageTextConten import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent -import im.vector.matrix.android.api.session.room.model.message.OptionItems -import im.vector.matrix.android.api.session.room.model.message.OptionsType +import im.vector.matrix.android.api.session.room.model.message.OPTION_TYPE_POLL +import im.vector.matrix.android.api.session.room.model.message.OptionItem import im.vector.matrix.android.api.session.room.model.message.ThumbnailInfo import im.vector.matrix.android.api.session.room.model.message.VideoInfo import im.vector.matrix.android.api.session.room.model.message.isReply @@ -153,13 +153,13 @@ internal class LocalEchoEventFactory @Inject constructor( fun createPollEvent(roomId: String, question: String, - options: List>): Event { + options: List): Event { val compatLabel = buildString { + append("[Poll] ") append(question) - append("\n") options.forEach { append("\n") - append(it.second) + append(it.value) } } return createEvent( @@ -167,10 +167,8 @@ internal class LocalEchoEventFactory @Inject constructor( MessageOptionsContent( body = compatLabel, label = question, - optionType = OptionsType.POLL.value, - options = options.map { - OptionItems(it.first, it.second) - } + optionType = OPTION_TYPE_POLL, + options = options.toList() ) ) } diff --git a/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt b/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt index 6407cafaf6..abc047e273 100644 --- a/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt +++ b/vector/src/main/java/im/vector/riotx/features/command/CommandParser.kt @@ -265,11 +265,11 @@ object CommandParser { } Command.POLL.command -> { val rawCommand = textMessage.substring(Command.POLL.command.length).trim() - val splited = rawCommand.split("|").map { it.trim() } - if (splited.size > 2) { - ParsedCommand.SendPoll(splited[0], splited.subList(1, splited.size)) + val split = rawCommand.split("|").map { it.trim() } + if (split.size > 2) { + ParsedCommand.SendPoll(split[0], split.subList(1, split.size)) } else { - ParsedCommand.ErrorUnknownSlashCommand(slashCommand) + ParsedCommand.ErrorSyntax(Command.POLL) } } else -> { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt index 48d7f15e53..eff34ed764 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt @@ -53,8 +53,7 @@ sealed class RoomDetailAction : VectorViewModelAction { data class ResendMessage(val eventId: String) : RoomDetailAction() data class RemoveFailedEcho(val eventId: String) : RoomDetailAction() - data class ReplyToOptionsPoll(val eventId: String, val optionIndex: Int, val optionValue: String) : RoomDetailAction() - data class ReplyToOptionsButtons(val eventId: String, val optionIndex: Int, val optionValue: String) : RoomDetailAction() + data class ReplyToOptions(val eventId: String, val optionIndex: Int, val optionValue: String) : RoomDetailAction() data class ReportContent( val eventId: String, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 31f4e2ea6b..e5f4576cd1 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -43,6 +43,7 @@ import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary 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.model.message.OptionItem import im.vector.matrix.android.api.session.room.model.message.getFileUrl import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent import im.vector.matrix.android.api.session.room.read.ReadService @@ -199,8 +200,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages() is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() - is RoomDetailAction.ReplyToOptionsPoll -> replyToPoll(action) - is RoomDetailAction.ReplyToOptionsButtons -> replyToButtons(action) + is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action) is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) is RoomDetailAction.RequestVerification -> handleRequestVerification(action) @@ -424,8 +424,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) popDraft() } - is ParsedCommand.SendPoll -> { - room.sendPoll(slashCommandResult.question, slashCommandResult.options.mapIndexed { index, s -> s to "$index. $s" }) + is ParsedCommand.SendPoll -> { + room.sendPoll(slashCommandResult.question, slashCommandResult.options.mapIndexed { index, s -> OptionItem(s, "$index. $s") }) _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) popDraft() } @@ -862,11 +862,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } } - private fun replyToPoll(action: RoomDetailAction.ReplyToOptionsPoll) { - room.sendOptionsReply(action.eventId, action.optionIndex, action.optionValue) - } - - private fun replyToButtons(action: RoomDetailAction.ReplyToOptionsButtons) { + private fun handleReplyToOptions(action: RoomDetailAction.ReplyToOptions) { room.sendOptionsReply(action.eventId, action.optionIndex, action.optionValue) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 381e3711cd..ef4f66fe5d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -39,7 +39,8 @@ import im.vector.matrix.android.api.session.room.model.message.MessageTextConten import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent -import im.vector.matrix.android.api.session.room.model.message.OptionsType +import im.vector.matrix.android.api.session.room.model.message.OPTION_TYPE_BUTTONS +import im.vector.matrix.android.api.session.room.model.message.OPTION_TYPE_POLL import im.vector.matrix.android.api.session.room.model.message.getFileUrl import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent @@ -152,24 +153,29 @@ class MessageItemFactory @Inject constructor( highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes): VectorEpoxyModel<*>? { - if (messageContent.optionType == OptionsType.POLL.value) { - return MessagePollItem_() - .attributes(attributes) - .callback(callback) - .informationData(informationData) - .leftGuideline(avatarSizeProvider.leftGuideline) - .optionsContent(messageContent) - .highlighted(highlight) - } else if (messageContent.optionType == OptionsType.BUTTONS.value) { - return MessageOptionsItem_() - .attributes(attributes) - .callback(callback) - .informationData(informationData) - .leftGuideline(avatarSizeProvider.leftGuideline) - .optionsContent(messageContent) - .highlighted(highlight) - } else { - return null + return when (messageContent.optionType) { + OPTION_TYPE_POLL -> { + MessagePollItem_() + .attributes(attributes) + .callback(callback) + .informationData(informationData) + .leftGuideline(avatarSizeProvider.leftGuideline) + .optionsContent(messageContent) + .highlighted(highlight) + } + OPTION_TYPE_BUTTONS -> { + MessageOptionsItem_() + .attributes(attributes) + .callback(callback) + .informationData(informationData) + .leftGuideline(avatarSizeProvider.leftGuideline) + .optionsContent(messageContent) + .highlighted(highlight) + } + else -> { + // Not supported optionType + buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageOptionsItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageOptionsItem.kt index e13a28deaa..080ff26992 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageOptionsItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageOptionsItem.kt @@ -60,8 +60,7 @@ abstract class MessageOptionsItem : AbsMessageItem() holder.buttonContainer.addView(materialButton, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) materialButton.text = option.label materialButton.setOnClickListener { - callback?.onTimelineItemAction(RoomDetailAction.ReplyToOptionsButtons(relatedEventId, index, option.value - ?: "$index")) + callback?.onTimelineItemAction(RoomDetailAction.ReplyToOptions(relatedEventId, index, option.value ?: "$index")) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt index 8bcba9be62..5c92c22935 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessagePollItem.kt @@ -145,8 +145,7 @@ abstract class MessagePollItem : AbsMessageItem() { val optionIndex = buttons.indexOf(it) if (optionIndex != -1 && pollId != null) { val compatValue = if (optionIndex < optionValues?.size ?: 0) optionValues?.get(optionIndex) else null - callback?.onTimelineItemAction(RoomDetailAction.ReplyToOptionsPoll(pollId!!, optionIndex, compatValue - ?: "$optionIndex")) + callback?.onTimelineItemAction(RoomDetailAction.ReplyToOptions(pollId!!, optionIndex, compatValue ?: "$optionIndex")) } }) buttons.forEach { it.setOnClickListener(clickListener) } diff --git a/vector/src/main/res/layout/item_timeline_event_option_buttons_stub.xml b/vector/src/main/res/layout/item_timeline_event_option_buttons_stub.xml index 1eb2d5aed1..e9ccb32860 100644 --- a/vector/src/main/res/layout/item_timeline_event_option_buttons_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_option_buttons_stub.xml @@ -9,9 +9,9 @@ android:id="@+id/optionLabelText" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginBottom="8dp" android:textColor="?riotx_text_primary" android:textSize="14sp" - android:layout_marginBottom="8dp" android:textStyle="normal" tools:text="What would you like to do?" /> diff --git a/vector/src/main/res/layout/item_timeline_event_poll_stub.xml b/vector/src/main/res/layout/item_timeline_event_poll_stub.xml index 0ee6b089da..febb2dd0f3 100644 --- a/vector/src/main/res/layout/item_timeline_event_poll_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_poll_stub.xml @@ -11,22 +11,24 @@ android:orientation="horizontal"> + android:src="@drawable/ic_poll" + android:tint="@color/riotx_accent" /> + + android:visibility="gone" + tools:text="Create Github issue" + tools:visibility="visible" /> + android:visibility="gone" + tools:text="Search Github" + tools:visibility="visible" /> + android:visibility="gone" + tools:text="Logout" + tools:visibility="visible" /> + tools:text="Option 4" + tools:visibility="visible" /> + tools:text="Option 5" + tools:visibility="visible" /> + android:paddingEnd="8dp" + android:visibility="gone" + tools:visibility="visible"> + android:visibility="gone" + tools:text="12 votes - Final Results" + tools:visibility="visible" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/option_buttons.xml b/vector/src/main/res/layout/option_buttons.xml index c1b05ec6af..c813dea83a 100644 --- a/vector/src/main/res/layout/option_buttons.xml +++ b/vector/src/main/res/layout/option_buttons.xml @@ -1,6 +1,7 @@ + \ No newline at end of file + tools:text="Create Github issue" />