|
|
|
@ -12,6 +12,10 @@ import org.session.libsignal.utilities.logging.Log
|
|
|
|
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
|
|
|
|
|
|
|
|
|
|
class VisibleMessage : Message() {
|
|
|
|
|
/** In the case of a sync message, the public key of the person the message was targeted at.
|
|
|
|
|
*
|
|
|
|
|
* **Note:** `nil` if this isn't a sync message.
|
|
|
|
|
*/
|
|
|
|
|
var syncTarget: String? = null
|
|
|
|
|
var text: String? = null
|
|
|
|
|
val attachmentIDs: MutableList<Long> = mutableListOf()
|
|
|
|
@ -21,103 +25,92 @@ class VisibleMessage : Message() {
|
|
|
|
|
|
|
|
|
|
override val isSelfSendValid: Boolean = true
|
|
|
|
|
|
|
|
|
|
// region Validation
|
|
|
|
|
override fun isValid(): Boolean {
|
|
|
|
|
if (!super.isValid()) return false
|
|
|
|
|
if (attachmentIDs.isNotEmpty()) return true
|
|
|
|
|
val text = text?.trim() ?: return false
|
|
|
|
|
if (text.isNotEmpty()) return true
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// endregion
|
|
|
|
|
|
|
|
|
|
// region Proto Conversion
|
|
|
|
|
companion object {
|
|
|
|
|
const val TAG = "VisibleMessage"
|
|
|
|
|
|
|
|
|
|
fun fromProto(proto: SignalServiceProtos.Content): VisibleMessage? {
|
|
|
|
|
val dataMessage = if (proto.hasDataMessage()) proto.dataMessage else return null
|
|
|
|
|
val dataMessage = proto.dataMessage ?: return null
|
|
|
|
|
val result = VisibleMessage()
|
|
|
|
|
if (dataMessage.hasSyncTarget()) {
|
|
|
|
|
result.syncTarget = dataMessage.syncTarget
|
|
|
|
|
}
|
|
|
|
|
if (dataMessage.hasSyncTarget()) { result.syncTarget = dataMessage.syncTarget }
|
|
|
|
|
result.text = dataMessage.body
|
|
|
|
|
// Attachments are handled in MessageReceiver
|
|
|
|
|
val quoteProto = if (dataMessage.hasQuote()) dataMessage.quote else null
|
|
|
|
|
quoteProto?.let {
|
|
|
|
|
if (quoteProto != null) {
|
|
|
|
|
val quote = Quote.fromProto(quoteProto)
|
|
|
|
|
quote?.let { result.quote = quote }
|
|
|
|
|
result.quote = quote
|
|
|
|
|
}
|
|
|
|
|
val linkPreviewProto = dataMessage.previewList.firstOrNull()
|
|
|
|
|
linkPreviewProto?.let {
|
|
|
|
|
if (linkPreviewProto != null) {
|
|
|
|
|
val linkPreview = LinkPreview.fromProto(linkPreviewProto)
|
|
|
|
|
linkPreview?.let { result.linkPreview = linkPreview }
|
|
|
|
|
result.linkPreview = linkPreview
|
|
|
|
|
}
|
|
|
|
|
// TODO Contact
|
|
|
|
|
// TODO: Contact
|
|
|
|
|
val profile = Profile.fromProto(dataMessage)
|
|
|
|
|
profile?.let { result.profile = profile }
|
|
|
|
|
if (profile != null) { result.profile = profile }
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun addSignalAttachments(signalAttachments: List<SignalAttachment>) {
|
|
|
|
|
val attachmentIDs = signalAttachments.map {
|
|
|
|
|
val databaseAttachment = it as DatabaseAttachment
|
|
|
|
|
databaseAttachment.attachmentId.rowId
|
|
|
|
|
}
|
|
|
|
|
this.attachmentIDs.addAll(attachmentIDs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun isMediaMessage(): Boolean {
|
|
|
|
|
return attachmentIDs.isNotEmpty() || quote != null || linkPreview != null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun isValid(): Boolean {
|
|
|
|
|
if (!super.isValid()) return false
|
|
|
|
|
if (attachmentIDs.isNotEmpty()) return true
|
|
|
|
|
val text = text?.trim() ?: return false
|
|
|
|
|
if (text.isNotEmpty()) return true
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun toProto(): SignalServiceProtos.Content? {
|
|
|
|
|
val proto = SignalServiceProtos.Content.newBuilder()
|
|
|
|
|
val dataMessage: SignalServiceProtos.DataMessage.Builder
|
|
|
|
|
// Profile
|
|
|
|
|
val profile = profile
|
|
|
|
|
val profileProto = profile?.toProto()
|
|
|
|
|
val profileProto = profile?.let { it.toProto() }
|
|
|
|
|
if (profileProto != null) {
|
|
|
|
|
dataMessage = profileProto.toBuilder()
|
|
|
|
|
} else {
|
|
|
|
|
dataMessage = SignalServiceProtos.DataMessage.newBuilder()
|
|
|
|
|
}
|
|
|
|
|
// Text
|
|
|
|
|
text?.let { dataMessage.body = text }
|
|
|
|
|
if (text != null) { dataMessage.body = text }
|
|
|
|
|
// Quote
|
|
|
|
|
quote?.let {
|
|
|
|
|
val quoteProto = it.toProto()
|
|
|
|
|
if (quoteProto != null) dataMessage.quote = quoteProto
|
|
|
|
|
val quoteProto = quote?.let { it.toProto() }
|
|
|
|
|
if (quoteProto != null) {
|
|
|
|
|
dataMessage.quote = quoteProto
|
|
|
|
|
}
|
|
|
|
|
//Link preview
|
|
|
|
|
linkPreview?.let {
|
|
|
|
|
val linkPreviewProto = it.toProto()
|
|
|
|
|
linkPreviewProto?.let {
|
|
|
|
|
dataMessage.addAllPreview(listOf(linkPreviewProto))
|
|
|
|
|
}
|
|
|
|
|
// Link preview
|
|
|
|
|
val linkPreviewProto = linkPreview?.let { it.toProto() }
|
|
|
|
|
if (linkPreviewProto != null) {
|
|
|
|
|
dataMessage.addAllPreview(listOf(linkPreviewProto))
|
|
|
|
|
}
|
|
|
|
|
//Attachments
|
|
|
|
|
val attachments = attachmentIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(it) }
|
|
|
|
|
if (!attachments.all { !it.url.isNullOrEmpty() }) {
|
|
|
|
|
// Attachments
|
|
|
|
|
val database = MessagingModuleConfiguration.shared.messageDataProvider
|
|
|
|
|
val attachments = attachmentIDs.mapNotNull { database.getSignalAttachmentPointer(it) }
|
|
|
|
|
if (attachments.any { it.url.isNullOrEmpty() }) {
|
|
|
|
|
if (BuildConfig.DEBUG) {
|
|
|
|
|
//TODO equivalent to iOS's preconditionFailure
|
|
|
|
|
Log.d(TAG, "Sending a message before all associated attachments have been uploaded.")
|
|
|
|
|
Log.w(TAG, "Sending a message before all associated attachments have been uploaded.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
val attachmentPointers = attachments.mapNotNull { Attachment.createAttachmentPointer(it) }
|
|
|
|
|
dataMessage.addAllAttachments(attachmentPointers)
|
|
|
|
|
// TODO Contact
|
|
|
|
|
val pointers = attachments.mapNotNull { Attachment.createAttachmentPointer(it) }
|
|
|
|
|
dataMessage.addAllAttachments(pointers)
|
|
|
|
|
// TODO: Contact
|
|
|
|
|
// Expiration timer
|
|
|
|
|
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
|
|
|
|
|
// if it receives a message without the current expiration timer value attached to it...
|
|
|
|
|
// if it receives a message without the current expiration timer value attached to it...
|
|
|
|
|
val storage = MessagingModuleConfiguration.shared.storage
|
|
|
|
|
val context = MessagingModuleConfiguration.shared.context
|
|
|
|
|
val expiration = if (storage.isClosedGroup(recipient!!)) Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
|
|
|
|
|
else Recipient.from(context, Address.fromSerialized(recipient!!), false).expireMessages
|
|
|
|
|
val expiration = if (storage.isClosedGroup(recipient!!)) {
|
|
|
|
|
Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(recipient!!)), false).expireMessages
|
|
|
|
|
} else {
|
|
|
|
|
Recipient.from(context, Address.fromSerialized(recipient!!), false).expireMessages
|
|
|
|
|
}
|
|
|
|
|
dataMessage.expireTimer = expiration
|
|
|
|
|
// Group context
|
|
|
|
|
if (storage.isClosedGroup(recipient!!)) {
|
|
|
|
|
try {
|
|
|
|
|
setGroupContext(dataMessage)
|
|
|
|
|
} catch(e: Exception) {
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.w(TAG, "Couldn't construct visible message proto from: $this")
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
@ -135,4 +128,17 @@ class VisibleMessage : Message() {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// endregion
|
|
|
|
|
|
|
|
|
|
fun addSignalAttachments(signalAttachments: List<SignalAttachment>) {
|
|
|
|
|
val attachmentIDs = signalAttachments.map {
|
|
|
|
|
val databaseAttachment = it as DatabaseAttachment
|
|
|
|
|
databaseAttachment.attachmentId.rowId
|
|
|
|
|
}
|
|
|
|
|
this.attachmentIDs.addAll(attachmentIDs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun isMediaMessage(): Boolean {
|
|
|
|
|
return attachmentIDs.isNotEmpty() || quote != null || linkPreview != null
|
|
|
|
|
}
|
|
|
|
|
}
|