From 26b2ead673628a43542f0cda2e6c057fceaf58ea Mon Sep 17 00:00:00 2001 From: Brice Date: Thu, 17 Dec 2020 14:27:04 +1100 Subject: [PATCH] kotlin impl of SignalServiceAttachment classes --- .../database/dto/DatabaseAttachmentDTO.kt | 75 ----------- .../attachments/SignalServiceAttachment.kt | 123 ++++++++++++++++++ .../SignalServiceAttachmentPointer.kt | 30 +++++ .../SignalServiceAttachmentStream.kt | 75 +++++++++++ 4 files changed, 228 insertions(+), 75 deletions(-) delete mode 100644 libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt create mode 100644 libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachment.kt create mode 100644 libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentPointer.kt create mode 100644 libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentStream.kt 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 deleted file mode 100644 index bf5f31f822..0000000000 --- a/libsession/src/main/java/org/session/libsession/database/dto/DatabaseAttachmentDTO.kt +++ /dev/null @@ -1,75 +0,0 @@ -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 attachmentId: Long = 0 - - 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 = 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) 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 < Int.MAX_VALUE && this.height < Int.MAX_VALUE) { - val imageSize= 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/sending_receiving/attachments/SignalServiceAttachment.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachment.kt new file mode 100644 index 0000000000..7c8a1f2543 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachment.kt @@ -0,0 +1,123 @@ +package org.session.libsession.messaging.sending_receiving.attachments + +import com.google.protobuf.ByteString +import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.api.messages.SignalServiceAttachment +import java.io.InputStream + +abstract class SignalServiceAttachment protected constructor(val contentType: String?) { + + var attachmentId: Long = 0 + var isGif: Boolean = false + var isImage: Boolean = false + var isVideo: Boolean = false + var isAudio: Boolean = false + var url: String? = null + var key: ByteString? = null + + abstract fun isStream(): Boolean + abstract fun isPointer(): Boolean + fun asStream(): SignalServiceAttachmentStream { + return this as SignalServiceAttachmentStream + } + + fun asPointer(): SignalServiceAttachmentPointer { + return this as SignalServiceAttachmentPointer + } + + fun shouldHaveImageSize(): Boolean { + return (isVideo || isImage || isGif); + } + + class Builder internal constructor() { + private var inputStream: InputStream? = null + private var contentType: String? = null + private var fileName: String? = null + private var length: Long = 0 + private var listener: SignalServiceAttachment.ProgressListener? = null + private var voiceNote = false + private var width = 0 + private var height = 0 + private var caption: String? = null + fun withStream(inputStream: InputStream?): Builder { + this.inputStream = inputStream + return this + } + + fun withContentType(contentType: String?): Builder { + this.contentType = contentType + return this + } + + fun withLength(length: Long): Builder { + this.length = length + return this + } + + fun withFileName(fileName: String?): Builder { + this.fileName = fileName + return this + } + + fun withListener(listener: SignalServiceAttachment.ProgressListener?): Builder { + this.listener = listener + return this + } + + fun withVoiceNote(voiceNote: Boolean): Builder { + this.voiceNote = voiceNote + return this + } + + fun withWidth(width: Int): Builder { + this.width = width + return this + } + + fun withHeight(height: Int): Builder { + this.height = height + return this + } + + fun withCaption(caption: String?): Builder { + this.caption = caption + return this + } + + fun build(): SignalServiceAttachmentStream { + requireNotNull(inputStream) { "Must specify stream!" } + requireNotNull(contentType) { "No content type specified!" } + require(length != 0L) { "No length specified!" } + return SignalServiceAttachmentStream(inputStream, contentType, length, Optional.fromNullable(fileName), voiceNote, Optional.absent(), width, height, Optional.fromNullable(caption), listener) + } + } + + /** + * An interface to receive progress information on upload/download of + * an attachment. + */ + /*interface ProgressListener { + /** + * Called on a progress change event. + * + * @param total The total amount to transmit/receive in bytes. + * @param progress The amount that has been transmitted/received in bytes thus far + */ + fun onAttachmentProgress(total: Long, progress: Long) + }*/ + + companion object { + @JvmStatic + fun newStreamBuilder(): Builder { + return Builder() + } + } +} + +// matches values in AttachmentDatabase.java +enum class AttachmentState(val value: Int) { + DONE(0), + STARTED(1), + PENDING(2), + FAILED(3) +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentPointer.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentPointer.kt new file mode 100644 index 0000000000..07f39e2609 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentPointer.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014-2017 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsession.messaging.sending_receiving.attachments + +import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.api.messages.SignalServiceAttachment + +/** + * Represents a received SignalServiceAttachment "handle." This + * is a pointer to the actual attachment content, which needs to be + * retrieved using [SignalServiceMessageReceiver.retrieveAttachment] + * + * @author Moxie Marlinspike + */ +class SignalServiceAttachmentPointer(val id: Long, contentType: String?, val key: ByteArray?, + val size: Optional, val preview: Optional, + val width: Int, val height: Int, + val digest: Optional, val fileName: Optional, + val voiceNote: Boolean, val caption: Optional, val url: String) : SignalServiceAttachment(contentType) { + override fun isStream(): Boolean { + return false + } + + override fun isPointer(): Boolean { + return true + } +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentStream.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentStream.kt new file mode 100644 index 0000000000..24fd214bee --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/attachments/SignalServiceAttachmentStream.kt @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ +package org.session.libsession.messaging.sending_receiving.attachments + +import android.util.Size +import com.google.protobuf.ByteString +import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.api.messages.SignalServiceAttachment as SAttachment +import java.io.InputStream +import kotlin.math.round + +/** + * Represents a local SignalServiceAttachment to be sent. + */ +class SignalServiceAttachmentStream(val inputStream: InputStream?, contentType: String?, val length: Long, val fileName: Optional?, val voiceNote: Boolean, val preview: Optional, val width: Int, val height: Int, val caption: Optional, val listener: SAttachment.ProgressListener?) : SignalServiceAttachment(contentType) { + + constructor(inputStream: InputStream?, contentType: String?, length: Long, fileName: Optional?, voiceNote: Boolean, listener: SAttachment.ProgressListener?) : this(inputStream, contentType, length, fileName, voiceNote, Optional.absent(), 0, 0, Optional.absent(), listener) {} + + // Though now required, `digest` may be null for pre-existing records or from + // messages received from other clients + var digest: ByteArray? = null + + // This only applies for attachments being uploaded. + var isUploaded: Boolean = false + + override fun isStream(): Boolean { + return true + } + + override fun isPointer(): Boolean { + return false + } + + fun toProto(): SignalServiceProtos.AttachmentPointer? { + val builder = SignalServiceProtos.AttachmentPointer.newBuilder() + builder.contentType = this.contentType + + if (!this.fileName?.get().isNullOrEmpty()) { + builder.fileName = this.fileName?.get() + } + if (!this.caption.get().isNullOrEmpty()) { + builder.caption = this.caption.get() + } + + builder.size = this.length.toInt() + builder.key = this.key + builder.digest = ByteString.copyFrom(this.digest) + builder.flags = if (this.voiceNote) 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 < Int.MAX_VALUE && this.height < Int.MAX_VALUE) { + val imageSize= 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