mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-13 17:23:56 +01:00
Add backup support for contact nicknames and notes.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
@@ -46,6 +46,9 @@ fun RecipientTable.getContactsForBackup(selfId: Long): ContactArchiveExporter {
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_GIVEN_NAME}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_FAMILY_NAME}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_JOINED_NAME}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.NICKNAME_GIVEN_NAME}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.NICKNAME_FAMILY_NAME}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.NOTE}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.CHAT_COLORS}",
|
||||
"${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}",
|
||||
|
||||
+29
-34
@@ -673,36 +673,34 @@ private fun BackupMessageRecord.toRemotePaymentNotificationUpdate(db: SignalData
|
||||
}
|
||||
}
|
||||
|
||||
private fun BackupMessageRecord.toRemoteSharedContacts(attachments: List<DatabaseAttachment>?): List<Contact>? {
|
||||
private fun BackupMessageRecord.toRemoteSharedContact(attachments: List<DatabaseAttachment>?): Contact? {
|
||||
if (this.sharedContacts.isNullOrEmpty()) {
|
||||
return emptyList()
|
||||
return null
|
||||
}
|
||||
|
||||
val attachmentIdMap: Map<AttachmentId, DatabaseAttachment> = attachments?.associateBy { it.attachmentId } ?: emptyMap()
|
||||
|
||||
return try {
|
||||
val contacts: MutableList<Contact> = LinkedList()
|
||||
val jsonContacts = JSONArray(sharedContacts)
|
||||
|
||||
for (i in 0 until jsonContacts.length()) {
|
||||
val contact: Contact = Contact.deserialize(jsonContacts.getJSONObject(i).toString())
|
||||
|
||||
if (contact.avatar != null && contact.avatar!!.attachmentId != null) {
|
||||
val attachment = attachmentIdMap[contact.avatar!!.attachmentId]
|
||||
|
||||
val updatedAvatar = Contact.Avatar(
|
||||
contact.avatar!!.attachmentId,
|
||||
attachment,
|
||||
contact.avatar!!.isProfile
|
||||
)
|
||||
|
||||
contacts += Contact(contact, updatedAvatar)
|
||||
} else {
|
||||
contacts += contact
|
||||
}
|
||||
if (jsonContacts.length() == 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
contacts
|
||||
val contact: Contact = Contact.deserialize(jsonContacts.getJSONObject(0).toString())
|
||||
|
||||
return if (contact.avatar != null && contact.avatar!!.attachmentId != null) {
|
||||
val attachment = attachmentIdMap[contact.avatar!!.attachmentId]
|
||||
|
||||
val updatedAvatar = Contact.Avatar(
|
||||
contact.avatar!!.attachmentId,
|
||||
attachment,
|
||||
contact.avatar!!.isProfile
|
||||
)
|
||||
|
||||
Contact(contact, updatedAvatar)
|
||||
} else {
|
||||
contact
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
Log.w(TAG, ExportSkips.failedToParseSharedContact(this.dateSent), e)
|
||||
null
|
||||
@@ -768,28 +766,28 @@ private fun BackupMessageRecord.toRemoteViewOnceMessage(mediaArchiveEnabled: Boo
|
||||
}
|
||||
|
||||
private fun BackupMessageRecord.toRemoteContactMessage(mediaArchiveEnabled: Boolean, reactionRecords: List<ReactionRecord>?, attachments: List<DatabaseAttachment>?): ContactMessage? {
|
||||
val sharedContacts = toRemoteSharedContacts(attachments) ?: return null
|
||||
val sharedContact = toRemoteSharedContact(attachments) ?: return null
|
||||
|
||||
val contacts = sharedContacts.map {
|
||||
ContactAttachment(
|
||||
name = it.name.toRemote(),
|
||||
avatar = (it.avatar?.attachment as? DatabaseAttachment)?.toRemoteMessageAttachment(mediaArchiveEnabled)?.pointer,
|
||||
organization = it.organization ?: "",
|
||||
number = it.phoneNumbers.map { phone ->
|
||||
return ContactMessage(
|
||||
contact = ContactAttachment(
|
||||
name = sharedContact.name.toRemote(),
|
||||
avatar = (sharedContact.avatar?.attachment as? DatabaseAttachment)?.toRemoteMessageAttachment(mediaArchiveEnabled)?.pointer,
|
||||
organization = sharedContact.organization ?: "",
|
||||
number = sharedContact.phoneNumbers.map { phone ->
|
||||
ContactAttachment.Phone(
|
||||
value_ = phone.number,
|
||||
type = phone.type.toRemote(),
|
||||
label = phone.label ?: ""
|
||||
)
|
||||
},
|
||||
email = it.emails.map { email ->
|
||||
email = sharedContact.emails.map { email ->
|
||||
ContactAttachment.Email(
|
||||
value_ = email.email,
|
||||
label = email.label ?: "",
|
||||
type = email.type.toRemote()
|
||||
)
|
||||
},
|
||||
address = it.postalAddresses.map { address ->
|
||||
address = sharedContact.postalAddresses.map { address ->
|
||||
ContactAttachment.PostalAddress(
|
||||
type = address.type.toRemote(),
|
||||
label = address.label ?: "",
|
||||
@@ -802,10 +800,7 @@ private fun BackupMessageRecord.toRemoteContactMessage(mediaArchiveEnabled: Bool
|
||||
country = address.country ?: ""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
return ContactMessage(
|
||||
contact = contacts,
|
||||
),
|
||||
reactions = reactionRecords.toRemote()
|
||||
)
|
||||
}
|
||||
|
||||
+16
@@ -76,6 +76,8 @@ class ContactArchiveExporter(private val cursor: Cursor, private val selfId: Lon
|
||||
.hideStory(RecipientTableCursorUtil.getExtras(cursor)?.hideStory() ?: false)
|
||||
.identityKey(cursor.requireString(IdentityTable.IDENTITY_KEY)?.let { Base64.decode(it).toByteString() })
|
||||
.identityState(cursor.optionalInt(IdentityTable.VERIFIED).map { IdentityTable.VerifiedStatus.forState(it) }.orElse(IdentityTable.VerifiedStatus.DEFAULT).toRemote())
|
||||
.note(cursor.requireString(RecipientTable.NOTE) ?: "")
|
||||
.nickname(cursor.readNickname())
|
||||
|
||||
val registeredState = RecipientTable.RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED))
|
||||
if (registeredState == RecipientTable.RegisteredState.REGISTERED) {
|
||||
@@ -95,6 +97,20 @@ class ContactArchiveExporter(private val cursor: Cursor, private val selfId: Lon
|
||||
}
|
||||
}
|
||||
|
||||
private fun Cursor.readNickname(): Contact.Name? {
|
||||
val given = this.requireString(RecipientTable.NICKNAME_GIVEN_NAME)
|
||||
val family = this.requireString(RecipientTable.NICKNAME_FAMILY_NAME)
|
||||
|
||||
if (given.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Contact.Name(
|
||||
given = given,
|
||||
family = family ?: ""
|
||||
)
|
||||
}
|
||||
|
||||
private fun Recipient.HiddenState.toRemote(): Contact.Visibility {
|
||||
return when (this) {
|
||||
Recipient.HiddenState.NOT_HIDDEN -> return Contact.Visibility.VISIBLE
|
||||
|
||||
+6
-6
@@ -310,7 +310,7 @@ class ChatItemArchiveImporter(
|
||||
}
|
||||
|
||||
if (this.contactMessage != null) {
|
||||
val contacts = this.contactMessage.contact.map { backupContact ->
|
||||
val contact = this.contactMessage.contact?.let { backupContact ->
|
||||
Contact(
|
||||
backupContact.name.toLocal(),
|
||||
backupContact.organization,
|
||||
@@ -345,18 +345,18 @@ class ChatItemArchiveImporter(
|
||||
)
|
||||
}
|
||||
|
||||
val contactAttachments = contacts.mapNotNull { it.avatarAttachment }
|
||||
if (contacts.isNotEmpty()) {
|
||||
if (contact != null) {
|
||||
val contactAttachment: Attachment? = contact.avatarAttachment
|
||||
followUp = { messageRowId ->
|
||||
val attachmentMap = if (contactAttachments.isNotEmpty()) {
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, contactAttachments, emptyList())
|
||||
val attachmentMap = if (contactAttachment != null) {
|
||||
SignalDatabase.attachments.insertAttachmentsForMessage(messageRowId, listOf(contactAttachment), emptyList())
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
db.update(
|
||||
MessageTable.TABLE_NAME,
|
||||
contentValuesOf(
|
||||
MessageTable.SHARED_CONTACTS to SignalDatabase.messages.getSerializedSharedContacts(attachmentMap, contacts)
|
||||
MessageTable.SHARED_CONTACTS to SignalDatabase.messages.getSerializedSharedContacts(attachmentMap, listOf(contact))
|
||||
),
|
||||
"${MessageTable.ID} = ?",
|
||||
SqlUtil.buildArgs(messageRowId)
|
||||
|
||||
+4
-1
@@ -49,7 +49,10 @@ object ContactArchiveImporter {
|
||||
RecipientTable.PROFILE_KEY to if (profileKey == null) null else Base64.encodeWithPadding(profileKey),
|
||||
RecipientTable.PROFILE_SHARING to contact.profileSharing.toInt(),
|
||||
RecipientTable.USERNAME to contact.username,
|
||||
RecipientTable.EXTRAS to contact.toLocalExtras().encode()
|
||||
RecipientTable.EXTRAS to contact.toLocalExtras().encode(),
|
||||
RecipientTable.NOTE to contact.note,
|
||||
RecipientTable.NICKNAME_GIVEN_NAME to contact.nickname?.given,
|
||||
RecipientTable.NICKNAME_FAMILY_NAME to contact.nickname?.family
|
||||
)
|
||||
|
||||
if (contact.registered != null) {
|
||||
|
||||
@@ -147,6 +147,11 @@ message Contact {
|
||||
HIDDEN_MESSAGE_REQUEST = 2;
|
||||
}
|
||||
|
||||
message Name {
|
||||
string given = 1;
|
||||
string family = 2;
|
||||
}
|
||||
|
||||
optional bytes aci = 1; // should be 16 bytes
|
||||
optional bytes pni = 2; // should be 16 bytes
|
||||
optional string username = 3;
|
||||
@@ -166,6 +171,8 @@ message Contact {
|
||||
bool hideStory = 13;
|
||||
optional bytes identityKey = 14;
|
||||
IdentityState identityState = 15;
|
||||
Name nickname = 16; // absent iff both `given` and `family` are empty
|
||||
string note = 17;
|
||||
}
|
||||
|
||||
message Group {
|
||||
@@ -373,6 +380,7 @@ message ChatItem {
|
||||
PaymentNotification paymentNotification = 16;
|
||||
GiftBadge giftBadge = 17;
|
||||
ViewOnceMessage viewOnceMessage = 18;
|
||||
DirectStoryReplyMessage directStoryReplyMessage = 19; // group story reply messages are not backed up
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,10 +445,25 @@ message StandardMessage {
|
||||
}
|
||||
|
||||
message ContactMessage {
|
||||
repeated ContactAttachment contact = 1;
|
||||
ContactAttachment contact = 1;
|
||||
repeated Reaction reactions = 2;
|
||||
}
|
||||
|
||||
message DirectStoryReplyMessage {
|
||||
message TextReply {
|
||||
Text text = 1;
|
||||
FilePointer longText = 2;
|
||||
}
|
||||
|
||||
oneof reply {
|
||||
TextReply textReply = 1;
|
||||
string emoji = 2;
|
||||
}
|
||||
|
||||
repeated Reaction reactions = 3;
|
||||
optional uint64 storySentTimestamp = 4;
|
||||
}
|
||||
|
||||
message PaymentNotification {
|
||||
message TransactionDetails {
|
||||
message MobileCoinTxoIdentification { // Used to map to payments on the ledger
|
||||
@@ -1234,4 +1257,4 @@ message ChatFolder {
|
||||
FolderType folderType = 6;
|
||||
repeated uint64 includedRecipientIds = 7; // generated recipient id of groups, contacts, and/or note to self
|
||||
repeated uint64 excludedRecipientIds = 8; // generated recipient id of groups, contacts, and/or note to self
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user