Get shared backup tests working.

This commit is contained in:
Greyson Parrelli 2024-07-24 10:28:09 -04:00 committed by Nicholas Tinsley
parent 36640edfee
commit 7b0badef19
30 changed files with 184 additions and 108 deletions

View file

@ -587,7 +587,7 @@ class ImportExportTest {
)
)
import(importData)
val exported = BackupRepository.export()
val exported = BackupRepository.debugExport()
val expected = exportFrames(
*standardFrames,
alexa
@ -1012,7 +1012,7 @@ class ImportExportTest {
expirationNotStarted
)
import(importData)
val exported = BackupRepository.export()
val exported = BackupRepository.debugExport()
val expected = exportFrames(
*standardFrames,
alice,
@ -1649,7 +1649,7 @@ class ImportExportTest {
import(originalBackupData)
val generatedBackupData = BackupRepository.export()
val generatedBackupData = BackupRepository.debugExport()
compare(originalBackupData, generatedBackupData)
}

View file

@ -10,7 +10,6 @@ import com.github.difflib.DiffUtils
import com.github.difflib.UnifiedDiffUtils
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@ -26,7 +25,6 @@ import java.io.ByteArrayInputStream
import java.util.UUID
import kotlin.random.Random
@Ignore("Not passing yet")
@RunWith(Parameterized::class)
class ImportExportTestSuite(private val path: String) {
companion object {
@ -41,8 +39,10 @@ class ImportExportTestSuite(private val path: String) {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun data(): Collection<Array<String>> {
val testFiles = InstrumentationRegistry.getInstrumentation().context.resources.assets.list(TESTS_FOLDER)
return testFiles?.map { arrayOf(it) }!!.toList()
val testFiles = InstrumentationRegistry.getInstrumentation().context.resources.assets.list(TESTS_FOLDER)!!
return testFiles
.map { arrayOf(it) }
.toList()
}
}
@ -65,8 +65,11 @@ class ImportExportTestSuite(private val path: String) {
assertTrue(importResult is ImportResult.Success)
val success = importResult as ImportResult.Success
val generatedBackupData = BackupRepository.export(plaintext = true, currentTime = success.backupTime)
compare(binProtoBytes, generatedBackupData)
val generatedBackupData = BackupRepository.debugExport(plaintext = true, currentTime = success.backupTime)
// TODO [backup] Currently fails, need to look into it
// assertPassesValidator(generatedBackupData)
assertEquivalent(binProtoBytes, generatedBackupData)
}
private fun import(importData: ByteArray): ImportResult {
@ -78,7 +81,15 @@ class ImportExportTestSuite(private val path: String) {
)
}
private fun compare(import: ByteArray, export: ByteArray) {
private fun assertPassesValidator(generatedBackupData: ByteArray) {
BackupRepository.validate(
length = generatedBackupData.size.toLong(),
inputStreamFactory = { ByteArrayInputStream(generatedBackupData) },
selfData = BackupRepository.SelfData(SELF_ACI, SELF_PNI, SELF_E164, SELF_PROFILE_KEY)
)
}
private fun assertEquivalent(import: ByteArray, export: ByteArray) {
val importComparable = ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, import.inputStream(), import.size.toLong())
val exportComparable = ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, export.inputStream(), import.size.toLong())

View file

@ -61,7 +61,7 @@ object MessageTableTestUtils {
isProfileChange:${type == MessageTypes.PROFILE_CHANGE_TYPE}
isGroupV1MigrationEvent:${type == MessageTypes.GV1_MIGRATION_TYPE}
isChangeNumber:${type == MessageTypes.CHANGE_NUMBER_TYPE}
isBoostRequest:${type == MessageTypes.BOOST_REQUEST_TYPE}
isDonationChannelDonationRequest:${type == MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE}
isThreadMerge:${type == MessageTypes.THREAD_MERGE_TYPE}
isSmsExport:${type == MessageTypes.SMS_EXPORT_TYPE}
isGroupV2LeaveOnly:${type and MessageTypes.GROUP_V2_LEAVE_BITS == MessageTypes.GROUP_V2_LEAVE_BITS}

View file

@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFe
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider
import org.thoughtcrime.securesms.database.DistributionListTables
import org.thoughtcrime.securesms.database.KeyValueDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
@ -249,19 +248,15 @@ object BackupRepository {
}
}
fun export(plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis()): ByteArray {
/**
* Exports to a blob in memory. Should only be used for testing.
*/
fun debugExport(plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis()): ByteArray {
val outputStream = ByteArrayOutputStream()
export(outputStream = outputStream, append = { mac -> outputStream.write(mac) }, plaintext = plaintext, currentTime = currentTime)
return outputStream.toByteArray()
}
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
val masterKey = SignalStore.svr.getOrCreateMasterKey()
val key = MessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray()))
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
}
/**
* @return The time the backup was created, or null if the backup could not be read.
*/
@ -306,9 +301,6 @@ object BackupRepository {
SignalDatabase.recipients.setProfileKey(selfId, selfData.profileKey)
SignalDatabase.recipients.setProfileSharing(selfId, true)
// Add back my story after clearing data
DistributionListTables.insertInitialDistributionListAtCreationTime(it)
eventTimer.emit("setup")
val backupState = BackupState(backupKey)
val chatItemInserter: ChatItemImportInserter = ChatItemBackupProcessor.beginImport(backupState)
@ -373,6 +365,13 @@ object BackupRepository {
return ImportResult.Success(backupTime = header.backupTimeMs)
}
fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult {
val masterKey = SignalStore.svr.getOrCreateMasterKey()
val key = MessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray()))
return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length)
}
fun listRemoteMediaObjects(limit: Int, cursor: String? = null): NetworkResult<ArchiveGetMediaItemsResponse> {
val api = AppDependencies.signalServiceAccountManager.archiveApi
val backupKey = SignalStore.svr.getOrCreateMasterKey().deriveBackupKey()

View file

@ -158,8 +158,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
MessageTypes.isChangeNumber(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHANGE_NUMBER)
}
MessageTypes.isBoostRequest(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BOOST_REQUEST)
MessageTypes.isReleaseChannelDonationRequest(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST)
}
MessageTypes.isEndSessionType(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.END_SESSION)
@ -176,6 +176,12 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
MessageTypes.isPaymentsRequestToActivate(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST)
}
MessageTypes.isUnsupportedMessageType(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE)
}
MessageTypes.isReportedSpam(record.type) -> {
builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.REPORTED_SPAM)
}
MessageTypes.isExpirationTimerUpdate(record.type) -> {
builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn.toInt()))
builder.expiresInMs = 0
@ -265,7 +271,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0
revisions = emptyList()
sms = record.type.isSmsType()
if (MessageTypes.isCallLog(record.type)) {
if (record.type.isDirectionlessType()) {
directionless = ChatItem.DirectionlessMessageDetails()
} else if (MessageTypes.isOutgoingMessageType(record.type)) {
outgoing = ChatItem.OutgoingMessageDetails(
@ -999,6 +1005,27 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize:
return MessageTypes.isOutgoingMessageType(this) || MessageTypes.isInboxType(this)
}
private fun Long.isDirectionlessType(): Boolean {
return MessageTypes.isCallLog(this) ||
MessageTypes.isExpirationTimerUpdate(this) ||
MessageTypes.isThreadMergeType(this) ||
MessageTypes.isSessionSwitchoverType(this) ||
MessageTypes.isProfileChange(this) ||
MessageTypes.isJoinedType(this) ||
MessageTypes.isIdentityUpdate(this) ||
MessageTypes.isIdentityVerified(this) ||
MessageTypes.isIdentityDefault(this) ||
MessageTypes.isReleaseChannelDonationRequest(this) ||
MessageTypes.isChangeNumber(this) ||
MessageTypes.isEndSessionType(this) ||
MessageTypes.isChatSessionRefresh(this) ||
MessageTypes.isBadDecryptType(this) ||
MessageTypes.isPaymentsActivated(this) ||
MessageTypes.isPaymentsRequestToActivate(this) ||
MessageTypes.isUnsupportedMessageType(this) ||
MessageTypes.isReportedSpam(this)
}
private fun String.e164ToLong(): Long? {
val fixed = if (this.startsWith("+")) {
this.substring(1)

View file

@ -624,13 +624,14 @@ class ChatItemImportInserter(
SimpleChatUpdate.Type.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT or typeWithoutBase
SimpleChatUpdate.Type.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT or typeWithoutBase
SimpleChatUpdate.Type.CHANGE_NUMBER -> MessageTypes.CHANGE_NUMBER_TYPE
SimpleChatUpdate.Type.BOOST_REQUEST -> MessageTypes.BOOST_REQUEST_TYPE
SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST -> MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE
SimpleChatUpdate.Type.END_SESSION -> MessageTypes.END_SESSION_BIT or typeWithoutBase
SimpleChatUpdate.Type.CHAT_SESSION_REFRESH -> MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT or typeWithoutBase
SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE or typeWithoutBase
SimpleChatUpdate.Type.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED or typeWithoutBase
SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST or typeWithoutBase
SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE -> MessageTypes.UNSUPPORTED_MESSAGE_TYPE or typeWithoutBase
SimpleChatUpdate.Type.REPORTED_SPAM -> MessageTypes.SPECIAL_TYPE_REPORTED_SPAM or typeWithoutBase
}
}
updateMessage.expirationTimerChange != null -> {

View file

@ -6,14 +6,15 @@
package org.thoughtcrime.securesms.backup.v2.database
import okio.ByteString.Companion.toByteString
import org.signal.core.util.CursorUtil
import org.signal.core.util.deleteAll
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireLong
import org.signal.core.util.requireNonNullString
import org.signal.core.util.requireObject
import org.signal.core.util.select
import org.signal.core.util.withinTransaction
import org.thoughtcrime.securesms.backup.v2.BackupState
import org.thoughtcrime.securesms.backup.v2.proto.DistributionList
import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem
@ -36,7 +37,6 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
val records = readableDatabase
.select()
.from(DistributionListTables.ListTable.TABLE_NAME)
.where(DistributionListTables.ListTable.IS_NOT_DELETED)
.run()
.readToList { cursor ->
val id: DistributionListId = DistributionListId.from(cursor.requireLong(DistributionListTables.ListTable.ID))
@ -48,11 +48,11 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
id = id,
name = cursor.requireNonNullString(DistributionListTables.ListTable.NAME),
distributionId = DistributionId.from(cursor.requireNonNullString(DistributionListTables.ListTable.DISTRIBUTION_ID)),
allowsReplies = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.ALLOWS_REPLIES),
allowsReplies = cursor.requireBoolean(DistributionListTables.ListTable.ALLOWS_REPLIES),
rawMembers = getRawMembers(id, privacyMode),
members = getMembers(id),
deletedAtTimestamp = 0L,
isUnknown = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.IS_UNKNOWN),
members = getMembersForBackup(id),
deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP),
isUnknown = cursor.requireBoolean(DistributionListTables.ListTable.IS_UNKNOWN),
privacyMode = privacyMode
)
)
@ -82,7 +82,37 @@ fun DistributionListTables.getAllForBackup(): List<BackupRecipient> {
}
}
fun DistributionListTables.getMembersForBackup(id: DistributionListId): List<RecipientId> {
lateinit var privacyMode: DistributionListPrivacyMode
lateinit var rawMembers: List<RecipientId>
readableDatabase.withinTransaction {
privacyMode = getPrivacyMode(id)
rawMembers = getRawMembers(id, privacyMode)
}
return when (privacyMode) {
DistributionListPrivacyMode.ALL -> emptyList()
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
DistributionListPrivacyMode.ALL_EXCEPT -> rawMembers
}
}
fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, backupState: BackupState): RecipientId? {
if (dlistItem.deletionTimestamp != null && dlistItem.deletionTimestamp > 0) {
val dlistId = createList(
name = "",
members = emptyList(),
distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)),
allowsReplies = false,
deletionTimestamp = dlistItem.deletionTimestamp,
storageId = null,
privacyMode = DistributionListPrivacyMode.ONLY_WITH
)!!
return SignalDatabase.distributionLists.getRecipientId(dlistId)!!
}
val dlist = dlistItem.distributionList ?: return null
val members: List<RecipientId> = dlist.memberRecipientIds
.mapNotNull { backupState.backupToLocalRecipientId[it] }
@ -94,32 +124,22 @@ fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, ba
val distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId))
val privacyMode = dlist.privacyMode.toLocalPrivacyMode()
val dlistId = if (distributionId == DistributionId.MY_STORY) {
setPrivacyMode(DistributionListId.MY_STORY, privacyMode)
members.forEach { addMemberToList(DistributionListId.MY_STORY, privacyMode, it) }
setAllowsReplies(DistributionListId.MY_STORY, dlist.allowReplies)
DistributionListId.MY_STORY
} else {
createList(
name = dlist.name,
members = members,
distributionId = distributionId,
allowsReplies = dlist.allowReplies,
deletionTimestamp = dlistItem.deletionTimestamp ?: 0,
storageId = null,
privacyMode = privacyMode
)!!
}
val dlistId = createList(
name = dlist.name,
members = members,
distributionId = distributionId,
allowsReplies = dlist.allowReplies,
deletionTimestamp = dlistItem.deletionTimestamp ?: 0,
storageId = null,
privacyMode = privacyMode
)!!
return SignalDatabase.distributionLists.getRecipientId(dlistId)!!
}
fun DistributionListTables.clearAllDataForBackupRestore() {
writableDatabase
.deleteAll(DistributionListTables.ListTable.TABLE_NAME)
writableDatabase
.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME)
writableDatabase.deleteAll(DistributionListTables.ListTable.TABLE_NAME)
writableDatabase.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME)
}
private fun DistributionListPrivacyMode.toBackupPrivacyMode(): BackupDistributionList.PrivacyMode {

View file

@ -45,6 +45,7 @@ fun ThreadTable.getThreadsForBackup(): ChatIterator {
${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID}
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)

View file

@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.push.UsernameLinkComponents
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
import org.whispersystems.signalservice.api.util.UuidUtil
import org.whispersystems.signalservice.api.util.toByteArray
import java.util.Currency
object AccountDataProcessor {
@ -48,6 +49,15 @@ object AccountDataProcessor {
familyName = selfRecord.signalProfileName.familyName,
avatarUrlPath = selfRecord.signalProfileAvatar ?: "",
username = selfRecord.username,
usernameLink = if (signalStore.accountValues.usernameLink != null) {
AccountData.UsernameLink(
entropy = signalStore.accountValues.usernameLink?.entropy?.toByteString() ?: EMPTY,
serverId = signalStore.accountValues.usernameLink?.serverId?.toByteArray()?.toByteString() ?: EMPTY,
color = signalStore.miscValues.usernameQrCodeColorScheme.toBackupUsernameColor() ?: AccountData.UsernameLink.Color.BLUE
)
} else {
null
},
accountSettings = AccountData.AccountSettings(
storyViewReceiptsEnabled = signalStore.storyValues.viewedReceiptsEnabled,
typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context),
@ -73,12 +83,6 @@ object AccountDataProcessor {
)
}
private fun InAppPaymentSubscriberRecord.toSubscriberData(manuallyCancelled: Boolean): AccountData.SubscriberData {
val subscriberId = subscriberId.bytes.toByteString()
val currencyCode = currency.currencyCode
return AccountData.SubscriberData(subscriberId = subscriberId, currencyCode = currencyCode, manuallyCancelled = manuallyCancelled)
}
fun import(accountData: AccountData, selfId: RecipientId) {
SignalDatabase.recipients.restoreSelfFromBackup(accountData, selfId)
@ -181,4 +185,23 @@ object AccountDataProcessor {
else -> UsernameQrCodeColorScheme.Blue
}
}
private fun UsernameQrCodeColorScheme.toBackupUsernameColor(): AccountData.UsernameLink.Color {
return when (this) {
UsernameQrCodeColorScheme.Blue -> AccountData.UsernameLink.Color.BLUE
UsernameQrCodeColorScheme.White -> AccountData.UsernameLink.Color.WHITE
UsernameQrCodeColorScheme.Grey -> AccountData.UsernameLink.Color.GREY
UsernameQrCodeColorScheme.Tan -> AccountData.UsernameLink.Color.OLIVE
UsernameQrCodeColorScheme.Green -> AccountData.UsernameLink.Color.GREEN
UsernameQrCodeColorScheme.Orange -> AccountData.UsernameLink.Color.ORANGE
UsernameQrCodeColorScheme.Pink -> AccountData.UsernameLink.Color.PINK
UsernameQrCodeColorScheme.Purple -> AccountData.UsernameLink.Color.PURPLE
}
}
private fun InAppPaymentSubscriberRecord.toSubscriberData(manuallyCancelled: Boolean): AccountData.SubscriberData {
val subscriberId = subscriberId.bytes.toByteString()
val currencyCode = currency.currencyCode
return AccountData.SubscriberData(subscriberId = subscriberId, currencyCode = currencyCode, manuallyCancelled = manuallyCancelled)
}
}

View file

@ -32,6 +32,7 @@ object RecipientBackupProcessor {
val selfId = db.recipientTable.getByAci(signalStore.accountValues.aci!!).get().toLong()
val releaseChannelId = signalStore.releaseChannelValues.releaseChannelRecipientId
if (releaseChannelId != null) {
state.recipientIds.add(releaseChannelId.toLong())
emitter.emit(
Frame(
recipient = BackupRecipient(

View file

@ -57,7 +57,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() {
_state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS)
val plaintext = _state.value.plaintext
disposables += Single.fromCallable { BackupRepository.export(plaintext = plaintext) }
disposables += Single.fromCallable { BackupRepository.debugExport(plaintext = plaintext) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { data ->

View file

@ -186,7 +186,7 @@ public final class ConversationUpdateItem extends FrameLayout
shouldCollapse(messageRecord, nextMessageRecord),
hasWallpaper);
presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isBoostRequest());
presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isReleaseChannelDonationRequest());
updateSelectedState();
}
@ -538,7 +538,7 @@ public final class ConversationUpdateItem extends FrameLayout
eventListener.onBlockJoinRequest(conversationMessage.getMessageRecord().getFromRecipient());
}
});
} else if (conversationMessage.getMessageRecord().isBoostRequest()) {
} else if (conversationMessage.getMessageRecord().isReleaseChannelDonationRequest()) {
actionButton.setVisibility(VISIBLE);
actionButton.setOnClickListener(v -> {
if (batchSelected.isEmpty() && eventListener != null) {

View file

@ -650,7 +650,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_could_not_be_processed), defaultTint);
} else if (MessageTypes.isProfileChange(thread.getType())) {
return emphasisAdded(context, "", defaultTint);
} else if (MessageTypes.isChangeNumber(thread.getType()) || MessageTypes.isBoostRequest(thread.getType())) {
} else if (MessageTypes.isChangeNumber(thread.getType()) || MessageTypes.isReleaseChannelDonationRequest(thread.getType())) {
return emphasisAdded(context, "", defaultTint);
} else if (MessageTypes.isBadDecryptType(thread.getType())) {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_delivery_issue), defaultTint);

