mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 10:20:25 +01:00
Add support for backing up wallpapers.
This commit is contained in:
@@ -6,8 +6,14 @@
|
||||
package org.thoughtcrime.securesms.backup.v2.database
|
||||
|
||||
import org.signal.core.util.deleteAll
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
|
||||
fun AttachmentTable.clearAllDataForBackupRestore() {
|
||||
writableDatabase.deleteAll(AttachmentTable.TABLE_NAME)
|
||||
}
|
||||
|
||||
fun AttachmentTable.restoreWallpaperAttachment(attachment: Attachment): AttachmentId? {
|
||||
return insertAttachmentsForMessage(AttachmentTable.WALLPAPER_MESSAGE_ID, listOf(attachment), emptyList()).values.firstOrNull()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ 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
|
||||
@@ -17,17 +16,13 @@ import org.signal.core.util.orNull
|
||||
import org.signal.core.util.requireLong
|
||||
import org.signal.core.util.toInt
|
||||
import org.signal.core.util.update
|
||||
import org.thoughtcrime.securesms.attachments.ArchivedAttachment
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.attachments.Cdn
|
||||
import org.thoughtcrime.securesms.attachments.PointerAttachment
|
||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
|
||||
import org.thoughtcrime.securesms.backup.v2.ImportState
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.BodyRange
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatItem
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ContactAttachment
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.FilePointer
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupCall
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.IndividualCall
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.LinkPreview
|
||||
@@ -39,8 +34,8 @@ import org.thoughtcrime.securesms.backup.v2.proto.SendStatus
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Sticker
|
||||
import org.thoughtcrime.securesms.backup.v2.util.toLocalAttachment
|
||||
import org.thoughtcrime.securesms.contactshare.Contact
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.database.CallTable
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptTable
|
||||
import org.thoughtcrime.securesms.database.MessageTable
|
||||
@@ -74,9 +69,6 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.thoughtcrime.securesms.util.JsonUtils
|
||||
import org.whispersystems.signalservice.api.backup.MediaName
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
|
||||
import org.whispersystems.signalservice.api.payments.Money
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
@@ -342,7 +334,7 @@ class ChatItemImportInserter(
|
||||
address.country
|
||||
)
|
||||
},
|
||||
Contact.Avatar(null, backupContact.avatar.toLocalAttachment(), true)
|
||||
Contact.Avatar(null, backupContact.avatar.toLocalAttachment(importState = importState, voiceNote = false, borderless = false, gif = false, wasDownloaded = true), true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -917,29 +909,6 @@ class ChatItemImportInserter(
|
||||
?: false
|
||||
}
|
||||
|
||||
private fun LinkPreview.toLocalLinkPreview(): org.thoughtcrime.securesms.linkpreview.LinkPreview {
|
||||
return org.thoughtcrime.securesms.linkpreview.LinkPreview(
|
||||
this.url,
|
||||
this.title ?: "",
|
||||
this.description ?: "",
|
||||
this.date ?: 0,
|
||||
Optional.ofNullable(this.image?.toLocalAttachment())
|
||||
)
|
||||
}
|
||||
|
||||
private fun MessageAttachment.toLocalAttachment(contentType: String? = this.pointer?.contentType, fileName: String? = this.pointer?.fileName): Attachment? {
|
||||
return this.pointer?.toLocalAttachment(
|
||||
voiceNote = this.flag == MessageAttachment.Flag.VOICE_MESSAGE,
|
||||
borderless = this.flag == MessageAttachment.Flag.BORDERLESS,
|
||||
gif = this.flag == MessageAttachment.Flag.GIF,
|
||||
wasDownloaded = this.wasDownloaded,
|
||||
stickerLocator = null,
|
||||
contentType = contentType,
|
||||
fileName = fileName,
|
||||
uuid = this.clientUuid
|
||||
)
|
||||
}
|
||||
|
||||
private fun Quote.QuotedAttachment.toLocalAttachment(): Attachment? {
|
||||
val thumbnail = this.thumbnail?.toLocalAttachment(this.contentType, this.fileName)
|
||||
|
||||
@@ -962,6 +931,11 @@ class ChatItemImportInserter(
|
||||
if (this == null) return null
|
||||
|
||||
return data_.toLocalAttachment(
|
||||
importState = importState,
|
||||
voiceNote = false,
|
||||
gif = false,
|
||||
borderless = false,
|
||||
wasDownloaded = true,
|
||||
stickerLocator = StickerLocator(
|
||||
packId = Hex.toStringCondensed(packId.toByteArray()),
|
||||
packKey = Hex.toStringCondensed(packKey.toByteArray()),
|
||||
@@ -971,90 +945,38 @@ class ChatItemImportInserter(
|
||||
)
|
||||
}
|
||||
|
||||
private fun FilePointer?.toLocalAttachment(
|
||||
borderless: Boolean = false,
|
||||
gif: Boolean = false,
|
||||
voiceNote: Boolean = false,
|
||||
wasDownloaded: Boolean = true,
|
||||
stickerLocator: StickerLocator? = null,
|
||||
contentType: String? = this?.contentType,
|
||||
fileName: String? = this?.fileName,
|
||||
uuid: ByteString? = null
|
||||
): Attachment? {
|
||||
return if (this == null) {
|
||||
null
|
||||
} else if (this.attachmentLocator != null) {
|
||||
val signalAttachmentPointer = SignalServiceAttachmentPointer(
|
||||
cdnNumber = this.attachmentLocator.cdnNumber,
|
||||
remoteId = SignalServiceAttachmentRemoteId.from(this.attachmentLocator.cdnKey),
|
||||
contentType = contentType,
|
||||
key = this.attachmentLocator.key.toByteArray(),
|
||||
size = Optional.ofNullable(this.attachmentLocator.size),
|
||||
preview = Optional.empty(),
|
||||
width = this.width ?: 0,
|
||||
height = this.height ?: 0,
|
||||
digest = Optional.ofNullable(this.attachmentLocator.digest.toByteArray()),
|
||||
incrementalDigest = Optional.ofNullable(this.incrementalMac?.toByteArray()),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize ?: 0,
|
||||
fileName = Optional.ofNullable(fileName),
|
||||
voiceNote = voiceNote,
|
||||
isBorderless = borderless,
|
||||
isGif = gif,
|
||||
caption = Optional.ofNullable(this.caption),
|
||||
blurHash = Optional.ofNullable(this.blurHash),
|
||||
uploadTimestamp = this.attachmentLocator.uploadTimestamp,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
PointerAttachment.forPointer(
|
||||
pointer = Optional.of(signalAttachmentPointer),
|
||||
stickerLocator = stickerLocator,
|
||||
transferState = if (wasDownloaded) AttachmentTable.TRANSFER_NEEDS_RESTORE else AttachmentTable.TRANSFER_PROGRESS_PENDING
|
||||
).orNull()
|
||||
} else if (this.invalidAttachmentLocator != null) {
|
||||
TombstoneAttachment(
|
||||
contentType = contentType,
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = false,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
} else if (this.backupLocator != null) {
|
||||
ArchivedAttachment(
|
||||
contentType = contentType,
|
||||
size = this.backupLocator.size.toLong(),
|
||||
cdn = this.backupLocator.transitCdnNumber ?: Cdn.CDN_0.cdnNumber,
|
||||
key = this.backupLocator.key.toByteArray(),
|
||||
iv = null,
|
||||
cdnKey = this.backupLocator.transitCdnKey,
|
||||
archiveCdn = this.backupLocator.cdnNumber,
|
||||
archiveMediaName = this.backupLocator.mediaName,
|
||||
archiveMediaId = importState.backupKey.deriveMediaId(MediaName(this.backupLocator.mediaName)).encode(),
|
||||
archiveThumbnailMediaId = importState.backupKey.deriveMediaId(MediaName.forThumbnailFromMediaName(this.backupLocator.mediaName)).encode(),
|
||||
digest = this.backupLocator.digest.toByteArray(),
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = false,
|
||||
stickerLocator = stickerLocator,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid),
|
||||
fileName = fileName
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private fun LinkPreview.toLocalLinkPreview(): org.thoughtcrime.securesms.linkpreview.LinkPreview {
|
||||
return org.thoughtcrime.securesms.linkpreview.LinkPreview(
|
||||
this.url,
|
||||
this.title ?: "",
|
||||
this.description ?: "",
|
||||
this.date ?: 0,
|
||||
Optional.ofNullable(this.image?.toLocalAttachment(importState = importState, voiceNote = false, borderless = false, gif = false, wasDownloaded = true))
|
||||
)
|
||||
}
|
||||
|
||||
private fun MessageAttachment.toLocalAttachment(): Attachment? {
|
||||
return pointer?.toLocalAttachment(
|
||||
importState = importState,
|
||||
voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE,
|
||||
gif = flag == MessageAttachment.Flag.GIF,
|
||||
borderless = flag == MessageAttachment.Flag.BORDERLESS,
|
||||
wasDownloaded = wasDownloaded,
|
||||
uuid = clientUuid
|
||||
)
|
||||
}
|
||||
|
||||
private fun MessageAttachment.toLocalAttachment(contentType: String?, fileName: String?): Attachment? {
|
||||
return pointer?.toLocalAttachment(
|
||||
importState = importState,
|
||||
voiceNote = flag == MessageAttachment.Flag.VOICE_MESSAGE,
|
||||
gif = flag == MessageAttachment.Flag.GIF,
|
||||
borderless = flag == MessageAttachment.Flag.BORDERLESS,
|
||||
wasDownloaded = wasDownloaded,
|
||||
contentType = contentType,
|
||||
fileName = fileName,
|
||||
uuid = clientUuid
|
||||
)
|
||||
}
|
||||
|
||||
private fun ContactAttachment.Name?.toLocal(): Contact.Name {
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.thoughtcrime.securesms.backup.v2.database
|
||||
|
||||
import android.database.Cursor
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.insertInto
|
||||
import org.signal.core.util.logging.Log
|
||||
@@ -16,15 +15,26 @@ import org.signal.core.util.requireBoolean
|
||||
import org.signal.core.util.requireInt
|
||||
import org.signal.core.util.requireLong
|
||||
import org.signal.core.util.toInt
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||
import org.thoughtcrime.securesms.backup.v2.ImportState
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Chat
|
||||
import org.thoughtcrime.securesms.backup.v2.util.BackupConverters
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatStyle
|
||||
import org.thoughtcrime.securesms.backup.v2.util.ChatStyleConverter
|
||||
import org.thoughtcrime.securesms.backup.v2.util.toLocal
|
||||
import org.thoughtcrime.securesms.backup.v2.util.toLocalAttachment
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.SQLiteDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadTable
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.decodeOrNull
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory
|
||||
import org.thoughtcrime.securesms.wallpaper.UriChatWallpaper
|
||||
import java.io.Closeable
|
||||
|
||||
private val TAG = Log.tag(ThreadTable::class.java)
|
||||
@@ -43,14 +53,15 @@ fun ThreadTable.getThreadsForBackup(): ChatExportIterator {
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL},
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.MENTION_SETTING},
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.CHAT_COLORS},
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID},
|
||||
${RecipientTable.TABLE_NAME}.${RecipientTable.WALLPAPER}
|
||||
FROM ${ThreadTable.TABLE_NAME}
|
||||
LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID}
|
||||
WHERE ${ThreadTable.ACTIVE} = 1
|
||||
"""
|
||||
val cursor = readableDatabase.query(query)
|
||||
|
||||
return ChatExportIterator(cursor)
|
||||
return ChatExportIterator(cursor, readableDatabase)
|
||||
}
|
||||
|
||||
fun ThreadTable.clearAllDataForBackupRestore() {
|
||||
@@ -59,10 +70,29 @@ fun ThreadTable.clearAllDataForBackupRestore() {
|
||||
clearCache()
|
||||
}
|
||||
|
||||
fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId, importState: ImportState): Long? {
|
||||
fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId, importState: ImportState): Long {
|
||||
val chatColor = chat.style?.toLocal(importState)
|
||||
val chatColorWithId = if (chatColor != null && chatColor.id is ChatColors.Id.NotSet) {
|
||||
val savedColors = SignalDatabase.chatColors.getSavedChatColors()
|
||||
val match = savedColors.find { it.matchesWithoutId(chatColor) }
|
||||
match ?: SignalDatabase.chatColors.saveChatColors(chatColor)
|
||||
} else {
|
||||
chatColor
|
||||
}
|
||||
|
||||
// TODO [backup] Wallpaper
|
||||
val wallpaperAttachmentId: AttachmentId? = chat.style?.wallpaperPhoto?.let { filePointer ->
|
||||
filePointer.toLocalAttachment(importState)?.let {
|
||||
SignalDatabase.attachments.restoreWallpaperAttachment(it)
|
||||
}
|
||||
}
|
||||
|
||||
val chatWallpaper = chat.style?.parseChatWallpaper(wallpaperAttachmentId)?.let {
|
||||
if (chat.style.dimWallpaperInDarkMode) {
|
||||
ChatWallpaperFactory.updateWithDimming(it, ChatWallpaper.FIXED_DIM_LEVEL_FOR_DARK_THEME)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
val threadId = writableDatabase
|
||||
.insertInto(ThreadTable.TABLE_NAME)
|
||||
@@ -84,7 +114,9 @@ fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId, importSt
|
||||
RecipientTable.MESSAGE_EXPIRATION_TIME to chat.expirationTimerMs,
|
||||
RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION to chat.expireTimerVersion,
|
||||
RecipientTable.CHAT_COLORS to chatColor?.serialize()?.encode(),
|
||||
RecipientTable.CUSTOM_CHAT_COLORS_ID to (chatColor?.id ?: ChatColors.Id.NotSet).longValue
|
||||
RecipientTable.CUSTOM_CHAT_COLORS_ID to (chatColor?.id ?: ChatColors.Id.NotSet).longValue,
|
||||
RecipientTable.WALLPAPER_URI to if (chatWallpaper is UriChatWallpaper) chatWallpaper.uri.toString() else null,
|
||||
RecipientTable.WALLPAPER to chatWallpaper?.serialize()?.encode()
|
||||
),
|
||||
"${RecipientTable.ID} = ?",
|
||||
SqlUtil.buildArgs(recipientId.toLong())
|
||||
@@ -93,7 +125,7 @@ fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId, importSt
|
||||
return threadId
|
||||
}
|
||||
|
||||
class ChatExportIterator(private val cursor: Cursor) : Iterator<Chat>, Closeable {
|
||||
class ChatExportIterator(private val cursor: Cursor, private val readableDatabase: SQLiteDatabase) : Iterator<Chat>, Closeable {
|
||||
override fun hasNext(): Boolean {
|
||||
return cursor.count > 0 && !cursor.isLast
|
||||
}
|
||||
@@ -103,14 +135,15 @@ class ChatExportIterator(private val cursor: Cursor) : Iterator<Chat>, Closeable
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
|
||||
val serializedChatColors = cursor.requireBlob(RecipientTable.CHAT_COLORS)
|
||||
val chatColorId = ChatColors.Id.forLongValue(cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID))
|
||||
val chatColors: ChatColors? = serializedChatColors?.let { serialized ->
|
||||
try {
|
||||
ChatColors.forChatColor(chatColorId, ChatColor.ADAPTER.decode(serialized))
|
||||
} catch (e: InvalidProtocolBufferException) {
|
||||
null
|
||||
}
|
||||
val customChatColorsId = ChatColors.Id.forLongValue(cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID))
|
||||
|
||||
val chatColors: ChatColors? = cursor.requireBlob(RecipientTable.CHAT_COLORS)?.let { serializedChatColors ->
|
||||
val chatColor = ChatColor.ADAPTER.decodeOrNull(serializedChatColors)
|
||||
chatColor?.let { ChatColors.forChatColor(customChatColorsId, it) }
|
||||
}
|
||||
|
||||
val chatWallpaper: Wallpaper? = cursor.requireBlob(RecipientTable.WALLPAPER)?.let { serializedWallpaper ->
|
||||
Wallpaper.ADAPTER.decodeOrNull(serializedWallpaper)
|
||||
}
|
||||
|
||||
return Chat(
|
||||
@@ -123,7 +156,12 @@ class ChatExportIterator(private val cursor: Cursor) : Iterator<Chat>, Closeable
|
||||
muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL),
|
||||
markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD,
|
||||
dontNotifyForMentionsIfMuted = RecipientTable.MentionSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING),
|
||||
style = BackupConverters.constructRemoteChatStyle(chatColors, chatColorId)
|
||||
style = ChatStyleConverter.constructRemoteChatStyle(
|
||||
readableDatabase = readableDatabase,
|
||||
chatColors = chatColors,
|
||||
chatColorId = customChatColorsId,
|
||||
chatWallpaper = chatWallpaper
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -131,3 +169,15 @@ class ChatExportIterator(private val cursor: Cursor) : Iterator<Chat>, Closeable
|
||||
cursor.close()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ChatStyle.parseChatWallpaper(wallpaperAttachmentId: AttachmentId?): ChatWallpaper? {
|
||||
if (this.wallpaperPreset != null) {
|
||||
return this.wallpaperPreset.toLocal()
|
||||
}
|
||||
|
||||
if (wallpaperAttachmentId != null) {
|
||||
return UriChatWallpaper(PartAuthority.getAttachmentDataUri(wallpaperAttachmentId), 0f)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.AccountData
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatStyle
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.Frame
|
||||
import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter
|
||||
import org.thoughtcrime.securesms.backup.v2.util.BackupConverters
|
||||
import org.thoughtcrime.securesms.backup.v2.util.ChatStyleConverter
|
||||
import org.thoughtcrime.securesms.backup.v2.util.toLocal
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme
|
||||
@@ -50,6 +50,7 @@ object AccountDataProcessor {
|
||||
val donationSubscriber = db.inAppPaymentSubscriberTable.getByCurrencyCode(donationCurrency.currencyCode, InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
|
||||
val chatColors = SignalStore.chatColors.chatColors
|
||||
val chatWallpaper = SignalStore.wallpaper.currentRawWallpaper
|
||||
|
||||
emitter.emit(
|
||||
Frame(
|
||||
@@ -87,12 +88,12 @@ object AccountDataProcessor {
|
||||
hasSeenGroupStoryEducationSheet = signalStore.storyValues.userHasSeenGroupStoryEducationSheet,
|
||||
hasCompletedUsernameOnboarding = signalStore.uiHintValues.hasCompletedUsernameOnboarding(),
|
||||
customChatColors = db.chatColorsTable.getSavedChatColors().toRemoteChatColors(),
|
||||
defaultChatStyle = BackupConverters.constructRemoteChatStyle(chatColors, chatColors?.id ?: ChatColors.Id.NotSet)?.also {
|
||||
it.newBuilder().apply {
|
||||
// TODO [backup] We should do this elsewhere once we handle wallpaper better
|
||||
dimWallpaperInDarkMode = (SignalStore.wallpaper.wallpaper?.dimLevelForDarkTheme ?: 0f) > 0f
|
||||
}.build()
|
||||
}
|
||||
defaultChatStyle = ChatStyleConverter.constructRemoteChatStyle(
|
||||
readableDatabase = db.signalReadableDatabase,
|
||||
chatColors = chatColors,
|
||||
chatColorId = chatColors?.id ?: ChatColors.Id.NotSet,
|
||||
chatWallpaper = chatWallpaper
|
||||
)
|
||||
),
|
||||
donationSubscriberData = donationSubscriber?.toSubscriberData(signalStore.inAppPaymentValues.isDonationSubscriptionManuallyCancelled())
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ object ChatBackupProcessor {
|
||||
return
|
||||
}
|
||||
|
||||
SignalDatabase.threads.restoreFromBackup(chat, recipientId, importState)?.let { threadId ->
|
||||
SignalDatabase.threads.restoreFromBackup(chat, recipientId, importState).let { threadId ->
|
||||
importState.chatIdToLocalRecipientId[chat.id] = recipientId
|
||||
importState.chatIdToLocalThreadId[chat.id] = threadId
|
||||
importState.chatIdToBackupRecipientId[chat.id] = chat.recipientId
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.backup.v2.util
|
||||
|
||||
import okio.ByteString
|
||||
import org.signal.core.util.orNull
|
||||
import org.thoughtcrime.securesms.attachments.ArchivedAttachment
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.attachments.Cdn
|
||||
import org.thoughtcrime.securesms.attachments.PointerAttachment
|
||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
|
||||
import org.thoughtcrime.securesms.backup.v2.ImportState
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.FilePointer
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.whispersystems.signalservice.api.backup.MediaName
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* Converts a [FilePointer] to a local [Attachment] object for inserting into the database.
|
||||
*/
|
||||
fun FilePointer?.toLocalAttachment(
|
||||
importState: ImportState,
|
||||
voiceNote: Boolean = false,
|
||||
borderless: Boolean = false,
|
||||
gif: Boolean = false,
|
||||
wasDownloaded: Boolean = false,
|
||||
stickerLocator: StickerLocator? = null,
|
||||
contentType: String? = this?.contentType,
|
||||
fileName: String? = this?.fileName,
|
||||
uuid: ByteString? = null
|
||||
): Attachment? {
|
||||
if (this == null) return null
|
||||
|
||||
if (this.attachmentLocator != null) {
|
||||
val signalAttachmentPointer = SignalServiceAttachmentPointer(
|
||||
cdnNumber = this.attachmentLocator.cdnNumber,
|
||||
remoteId = SignalServiceAttachmentRemoteId.from(attachmentLocator.cdnKey),
|
||||
contentType = contentType,
|
||||
key = this.attachmentLocator.key.toByteArray(),
|
||||
size = Optional.ofNullable(attachmentLocator.size),
|
||||
preview = Optional.empty(),
|
||||
width = this.width ?: 0,
|
||||
height = this.height ?: 0,
|
||||
digest = Optional.ofNullable(this.attachmentLocator.digest.toByteArray()),
|
||||
incrementalDigest = Optional.ofNullable(this.incrementalMac?.toByteArray()),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize ?: 0,
|
||||
fileName = Optional.ofNullable(fileName),
|
||||
voiceNote = voiceNote,
|
||||
isBorderless = borderless,
|
||||
isGif = gif,
|
||||
caption = Optional.ofNullable(this.caption),
|
||||
blurHash = Optional.ofNullable(this.blurHash),
|
||||
uploadTimestamp = this.attachmentLocator.uploadTimestamp,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
return PointerAttachment.forPointer(
|
||||
pointer = Optional.of(signalAttachmentPointer),
|
||||
stickerLocator = stickerLocator,
|
||||
transferState = if (wasDownloaded) AttachmentTable.TRANSFER_NEEDS_RESTORE else AttachmentTable.TRANSFER_PROGRESS_PENDING
|
||||
).orNull()
|
||||
} else if (this.invalidAttachmentLocator != null) {
|
||||
return TombstoneAttachment(
|
||||
contentType = contentType,
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = false,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid)
|
||||
)
|
||||
} else if (this.backupLocator != null) {
|
||||
return ArchivedAttachment(
|
||||
contentType = contentType,
|
||||
size = this.backupLocator.size.toLong(),
|
||||
cdn = this.backupLocator.transitCdnNumber ?: Cdn.CDN_0.cdnNumber,
|
||||
key = this.backupLocator.key.toByteArray(),
|
||||
iv = null,
|
||||
cdnKey = this.backupLocator.transitCdnKey,
|
||||
archiveCdn = this.backupLocator.cdnNumber,
|
||||
archiveMediaName = this.backupLocator.mediaName,
|
||||
archiveMediaId = importState.backupKey.deriveMediaId(MediaName(this.backupLocator.mediaName)).encode(),
|
||||
archiveThumbnailMediaId = importState.backupKey.deriveMediaId(MediaName.forThumbnailFromMediaName(this.backupLocator.mediaName)).encode(),
|
||||
digest = this.backupLocator.digest.toByteArray(),
|
||||
incrementalMac = this.incrementalMac?.toByteArray(),
|
||||
incrementalMacChunkSize = this.incrementalMacChunkSize,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
caption = this.caption,
|
||||
blurHash = this.blurHash,
|
||||
voiceNote = voiceNote,
|
||||
borderless = borderless,
|
||||
gif = gif,
|
||||
quote = false,
|
||||
stickerLocator = stickerLocator,
|
||||
uuid = UuidUtil.fromByteStringOrNull(uuid),
|
||||
fileName = fileName
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.backup.v2.util
|
||||
|
||||
import android.database.Cursor
|
||||
import okio.ByteString
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.readToSingleObject
|
||||
import org.signal.core.util.requireBlob
|
||||
import org.signal.core.util.requireInt
|
||||
import org.signal.core.util.requireLong
|
||||
import org.signal.core.util.requireString
|
||||
import org.signal.core.util.select
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||
import org.thoughtcrime.securesms.backup.v2.ImportState
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatStyle
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.FilePointer
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.database.SQLiteDatabase
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
|
||||
import org.thoughtcrime.securesms.mms.PartUriParser
|
||||
import org.thoughtcrime.securesms.util.UriUtil
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
|
||||
import org.thoughtcrime.securesms.wallpaper.GradientChatWallpaper
|
||||
import org.thoughtcrime.securesms.wallpaper.SingleColorChatWallpaper
|
||||
|
||||
/**
|
||||
* Contains a collection of methods to chat styles to and from their archive format.
|
||||
* These are in a file of their own just because they're rather long (with all of the various constants to map between) and used in multiple places.
|
||||
*/
|
||||
object ChatStyleConverter {
|
||||
fun constructRemoteChatStyle(
|
||||
readableDatabase: SQLiteDatabase,
|
||||
chatColors: ChatColors?,
|
||||
chatColorId: ChatColors.Id,
|
||||
chatWallpaper: Wallpaper?
|
||||
): ChatStyle? {
|
||||
if (chatColors == null && chatWallpaper == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val chatStyleBuilder = ChatStyle.Builder()
|
||||
|
||||
if (chatColors != null) {
|
||||
when (chatColorId) {
|
||||
ChatColors.Id.NotSet -> {}
|
||||
ChatColors.Id.Auto -> {
|
||||
chatStyleBuilder.autoBubbleColor = ChatStyle.AutomaticBubbleColor()
|
||||
}
|
||||
|
||||
ChatColors.Id.BuiltIn -> {
|
||||
chatStyleBuilder.bubbleColorPreset = chatColors.toRemote()
|
||||
}
|
||||
|
||||
is ChatColors.Id.Custom -> {
|
||||
chatStyleBuilder.customColorId = chatColorId.longValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chatWallpaper != null) {
|
||||
when {
|
||||
chatWallpaper.singleColor != null -> {
|
||||
chatStyleBuilder.wallpaperPreset = chatWallpaper.singleColor.color.toRemoteWallpaperPreset()
|
||||
}
|
||||
chatWallpaper.linearGradient != null -> {
|
||||
chatStyleBuilder.wallpaperPreset = chatWallpaper.linearGradient.toRemoteWallpaperPreset()
|
||||
}
|
||||
chatWallpaper.file_ != null -> {
|
||||
chatStyleBuilder.wallpaperPhoto = chatWallpaper.file_.toFilePointer(readableDatabase)
|
||||
}
|
||||
}
|
||||
|
||||
chatStyleBuilder.dimWallpaperInDarkMode = chatWallpaper.dimLevelInDarkTheme > 0
|
||||
}
|
||||
|
||||
return chatStyleBuilder.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun ChatStyle.toLocal(importState: ImportState): ChatColors? {
|
||||
if (this.bubbleColorPreset != null) {
|
||||
return when (this.bubbleColorPreset) {
|
||||
// Solids
|
||||
ChatStyle.BubbleColorPreset.SOLID_CRIMSON -> ChatColorsPalette.Bubbles.CRIMSON
|
||||
ChatStyle.BubbleColorPreset.SOLID_VERMILION -> ChatColorsPalette.Bubbles.VERMILION
|
||||
ChatStyle.BubbleColorPreset.SOLID_BURLAP -> ChatColorsPalette.Bubbles.BURLAP
|
||||
ChatStyle.BubbleColorPreset.SOLID_FOREST -> ChatColorsPalette.Bubbles.FOREST
|
||||
ChatStyle.BubbleColorPreset.SOLID_WINTERGREEN -> ChatColorsPalette.Bubbles.WINTERGREEN
|
||||
ChatStyle.BubbleColorPreset.SOLID_TEAL -> ChatColorsPalette.Bubbles.TEAL
|
||||
ChatStyle.BubbleColorPreset.SOLID_BLUE -> ChatColorsPalette.Bubbles.BLUE
|
||||
ChatStyle.BubbleColorPreset.SOLID_INDIGO -> ChatColorsPalette.Bubbles.INDIGO
|
||||
ChatStyle.BubbleColorPreset.SOLID_VIOLET -> ChatColorsPalette.Bubbles.VIOLET
|
||||
ChatStyle.BubbleColorPreset.SOLID_PLUM -> ChatColorsPalette.Bubbles.PLUM
|
||||
ChatStyle.BubbleColorPreset.SOLID_TAUPE -> ChatColorsPalette.Bubbles.TAUPE
|
||||
ChatStyle.BubbleColorPreset.SOLID_STEEL -> ChatColorsPalette.Bubbles.STEEL
|
||||
// Gradients
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_EMBER -> ChatColorsPalette.Bubbles.EMBER
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_MIDNIGHT -> ChatColorsPalette.Bubbles.MIDNIGHT
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_INFRARED -> ChatColorsPalette.Bubbles.INFRARED
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_LAGOON -> ChatColorsPalette.Bubbles.LAGOON
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_FLUORESCENT -> ChatColorsPalette.Bubbles.FLUORESCENT
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_BASIL -> ChatColorsPalette.Bubbles.BASIL
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_SUBLIME -> ChatColorsPalette.Bubbles.SUBLIME
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_SEA -> ChatColorsPalette.Bubbles.SEA
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_TANGERINE -> ChatColorsPalette.Bubbles.TANGERINE
|
||||
ChatStyle.BubbleColorPreset.UNKNOWN_BUBBLE_COLOR_PRESET, ChatStyle.BubbleColorPreset.SOLID_ULTRAMARINE -> ChatColorsPalette.Bubbles.ULTRAMARINE
|
||||
}
|
||||
}
|
||||
|
||||
if (this.autoBubbleColor != null) {
|
||||
return ChatColorsPalette.Bubbles.default.withId(ChatColors.Id.Auto)
|
||||
}
|
||||
|
||||
if (this.customColorId != null) {
|
||||
return importState.remoteToLocalColorId[this.customColorId]?.let { localId ->
|
||||
val colorId = ChatColors.Id.forLongValue(localId)
|
||||
ChatColorsPalette.Bubbles.default.withId(colorId)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun ChatColors.toRemote(): ChatStyle.BubbleColorPreset? {
|
||||
when (this) {
|
||||
// Solids
|
||||
ChatColorsPalette.Bubbles.CRIMSON -> return ChatStyle.BubbleColorPreset.SOLID_CRIMSON
|
||||
ChatColorsPalette.Bubbles.VERMILION -> return ChatStyle.BubbleColorPreset.SOLID_VERMILION
|
||||
ChatColorsPalette.Bubbles.BURLAP -> return ChatStyle.BubbleColorPreset.SOLID_BURLAP
|
||||
ChatColorsPalette.Bubbles.FOREST -> return ChatStyle.BubbleColorPreset.SOLID_FOREST
|
||||
ChatColorsPalette.Bubbles.WINTERGREEN -> return ChatStyle.BubbleColorPreset.SOLID_WINTERGREEN
|
||||
ChatColorsPalette.Bubbles.TEAL -> return ChatStyle.BubbleColorPreset.SOLID_TEAL
|
||||
ChatColorsPalette.Bubbles.BLUE -> return ChatStyle.BubbleColorPreset.SOLID_BLUE
|
||||
ChatColorsPalette.Bubbles.INDIGO -> return ChatStyle.BubbleColorPreset.SOLID_INDIGO
|
||||
ChatColorsPalette.Bubbles.VIOLET -> return ChatStyle.BubbleColorPreset.SOLID_VIOLET
|
||||
ChatColorsPalette.Bubbles.PLUM -> return ChatStyle.BubbleColorPreset.SOLID_PLUM
|
||||
ChatColorsPalette.Bubbles.TAUPE -> return ChatStyle.BubbleColorPreset.SOLID_TAUPE
|
||||
ChatColorsPalette.Bubbles.STEEL -> return ChatStyle.BubbleColorPreset.SOLID_STEEL
|
||||
ChatColorsPalette.Bubbles.ULTRAMARINE -> return ChatStyle.BubbleColorPreset.SOLID_ULTRAMARINE
|
||||
// Gradients
|
||||
ChatColorsPalette.Bubbles.EMBER -> return ChatStyle.BubbleColorPreset.GRADIENT_EMBER
|
||||
ChatColorsPalette.Bubbles.MIDNIGHT -> return ChatStyle.BubbleColorPreset.GRADIENT_MIDNIGHT
|
||||
ChatColorsPalette.Bubbles.INFRARED -> return ChatStyle.BubbleColorPreset.GRADIENT_INFRARED
|
||||
ChatColorsPalette.Bubbles.LAGOON -> return ChatStyle.BubbleColorPreset.GRADIENT_LAGOON
|
||||
ChatColorsPalette.Bubbles.FLUORESCENT -> return ChatStyle.BubbleColorPreset.GRADIENT_FLUORESCENT
|
||||
ChatColorsPalette.Bubbles.BASIL -> return ChatStyle.BubbleColorPreset.GRADIENT_BASIL
|
||||
ChatColorsPalette.Bubbles.SUBLIME -> return ChatStyle.BubbleColorPreset.GRADIENT_SUBLIME
|
||||
ChatColorsPalette.Bubbles.SEA -> return ChatStyle.BubbleColorPreset.GRADIENT_SEA
|
||||
ChatColorsPalette.Bubbles.TANGERINE -> return ChatStyle.BubbleColorPreset.GRADIENT_TANGERINE
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun ChatStyle.WallpaperPreset.toLocal(): ChatWallpaper? {
|
||||
return when (this) {
|
||||
ChatStyle.WallpaperPreset.SOLID_BLUSH -> SingleColorChatWallpaper.BLUSH
|
||||
ChatStyle.WallpaperPreset.SOLID_COPPER -> SingleColorChatWallpaper.COPPER
|
||||
ChatStyle.WallpaperPreset.SOLID_DUST -> SingleColorChatWallpaper.DUST
|
||||
ChatStyle.WallpaperPreset.SOLID_CELADON -> SingleColorChatWallpaper.CELADON
|
||||
ChatStyle.WallpaperPreset.SOLID_RAINFOREST -> SingleColorChatWallpaper.RAINFOREST
|
||||
ChatStyle.WallpaperPreset.SOLID_PACIFIC -> SingleColorChatWallpaper.PACIFIC
|
||||
ChatStyle.WallpaperPreset.SOLID_FROST -> SingleColorChatWallpaper.FROST
|
||||
ChatStyle.WallpaperPreset.SOLID_NAVY -> SingleColorChatWallpaper.NAVY
|
||||
ChatStyle.WallpaperPreset.SOLID_LILAC -> SingleColorChatWallpaper.LILAC
|
||||
ChatStyle.WallpaperPreset.SOLID_PINK -> SingleColorChatWallpaper.PINK
|
||||
ChatStyle.WallpaperPreset.SOLID_EGGPLANT -> SingleColorChatWallpaper.EGGPLANT
|
||||
ChatStyle.WallpaperPreset.SOLID_SILVER -> SingleColorChatWallpaper.SILVER
|
||||
ChatStyle.WallpaperPreset.GRADIENT_SUNSET -> GradientChatWallpaper.SUNSET
|
||||
ChatStyle.WallpaperPreset.GRADIENT_NOIR -> GradientChatWallpaper.NOIR
|
||||
ChatStyle.WallpaperPreset.GRADIENT_HEATMAP -> GradientChatWallpaper.HEATMAP
|
||||
ChatStyle.WallpaperPreset.GRADIENT_AQUA -> GradientChatWallpaper.AQUA
|
||||
ChatStyle.WallpaperPreset.GRADIENT_IRIDESCENT -> GradientChatWallpaper.IRIDESCENT
|
||||
ChatStyle.WallpaperPreset.GRADIENT_MONSTERA -> GradientChatWallpaper.MONSTERA
|
||||
ChatStyle.WallpaperPreset.GRADIENT_BLISS -> GradientChatWallpaper.BLISS
|
||||
ChatStyle.WallpaperPreset.GRADIENT_SKY -> GradientChatWallpaper.SKY
|
||||
ChatStyle.WallpaperPreset.GRADIENT_PEACH -> GradientChatWallpaper.PEACH
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.toRemoteWallpaperPreset(): ChatStyle.WallpaperPreset {
|
||||
return when (this) {
|
||||
SingleColorChatWallpaper.BLUSH.color -> ChatStyle.WallpaperPreset.SOLID_BLUSH
|
||||
SingleColorChatWallpaper.COPPER.color -> ChatStyle.WallpaperPreset.SOLID_COPPER
|
||||
SingleColorChatWallpaper.DUST.color -> ChatStyle.WallpaperPreset.SOLID_DUST
|
||||
SingleColorChatWallpaper.CELADON.color -> ChatStyle.WallpaperPreset.SOLID_CELADON
|
||||
SingleColorChatWallpaper.RAINFOREST.color -> ChatStyle.WallpaperPreset.SOLID_RAINFOREST
|
||||
SingleColorChatWallpaper.PACIFIC.color -> ChatStyle.WallpaperPreset.SOLID_PACIFIC
|
||||
SingleColorChatWallpaper.FROST.color -> ChatStyle.WallpaperPreset.SOLID_FROST
|
||||
SingleColorChatWallpaper.NAVY.color -> ChatStyle.WallpaperPreset.SOLID_NAVY
|
||||
SingleColorChatWallpaper.LILAC.color -> ChatStyle.WallpaperPreset.SOLID_LILAC
|
||||
SingleColorChatWallpaper.PINK.color -> ChatStyle.WallpaperPreset.SOLID_PINK
|
||||
SingleColorChatWallpaper.EGGPLANT.color -> ChatStyle.WallpaperPreset.SOLID_EGGPLANT
|
||||
SingleColorChatWallpaper.SILVER.color -> ChatStyle.WallpaperPreset.SOLID_SILVER
|
||||
else -> ChatStyle.WallpaperPreset.UNKNOWN_WALLPAPER_PRESET
|
||||
}
|
||||
}
|
||||
|
||||
private fun Wallpaper.LinearGradient.toRemoteWallpaperPreset(): ChatStyle.WallpaperPreset {
|
||||
val colorArray = colors.toIntArray()
|
||||
return when {
|
||||
colorArray contentEquals GradientChatWallpaper.SUNSET.colors -> ChatStyle.WallpaperPreset.GRADIENT_SUNSET
|
||||
colorArray contentEquals GradientChatWallpaper.NOIR.colors -> ChatStyle.WallpaperPreset.GRADIENT_NOIR
|
||||
colorArray contentEquals GradientChatWallpaper.HEATMAP.colors -> ChatStyle.WallpaperPreset.GRADIENT_HEATMAP
|
||||
colorArray contentEquals GradientChatWallpaper.AQUA.colors -> ChatStyle.WallpaperPreset.GRADIENT_AQUA
|
||||
colorArray contentEquals GradientChatWallpaper.IRIDESCENT.colors -> ChatStyle.WallpaperPreset.GRADIENT_IRIDESCENT
|
||||
colorArray contentEquals GradientChatWallpaper.MONSTERA.colors -> ChatStyle.WallpaperPreset.GRADIENT_MONSTERA
|
||||
colorArray contentEquals GradientChatWallpaper.BLISS.colors -> ChatStyle.WallpaperPreset.GRADIENT_BLISS
|
||||
colorArray contentEquals GradientChatWallpaper.SKY.colors -> ChatStyle.WallpaperPreset.GRADIENT_SKY
|
||||
colorArray contentEquals GradientChatWallpaper.PEACH.colors -> ChatStyle.WallpaperPreset.GRADIENT_PEACH
|
||||
else -> ChatStyle.WallpaperPreset.UNKNOWN_WALLPAPER_PRESET
|
||||
}
|
||||
}
|
||||
|
||||
private fun Wallpaper.File.toFilePointer(readableDatabase: SQLiteDatabase): FilePointer? {
|
||||
val attachmentId: AttachmentId = UriUtil.parseOrNull(this.uri)?.let { PartUriParser(it).partId } ?: return null
|
||||
|
||||
val wallpaperAttachment: ArchiveAttachmentData? = readableDatabase
|
||||
.select(
|
||||
AttachmentTable.ARCHIVE_MEDIA_NAME,
|
||||
AttachmentTable.ARCHIVE_CDN,
|
||||
AttachmentTable.REMOTE_KEY,
|
||||
AttachmentTable.REMOTE_DIGEST,
|
||||
AttachmentTable.DATA_SIZE,
|
||||
AttachmentTable.CONTENT_TYPE,
|
||||
AttachmentTable.WIDTH,
|
||||
AttachmentTable.HEIGHT
|
||||
)
|
||||
.from(AttachmentTable.TABLE_NAME)
|
||||
.where("${AttachmentTable.ID} = ?", attachmentId.id)
|
||||
.run()
|
||||
.readToSingleObject { cursor -> cursor.toArchiveAttachmentData() }
|
||||
|
||||
return wallpaperAttachment?.let { attachment ->
|
||||
FilePointer(
|
||||
backupLocator = FilePointer.BackupLocator(
|
||||
mediaName = attachment.archiveMediaName ?: "",
|
||||
cdnNumber = attachment.archiveCdn,
|
||||
key = attachment.remoteKey?.toByteString() ?: ByteString.EMPTY,
|
||||
size = attachment.size.toInt(),
|
||||
digest = attachment.remoteDigest?.toByteString() ?: ByteString.EMPTY
|
||||
),
|
||||
contentType = attachment.contentType,
|
||||
width = attachment.width,
|
||||
height = attachment.height
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Cursor.toArchiveAttachmentData(): ArchiveAttachmentData {
|
||||
return ArchiveAttachmentData(
|
||||
archiveMediaName = this.requireString(AttachmentTable.ARCHIVE_MEDIA_NAME),
|
||||
archiveCdn = this.requireInt(AttachmentTable.ARCHIVE_CDN),
|
||||
remoteKey = this.requireString(AttachmentTable.REMOTE_KEY)?.let { Base64.decodeOrNull(it) },
|
||||
remoteDigest = this.requireBlob(AttachmentTable.REMOTE_DIGEST),
|
||||
size = this.requireLong(AttachmentTable.DATA_SIZE),
|
||||
contentType = this.requireString(AttachmentTable.CONTENT_TYPE),
|
||||
width = this.requireInt(AttachmentTable.WIDTH),
|
||||
height = this.requireInt(AttachmentTable.HEIGHT)
|
||||
)
|
||||
}
|
||||
|
||||
private class ArchiveAttachmentData(
|
||||
val archiveMediaName: String?,
|
||||
val archiveCdn: Int,
|
||||
val remoteKey: ByteArray?,
|
||||
val remoteDigest: ByteArray?,
|
||||
val size: Long,
|
||||
val contentType: String?,
|
||||
val width: Int,
|
||||
val height: Int
|
||||
)
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.backup.v2.util
|
||||
|
||||
import org.thoughtcrime.securesms.backup.v2.ImportState
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.ChatStyle
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette
|
||||
|
||||
// TODO [backup] Passing in chatColorId probably unnecessary. Only stored as separate column in recipient table for querying, I believe.
|
||||
object BackupConverters {
|
||||
fun constructRemoteChatStyle(chatColors: ChatColors?, chatColorId: ChatColors.Id): ChatStyle? {
|
||||
var chatStyleBuilder: ChatStyle.Builder? = null
|
||||
|
||||
if (chatColors != null) {
|
||||
chatStyleBuilder = ChatStyle.Builder()
|
||||
when (chatColorId) {
|
||||
ChatColors.Id.NotSet -> {}
|
||||
ChatColors.Id.Auto -> {
|
||||
chatStyleBuilder.autoBubbleColor = ChatStyle.AutomaticBubbleColor()
|
||||
}
|
||||
|
||||
ChatColors.Id.BuiltIn -> {
|
||||
chatStyleBuilder.bubbleColorPreset = chatColors.toRemote()
|
||||
}
|
||||
|
||||
is ChatColors.Id.Custom -> {
|
||||
chatStyleBuilder.customColorId = chatColorId.longValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [backup] wallpaper
|
||||
|
||||
return chatStyleBuilder?.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun ChatStyle.toLocal(importState: ImportState): ChatColors? {
|
||||
if (this.bubbleColorPreset != null) {
|
||||
return when (this.bubbleColorPreset) {
|
||||
ChatStyle.BubbleColorPreset.SOLID_CRIMSON -> ChatColorsPalette.Bubbles.CRIMSON
|
||||
ChatStyle.BubbleColorPreset.SOLID_VERMILION -> ChatColorsPalette.Bubbles.VERMILION
|
||||
ChatStyle.BubbleColorPreset.SOLID_BURLAP -> ChatColorsPalette.Bubbles.BURLAP
|
||||
ChatStyle.BubbleColorPreset.SOLID_FOREST -> ChatColorsPalette.Bubbles.FOREST
|
||||
ChatStyle.BubbleColorPreset.SOLID_WINTERGREEN -> ChatColorsPalette.Bubbles.WINTERGREEN
|
||||
ChatStyle.BubbleColorPreset.SOLID_TEAL -> ChatColorsPalette.Bubbles.TEAL
|
||||
ChatStyle.BubbleColorPreset.SOLID_BLUE -> ChatColorsPalette.Bubbles.BLUE
|
||||
ChatStyle.BubbleColorPreset.SOLID_INDIGO -> ChatColorsPalette.Bubbles.INDIGO
|
||||
ChatStyle.BubbleColorPreset.SOLID_VIOLET -> ChatColorsPalette.Bubbles.VIOLET
|
||||
ChatStyle.BubbleColorPreset.SOLID_PLUM -> ChatColorsPalette.Bubbles.PLUM
|
||||
ChatStyle.BubbleColorPreset.SOLID_TAUPE -> ChatColorsPalette.Bubbles.TAUPE
|
||||
ChatStyle.BubbleColorPreset.SOLID_STEEL -> ChatColorsPalette.Bubbles.STEEL
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_EMBER -> ChatColorsPalette.Bubbles.EMBER
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_MIDNIGHT -> ChatColorsPalette.Bubbles.MIDNIGHT
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_INFRARED -> ChatColorsPalette.Bubbles.INFRARED
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_LAGOON -> ChatColorsPalette.Bubbles.LAGOON
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_FLUORESCENT -> ChatColorsPalette.Bubbles.FLUORESCENT
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_BASIL -> ChatColorsPalette.Bubbles.BASIL
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_SUBLIME -> ChatColorsPalette.Bubbles.SUBLIME
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_SEA -> ChatColorsPalette.Bubbles.SEA
|
||||
ChatStyle.BubbleColorPreset.GRADIENT_TANGERINE -> ChatColorsPalette.Bubbles.TANGERINE
|
||||
ChatStyle.BubbleColorPreset.UNKNOWN_BUBBLE_COLOR_PRESET, ChatStyle.BubbleColorPreset.SOLID_ULTRAMARINE -> ChatColorsPalette.Bubbles.ULTRAMARINE
|
||||
}
|
||||
}
|
||||
|
||||
if (this.autoBubbleColor != null) {
|
||||
return ChatColorsPalette.Bubbles.default.withId(ChatColors.Id.Auto)
|
||||
}
|
||||
|
||||
if (this.customColorId != null) {
|
||||
return importState.remoteToLocalColorId[this.customColorId]?.let { localId ->
|
||||
val colorId = ChatColors.Id.forLongValue(localId)
|
||||
ChatColorsPalette.Bubbles.default.withId(colorId)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun ChatColors.toRemote(): ChatStyle.BubbleColorPreset? {
|
||||
when (this) {
|
||||
// Solids
|
||||
ChatColorsPalette.Bubbles.CRIMSON -> return ChatStyle.BubbleColorPreset.SOLID_CRIMSON
|
||||
ChatColorsPalette.Bubbles.VERMILION -> return ChatStyle.BubbleColorPreset.SOLID_VERMILION
|
||||
ChatColorsPalette.Bubbles.BURLAP -> return ChatStyle.BubbleColorPreset.SOLID_BURLAP
|
||||
ChatColorsPalette.Bubbles.FOREST -> return ChatStyle.BubbleColorPreset.SOLID_FOREST
|
||||
ChatColorsPalette.Bubbles.WINTERGREEN -> return ChatStyle.BubbleColorPreset.SOLID_WINTERGREEN
|
||||
ChatColorsPalette.Bubbles.TEAL -> return ChatStyle.BubbleColorPreset.SOLID_TEAL
|
||||
ChatColorsPalette.Bubbles.BLUE -> return ChatStyle.BubbleColorPreset.SOLID_BLUE
|
||||
ChatColorsPalette.Bubbles.INDIGO -> return ChatStyle.BubbleColorPreset.SOLID_INDIGO
|
||||
ChatColorsPalette.Bubbles.VIOLET -> return ChatStyle.BubbleColorPreset.SOLID_VIOLET
|
||||
ChatColorsPalette.Bubbles.PLUM -> return ChatStyle.BubbleColorPreset.SOLID_PLUM
|
||||
ChatColorsPalette.Bubbles.TAUPE -> return ChatStyle.BubbleColorPreset.SOLID_TAUPE
|
||||
ChatColorsPalette.Bubbles.STEEL -> return ChatStyle.BubbleColorPreset.SOLID_STEEL
|
||||
ChatColorsPalette.Bubbles.ULTRAMARINE -> return ChatStyle.BubbleColorPreset.SOLID_ULTRAMARINE
|
||||
// Gradients
|
||||
ChatColorsPalette.Bubbles.EMBER -> return ChatStyle.BubbleColorPreset.GRADIENT_EMBER
|
||||
ChatColorsPalette.Bubbles.MIDNIGHT -> return ChatStyle.BubbleColorPreset.GRADIENT_MIDNIGHT
|
||||
ChatColorsPalette.Bubbles.INFRARED -> return ChatStyle.BubbleColorPreset.GRADIENT_INFRARED
|
||||
ChatColorsPalette.Bubbles.LAGOON -> return ChatStyle.BubbleColorPreset.GRADIENT_LAGOON
|
||||
ChatColorsPalette.Bubbles.FLUORESCENT -> return ChatStyle.BubbleColorPreset.GRADIENT_FLUORESCENT
|
||||
ChatColorsPalette.Bubbles.BASIL -> return ChatStyle.BubbleColorPreset.GRADIENT_BASIL
|
||||
ChatColorsPalette.Bubbles.SUBLIME -> return ChatStyle.BubbleColorPreset.GRADIENT_SUBLIME
|
||||
ChatColorsPalette.Bubbles.SEA -> return ChatStyle.BubbleColorPreset.GRADIENT_SEA
|
||||
ChatColorsPalette.Bubbles.TANGERINE -> return ChatStyle.BubbleColorPreset.GRADIENT_TANGERINE
|
||||
}
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user