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 338b8e0855..4e9814fcc2 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 b861d55951..974e8b94ae 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 @@ -65,4 +66,8 @@ class Attachment { 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 86d4da20d0..e375834b05 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() { @@ -13,4 +14,8 @@ class Contact() { 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 20eb516739..d077986d7a 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 @@ -43,7 +45,8 @@ class LinkPreview() { title?.let { linkPreviewProto.title = title } val attachmentID = attachmentID attachmentID?.let { - //TODO database stuff + val attachmentProto = messageDataProvider.getAttachment(attachmentID) + attachmentProto?.let { linkPreviewProto.image = attachmentProto.toProto() } } // Build try { diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt index 9cecad42c8..fbf15d7b54 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/visible/Profile.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.messages.visible import com.google.protobuf.ByteString +import org.session.libsession.database.MessageDataProvider import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -56,4 +57,8 @@ class Profile() { 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 a85ff037df..552ef8c0b3 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,7 +31,6 @@ class Quote() { this.attachmentID = attachmentID } - // validation fun isValid(): Boolean { return (timestamp != null && publicKey != null) @@ -46,7 +47,7 @@ class Quote() { quoteProto.id = timestamp quoteProto.author = publicKey text?.let { quoteProto.text = text } - addAttachmentsIfNeeded(quoteProto) + addAttachmentsIfNeeded(quoteProto, messageDataProvider) // Build try { return quoteProto.build() @@ -56,12 +57,25 @@ class Quote() { } } - 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) { 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 424edc684f..a5f88c1f2c 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,6 +1,10 @@ package org.session.libsession.messaging.messages.visible +import com.goterl.lazycode.lazysodium.BuildConfig + +import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Message + import org.session.libsignal.libsignal.logging.Log import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -47,7 +51,7 @@ class VisibleMessage : Message() { return false } - override fun toProto(): SignalServiceProtos.Content? { + fun toProto(): SignalServiceProtos.Content? { val proto = SignalServiceProtos.Content.newBuilder() var attachmentIDs = this.attachmentIDs val dataMessage: SignalServiceProtos.DataMessage.Builder @@ -86,9 +90,15 @@ class VisibleMessage : Message() { } } //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,5 +109,4 @@ class VisibleMessage : Message() { return null } } - } \ No newline at end of file