View file

@ -411,11 +411,8 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
}
return when (privacyMode) {
DistributionListPrivacyMode.ALL -> {
SignalDatabase.recipients
.getSignalContacts(false)!!
.readToList { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) }
}
DistributionListPrivacyMode.ALL -> emptyList()
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
DistributionListPrivacyMode.ALL_EXCEPT -> {
SignalDatabase.recipients
.getSignalContacts(false)!!
@ -424,7 +421,6 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
mapper = { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) }
)
}
DistributionListPrivacyMode.ONLY_WITH -> rawMembers
}
}
@ -477,7 +473,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
}
}
private fun getPrivacyMode(listId: DistributionListId): DistributionListPrivacyMode {
fun getPrivacyMode(listId: DistributionListId): DistributionListPrivacyMode {
return readableDatabase
.select(ListTable.PRIVACY_MODE)
.from(ListTable.TABLE_NAME)

View file

@ -420,7 +420,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
${MessageTypes.PROFILE_CHANGE_TYPE},
${MessageTypes.GV1_MIGRATION_TYPE},
${MessageTypes.CHANGE_NUMBER_TYPE},
${MessageTypes.BOOST_REQUEST_TYPE},
${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE},
${MessageTypes.SMS_EXPORT_TYPE}
)
ORDER BY $DATE_RECEIVED DESC LIMIT 1
@ -1237,7 +1237,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
DATE_RECEIVED to System.currentTimeMillis(),
DATE_SENT to System.currentTimeMillis(),
READ to 1,
TYPE to MessageTypes.BOOST_REQUEST_TYPE,
TYPE to MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE,
THREAD_ID to threadId,
BODY to null
)
@ -1882,7 +1882,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
$TYPE != ${MessageTypes.PROFILE_CHANGE_TYPE} AND
$TYPE != ${MessageTypes.CHANGE_NUMBER_TYPE} AND
$TYPE != ${MessageTypes.SMS_EXPORT_TYPE} AND
$TYPE != ${MessageTypes.BOOST_REQUEST_TYPE} AND
$TYPE != ${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE} AND
$TYPE & ${MessageTypes.GROUP_V2_LEAVE_BITS} != ${MessageTypes.GROUP_V2_LEAVE_BITS} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_REPORTED_SPAM} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED}

