Merge pull request #7397 from vector-im/feature/fre/voice_broadcast_additional_content
Add additional data in voice broadcast events
This commit is contained in:
commit
13972661e0
1
changelog.d/7397.wip
Normal file
1
changelog.d/7397.wip
Normal file
@ -0,0 +1 @@
|
|||||||
|
[Voice Broadcast] Add additional data in events
|
@ -45,18 +45,30 @@ interface SendService {
|
|||||||
* @param text the text message to send
|
* @param text the text message to send
|
||||||
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
|
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
|
||||||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendTextMessage(text: CharSequence, msgType: String = MessageType.MSGTYPE_TEXT, autoMarkdown: Boolean = false): Cancelable
|
fun sendTextMessage(
|
||||||
|
text: CharSequence,
|
||||||
|
msgType: String = MessageType.MSGTYPE_TEXT,
|
||||||
|
autoMarkdown: Boolean = false,
|
||||||
|
additionalContent: Content? = null,
|
||||||
|
): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to send a text message with a formatted body.
|
* Method to send a text message with a formatted body.
|
||||||
* @param text the text message to send
|
* @param text the text message to send
|
||||||
* @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
|
* @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
|
||||||
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
|
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable
|
fun sendFormattedTextMessage(
|
||||||
|
text: String,
|
||||||
|
formattedText: String,
|
||||||
|
msgType: String = MessageType.MSGTYPE_TEXT,
|
||||||
|
additionalContent: Content? = null,
|
||||||
|
): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to quote an events content.
|
* Method to quote an events content.
|
||||||
@ -65,6 +77,7 @@ interface SendService {
|
|||||||
* @param formattedText the formatted text message to send
|
* @param formattedText the formatted text message to send
|
||||||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||||
* @param rootThreadEventId when this param is not null, the message will be sent in this specific thread
|
* @param rootThreadEventId when this param is not null, the message will be sent in this specific thread
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendQuotedTextMessage(
|
fun sendQuotedTextMessage(
|
||||||
@ -73,6 +86,7 @@ interface SendService {
|
|||||||
formattedText: String? = null,
|
formattedText: String? = null,
|
||||||
autoMarkdown: Boolean,
|
autoMarkdown: Boolean,
|
||||||
rootThreadEventId: String? = null,
|
rootThreadEventId: String? = null,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Cancelable
|
): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,6 +97,7 @@ interface SendService {
|
|||||||
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||||
* @param rootThreadEventId when this param is not null, the Media will be sent in this specific thread
|
* @param rootThreadEventId when this param is not null, the Media will be sent in this specific thread
|
||||||
* @param relatesTo add a relation content to the media event
|
* @param relatesTo add a relation content to the media event
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedia(
|
fun sendMedia(
|
||||||
@ -91,6 +106,7 @@ interface SendService {
|
|||||||
roomIds: Set<String>,
|
roomIds: Set<String>,
|
||||||
rootThreadEventId: String? = null,
|
rootThreadEventId: String? = null,
|
||||||
relatesTo: RelationDefaultContent? = null,
|
relatesTo: RelationDefaultContent? = null,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Cancelable
|
): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,6 +116,7 @@ interface SendService {
|
|||||||
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||||
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||||
* @param rootThreadEventId when this param is not null, all the Media will be sent in this specific thread
|
* @param rootThreadEventId when this param is not null, all the Media will be sent in this specific thread
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedias(
|
fun sendMedias(
|
||||||
@ -107,6 +124,7 @@ interface SendService {
|
|||||||
compressBeforeSending: Boolean,
|
compressBeforeSending: Boolean,
|
||||||
roomIds: Set<String>,
|
roomIds: Set<String>,
|
||||||
rootThreadEventId: String? = null,
|
rootThreadEventId: String? = null,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Cancelable
|
): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,31 +132,35 @@ interface SendService {
|
|||||||
* @param pollType indicates open or closed polls
|
* @param pollType indicates open or closed polls
|
||||||
* @param question the question
|
* @param question the question
|
||||||
* @param options list of options
|
* @param options list of options
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendPoll(pollType: PollType, question: String, options: List<String>): Cancelable
|
fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content? = null): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to send a poll response.
|
* Method to send a poll response.
|
||||||
* @param pollEventId the poll currently replied to
|
* @param pollEventId the poll currently replied to
|
||||||
* @param answerId The id of the answer
|
* @param answerId The id of the answer
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun voteToPoll(pollEventId: String, answerId: String): Cancelable
|
fun voteToPoll(pollEventId: String, answerId: String, additionalContent: Content? = null): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End a poll in the room.
|
* End a poll in the room.
|
||||||
* @param pollEventId event id of the poll
|
* @param pollEventId event id of the poll
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun endPoll(pollEventId: String): Cancelable
|
fun endPoll(pollEventId: String, additionalContent: Content? = null): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redact (delete) the given event.
|
* Redact (delete) the given event.
|
||||||
* @param event The event to redact
|
* @param event The event to redact
|
||||||
* @param reason Optional reason string
|
* @param reason Optional reason string
|
||||||
|
* @param additionalContent additional content to put in the event content
|
||||||
*/
|
*/
|
||||||
fun redactEvent(event: Event, reason: String?): Cancelable
|
fun redactEvent(event: Event, reason: String?, additionalContent: Content? = null): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule this message to be resent.
|
* Schedule this message to be resent.
|
||||||
|
@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
|||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||||
@ -407,7 +408,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||||||
newAttachmentAttributes: NewAttachmentAttributes
|
newAttachmentAttributes: NewAttachmentAttributes
|
||||||
) {
|
) {
|
||||||
localEchoRepository.updateEcho(eventId) { _, event ->
|
localEchoRepository.updateEcho(eventId) { _, event ->
|
||||||
val messageContent: MessageContent? = event.asDomain().content.toModel()
|
val content: Content? = event.asDomain().content
|
||||||
|
val messageContent: MessageContent? = content.toModel()
|
||||||
|
// Retrieve potential additional content from the original event
|
||||||
|
val additionalContent = content.orEmpty() - messageContent?.toContent().orEmpty().keys
|
||||||
val updatedContent = when (messageContent) {
|
val updatedContent = when (messageContent) {
|
||||||
is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes)
|
is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes)
|
||||||
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newAttachmentAttributes)
|
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newAttachmentAttributes)
|
||||||
@ -415,7 +419,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||||||
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes.newFileSize)
|
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes.newFileSize)
|
||||||
else -> messageContent
|
else -> messageContent
|
||||||
}
|
}
|
||||||
event.content = ContentMapper.map(updatedContent.toContent())
|
event.content = ContentMapper.map(updatedContent.toContent().plus(additionalContent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import dagger.assisted.AssistedInject
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
|
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
|
||||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
|
import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
|
||||||
import org.matrix.android.sdk.api.session.events.model.isTextMessage
|
import org.matrix.android.sdk.api.session.events.model.isTextMessage
|
||||||
@ -88,14 +89,14 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendTextMessage(text: CharSequence, msgType: String, autoMarkdown: Boolean): Cancelable {
|
override fun sendTextMessage(text: CharSequence, msgType: String, autoMarkdown: Boolean, additionalContent: Content?): Cancelable {
|
||||||
return localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown)
|
return localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown, additionalContent)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String): Cancelable {
|
override fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String, additionalContent: Content?): Cancelable {
|
||||||
return localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType)
|
return localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType, additionalContent)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
@ -105,7 +106,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
text: String,
|
text: String,
|
||||||
formattedText: String?,
|
formattedText: String?,
|
||||||
autoMarkdown: Boolean,
|
autoMarkdown: Boolean,
|
||||||
rootThreadEventId: String?
|
rootThreadEventId: String?,
|
||||||
|
additionalContent: Content?,
|
||||||
): Cancelable {
|
): Cancelable {
|
||||||
return localEchoEventFactory.createQuotedTextEvent(
|
return localEchoEventFactory.createQuotedTextEvent(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
@ -113,33 +115,34 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
text = text,
|
text = text,
|
||||||
formattedText = formattedText,
|
formattedText = formattedText,
|
||||||
autoMarkdown = autoMarkdown,
|
autoMarkdown = autoMarkdown,
|
||||||
rootThreadEventId = rootThreadEventId
|
rootThreadEventId = rootThreadEventId,
|
||||||
|
additionalContent = additionalContent,
|
||||||
)
|
)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendPoll(pollType: PollType, question: String, options: List<String>): Cancelable {
|
override fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content?): Cancelable {
|
||||||
return localEchoEventFactory.createPollEvent(roomId, pollType, question, options)
|
return localEchoEventFactory.createPollEvent(roomId, pollType, question, options, additionalContent)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun voteToPoll(pollEventId: String, answerId: String): Cancelable {
|
override fun voteToPoll(pollEventId: String, answerId: String, additionalContent: Content?): Cancelable {
|
||||||
return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId)
|
return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId, additionalContent)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun endPoll(pollEventId: String): Cancelable {
|
override fun endPoll(pollEventId: String, additionalContent: Content?): Cancelable {
|
||||||
return localEchoEventFactory.createEndPollEvent(roomId, pollEventId)
|
return localEchoEventFactory.createEndPollEvent(roomId, pollEventId, additionalContent)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
override fun redactEvent(event: Event, reason: String?, additionalContent: Content?): Cancelable {
|
||||||
// TODO manage media/attachements?
|
// TODO manage media/attachements?
|
||||||
val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason)
|
val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason, additionalContent)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
return eventSenderProcessor.postRedaction(redactionEcho, reason)
|
return eventSenderProcessor.postRedaction(redactionEcho, reason)
|
||||||
}
|
}
|
||||||
@ -265,7 +268,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
attachments: List<ContentAttachmentData>,
|
attachments: List<ContentAttachmentData>,
|
||||||
compressBeforeSending: Boolean,
|
compressBeforeSending: Boolean,
|
||||||
roomIds: Set<String>,
|
roomIds: Set<String>,
|
||||||
rootThreadEventId: String?
|
rootThreadEventId: String?,
|
||||||
|
additionalContent: Content?,
|
||||||
): Cancelable {
|
): Cancelable {
|
||||||
return attachments.mapTo(CancelableBag()) {
|
return attachments.mapTo(CancelableBag()) {
|
||||||
sendMedia(
|
sendMedia(
|
||||||
@ -283,6 +287,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
roomIds: Set<String>,
|
roomIds: Set<String>,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
relatesTo: RelationDefaultContent?,
|
relatesTo: RelationDefaultContent?,
|
||||||
|
additionalContent: Content?,
|
||||||
): Cancelable {
|
): Cancelable {
|
||||||
// Ensure that the event will not be send in a thread if we are a different flow.
|
// Ensure that the event will not be send in a thread if we are a different flow.
|
||||||
// Like sending files to multiple rooms
|
// Like sending files to multiple rooms
|
||||||
@ -299,6 +304,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
attachment = attachment,
|
attachment = attachment,
|
||||||
rootThreadEventId = rootThreadId,
|
rootThreadEventId = rootThreadId,
|
||||||
relatesTo,
|
relatesTo,
|
||||||
|
additionalContent,
|
||||||
).also { event ->
|
).also { event ->
|
||||||
createLocalEcho(event)
|
createLocalEcho(event)
|
||||||
}
|
}
|
||||||
|
@ -95,12 +95,12 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
private val permalinkFactory: PermalinkFactory,
|
private val permalinkFactory: PermalinkFactory,
|
||||||
private val clock: Clock,
|
private val clock: Clock,
|
||||||
) {
|
) {
|
||||||
fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event {
|
fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean, additionalContent: Content? = null): Event {
|
||||||
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
|
if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
|
||||||
return createFormattedTextEvent(roomId, createTextContent(text, autoMarkdown), msgType)
|
return createFormattedTextEvent(roomId, createTextContent(text, autoMarkdown), msgType, additionalContent)
|
||||||
}
|
}
|
||||||
val content = MessageTextContent(msgType = msgType, body = text.toString())
|
val content = MessageTextContent(msgType = msgType, body = text.toString())
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTextContent(text: CharSequence, autoMarkdown: Boolean): TextContent {
|
private fun createTextContent(text: CharSequence, autoMarkdown: Boolean): TextContent {
|
||||||
@ -116,8 +116,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
return TextContent(text.toString())
|
return TextContent(text.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFormattedTextEvent(roomId: String, textContent: TextContent, msgType: String): Event {
|
fun createFormattedTextEvent(roomId: String, textContent: TextContent, msgType: String, additionalContent: Content? = null): Event {
|
||||||
return createMessageEvent(roomId, textContent.toMessageTextContent(msgType))
|
return createMessageEvent(roomId, textContent.toMessageTextContent(msgType), additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createReplaceTextEvent(
|
fun createReplaceTextEvent(
|
||||||
@ -128,6 +128,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
newBodyAutoMarkdown: Boolean,
|
newBodyAutoMarkdown: Boolean,
|
||||||
msgType: String,
|
msgType: String,
|
||||||
compatibilityText: String,
|
compatibilityText: String,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val content = if (newBodyFormattedText != null) {
|
val content = if (newBodyFormattedText != null) {
|
||||||
TextContent(newBodyText.toString(), newBodyFormattedText.toString()).toMessageTextContent(msgType)
|
TextContent(newBodyText.toString(), newBodyFormattedText.toString()).toMessageTextContent(msgType)
|
||||||
@ -141,7 +142,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
body = compatibilityText,
|
body = compatibilityText,
|
||||||
relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
|
relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
|
||||||
newContent = content,
|
newContent = content,
|
||||||
)
|
),
|
||||||
|
additionalContent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +169,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
targetEventId: String,
|
targetEventId: String,
|
||||||
question: String,
|
question: String,
|
||||||
options: List<String>,
|
options: List<String>,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val newContent = MessagePollContent(
|
val newContent = MessagePollContent(
|
||||||
relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
|
relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
|
||||||
@ -179,7 +182,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.POLL_START.first(),
|
type = EventType.POLL_START.first(),
|
||||||
content = newContent.toContent()
|
content = newContent.toContent().plus(additionalContent.orEmpty())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +190,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
roomId: String,
|
roomId: String,
|
||||||
pollEventId: String,
|
pollEventId: String,
|
||||||
answerId: String,
|
answerId: String,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val content = MessagePollResponseContent(
|
val content = MessagePollResponseContent(
|
||||||
body = answerId,
|
body = answerId,
|
||||||
@ -203,7 +207,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.POLL_RESPONSE.first(),
|
type = EventType.POLL_RESPONSE.first(),
|
||||||
content = content.toContent(),
|
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -213,6 +217,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
pollType: PollType,
|
pollType: PollType,
|
||||||
question: String,
|
question: String,
|
||||||
options: List<String>,
|
options: List<String>,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val content = createPollContent(question, options, pollType)
|
val content = createPollContent(question, options, pollType)
|
||||||
val localId = LocalEcho.createLocalEchoId()
|
val localId = LocalEcho.createLocalEchoId()
|
||||||
@ -222,7 +227,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.POLL_START.first(),
|
type = EventType.POLL_START.first(),
|
||||||
content = content.toContent(),
|
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -230,6 +235,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
fun createEndPollEvent(
|
fun createEndPollEvent(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
eventId: String,
|
eventId: String,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val content = MessageEndPollContent(
|
val content = MessageEndPollContent(
|
||||||
relatesTo = RelationDefaultContent(
|
relatesTo = RelationDefaultContent(
|
||||||
@ -244,7 +250,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.POLL_END.first(),
|
type = EventType.POLL_END.first(),
|
||||||
content = content.toContent(),
|
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -255,6 +261,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
longitude: Double,
|
longitude: Double,
|
||||||
uncertainty: Double?,
|
uncertainty: Double?,
|
||||||
isUserLocation: Boolean,
|
isUserLocation: Boolean,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val geoUri = buildGeoUri(latitude, longitude, uncertainty)
|
val geoUri = buildGeoUri(latitude, longitude, uncertainty)
|
||||||
val assetType = if (isUserLocation) LocationAssetType.SELF else LocationAssetType.PIN
|
val assetType = if (isUserLocation) LocationAssetType.SELF else LocationAssetType.PIN
|
||||||
@ -266,7 +273,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
unstableTimestampMillis = clock.epochMillis(),
|
unstableTimestampMillis = clock.epochMillis(),
|
||||||
unstableText = geoUri
|
unstableText = geoUri
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createLiveLocationEvent(
|
fun createLiveLocationEvent(
|
||||||
@ -275,6 +282,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
latitude: Double,
|
latitude: Double,
|
||||||
longitude: Double,
|
longitude: Double,
|
||||||
uncertainty: Double?,
|
uncertainty: Double?,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val geoUri = buildGeoUri(latitude, longitude, uncertainty)
|
val geoUri = buildGeoUri(latitude, longitude, uncertainty)
|
||||||
val content = MessageBeaconLocationDataContent(
|
val content = MessageBeaconLocationDataContent(
|
||||||
@ -293,7 +301,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.BEACON_LOCATION_DATA.first(),
|
type = EventType.BEACON_LOCATION_DATA.first(),
|
||||||
content = content.toContent(),
|
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -306,6 +314,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
autoMarkdown: Boolean,
|
autoMarkdown: Boolean,
|
||||||
msgType: String,
|
msgType: String,
|
||||||
compatibilityText: String,
|
compatibilityText: String,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false)
|
val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false)
|
||||||
val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: ""
|
val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: ""
|
||||||
@ -340,7 +349,8 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
formattedBody = replyFormatted
|
formattedBody = replyFormatted
|
||||||
)
|
)
|
||||||
.toContent()
|
.toContent()
|
||||||
)
|
),
|
||||||
|
additionalContent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,23 +359,32 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
attachment: ContentAttachmentData,
|
attachment: ContentAttachmentData,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
relatesTo: RelationDefaultContent?,
|
relatesTo: RelationDefaultContent?,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
return when (attachment.type) {
|
return when (attachment.type) {
|
||||||
ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment, rootThreadEventId, relatesTo)
|
ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment, rootThreadEventId, relatesTo, additionalContent)
|
||||||
ContentAttachmentData.Type.VIDEO -> createVideoEvent(roomId, attachment, rootThreadEventId, relatesTo)
|
ContentAttachmentData.Type.VIDEO -> createVideoEvent(roomId, attachment, rootThreadEventId, relatesTo, additionalContent)
|
||||||
ContentAttachmentData.Type.AUDIO -> createAudioEvent(roomId, attachment, isVoiceMessage = false, rootThreadEventId = rootThreadEventId, relatesTo)
|
ContentAttachmentData.Type.AUDIO -> createAudioEvent(
|
||||||
|
roomId,
|
||||||
|
attachment,
|
||||||
|
isVoiceMessage = false,
|
||||||
|
rootThreadEventId = rootThreadEventId,
|
||||||
|
relatesTo,
|
||||||
|
additionalContent
|
||||||
|
)
|
||||||
ContentAttachmentData.Type.VOICE_MESSAGE -> createAudioEvent(
|
ContentAttachmentData.Type.VOICE_MESSAGE -> createAudioEvent(
|
||||||
roomId,
|
roomId,
|
||||||
attachment,
|
attachment,
|
||||||
isVoiceMessage = true,
|
isVoiceMessage = true,
|
||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
relatesTo,
|
relatesTo,
|
||||||
|
additionalContent,
|
||||||
)
|
)
|
||||||
ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId, relatesTo)
|
ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId, relatesTo, additionalContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createReactionEvent(roomId: String, targetEventId: String, reaction: String): Event {
|
fun createReactionEvent(roomId: String, targetEventId: String, reaction: String, additionalContent: Content? = null): Event {
|
||||||
val content = ReactionContent(
|
val content = ReactionContent(
|
||||||
ReactionInfo(
|
ReactionInfo(
|
||||||
RelationType.ANNOTATION,
|
RelationType.ANNOTATION,
|
||||||
@ -380,7 +399,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.REACTION,
|
type = EventType.REACTION,
|
||||||
content = content.toContent(),
|
content = content.toContent().plus(additionalContent.orEmpty()),
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -390,6 +409,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
attachment: ContentAttachmentData,
|
attachment: ContentAttachmentData,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
relatesTo: RelationDefaultContent?,
|
relatesTo: RelationDefaultContent?,
|
||||||
|
additionalContent: Content?,
|
||||||
): Event {
|
): Event {
|
||||||
var width = attachment.width
|
var width = attachment.width
|
||||||
var height = attachment.height
|
var height = attachment.height
|
||||||
@ -417,7 +437,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
url = attachment.queryUri.toString(),
|
url = attachment.queryUri.toString(),
|
||||||
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVideoEvent(
|
private fun createVideoEvent(
|
||||||
@ -425,6 +445,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
attachment: ContentAttachmentData,
|
attachment: ContentAttachmentData,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
relatesTo: RelationDefaultContent?,
|
relatesTo: RelationDefaultContent?,
|
||||||
|
additionalContent: Content?,
|
||||||
): Event {
|
): Event {
|
||||||
val mediaDataRetriever = MediaMetadataRetriever()
|
val mediaDataRetriever = MediaMetadataRetriever()
|
||||||
mediaDataRetriever.setDataSource(context, attachment.queryUri)
|
mediaDataRetriever.setDataSource(context, attachment.queryUri)
|
||||||
@ -459,7 +480,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
url = attachment.queryUri.toString(),
|
url = attachment.queryUri.toString(),
|
||||||
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAudioEvent(
|
private fun createAudioEvent(
|
||||||
@ -468,6 +489,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
isVoiceMessage: Boolean,
|
isVoiceMessage: Boolean,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
relatesTo: RelationDefaultContent?,
|
relatesTo: RelationDefaultContent?,
|
||||||
|
additionalContent: Content?
|
||||||
): Event {
|
): Event {
|
||||||
val content = MessageAudioContent(
|
val content = MessageAudioContent(
|
||||||
msgType = MessageType.MSGTYPE_AUDIO,
|
msgType = MessageType.MSGTYPE_AUDIO,
|
||||||
@ -485,7 +507,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
voiceMessageIndicator = if (!isVoiceMessage) null else emptyMap(),
|
voiceMessageIndicator = if (!isVoiceMessage) null else emptyMap(),
|
||||||
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFileEvent(
|
private fun createFileEvent(
|
||||||
@ -493,6 +515,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
attachment: ContentAttachmentData,
|
attachment: ContentAttachmentData,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
relatesTo: RelationDefaultContent?,
|
relatesTo: RelationDefaultContent?,
|
||||||
|
additionalContent: Content?
|
||||||
): Event {
|
): Event {
|
||||||
val content = MessageFileContent(
|
val content = MessageFileContent(
|
||||||
msgType = MessageType.MSGTYPE_FILE,
|
msgType = MessageType.MSGTYPE_FILE,
|
||||||
@ -504,15 +527,16 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
url = attachment.queryUri.toString(),
|
url = attachment.queryUri.toString(),
|
||||||
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMessageEvent(roomId: String, content: MessageContent? = null): Event {
|
private fun createMessageEvent(roomId: String, content: MessageContent, additionalContent: Content?): Event {
|
||||||
return createEvent(roomId, EventType.MESSAGE, content.toContent())
|
return createEvent(roomId, EventType.MESSAGE, content.toContent(), additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createEvent(roomId: String, type: String, content: Content?): Event {
|
fun createEvent(roomId: String, type: String, content: Content?, additionalContent: Content? = null): Event {
|
||||||
val newContent = enhanceStickerIfNeeded(type, content) ?: content
|
val newContent = enhanceStickerIfNeeded(type, content) ?: content
|
||||||
|
val updatedNewContent = newContent?.plus(additionalContent.orEmpty()) ?: additionalContent
|
||||||
val localId = LocalEcho.createLocalEchoId()
|
val localId = LocalEcho.createLocalEchoId()
|
||||||
return Event(
|
return Event(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
@ -520,7 +544,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
senderId = userId,
|
senderId = userId,
|
||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = type,
|
type = type,
|
||||||
content = newContent,
|
content = updatedNewContent,
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -555,6 +579,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
msgType: String,
|
msgType: String,
|
||||||
autoMarkdown: Boolean,
|
autoMarkdown: Boolean,
|
||||||
formattedText: String?,
|
formattedText: String?,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val content = formattedText?.let { TextContent(text.toString(), it) } ?: createTextContent(text, autoMarkdown)
|
val content = formattedText?.let { TextContent(text.toString(), it) } ?: createTextContent(text, autoMarkdown)
|
||||||
return createEvent(
|
return createEvent(
|
||||||
@ -564,8 +589,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
||||||
msgType = msgType
|
msgType = msgType
|
||||||
)
|
).toContent().plus(additionalContent.orEmpty())
|
||||||
.toContent()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,6 +608,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
autoMarkdown: Boolean,
|
autoMarkdown: Boolean,
|
||||||
rootThreadEventId: String? = null,
|
rootThreadEventId: String? = null,
|
||||||
showInThread: Boolean,
|
showInThread: Boolean,
|
||||||
|
additionalContent: Content? = null
|
||||||
): Event? {
|
): Event? {
|
||||||
// Fallbacks and event representation
|
// Fallbacks and event representation
|
||||||
// TODO Add error/warning logs when any of this is null
|
// TODO Add error/warning logs when any of this is null
|
||||||
@ -621,7 +646,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
showInThread = showInThread
|
showInThread = showInThread
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content, additionalContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateThreadRelationContent(rootThreadEventId: String) =
|
private fun generateThreadRelationContent(rootThreadEventId: String) =
|
||||||
@ -750,7 +775,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
fun createRedactEvent(roomId: String, eventId: String, reason: String?): Event {
|
fun createRedactEvent(roomId: String, eventId: String, reason: String?, additionalContent: Content? = null): Event {
|
||||||
val localId = LocalEcho.createLocalEchoId()
|
val localId = LocalEcho.createLocalEchoId()
|
||||||
return Event(
|
return Event(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
@ -759,7 +784,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
eventId = localId,
|
eventId = localId,
|
||||||
type = EventType.REDACTION,
|
type = EventType.REDACTION,
|
||||||
redacts = eventId,
|
redacts = eventId,
|
||||||
content = reason?.let { mapOf("reason" to it).toContent() },
|
content = reason?.let { mapOf("reason" to it).toContent().plus(additionalContent.orEmpty()) } ?: additionalContent,
|
||||||
unsignedData = UnsignedData(age = null, transactionId = localId)
|
unsignedData = UnsignedData(age = null, transactionId = localId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -776,9 +801,14 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
formattedText: String?,
|
formattedText: String?,
|
||||||
autoMarkdown: Boolean,
|
autoMarkdown: Boolean,
|
||||||
rootThreadEventId: String?,
|
rootThreadEventId: String?,
|
||||||
|
additionalContent: Content? = null,
|
||||||
): Event {
|
): Event {
|
||||||
val messageContent = quotedEvent.getLastMessageContent()
|
val messageContent = quotedEvent.getLastMessageContent()
|
||||||
val textMsg = if (messageContent is MessageContentWithFormattedBody) { messageContent.formattedBody } else { messageContent?.body }
|
val textMsg = if (messageContent is MessageContentWithFormattedBody) {
|
||||||
|
messageContent.formattedBody
|
||||||
|
} else {
|
||||||
|
messageContent?.body
|
||||||
|
}
|
||||||
val quoteText = legacyRiotQuoteText(textMsg, text)
|
val quoteText = legacyRiotQuoteText(textMsg, text)
|
||||||
val quoteFormattedText = "<blockquote>$textMsg</blockquote>$formattedText"
|
val quoteFormattedText = "<blockquote>$textMsg</blockquote>$formattedText"
|
||||||
|
|
||||||
@ -791,13 +821,15 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
|
||||||
msgType = MessageType.MSGTYPE_TEXT
|
msgType = MessageType.MSGTYPE_TEXT
|
||||||
)
|
),
|
||||||
|
additionalContent,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
createFormattedTextEvent(
|
createFormattedTextEvent(
|
||||||
roomId,
|
roomId,
|
||||||
markdownParser.parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText),
|
markdownParser.parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText),
|
||||||
MessageType.MSGTYPE_TEXT
|
MessageType.MSGTYPE_TEXT,
|
||||||
|
additionalContent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,16 @@
|
|||||||
|
|
||||||
package im.vector.app.features.voicebroadcast
|
package im.vector.app.features.voicebroadcast
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||||
|
|
||||||
object VoiceBroadcastConstants {
|
object VoiceBroadcastConstants {
|
||||||
|
|
||||||
/** Voice Broadcast State Event. */
|
/** Voice Broadcast State Event. */
|
||||||
const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
|
const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
|
||||||
|
|
||||||
|
/** Custom key passed to the [MessageAudioContent] with Voice Broadcast information. */
|
||||||
|
const val VOICE_BROADCAST_CHUNK_KEY = "io.element.voice_broadcast_chunk"
|
||||||
|
|
||||||
/** Default voice broadcast chunk duration, in seconds. */
|
/** Default voice broadcast chunk duration, in seconds. */
|
||||||
const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 120
|
const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 120
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,19 @@
|
|||||||
|
|
||||||
package im.vector.app.features.voicebroadcast
|
package im.vector.app.features.voicebroadcast
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastChunk
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
|
||||||
|
|
||||||
fun MessageAudioEvent?.isVoiceBroadcast() = this?.getVoiceBroadcastEventId() != null
|
fun MessageAudioEvent?.isVoiceBroadcast() = this?.root?.getClearContent()?.get(VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY) != null
|
||||||
|
|
||||||
fun MessageAudioEvent.getVoiceBroadcastEventId(): String? =
|
fun MessageAudioEvent.getVoiceBroadcastEventId(): String? = if (isVoiceBroadcast()) root.getRelationContent()?.eventId else null
|
||||||
// TODO Improve this condition by checking the referenced event type
|
|
||||||
root.takeIf { content.voiceMessageIndicator != null }
|
fun MessageAudioEvent.getVoiceBroadcastChunk(): VoiceBroadcastChunk? {
|
||||||
?.getRelationContent()?.takeIf { it.type == RelationType.REFERENCE }
|
@Suppress("UNCHECKED_CAST")
|
||||||
?.eventId
|
return (root.getClearContent()?.get(VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY) as? Content).toModel<VoiceBroadcastChunk>()
|
||||||
|
}
|
||||||
|
|
||||||
|
val MessageAudioEvent.sequence: Int? get() = getVoiceBroadcastChunk()?.sequence
|
||||||
|
@ -85,7 +85,7 @@ class VoiceBroadcastPlayer @Inject constructor(
|
|||||||
private fun updatePlaylist(room: Room, eventId: String) {
|
private fun updatePlaylist(room: Room, eventId: String) {
|
||||||
val timelineEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId)
|
val timelineEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId)
|
||||||
val audioEvents = timelineEvents.mapNotNull { it.root.asMessageAudioEvent() }
|
val audioEvents = timelineEvents.mapNotNull { it.root.asMessageAudioEvent() }
|
||||||
playlist = audioEvents.sortedBy { it.root.originServerTs }
|
playlist = audioEvents.sortedBy { it.getVoiceBroadcastChunk()?.sequence?.toLong() ?: it.root.originServerTs }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startPlayback() {
|
private fun startPlayback() {
|
||||||
|
@ -38,6 +38,8 @@ data class MessageVoiceBroadcastInfoContent(
|
|||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||||
|
|
||||||
|
/** The device from which the broadcast has been started. */
|
||||||
|
@Json(name = "device_id") val deviceId: String? = null,
|
||||||
/** The [VoiceBroadcastState] value. **/
|
/** The [VoiceBroadcastState] value. **/
|
||||||
@Json(name = "state") val voiceBroadcastStateStr: String = "",
|
@Json(name = "state") val voiceBroadcastStateStr: String = "",
|
||||||
/** The length of the voice chunks in seconds. **/
|
/** The length of the voice chunks in seconds. **/
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.app.features.voicebroadcast.model
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class VoiceBroadcastChunk(
|
||||||
|
@Json(name = "sequence") val sequence: Int? = null
|
||||||
|
)
|
@ -23,6 +23,7 @@ import im.vector.app.features.attachments.toContentAttachmentData
|
|||||||
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
|
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
|
||||||
import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
|
import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
|
||||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||||
|
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastChunk
|
||||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||||
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
||||||
import im.vector.lib.multipicker.utils.toMultiPickerAudioType
|
import im.vector.lib.multipicker.utils.toMultiPickerAudioType
|
||||||
@ -70,6 +71,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
|
|||||||
eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
|
eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||||
stateKey = session.myUserId,
|
stateKey = session.myUserId,
|
||||||
body = MessageVoiceBroadcastInfoContent(
|
body = MessageVoiceBroadcastInfoContent(
|
||||||
|
deviceId = session.sessionParams.deviceId,
|
||||||
voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
|
voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
|
||||||
chunkLength = chunkLength,
|
chunkLength = chunkLength,
|
||||||
).toContent()
|
).toContent()
|
||||||
@ -93,12 +95,14 @@ class StartVoiceBroadcastUseCase @Inject constructor(
|
|||||||
"Voice Broadcast Part ($sequence).${voiceMessageFile.extension}"
|
"Voice Broadcast Part ($sequence).${voiceMessageFile.extension}"
|
||||||
)
|
)
|
||||||
val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return
|
val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return
|
||||||
// TODO put sequence in event content
|
|
||||||
room.sendService().sendMedia(
|
room.sendService().sendMedia(
|
||||||
attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
|
attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
|
||||||
compressBeforeSending = false,
|
compressBeforeSending = false,
|
||||||
roomIds = emptySet(),
|
roomIds = emptySet(),
|
||||||
relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId)
|
relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId),
|
||||||
|
additionalContent = mapOf(
|
||||||
|
VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY to VoiceBroadcastChunk(sequence = sequence).toContent()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.PollType
|
import org.matrix.android.sdk.api.session.room.model.message.PollType
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendService
|
import org.matrix.android.sdk.api.session.room.send.SendService
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
@ -25,5 +26,5 @@ class FakeSendService : SendService by mockk() {
|
|||||||
|
|
||||||
private val cancelable = mockk<Cancelable>()
|
private val cancelable = mockk<Cancelable>()
|
||||||
|
|
||||||
override fun sendPoll(pollType: PollType, question: String, options: List<String>): Cancelable = cancelable
|
override fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content?): Cancelable = cancelable
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user