From a2fc7102610644425dd48c3ae733437c3870d549 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 14 Jun 2024 11:35:52 -0400 Subject: [PATCH] Add support for addressing attachments within a message. --- .../conversation/ConversationItemPreviewer.kt | 3 ++- .../database/AttachmentTableTest_deduping.kt | 3 ++- .../attachments/ArchivedAttachment.kt | 7 +++++-- .../securesms/attachments/Attachment.kt | 10 ++++++++-- .../attachments/AttachmentUploadUtil.kt | 1 + .../attachments/DatabaseAttachment.kt | 7 +++++-- .../attachments/PointerAttachment.kt | 13 +++++++++---- .../attachments/TombstoneAttachment.kt | 10 +++++++--- .../securesms/attachments/UriAttachment.kt | 4 +++- .../v2/database/ChatItemExportIterator.kt | 3 ++- .../v2/database/ChatItemImportInserter.kt | 18 ++++++++++++------ .../securesms/database/AttachmentTable.kt | 17 +++++++++++++---- .../securesms/database/MediaTable.kt | 1 + .../securesms/database/MessageTable.kt | 3 ++- .../helpers/SignalDatabaseMigrations.kt | 6 ++++-- .../migration/V235_AttachmentUuidColumn.kt | 19 +++++++++++++++++++ .../securesms/jobs/AttachmentDownloadJob.kt | 3 ++- .../jobs/AvatarGroupsV1DownloadJob.java | 2 +- .../securesms/jobs/PushSendJob.java | 8 ++++++-- .../securesms/jobs/RestoreAttachmentJob.kt | 6 ++++-- .../jobs/RestoreAttachmentThumbnailJob.kt | 3 ++- .../jobs/RetrieveRemoteAnnouncementsJob.kt | 4 +++- .../releasechannel/ReleaseChannel.kt | 7 ++++--- app/src/main/protowire/Backup.proto | 1 + .../sms/UploadDependencyGraphTest.kt | 3 ++- .../securesms/database/FakeMessageRecords.kt | 3 ++- .../api/SignalServiceMessageSender.java | 6 ++++-- .../api/messages/SignalServiceAttachment.java | 16 +++++++++++----- .../SignalServiceAttachmentPointer.java | 12 +++++++++++- .../SignalServiceAttachmentStream.java | 14 ++++++++++++-- .../api/messages/SignalServiceDataMessage.kt | 5 ----- .../DeviceContactsInputStream.java | 3 ++- .../api/util/AttachmentPointerUtil.java | 8 ++++++-- .../signalservice/api/util/UuidUtil.java | 15 +++++++++++---- .../src/main/protowire/SignalService.proto | 3 ++- 35 files changed, 181 insertions(+), 66 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V235_AttachmentUuidColumn.kt diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt index 2ea7db24ab..3e8fc8cca5 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/ConversationItemPreviewer.kt @@ -154,7 +154,8 @@ class ConversationItemPreviewer { false, Optional.empty(), Optional.empty(), - System.currentTimeMillis() + System.currentTimeMillis(), + null ) } } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt index 6dc1ba5866..c8ef3f8438 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt @@ -824,7 +824,8 @@ class AttachmentTableTest_deduping { uploadTimestamp, databaseAttachment.caption, databaseAttachment.stickerLocator, - databaseAttachment.blurHash + databaseAttachment.blurHash, + databaseAttachment.uuid ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt index 5eb0d61884..90fa15aa11 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt @@ -11,6 +11,7 @@ import org.signal.core.util.Base64 import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator +import java.util.UUID class ArchivedAttachment : Attachment { @@ -47,7 +48,8 @@ class ArchivedAttachment : Attachment { borderless: Boolean, stickerLocator: StickerLocator?, gif: Boolean, - quote: Boolean + quote: Boolean, + uuid: UUID? ) : super( contentType = contentType ?: "", quote = quote, @@ -71,7 +73,8 @@ class ArchivedAttachment : Attachment { stickerLocator = stickerLocator, blurHash = BlurHash.parseOrNull(blurHash), audioHash = null, - transformProperties = null + transformProperties = null, + uuid = uuid ) { this.archiveCdn = archiveCdn ?: Cdn.CDN_3.cdnNumber this.archiveMediaName = archiveMediaName diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt index 6006dc701b..15d941f560 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt @@ -14,6 +14,8 @@ import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties import org.thoughtcrime.securesms.stickers.StickerLocator import org.thoughtcrime.securesms.util.ParcelUtil +import org.whispersystems.signalservice.api.util.UuidUtil +import java.util.UUID /** * Note: We have to use our own Parcelable implementation because we need to do custom stuff to preserve @@ -65,7 +67,9 @@ abstract class Attachment( @JvmField val audioHash: AudioHash?, @JvmField - val transformProperties: TransformProperties? + val transformProperties: TransformProperties?, + @JvmField + val uuid: UUID? ) : Parcelable { abstract val uri: Uri? @@ -97,7 +101,8 @@ abstract class Attachment( stickerLocator = ParcelCompat.readParcelable(parcel, StickerLocator::class.java.classLoader, StickerLocator::class.java), blurHash = ParcelCompat.readParcelable(parcel, BlurHash::class.java.classLoader, BlurHash::class.java), audioHash = ParcelCompat.readParcelable(parcel, AudioHash::class.java.classLoader, AudioHash::class.java), - transformProperties = ParcelCompat.readParcelable(parcel, TransformProperties::class.java.classLoader, TransformProperties::class.java) + transformProperties = ParcelCompat.readParcelable(parcel, TransformProperties::class.java.classLoader, TransformProperties::class.java), + uuid = UuidUtil.parseOrNull(parcel.readString()) ) override fun writeToParcel(dest: Parcel, flags: Int) { @@ -125,6 +130,7 @@ abstract class Attachment( dest.writeParcelable(blurHash, 0) dest.writeParcelable(audioHash, 0) dest.writeParcelable(transformProperties, 0) + dest.writeString(uuid?.toString()) } override fun describeContents(): Int { diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt index 3423a8150a..c74e291b03 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt @@ -55,6 +55,7 @@ object AttachmentUploadUtil { .withResumableUploadSpec(ResumableUploadSpec.from(uploadSpec)) .withCancelationSignal(cancellationSignal) .withListener(progressListener) + .withUuid(attachment.uuid) if (MediaUtil.isImageType(attachment.contentType)) { builder.withBlurHash(getImageBlurHash(context, attachment)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt index bfb7706937..abedc18b98 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt @@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.stickers.StickerLocator import org.thoughtcrime.securesms.util.ParcelUtil +import java.util.UUID class DatabaseAttachment : Attachment { @@ -79,7 +80,8 @@ class DatabaseAttachment : Attachment { archiveThumbnailCdn: Int, archiveMediaName: String?, archiveMediaId: String?, - thumbnailRestoreState: AttachmentTable.ThumbnailRestoreState + thumbnailRestoreState: AttachmentTable.ThumbnailRestoreState, + uuid: UUID? ) : super( contentType = contentType!!, transferState = transferProgress, @@ -102,7 +104,8 @@ class DatabaseAttachment : Attachment { stickerLocator = stickerLocator, blurHash = blurHash, audioHash = audioHash, - transformProperties = transformProperties + transformProperties = transformProperties, + uuid = uuid ) { this.attachmentId = attachmentId this.mmsId = mmsId diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt index e26b32d95f..73e6997cf8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt @@ -12,6 +12,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment import org.whispersystems.signalservice.api.util.AttachmentPointerUtil import org.whispersystems.signalservice.internal.push.DataMessage import java.util.Optional +import java.util.UUID class PointerAttachment : Attachment { @VisibleForTesting @@ -35,7 +36,8 @@ class PointerAttachment : Attachment { uploadTimestamp: Long, caption: String?, stickerLocator: StickerLocator?, - blurHash: BlurHash? + blurHash: BlurHash?, + uuid: UUID? ) : super( contentType = contentType, transferState = transferState, @@ -59,7 +61,8 @@ class PointerAttachment : Attachment { stickerLocator = stickerLocator, blurHash = blurHash, audioHash = null, - transformProperties = null + transformProperties = null, + uuid = uuid ) constructor(parcel: Parcel) : super(parcel) @@ -115,7 +118,8 @@ class PointerAttachment : Attachment { uploadTimestamp = pointer.get().asPointer().uploadTimestamp, caption = pointer.get().asPointer().caption.orElse(null), stickerLocator = stickerLocator, - blurHash = BlurHash.parseOrNull(pointer.get().asPointer().blurHash.orElse(null)) + blurHash = BlurHash.parseOrNull(pointer.get().asPointer().blurHash.orElse(null)), + uuid = pointer.get().asPointer().uuid ) ) } @@ -152,7 +156,8 @@ class PointerAttachment : Attachment { uploadTimestamp = thumbnail?.asPointer()?.uploadTimestamp ?: 0, caption = thumbnail?.asPointer()?.caption?.orElse(null), stickerLocator = null, - blurHash = null + blurHash = null, + uuid = thumbnail?.asPointer()?.uuid ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt index dd2d0364b4..552a1071bc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt @@ -4,6 +4,7 @@ import android.net.Uri import android.os.Parcel import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable +import java.util.UUID /** * An attachment that represents where an attachment used to be. Useful when you need to know that @@ -35,7 +36,8 @@ class TombstoneAttachment : Attachment { stickerLocator = null, blurHash = null, audioHash = null, - transformProperties = null + transformProperties = null, + uuid = null ) constructor( @@ -49,7 +51,8 @@ class TombstoneAttachment : Attachment { voiceNote: Boolean = false, borderless: Boolean = false, gif: Boolean = false, - quote: Boolean + quote: Boolean, + uuid: UUID? ) : super( contentType = contentType ?: "", quote = quote, @@ -73,7 +76,8 @@ class TombstoneAttachment : Attachment { stickerLocator = null, blurHash = BlurHash.parseOrNull(blurHash), audioHash = null, - transformProperties = null + transformProperties = null, + uuid = uuid ) constructor(parcel: Parcel) : super(parcel) diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt index 2de2f818e9..ae8f21f32c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt @@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties import org.thoughtcrime.securesms.stickers.StickerLocator import java.util.Objects +import java.util.UUID class UriAttachment : Attachment { @@ -87,7 +88,8 @@ class UriAttachment : Attachment { stickerLocator = stickerLocator, blurHash = blurHash, audioHash = audioHash, - transformProperties = transformProperties + transformProperties = transformProperties, + uuid = UUID.randomUUID() ) { uri = Objects.requireNonNull(dataUri) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 11ca2b4467..d009b8ae99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -537,7 +537,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: MessageAttachment.Flag.BORDERLESS } else { MessageAttachment.Flag.NONE - } + }, + uuid = uuid?.let { UuidUtil.toByteString(uuid) } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index 027969927d..13de09ab78 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.backup.v2.database import android.content.ContentValues import androidx.core.content.contentValuesOf +import okio.ByteString import org.signal.core.util.Base64 import org.signal.core.util.Hex import org.signal.core.util.SqlUtil @@ -817,7 +818,7 @@ class ChatItemImportInserter( } } - private fun FilePointer?.toLocalAttachment(voiceNote: Boolean, borderless: Boolean, gif: Boolean, wasDownloaded: Boolean, stickerLocator: StickerLocator? = null, contentType: String? = this?.contentType, fileName: String? = this?.fileName): Attachment? { + private fun FilePointer?.toLocalAttachment(voiceNote: Boolean, borderless: Boolean, gif: Boolean, wasDownloaded: Boolean, stickerLocator: StickerLocator? = null, contentType: String? = this?.contentType, fileName: String? = this?.fileName, uuid: ByteString? = null): Attachment? { if (this == null) return null if (attachmentLocator != null) { @@ -839,7 +840,8 @@ class ChatItemImportInserter( gif, Optional.ofNullable(caption), Optional.ofNullable(blurHash), - attachmentLocator.uploadTimestamp + attachmentLocator.uploadTimestamp, + UuidUtil.fromByteStringOrNull(uuid) ) return PointerAttachment.forPointer( pointer = Optional.of(signalAttachmentPointer), @@ -858,7 +860,8 @@ class ChatItemImportInserter( voiceNote = voiceNote, borderless = borderless, gif = gif, - quote = false + quote = false, + uuid = UuidUtil.fromByteStringOrNull(uuid) ) } else if (backupLocator != null) { return ArchivedAttachment( @@ -882,7 +885,8 @@ class ChatItemImportInserter( borderless = borderless, gif = gif, quote = false, - stickerLocator = stickerLocator + stickerLocator = stickerLocator, + uuid = UuidUtil.fromByteStringOrNull(uuid) ) } return null @@ -910,7 +914,8 @@ class ChatItemImportInserter( voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE, gif = flag == MessageAttachment.Flag.GIF, borderless = flag == MessageAttachment.Flag.BORDERLESS, - wasDownloaded = wasDownloaded + wasDownloaded = wasDownloaded, + uuid = uuid ) } @@ -921,7 +926,8 @@ class ChatItemImportInserter( borderless = flag == MessageAttachment.Flag.BORDERLESS, wasDownloaded = wasDownloaded, contentType = contentType, - fileName = fileName + fileName = fileName, + uuid = uuid ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index e851330f17..e38528ee7c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -87,6 +87,7 @@ import org.thoughtcrime.securesms.util.JsonUtils.SaneJSONObject import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.StorageUtil import org.thoughtcrime.securesms.video.EncryptedMediaDataSource +import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.internal.util.JsonUtil import java.io.File import java.io.FileNotFoundException @@ -152,6 +153,7 @@ class AttachmentTable( const val ARCHIVE_TRANSFER_FILE = "archive_transfer_file" const val ARCHIVE_TRANSFER_STATE = "archive_transfer_state" const val THUMBNAIL_RESTORE_STATE = "thumbnail_restore_state" + const val ATTACHMENT_UUID = "attachment_uuid" const val ATTACHMENT_JSON_ALIAS = "attachment_json" @@ -207,7 +209,8 @@ class AttachmentTable( ARCHIVE_MEDIA_ID, ARCHIVE_TRANSFER_FILE, THUMBNAIL_FILE, - THUMBNAIL_RESTORE_STATE + THUMBNAIL_RESTORE_STATE, + ATTACHMENT_UUID ) @JvmField @@ -255,7 +258,8 @@ class AttachmentTable( $ARCHIVE_THUMBNAIL_MEDIA_ID TEXT DEFAULT NULL, $THUMBNAIL_FILE TEXT DEFAULT NULL, $THUMBNAIL_RANDOM BLOB DEFAULT NULL, - $THUMBNAIL_RESTORE_STATE INTEGER DEFAULT ${ThumbnailRestoreState.NONE.value} + $THUMBNAIL_RESTORE_STATE INTEGER DEFAULT ${ThumbnailRestoreState.NONE.value}, + $ATTACHMENT_UUID TEXT DEFAULT NULL ) """ @@ -1417,7 +1421,8 @@ class AttachmentTable( archiveMediaName = jsonObject.getString(ARCHIVE_MEDIA_NAME), archiveMediaId = jsonObject.getString(ARCHIVE_MEDIA_ID), hasArchiveThumbnail = !TextUtils.isEmpty(jsonObject.getString(THUMBNAIL_FILE)), - thumbnailRestoreState = ThumbnailRestoreState.deserialize(jsonObject.getInt(THUMBNAIL_RESTORE_STATE)) + thumbnailRestoreState = ThumbnailRestoreState.deserialize(jsonObject.getInt(THUMBNAIL_RESTORE_STATE)), + uuid = UuidUtil.parseOrNull(jsonObject.getString(ATTACHMENT_UUID)) ) } } @@ -1740,6 +1745,7 @@ class AttachmentTable( put(CAPTION, attachment.caption) put(UPLOAD_TIMESTAMP, attachment.uploadTimestamp) put(BLUR_HASH, attachment.blurHash?.hash) + put(ATTACHMENT_UUID, attachment.uuid?.toString()) attachment.stickerLocator?.let { sticker -> put(STICKER_PACK_ID, sticker.packId) @@ -1795,6 +1801,7 @@ class AttachmentTable( put(ARCHIVE_MEDIA_ID, attachment.archiveMediaId) put(ARCHIVE_THUMBNAIL_MEDIA_ID, attachment.archiveThumbnailMediaId) put(THUMBNAIL_RESTORE_STATE, ThumbnailRestoreState.NEEDS_RESTORE.value) + put(ATTACHMENT_UUID, attachment.uuid?.toString()) attachment.stickerLocator?.let { sticker -> put(STICKER_PACK_ID, sticker.packId) @@ -1921,6 +1928,7 @@ class AttachmentTable( contentValues.put(CAPTION, attachment.caption) contentValues.put(UPLOAD_TIMESTAMP, uploadTemplate?.uploadTimestamp ?: 0) contentValues.put(TRANSFORM_PROPERTIES, transformProperties.serialize()) + contentValues.put(ATTACHMENT_UUID, attachment.uuid?.toString()) if (attachment.transformProperties?.videoEdited == true) { contentValues.putNull(BLUR_HASH) @@ -2012,7 +2020,8 @@ class AttachmentTable( archiveMediaName = cursor.requireString(ARCHIVE_MEDIA_NAME), archiveMediaId = cursor.requireString(ARCHIVE_MEDIA_ID), hasArchiveThumbnail = !cursor.isNull(THUMBNAIL_FILE), - thumbnailRestoreState = ThumbnailRestoreState.deserialize(cursor.requireInt(THUMBNAIL_RESTORE_STATE)) + thumbnailRestoreState = ThumbnailRestoreState.deserialize(cursor.requireInt(THUMBNAIL_RESTORE_STATE)), + uuid = UuidUtil.parseOrNull(cursor.requireString(ATTACHMENT_UUID)) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt index 4c802e6415..9ea66d9086 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt @@ -57,6 +57,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_ID}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_THUMBNAIL_CDN}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.THUMBNAIL_RESTORE_STATE}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ATTACHMENT_UUID}, ${MessageTable.TABLE_NAME}.${MessageTable.TYPE}, ${MessageTable.TABLE_NAME}.${MessageTable.DATE_SENT}, ${MessageTable.TABLE_NAME}.${MessageTable.DATE_RECEIVED}, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index 42d57f4150..58d0ef7154 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -386,7 +386,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat '${AttachmentTable.ARCHIVE_THUMBNAIL_CDN}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_THUMBNAIL_CDN}, '${AttachmentTable.ARCHIVE_MEDIA_NAME}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_NAME}, '${AttachmentTable.ARCHIVE_MEDIA_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_MEDIA_ID}, - '${AttachmentTable.THUMBNAIL_RESTORE_STATE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.THUMBNAIL_RESTORE_STATE} + '${AttachmentTable.THUMBNAIL_RESTORE_STATE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.THUMBNAIL_RESTORE_STATE}, + '${AttachmentTable.ATTACHMENT_UUID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ATTACHMENT_UUID} ) ) AS ${AttachmentTable.ATTACHMENT_JSON_ALIAS} """.toSingleLine() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 19eb484250..8a8f34dd81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -92,6 +92,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V231_ArchiveThumbna import org.thoughtcrime.securesms.database.helpers.migration.V232_CreateInAppPaymentTable import org.thoughtcrime.securesms.database.helpers.migration.V233_FixInAppPaymentTableDefaultNotifiedValue import org.thoughtcrime.securesms.database.helpers.migration.V234_ThumbnailRestoreStateColumn +import org.thoughtcrime.securesms.database.helpers.migration.V235_AttachmentUuidColumn /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -186,10 +187,11 @@ object SignalDatabaseMigrations { 231 to V231_ArchiveThumbnailColumns, 232 to V232_CreateInAppPaymentTable, 233 to V233_FixInAppPaymentTableDefaultNotifiedValue, - 234 to V234_ThumbnailRestoreStateColumn + 234 to V234_ThumbnailRestoreStateColumn, + 235 to V235_AttachmentUuidColumn ) - const val DATABASE_VERSION = 234 + const val DATABASE_VERSION = 235 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V235_AttachmentUuidColumn.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V235_AttachmentUuidColumn.kt new file mode 100644 index 0000000000..fad43460f8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V235_AttachmentUuidColumn.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Add a column for attachment uuids + */ +@Suppress("ClassName") +object V235_AttachmentUuidColumn : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE attachment ADD COLUMN attachment_uuid TEXT DEFAULT NULL") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt index 37dd057672..50482b71ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt @@ -380,7 +380,8 @@ class AttachmentDownloadJob private constructor( attachment.videoGif, Optional.empty(), Optional.ofNullable(attachment.blurHash).map { it.hash }, - attachment.uploadTimestamp + attachment.uploadTimestamp, + attachment.uuid ) } catch (e: IOException) { Log.w(TAG, e) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java index 18166034cf..3fa961e527 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java @@ -85,7 +85,7 @@ public final class AvatarGroupsV1DownloadJob extends BaseJob { attachment.deleteOnExit(); SignalServiceMessageReceiver receiver = AppDependencies.getSignalServiceMessageReceiver(); - SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId.V2(avatarId), contentType, key, Optional.of(0), Optional.empty(), 0, 0, digest, Optional.empty(), 0, fileName, false, false, false, Optional.empty(), Optional.empty(), System.currentTimeMillis()); + SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId.V2(avatarId), contentType, key, Optional.of(0), Optional.empty(), 0, 0, digest, Optional.empty(), 0, fileName, false, false, false, Optional.empty(), Optional.empty(), System.currentTimeMillis(), null); InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, AvatarHelper.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); AvatarHelper.setAvatar(context, record.get().getRecipientId(), inputStream); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index a5ff82143b..2ada6d577d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -88,6 +88,7 @@ import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -211,6 +212,7 @@ public abstract class PushSendJob extends SendJob { .withWidth(attachment.width) .withHeight(attachment.height) .withCaption(attachment.caption) + .withUuid(attachment.uuid) .withListener(new SignalServiceAttachment.ProgressListener() { @Override public void onAttachmentProgress(long total, long progress) { @@ -305,7 +307,8 @@ public abstract class PushSendJob extends SendJob { attachment.videoGif, Optional.ofNullable(attachment.caption), Optional.ofNullable(attachment.blurHash).map(BlurHash::getHash), - attachment.uploadTimestamp); + attachment.uploadTimestamp, + attachment.uuid); } catch (IOException | ArithmeticException e) { Log.w(TAG, e); return null; @@ -380,7 +383,8 @@ public abstract class PushSendJob extends SendJob { .withHeight(thumbnailData.getHeight()) .withLength(thumbnailData.getData().length) .withStream(new ByteArrayInputStream(thumbnailData.getData())) - .withResumableUploadSpec(AppDependencies.getSignalServiceMessageSender().getResumableUploadSpec()); + .withResumableUploadSpec(AppDependencies.getSignalServiceMessageSender().getResumableUploadSpec()) + .withUuid(UUID.randomUUID()); thumbnail = builder.build(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt index aed089f4e5..6b745cb08c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt @@ -366,7 +366,8 @@ class RestoreAttachmentJob private constructor( attachment.videoGif, Optional.empty(), Optional.ofNullable(attachment.blurHash).map { it.hash }, - attachment.uploadTimestamp + attachment.uploadTimestamp, + attachment.uuid ) } catch (e: IOException) { Log.w(TAG, e) @@ -410,7 +411,8 @@ class RestoreAttachmentJob private constructor( attachment.videoGif, Optional.empty(), Optional.ofNullable(attachment.blurHash).map { it.hash }, - attachment.uploadTimestamp + attachment.uploadTimestamp, + attachment.uuid ) } catch (e: IOException) { Log.w(TAG, e) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentThumbnailJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentThumbnailJob.kt index b3487baa38..5d41ff82a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentThumbnailJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentThumbnailJob.kt @@ -168,7 +168,8 @@ class RestoreAttachmentThumbnailJob private constructor( attachment.videoGif, Optional.empty(), Optional.ofNullable(attachment.blurHash).map { it.hash }, - attachment.uploadTimestamp + attachment.uploadTimestamp, + attachment.uuid ) } catch (e: IOException) { Log.w(TAG, e) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt index d736ba967b..25f70a53d7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt @@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.releasechannel.ReleaseChannel import org.thoughtcrime.securesms.s3.S3 import org.thoughtcrime.securesms.transport.RetryLaterException import org.thoughtcrime.securesms.util.LocaleRemoteConfig +import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.internal.ServiceResponse import java.io.IOException import java.lang.Integer.max @@ -232,7 +233,8 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool media = note.translation.media, mediaWidth = note.translation.mediaWidth?.toIntOrNull() ?: 0, mediaHeight = note.translation.mediaHeight?.toIntOrNull() ?: 0, - mediaType = note.translation.mediaContentType ?: "image/webp" + mediaType = note.translation.mediaContentType ?: "image/webp", + mediaAttachmentUuid = UuidUtil.parseOrNull(note.releaseNote.uuid) ) if (insertResult != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt b/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt index 1d5a9b861e..5c4de0662d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/releasechannel/ReleaseChannel.kt @@ -29,7 +29,7 @@ object ReleaseChannel { mediaWidth: Int = 0, mediaHeight: Int = 0, mediaType: String = "image/webp", - serverUuid: String? = UUID.randomUUID().toString(), + mediaAttachmentUuid: UUID? = UUID.randomUUID(), messageRanges: BodyRangeList? = null, storyType: StoryType = StoryType.NONE ): MessageTable.InsertResult? { @@ -52,7 +52,8 @@ object ReleaseChannel { MediaUtil.isVideo(mediaType), Optional.empty(), Optional.empty(), - System.currentTimeMillis() + System.currentTimeMillis(), + mediaAttachmentUuid ) Optional.of(listOf(attachment)) @@ -68,7 +69,7 @@ object ReleaseChannel { receivedTimeMillis = System.currentTimeMillis(), body = body, attachments = PointerAttachment.forPointers(attachments), - serverGuid = serverUuid, + serverGuid = UUID.randomUUID().toString(), messageRanges = messageRanges, storyType = storyType ) diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index 2e7a0b5c8d..9631f20a2c 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -551,6 +551,7 @@ message MessageAttachment { FilePointer pointer = 1; Flag flag = 2; bool wasDownloaded = 3; + optional bytes uuid = 4; } message FilePointer { diff --git a/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt b/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt index 0928801254..0bf8f48a82 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/sms/UploadDependencyGraphTest.kt @@ -262,7 +262,8 @@ class UploadDependencyGraphTest { archiveMediaName = null, archiveCdn = 0, archiveThumbnailCdn = 0, - thumbnailRestoreState = AttachmentTable.ThumbnailRestoreState.NONE + thumbnailRestoreState = AttachmentTable.ThumbnailRestoreState.NONE, + uuid = null ) } diff --git a/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt b/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt index e7fe657c09..bb6b48b6bb 100644 --- a/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt +++ b/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt @@ -101,7 +101,8 @@ object FakeMessageRecords { archiveThumbnailCdn, archiveMediaId, archiveMediaName, - thumbnailRestoreState + thumbnailRestoreState, + null ) } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 84fefec8af..ad534dc04f 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -862,7 +862,8 @@ public class SignalServiceMessageSender { attachment.isGif(), attachment.getCaption(), attachment.getBlurHash(), - attachment.getUploadTimestamp()); + attachment.getUploadTimestamp(), + attachment.getUuid()); } public ResumableUploadSpec getResumableUploadSpec() throws IOException { @@ -907,7 +908,8 @@ public class SignalServiceMessageSender { attachment.isGif(), attachment.getCaption(), attachment.getBlurHash(), - attachment.getUploadTimestamp()); + attachment.getUploadTimestamp(), + attachment.getUuid()); } private SendMessageResult sendVerifiedSyncMessage(VerifiedMessage message) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java index 01b1445e57..f6ebb8707a 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachment.java @@ -12,6 +12,9 @@ import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Optional; +import java.util.UUID; + +import javax.annotation.Nullable; public abstract class SignalServiceAttachment { @@ -40,10 +43,6 @@ public abstract class SignalServiceAttachment { return new Builder(); } - public static SignalServiceAttachmentStream emptyStream(String contentType) { - return new SignalServiceAttachmentStream(new ByteArrayInputStream(new byte[0]), contentType, 0, Optional.empty(), false, false, false, false, null, null); - } - public static class Builder { private InputStream inputStream; @@ -62,6 +61,7 @@ public abstract class SignalServiceAttachment { private String blurHash; private long uploadTimestamp; private ResumableUploadSpec resumableUploadSpec; + private UUID uuid; private Builder() {} @@ -145,6 +145,11 @@ public abstract class SignalServiceAttachment { return this; } + public Builder withUuid(@Nullable UUID uuid) { + this.uuid = uuid; + return this; + } + public SignalServiceAttachmentStream build() { if (inputStream == null) throw new IllegalArgumentException("Must specify stream!"); if (contentType == null) throw new IllegalArgumentException("No content type specified!"); @@ -166,7 +171,8 @@ public abstract class SignalServiceAttachment { Optional.ofNullable(blurHash), listener, cancelationSignal, - Optional.ofNullable(resumableUploadSpec)); + Optional.ofNullable(resumableUploadSpec), + uuid); } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java index 50df2140c8..14ce1d755e 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer.java @@ -9,6 +9,9 @@ package org.whispersystems.signalservice.api.messages; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import java.util.Optional; +import java.util.UUID; + +import javax.annotation.Nullable; /** * Represents a received SignalServiceAttachment "handle." This @@ -36,6 +39,7 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment { private final Optional caption; private final Optional blurHash; private final long uploadTimestamp; + private final UUID uuid; public SignalServiceAttachmentPointer(int cdnNumber, SignalServiceAttachmentRemoteId remoteId, @@ -54,7 +58,8 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment { boolean gif, Optional caption, Optional blurHash, - long uploadTimestamp) + long uploadTimestamp, + @Nullable UUID uuid) { super(contentType); this.cdnNumber = cdnNumber; @@ -74,6 +79,7 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment { this.blurHash = blurHash; this.uploadTimestamp = uploadTimestamp; this.gif = gif; + this.uuid = uuid; } public int getCdnNumber() { @@ -153,4 +159,8 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment { public long getUploadTimestamp() { return uploadTimestamp; } + + public @Nullable UUID getUuid() { + return uuid; + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java index 2459f4b9f1..afd859708d 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceAttachmentStream.java @@ -14,6 +14,9 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.util.Optional; +import java.util.UUID; + +import javax.annotation.Nullable; /** * Represents a local SignalServiceAttachment to be sent. @@ -36,6 +39,7 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment imple private final Optional caption; private final Optional blurHash; private final Optional resumableUploadSpec; + private final UUID uuid; public SignalServiceAttachmentStream(InputStream inputStream, String contentType, @@ -48,7 +52,7 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment imple ProgressListener listener, CancelationSignal cancelationSignal) { - this(inputStream, contentType, length, fileName, voiceNote, borderless, gif, faststart, Optional.empty(), 0, 0, System.currentTimeMillis(), Optional.empty(), Optional.empty(), listener, cancelationSignal, Optional.empty()); + this(inputStream, contentType, length, fileName, voiceNote, borderless, gif, faststart, Optional.empty(), 0, 0, System.currentTimeMillis(), Optional.empty(), Optional.empty(), listener, cancelationSignal, Optional.empty(), UUID.randomUUID()); } public SignalServiceAttachmentStream(InputStream inputStream, @@ -67,7 +71,8 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment imple Optional blurHash, ProgressListener listener, CancelationSignal cancelationSignal, - Optional resumableUploadSpec) + Optional resumableUploadSpec, + @Nullable UUID uuid) { super(contentType); this.inputStream = inputStream; @@ -86,6 +91,7 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment imple this.blurHash = blurHash; this.cancelationSignal = cancelationSignal; this.resumableUploadSpec = resumableUploadSpec; + this.uuid = uuid; } @Override @@ -162,6 +168,10 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment imple return resumableUploadSpec; } + public @Nullable UUID getUuid() { + return uuid; + } + @Override public void close() throws IOException { inputStream.close(); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.kt index c34876ab60..c6bbd8ff25 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceDataMessage.kt @@ -106,11 +106,6 @@ class SignalServiceDataMessage private constructor( return this } - fun withAttachment(attachment: SignalServiceAttachment?): Builder { - attachment?.let { attachments.add(attachment) } - return this - } - fun withAttachments(attachments: List?): Builder { attachments?.let { this.attachments.addAll(attachments) } return this diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java index 69fb58eb72..30b0e248ef 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/DeviceContactsInputStream.java @@ -12,6 +12,7 @@ import org.signal.libsignal.protocol.InvalidMessageException; import org.signal.libsignal.protocol.logging.Log; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.profiles.ProfileKey; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId.ACI; @@ -62,7 +63,7 @@ public class DeviceContactsInputStream extends ChunkedInputStream { InputStream avatarStream = new LimitedInputStream(in, avatarLength); String avatarContentType = details.avatar.contentType; - avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.empty(), false, false, false, false, null, null)); + avatar = Optional.of(SignalServiceAttachment.newStreamBuilder().withStream(avatarStream).withContentType(avatarContentType).withLength(avatarLength).build()); } if (details.verified != null) { diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java index 8ec9b7db75..dd78a4fae8 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/AttachmentPointerUtil.java @@ -35,8 +35,8 @@ public final class AttachmentPointerUtil { ((pointer.flags != null ? pointer.flags : 0) & FlagUtil.toBinaryFlag(AttachmentPointer.Flags.GIF.getValue())) != 0, pointer.caption != null ? Optional.of(pointer.caption) : Optional.empty(), pointer.blurHash != null ? Optional.of(pointer.blurHash) : Optional.empty(), - pointer.uploadTimestamp != null ? pointer.uploadTimestamp : 0); - + pointer.uploadTimestamp != null ? pointer.uploadTimestamp : 0, + UuidUtil.fromByteStringOrNull(pointer.uuid)); } public static AttachmentPointer createAttachmentPointer(SignalServiceAttachmentPointer attachment) { @@ -104,6 +104,10 @@ public final class AttachmentPointerUtil { builder.blurHash(attachment.getBlurHash().get()); } + if (attachment.getUuid() != null) { + builder.uuid(UuidUtil.toByteString(attachment.getUuid())); + } + return builder.build(); } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/UuidUtil.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/UuidUtil.java index cb9333c089..9d2175435b 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/UuidUtil.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/UuidUtil.java @@ -8,6 +8,9 @@ import java.util.Optional; import java.util.UUID; import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import okio.ByteString; public final class UuidUtil { @@ -23,7 +26,7 @@ public final class UuidUtil { return Optional.ofNullable(parseOrNull(uuid)); } - public static UUID parseOrNull(String uuid) { + public static UUID parseOrNull(@Nullable String uuid) { return isUuid(uuid) ? parseOrThrow(uuid) : null; } @@ -47,7 +50,7 @@ public final class UuidUtil { return uuid != null && UUID_PATTERN.matcher(uuid).matches(); } - public static byte[] toByteArray(UUID uuid) { + public static byte[] toByteArray(@Nonnull UUID uuid) { ByteBuffer buffer = ByteBuffer.wrap(new byte[16]); buffer.putLong(uuid.getMostSignificantBits()); buffer.putLong(uuid.getLeastSignificantBits()); @@ -55,7 +58,7 @@ public final class UuidUtil { return buffer.array(); } - public static ByteString toByteString(UUID uuid) { + public static ByteString toByteString(@Nonnull UUID uuid) { return ByteString.of(toByteArray(uuid)); } @@ -63,7 +66,11 @@ public final class UuidUtil { return parseOrThrow(bytes.toByteArray()); } - public static UUID fromByteStringOrNull(ByteString bytes) { + public static @Nullable UUID fromByteStringOrNull(@Nullable ByteString bytes) { + if (bytes == null) { + return null; + } + return parseOrNull(bytes.toByteArray()); } diff --git a/libsignal-service/src/main/protowire/SignalService.proto b/libsignal-service/src/main/protowire/SignalService.proto index 022da8f085..b14bdd649f 100644 --- a/libsignal-service/src/main/protowire/SignalService.proto +++ b/libsignal-service/src/main/protowire/SignalService.proto @@ -744,7 +744,8 @@ message AttachmentPointer { optional string blurHash = 12; optional uint64 uploadTimestamp = 13; optional uint32 cdnNumber = 14; - // Next ID: 19 + optional bytes uuid = 20; + // Next ID: 21 } message GroupContext {