View file

@ -33,24 +33,24 @@ public interface MessageTypes {
// Base Types
long BASE_TYPE_MASK = 0x1F;
long INCOMING_AUDIO_CALL_TYPE = 1;
long OUTGOING_AUDIO_CALL_TYPE = 2;
long MISSED_AUDIO_CALL_TYPE = 3;
long JOINED_TYPE = 4;
long UNSUPPORTED_MESSAGE_TYPE = 5;
long INVALID_MESSAGE_TYPE = 6;
long PROFILE_CHANGE_TYPE = 7;
long MISSED_VIDEO_CALL_TYPE = 8;
long GV1_MIGRATION_TYPE = 9;
long INCOMING_VIDEO_CALL_TYPE = 10;
long OUTGOING_VIDEO_CALL_TYPE = 11;
long GROUP_CALL_TYPE = 12;
long BAD_DECRYPT_TYPE = 13;
long CHANGE_NUMBER_TYPE = 14;
long BOOST_REQUEST_TYPE = 15;
long THREAD_MERGE_TYPE = 16;
long SMS_EXPORT_TYPE = 17;
long SESSION_SWITCHOVER_TYPE = 18;
long INCOMING_AUDIO_CALL_TYPE = 1;
long OUTGOING_AUDIO_CALL_TYPE = 2;
long MISSED_AUDIO_CALL_TYPE = 3;
long JOINED_TYPE = 4;
long UNSUPPORTED_MESSAGE_TYPE = 5;
long INVALID_MESSAGE_TYPE = 6;
long PROFILE_CHANGE_TYPE = 7;
long MISSED_VIDEO_CALL_TYPE = 8;
long GV1_MIGRATION_TYPE = 9;
long INCOMING_VIDEO_CALL_TYPE = 10;
long OUTGOING_VIDEO_CALL_TYPE = 11;
long GROUP_CALL_TYPE = 12;
long BAD_DECRYPT_TYPE = 13;
long CHANGE_NUMBER_TYPE = 14;
long RELEASE_CHANNEL_DONATION_REQUEST_TYPE = 15;
long THREAD_MERGE_TYPE = 16;
long SMS_EXPORT_TYPE = 17;
long SESSION_SWITCHOVER_TYPE = 18;
long BASE_INBOX_TYPE = 20;
long BASE_OUTBOX_TYPE = 21;
@ -59,7 +59,7 @@ public interface MessageTypes {
long BASE_SENT_FAILED_TYPE = 24;
long BASE_PENDING_SECURE_SMS_FALLBACK = 25;
long BASE_PENDING_INSECURE_SMS_FALLBACK = 26;
long BASE_DRAFT_TYPE = 27;
long BASE_DRAFT_TYPE = 27;
long[] OUTGOING_MESSAGE_TYPES = { BASE_OUTBOX_TYPE, BASE_SENT_TYPE,
BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE,
@ -351,8 +351,8 @@ public interface MessageTypes {
return type == CHANGE_NUMBER_TYPE;
}
static boolean isBoostRequest(long type) {
return type == BOOST_REQUEST_TYPE;
static boolean isReleaseChannelDonationRequest(long type) {
return type == RELEASE_CHANNEL_DONATION_REQUEST_TYPE;
}
static boolean isSmsExport(long type) {

View file

@ -1788,10 +1788,6 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
deactivateThread(query = null)
}
private fun SQLiteDatabase.deactivateThread(threadId: Long) {
deactivateThread(SqlUtil.Query("$ID = ?", SqlUtil.buildArgs(threadId)))
}
private fun SQLiteDatabase.deactivateThread(query: SqlUtil.Query?) {
val contentValues = contentValuesOf(
DATE to 0,
@ -1820,13 +1816,13 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
)
if (query != null) {
writableDatabase
this
.update(TABLE_NAME)
.values(contentValues)
.where(query.where, query.whereArgs)
.run()
} else {
writableDatabase
this
.updateAll(TABLE_NAME)
.values(contentValues)
.run()
@ -1979,7 +1975,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return MessageTypes.isProfileChange(type) ||
MessageTypes.isGroupV1MigrationEvent(type) ||
MessageTypes.isChangeNumber(type) ||
MessageTypes.isBoostRequest(type) ||
MessageTypes.isReleaseChannelDonationRequest(type) ||
MessageTypes.isGroupV2LeaveOnly(type) ||
MessageTypes.isThreadMergeType(type)
}

View file

@ -196,8 +196,8 @@ public abstract class DisplayRecord {
return MessageTypes.isChangeNumber(type);
}
public boolean isBoostRequest() {
return MessageTypes.isBoostRequest(type);
public boolean isReleaseChannelDonationRequest() {
return MessageTypes.isReleaseChannelDonationRequest(type);
}
public int getDeliveryStatus() {

View file

@ -232,7 +232,7 @@ public abstract class MessageRecord extends DisplayRecord {
return getProfileChangeDescription(context);
} else if (isChangeNumber()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_changed_their_phone_number, r.getDisplayName(context)), R.drawable.ic_phone_16);
} else if (isBoostRequest()) {
} else if (isReleaseChannelDonationRequest()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_like_this_new_feature_help_support_signal_with_a_one_time_donation), 0);
} else if (isEndSession()) {
if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset), R.drawable.ic_update_info_16);
@ -684,7 +684,7 @@ public abstract class MessageRecord extends DisplayRecord {
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() ||
isChangeNumber() || isReleaseChannelDonationRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() ||
isPaymentsRequestToActivate() || isPaymentsActivated() || isReportedSpam() || isMessageRequestAccepted();
}

View file

@ -740,13 +740,14 @@ message SimpleChatUpdate {
IDENTITY_VERIFIED = 3;
IDENTITY_DEFAULT = 4; // marking as unverified
CHANGE_NUMBER = 5;
BOOST_REQUEST = 6;
RELEASE_CHANNEL_DONATION_REQUEST = 6;
END_SESSION = 7;
CHAT_SESSION_REFRESH = 8;
BAD_DECRYPT = 9;
PAYMENTS_ACTIVATED = 10;
PAYMENT_ACTIVATION_REQUEST = 11;
UNSUPPORTED_PROTOCOL_MESSAGE = 12;
REPORTED_SPAM = 13;
}
Type type = 1;

View file

@ -81,7 +81,7 @@ class SmsDatabaseTest {
TestSms.insert(db, type = MessageTypes.CHANGE_NUMBER_TYPE)
assertFalse(messageTable.hasMeaningfulMessage(1))
TestSms.insert(db, type = MessageTypes.BOOST_REQUEST_TYPE)
TestSms.insert(db, type = MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE)
assertFalse(messageTable.hasMeaningfulMessage(1))
TestSms.insert(db, type = MessageTypes.SMS_EXPORT_TYPE)