Add some resiliance to custom chat color export.

This commit is contained in:
Greyson Parrelli
2025-10-29 16:11:44 -04:00
committed by Michelle Tang
parent 6aa6b490b5
commit 107ee5268e
6 changed files with 22 additions and 10 deletions

View File

@@ -981,7 +981,7 @@ object BackupRepository {
// We're using a snapshot, so the transaction is more for perf than correctness
dbSnapshot.rawWritableDatabase.withinTransaction {
progressEmitter?.onAccount()
AccountDataArchiveProcessor.export(dbSnapshot, signalStoreSnapshot) { frame ->
AccountDataArchiveProcessor.export(dbSnapshot, signalStoreSnapshot, exportState) { frame ->
writer.write(frame)
extraFrameOperation?.invoke(frame)
eventTimer.emit("account")
@@ -2429,6 +2429,7 @@ class ExportState(
val threadIdToRecipientId: MutableMap<Long, Long> = hashMapOf()
val recipientIdToAci: MutableMap<Long, ByteString> = hashMapOf()
val aciToRecipientId: MutableMap<String, Long> = hashMapOf()
val customChatColorIds: MutableSet<Long> = hashSetOf()
}
class ImportState(val mediaRootBackupKey: MediaRootBackupKey) {

View File

@@ -10,13 +10,14 @@ import org.signal.core.util.forEach
import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
import org.signal.core.util.select
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.exporters.ChatArchiveExporter
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadTable
fun ThreadTable.getThreadsForBackup(db: SignalDatabase, includeImageWallpapers: Boolean): ChatArchiveExporter {
fun ThreadTable.getThreadsForBackup(db: SignalDatabase, exportState: ExportState, includeImageWallpapers: Boolean): ChatArchiveExporter {
//language=sql
val query = """
SELECT
@@ -39,7 +40,7 @@ fun ThreadTable.getThreadsForBackup(db: SignalDatabase, includeImageWallpapers:
"""
val cursor = readableDatabase.query(query)
return ChatArchiveExporter(cursor, db, includeImageWallpapers)
return ChatArchiveExporter(cursor, db, exportState, includeImageWallpapers)
}
fun ThreadTable.getThreadGroupStatus(messageIds: Collection<Long>): Map<Long, Boolean> {

View File

@@ -12,8 +12,10 @@ import org.signal.core.util.requireBoolean
import org.signal.core.util.requireInt
import org.signal.core.util.requireIntOrNull
import org.signal.core.util.requireLong
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.proto.Chat
import org.thoughtcrime.securesms.backup.v2.util.ChatStyleConverter
import org.thoughtcrime.securesms.backup.v2.util.isValid
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
@@ -23,7 +25,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
import java.io.Closeable
import kotlin.time.Duration.Companion.seconds
class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalDatabase, private val includeImageWallpapers: Boolean) : Iterator<Chat>, Closeable {
class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalDatabase, private val exportState: ExportState, private val includeImageWallpapers: Boolean) : Iterator<Chat>, Closeable {
override fun hasNext(): Boolean {
return cursor.count > 0 && !cursor.isLast
}
@@ -33,9 +35,9 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData
throw NoSuchElementException()
}
val customChatColorsId = ChatColors.Id.forLongValue(cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID))
val customChatColorsId = ChatColors.Id.forLongValue(cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID)).takeIf { it.isValid(exportState) } ?: ChatColors.Id.NotSet
val chatColors: ChatColors? = cursor.requireBlob(RecipientTable.CHAT_COLORS)?.let { serializedChatColors ->
val chatColors: ChatColors? = cursor.requireBlob(RecipientTable.CHAT_COLORS)?.takeUnless { customChatColorsId is ChatColors.Id.NotSet }?.let { serializedChatColors ->
val chatColor = ChatColor.ADAPTER.decodeOrNull(serializedChatColors)
chatColor?.let { ChatColors.forChatColor(customChatColorsId, it) }
}

View File

@@ -12,6 +12,7 @@ import org.signal.core.util.isNotNullOrBlank
import org.signal.core.util.logging.Log
import org.signal.libsignal.zkgroup.backups.BackupLevel
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.ImportState
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.backup.v2.database.restoreSelfFromBackup
@@ -21,6 +22,7 @@ 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.ChatStyleConverter
import org.thoughtcrime.securesms.backup.v2.util.isValid
import org.thoughtcrime.securesms.backup.v2.util.parseChatWallpaper
import org.thoughtcrime.securesms.backup.v2.util.toLocal
import org.thoughtcrime.securesms.backup.v2.util.toLocalAttachment
@@ -54,7 +56,7 @@ object AccountDataArchiveProcessor {
private val TAG = Log.tag(AccountDataArchiveProcessor::class)
fun export(db: SignalDatabase, signalStore: SignalStore, emitter: BackupFrameEmitter) {
fun export(db: SignalDatabase, signalStore: SignalStore, exportState: ExportState, emitter: BackupFrameEmitter) {
val context = AppDependencies.application
val selfId = db.recipientTable.getByAci(signalStore.accountValues.aci!!).get()
@@ -104,13 +106,13 @@ object AccountDataArchiveProcessor {
displayBadgesOnProfile = signalStore.inAppPaymentValues.getDisplayBadgesOnProfile(),
hasSeenGroupStoryEducationSheet = signalStore.storyValues.userHasSeenGroupStoryEducationSheet,
hasCompletedUsernameOnboarding = signalStore.uiHintValues.hasCompletedUsernameOnboarding(),
customChatColors = db.chatColorsTable.getSavedChatColors().toRemoteChatColors(),
customChatColors = db.chatColorsTable.getSavedChatColors().toRemoteChatColors().also { colors -> exportState.customChatColorIds.addAll(colors.map { it.id }) },
optimizeOnDeviceStorage = signalStore.backupValues.optimizeStorage,
backupTier = signalStore.backupValues.backupTier.toRemoteBackupTier(),
defaultChatStyle = ChatStyleConverter.constructRemoteChatStyle(
db = db,
chatColors = chatColors,
chatColorId = chatColors?.id ?: ChatColors.Id.NotSet,
chatColorId = chatColors?.id?.takeIf { it.isValid(exportState) } ?: ChatColors.Id.NotSet,
chatWallpaper = chatWallpaper
)
),

View File

@@ -24,7 +24,7 @@ object ChatArchiveProcessor {
val TAG = Log.tag(ChatArchiveProcessor::class.java)
fun export(db: SignalDatabase, exportState: ExportState, emitter: BackupFrameEmitter) {
db.threadTable.getThreadsForBackup(db, includeImageWallpapers = true).use { reader ->
db.threadTable.getThreadsForBackup(db, exportState, includeImageWallpapers = true).use { reader ->
for (chat in reader) {
if (exportState.recipientIds.contains(chat.recipientId)) {
exportState.threadIds.add(chat.id)

View File

@@ -17,8 +17,10 @@ import org.thoughtcrime.securesms.attachments.Cdn
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
import org.thoughtcrime.securesms.backup.v2.ExportState
import org.thoughtcrime.securesms.backup.v2.proto.FilePointer
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
@@ -222,6 +224,10 @@ fun RemoteAvatarColor.toLocal(): AvatarColor {
}
}
fun ChatColors.Id.isValid(exportState: ExportState): Boolean {
return this !is ChatColors.Id.Custom || this.longValue in exportState.customChatColorIds
}
private fun DatabaseAttachment.toRemoteAttachmentType(): AttachmentType {
if (this.remoteKey.isNullOrBlank()) {
return AttachmentType.INVALID