diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java index afa4d615d0..7762d18f9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java @@ -12,6 +12,7 @@ public class DatabaseAttachment extends Attachment { private final long mmsId; private final boolean hasData; private final boolean hasThumbnail; + private boolean isUploaded = false; public DatabaseAttachment(AttachmentId attachmentId, long mmsId, boolean hasData, boolean hasThumbnail, @@ -75,4 +76,12 @@ public class DatabaseAttachment extends Attachment { public boolean hasThumbnail() { return hasThumbnail; } + + public boolean isUploaded() { + return isUploaded; + } + + public void setUploaded(boolean uploaded) { + isUploaded = uploaded; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt new file mode 100644 index 0000000000..671f0a508b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -0,0 +1,52 @@ +package org.thoughtcrime.securesms.attachments + +import android.content.Context +import com.google.protobuf.ByteString +import org.session.libsession.database.dto.DatabaseAttachmentDTO +import org.session.libsession.database.MessageDataProvider +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.util.MediaUtil + +class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), MessageDataProvider { + override fun getAttachment(uniqueID: String): DatabaseAttachmentDTO? { + + val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context) + val uniqueID = uniqueID.toLongOrNull() ?: return null + val attachmentID = AttachmentId(0, uniqueID) + val databaseAttachment = attachmentDatabase.getAttachment(attachmentID) ?: return null + + return databaseAttachment.toDTO() + } + +} + +// Extension to DatabaseAttachment class + +fun DatabaseAttachment.toDTO(): DatabaseAttachmentDTO { + var databaseAttachmentDTO = DatabaseAttachmentDTO() + databaseAttachmentDTO.contentType = this.contentType + databaseAttachmentDTO.fileName = this.fileName + databaseAttachmentDTO.caption = this.caption + + databaseAttachmentDTO.size = this.size.toInt() + databaseAttachmentDTO.key = ByteString.copyFrom(this.key?.toByteArray()) + databaseAttachmentDTO.digest = ByteString.copyFrom(this.digest) + databaseAttachmentDTO.flags = if (this.isVoiceNote) SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE.number else 0 + + databaseAttachmentDTO.url = this.url + + if (this.shouldHaveImageSize()) { + databaseAttachmentDTO.shouldHaveImageSize = true + databaseAttachmentDTO.width = this.width + databaseAttachmentDTO.height = this.height + } + + return databaseAttachmentDTO +} + +fun DatabaseAttachment.shouldHaveImageSize(): Boolean { + return (MediaUtil.isVideo(this) || MediaUtil.isImage(this) || MediaUtil.isGif(this)); +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java index 2e124f1579..0e82b96422 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java @@ -497,6 +497,7 @@ public class AttachmentDatabase extends Database { database.update(TABLE_NAME, values, PART_ID_WHERE, ((DatabaseAttachment)attachment).getAttachmentId().toStrings()); notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId)); + ((DatabaseAttachment) attachment).setUploaded(true); } public void setTransferState(long messageId, @NonNull Attachment attachment, int transferState) { diff --git a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt new file mode 100644 index 0000000000..2f089ff4ff --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt @@ -0,0 +1,9 @@ +package org.session.libsession.database + +import org.session.libsession.database.dto.DatabaseAttachmentDTO + +interface MessageDataProvider { + + fun getAttachment(uniqueID: String): DatabaseAttachmentDTO? + +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt b/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt new file mode 100644 index 0000000000..3ad5a23399 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt @@ -0,0 +1,73 @@ +package org.session.libsession.database.dto + +import android.util.Size +import com.google.protobuf.ByteString +import org.session.libsignal.service.internal.push.SignalServiceProtos +import kotlin.math.round + +class DatabaseAttachmentDTO { + var contentType: String? = null + + var fileName: String? = null + + var url: String? = null + + var caption: String? = null + + var size: Int = 0 + + var key: ByteString? = null + + var digest: ByteString? = null + + var flags: Int = 0 + + var width: Int = 0 + + var height: Int = 0 + + val isVoiceNote: Boolean = false + + var shouldHaveImageSize: Boolean = false + + val isUploaded: Boolean = false + + fun toProto(): SignalServiceProtos.AttachmentPointer? { + val builder = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.newBuilder() + builder.contentType = this.contentType + + if (!this.fileName.isNullOrEmpty()) { + builder.fileName = this.fileName + } + if (!this.caption.isNullOrEmpty()) { + builder.caption = this.caption + } + + builder.size = this.size + builder.key = this.key + builder.digest = this.digest + builder.flags = if (this.isVoiceNote) org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE.number else 0 + + //TODO I did copy the behavior of iOS below, not sure if that's relevant here... + if (this.shouldHaveImageSize) { + if (this.width < kotlin.Int.MAX_VALUE && this.height < kotlin.Int.MAX_VALUE) { + val imageSize: Size = Size(this.width, this.height) + val imageWidth = round(imageSize.width.toDouble()) + val imageHeight = round(imageSize.height.toDouble()) + if (imageWidth > 0 && imageHeight > 0) { + builder.width = imageWidth.toInt() + builder.height = imageHeight.toInt() + } + } + } + + builder.url = this.url + + try { + return builder.build() + } catch (e: Exception) { + return null + } + } + +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt index 7fd3aa8773..9a940b58fa 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt @@ -12,7 +12,7 @@ abstract class Message { var sender: String? = null var groupPublicKey: String? = null var openGroupServerMessageID: Long? = null - val ttl: Long = 2 * 24 * 60 * 60 * 1000 + open val ttl: Long = 2 * 24 * 60 * 60 * 1000 // validation open fun isValid(): Boolean { diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt index ec9ae23358..755d0fc1c8 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/TypingIndicator.kt @@ -5,11 +5,11 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos class TypingIndicator() : ControlMessage() { + override val ttl: Long = 30 * 1000 + companion object { const val TAG = "TypingIndicator" - //val ttl: 30 * 1000 //TODO - fun fromProto(proto: SignalServiceProtos.Content): TypingIndicator? { val typingIndicatorProto = proto.typingMessage ?: return null val kind = Kind.fromProto(typingIndicatorProto.action) diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt index 8ff6e476f4..96a90f2168 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Attachment.kt @@ -2,6 +2,7 @@ package org.session.libsession.messaging.messages.visible import android.util.Size import android.webkit.MimeTypeMap +import org.session.libsession.database.MessageDataProvider import org.session.libsignal.service.internal.push.SignalServiceProtos import java.io.File @@ -66,4 +67,8 @@ class Attachment : VisibleMessageProto() override fun toProto(): SignalServiceProtos.AttachmentPointer? { TODO("Not implemented") } + + override fun toProto(messageDataProvider: MessageDataProvider): SignalServiceProtos.AttachmentPointer? { + TODO("Not implemented") + } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt index 0c223a3896..46f56bdff0 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Contact.kt @@ -1,5 +1,6 @@ package org.session.libsession.messaging.messages.visible +import org.session.libsession.database.MessageDataProvider import org.session.libsignal.service.internal.push.SignalServiceProtos class Contact : VisibleMessageProto() { @@ -13,4 +14,8 @@ class Contact : VisibleMessageProto() override fun toProto(): SignalServiceProtos.DataMessage.Contact? { TODO("Not yet implemented") } + + override fun toProto(messageDataProvider: MessageDataProvider): SignalServiceProtos.DataMessage.Contact? { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt index 3e570f9e05..ff5dea0967 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/LinkPreview.kt @@ -1,5 +1,7 @@ package org.session.libsession.messaging.messages.visible +import android.content.Context +import org.session.libsession.database.MessageDataProvider import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -33,7 +35,7 @@ class LinkPreview() : VisibleMessageProto() { return null } } + + override fun toProto(messageDataProvider: MessageDataProvider): SignalServiceProtos.DataMessage? { + return toProto() + } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt index 165899c044..86fa2b0db7 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Quote.kt @@ -1,5 +1,7 @@ package org.session.libsession.messaging.messages.visible +import com.goterl.lazycode.lazysodium.BuildConfig +import org.session.libsession.database.MessageDataProvider import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -29,14 +31,13 @@ class Quote() : VisibleMessageProto() { this.attachmentID = attachmentID } - // validation override fun isValid(): Boolean { if (!super.isValid()) return false return (timestamp != null && publicKey != null) } - override fun toProto(): SignalServiceProtos.DataMessage.Quote? { + override fun toProto(messageDataProvider: MessageDataProvider): SignalServiceProtos.DataMessage.Quote? { val timestamp = timestamp val publicKey = publicKey if (timestamp == null || publicKey == null) { @@ -47,7 +48,7 @@ class Quote() : VisibleMessageProto() { quoteProto.id = timestamp quoteProto.author = publicKey text?.let { quoteProto.text = text } - addAttachmentsIfNeeded(quoteProto) + addAttachmentsIfNeeded(quoteProto, messageDataProvider) // Build try { return quoteProto.build() @@ -57,16 +58,33 @@ class Quote() : VisibleMessageProto() { } } - private fun addAttachmentsIfNeeded(quoteProto: SignalServiceProtos.DataMessage.Quote.Builder) { + private fun addAttachmentsIfNeeded(quoteProto: SignalServiceProtos.DataMessage.Quote.Builder, messageDataProvider: MessageDataProvider) { val attachmentID = attachmentID ?: return - //TODO databas stuff + val attachmentProto = messageDataProvider.getAttachment(attachmentID) + if (attachmentProto == null) { + Log.w(TAG, "Ignoring invalid attachment for quoted message.") + return + } + if (!attachmentProto.isUploaded) { + if (BuildConfig.DEBUG) { + //TODO equivalent to iOS's preconditionFailure + Log.d(TAG,"Sending a message before all associated attachments have been uploaded.") + return + } + } val quotedAttachmentProto = SignalServiceProtos.DataMessage.Quote.QuotedAttachment.newBuilder() - //TODO more database related stuff - //quotedAttachmentProto.contentType = + quotedAttachmentProto.contentType = attachmentProto.contentType + val fileName = attachmentProto.fileName + fileName?.let { quotedAttachmentProto.fileName = fileName } + quotedAttachmentProto.thumbnail = attachmentProto.toProto() try { quoteProto.addAttachments(quotedAttachmentProto.build()) } catch (e: Exception) { Log.w(TAG, "Couldn't construct quoted attachment proto from: $this") } } + + override fun toProto(): SignalServiceProtos.DataMessage.Quote? { + TODO("Not implemented") + } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt index dbdd8ffbf5..d05af38969 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt @@ -1,5 +1,7 @@ package org.session.libsession.messaging.messages.visible +import com.goterl.lazycode.lazysodium.BuildConfig +import org.session.libsession.database.MessageDataProvider import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -46,7 +48,7 @@ class VisibleMessage() : VisibleMessageProto() { return false } - override fun toProto(): SignalServiceProtos.Content? { + override fun toProto(messageDataProvider: MessageDataProvider): SignalServiceProtos.Content? { val proto = SignalServiceProtos.Content.newBuilder() var attachmentIDs = this.attachmentIDs val dataMessage: SignalServiceProtos.DataMessage.Builder @@ -85,9 +87,15 @@ class VisibleMessage() : VisibleMessageProto() { } } //Attachments - // TODO I'm blocking on that one... - //swift: let attachments = attachmentIDs.compactMap { TSAttachmentStream.fetch(uniqueId: $0, transaction: transaction) } - + val attachments = attachmentIDs.mapNotNull { messageDataProvider.getAttachment(it) } + if (!attachments.all { it.isUploaded }) { + if (BuildConfig.DEBUG) { + //TODO equivalent to iOS's preconditionFailure + Log.d(TAG,"Sending a message before all associated attachments have been uploaded.") + } + } + val attachmentProtos = attachments.mapNotNull { it.toProto() } + dataMessage.addAllAttachments(attachmentProtos) // TODO Contact // Build try { @@ -99,4 +107,8 @@ class VisibleMessage() : VisibleMessageProto() { } } + override fun toProto(): SignalServiceProtos.Content? { + TODO("Not implemented") + } + } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt index 97b1506d6c..a9b569688f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessageProto.kt @@ -1,6 +1,9 @@ package org.session.libsession.messaging.messages.visible +import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Message abstract class VisibleMessageProto : Message() { + + abstract fun toProto(messageDataProvider: MessageDataProvider): T } \ No newline at end of file