Convert SignalService, Database, Group, Payment, and other remaining protos to wire.

This commit is contained in:
Cody Henthorne 2023-09-18 15:32:43 -04:00 committed by Alex Hart
parent a6b7d0bcc5
commit efbd5cab85
267 changed files with 7100 additions and 7214 deletions

View file

@ -3,7 +3,6 @@ import com.android.build.api.dsl.ManagedVirtualDevice
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.protobuf'
id 'androidx.navigation.safeargs'
id 'org.jlleitschuh.gradle.ktlint'
id 'org.jetbrains.kotlin.android'
@ -16,21 +15,6 @@ plugins {
apply from: 'static-ips.gradle'
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.18.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
wire {
kotlin {
javaInterop = true
@ -538,11 +522,8 @@ dependencies {
implementation project(':photoview')
implementation libs.libsignal.android
implementation libs.google.protobuf.javalite
implementation(libs.mobilecoin) {
exclude group: 'com.google.protobuf'
}
implementation libs.mobilecoin
implementation libs.signal.ringrtc

View file

@ -293,22 +293,22 @@ class GroupTableTest {
private fun insertPushGroup(
members: List<DecryptedMember> = listOf(
DecryptedMember.newBuilder()
.setAciBytes(harness.self.requireAci().toByteString())
.setJoinedAtRevision(0)
.setRole(Member.Role.DEFAULT)
DecryptedMember.Builder()
.aciBytes(harness.self.requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build(),
DecryptedMember.newBuilder()
.setAciBytes(Recipient.resolved(harness.others[0]).requireAci().toByteString())
.setJoinedAtRevision(0)
.setRole(Member.Role.DEFAULT)
DecryptedMember.Builder()
.aciBytes(Recipient.resolved(harness.others[0]).requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build()
)
): GroupId {
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val decryptedGroupState = DecryptedGroup.newBuilder()
.addAllMembers(members)
.setRevision(0)
val decryptedGroupState = DecryptedGroup.Builder()
.members(members)
.revision(0)
.build()
return groupTable.create(groupMasterKey, decryptedGroupState)!!
@ -317,23 +317,23 @@ class GroupTableTest {
private fun insertPushGroupWithSelfAndOthers(others: List<RecipientId>): GroupId {
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val selfMember: DecryptedMember = DecryptedMember.newBuilder()
.setAciBytes(harness.self.requireAci().toByteString())
.setJoinedAtRevision(0)
.setRole(Member.Role.DEFAULT)
val selfMember: DecryptedMember = DecryptedMember.Builder()
.aciBytes(harness.self.requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build()
val otherMembers: List<DecryptedMember> = others.map { id ->
DecryptedMember.newBuilder()
.setAciBytes(Recipient.resolved(id).requireAci().toByteString())
.setJoinedAtRevision(0)
.setRole(Member.Role.DEFAULT)
DecryptedMember.Builder()
.aciBytes(Recipient.resolved(id).requireAci().toByteString())
.joinedAtRevision(0)
.role(Member.Role.DEFAULT)
.build()
}
val decryptedGroupState = DecryptedGroup.newBuilder()
.addAllMembers(listOf(selfMember) + otherMembers)
.setRevision(0)
val decryptedGroupState = DecryptedGroup.Builder()
.members(listOf(selfMember) + otherMembers)
.revision(0)
.build()
return groupTable.create(groupMasterKey, decryptedGroupState)!!

View file

@ -48,7 +48,7 @@ class MessageTableTest_gifts {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
@ -62,7 +62,7 @@ class MessageTableTest_gifts {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
mms.setOutgoingGiftsRevealed(listOf(messageId))
@ -76,13 +76,13 @@ class MessageTableTest_gifts {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId))
@ -96,13 +96,13 @@ class MessageTableTest_gifts {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val result = mms.setOutgoingGiftsRevealed(listOf(messageId, messageId2))
@ -115,13 +115,13 @@ class MessageTableTest_gifts {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
MmsHelper.insert(
@ -140,13 +140,13 @@ class MessageTableTest_gifts {
val messageId = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val messageId2 = MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val messageId3 = MmsHelper.insert(
@ -165,13 +165,13 @@ class MessageTableTest_gifts {
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 1,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
MmsHelper.insert(
recipient = Recipient.resolved(recipients[0]),
sentTimeMillis = 2,
giftBadge = GiftBadge.getDefaultInstance()
giftBadge = GiftBadge()
)
val messageId3 = MmsHelper.insert(

View file

@ -1228,7 +1228,7 @@ class RecipientTableTest_getAndPossiblyMerge {
.use { cursor: Cursor ->
if (cursor.moveToFirst()) {
val bytes = Base64.decode(cursor.requireNonNullString(MessageTable.BODY))
ThreadMergeEvent.parseFrom(bytes)
ThreadMergeEvent.ADAPTER.decode(bytes)
} else {
null
}
@ -1246,7 +1246,7 @@ class RecipientTableTest_getAndPossiblyMerge {
.use { cursor: Cursor ->
if (cursor.moveToFirst()) {
val bytes = Base64.decode(cursor.requireNonNullString(MessageTable.BODY))
SessionSwitchoverEvent.parseFrom(bytes)
SessionSwitchoverEvent.ADAPTER.decode(bytes)
} else {
null
}

View file

@ -22,8 +22,9 @@ import org.thoughtcrime.securesms.testing.MessageContentFuzzer
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.MessageTableTestUtils
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.EditMessage
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.EditMessage
import org.whispersystems.signalservice.internal.push.SyncMessage
import kotlin.time.Duration.Companion.seconds
@RunWith(AndroidJUnit4::class)
@ -67,16 +68,17 @@ class EditMessageSyncProcessorTest {
val content = MessageContentFuzzer.fuzzTextMessage()
val metadata = MessageContentFuzzer.envelopeMetadata(harness.self.id, toRecipient.id)
val syncContent = SignalServiceProtos.Content.newBuilder().setSyncMessage(
SignalServiceProtos.SyncMessage.newBuilder().setSent(
SignalServiceProtos.SyncMessage.Sent.newBuilder()
.setDestinationServiceId(metadata.destinationServiceId.toString())
.setTimestamp(originalTimestamp)
.setExpirationStartTimestamp(originalTimestamp)
.setMessage(content.dataMessage)
)
val syncContent = Content.Builder().syncMessage(
SyncMessage.Builder().sent(
SyncMessage.Sent.Builder()
.destinationServiceId(metadata.destinationServiceId.toString())
.timestamp(originalTimestamp)
.expirationStartTimestamp(originalTimestamp)
.message(content.dataMessage)
.build()
).build()
).build()
SignalDatabase.recipients.setExpireMessages(toRecipient.id, content.dataMessage.expireTimer)
SignalDatabase.recipients.setExpireMessages(toRecipient.id, content.dataMessage?.expireTimer ?: 0)
val syncTextMessage = TestMessage(
envelope = MessageContentFuzzer.envelope(originalTimestamp),
content = syncContent,
@ -86,18 +88,20 @@ class EditMessageSyncProcessorTest {
val editTimestamp = originalTimestamp + 200
val editedContent = MessageContentFuzzer.fuzzTextMessage()
val editSyncContent = SignalServiceProtos.Content.newBuilder().setSyncMessage(
SignalServiceProtos.SyncMessage.newBuilder().setSent(
SignalServiceProtos.SyncMessage.Sent.newBuilder()
.setDestinationServiceId(metadata.destinationServiceId.toString())
.setTimestamp(editTimestamp)
.setExpirationStartTimestamp(editTimestamp)
.setEditMessage(
EditMessage.newBuilder()
.setDataMessage(editedContent.dataMessage)
.setTargetSentTimestamp(originalTimestamp)
val editSyncContent = Content.Builder().syncMessage(
SyncMessage.Builder().sent(
SyncMessage.Sent.Builder()
.destinationServiceId(metadata.destinationServiceId.toString())
.timestamp(editTimestamp)
.expirationStartTimestamp(editTimestamp)
.editMessage(
EditMessage.Builder()
.dataMessage(editedContent.dataMessage)
.targetSentTimestamp(originalTimestamp)
.build()
)
)
.build()
).build()
).build()
val syncEditMessage = TestMessage(
@ -109,38 +113,38 @@ class EditMessageSyncProcessorTest {
testResult.runSync(listOf(syncTextMessage, syncEditMessage))
SignalDatabase.recipients.setExpireMessages(toRecipient.id, content.dataMessage.expireTimer / 1000)
SignalDatabase.recipients.setExpireMessages(toRecipient.id, (content.dataMessage?.expireTimer ?: 0) / 1000)
val originalTextMessage = OutgoingMessage(
threadRecipient = toRecipient,
sentTimeMillis = originalTimestamp,
body = content.dataMessage.body,
expiresIn = content.dataMessage.expireTimer.seconds.inWholeMilliseconds,
body = content.dataMessage?.body ?: "",
expiresIn = content.dataMessage?.expireTimer?.seconds?.inWholeMilliseconds ?: 0,
isUrgent = true,
isSecure = true,
bodyRanges = content.dataMessage.bodyRangesList.toBodyRangeList()
bodyRanges = content.dataMessage?.bodyRanges.toBodyRangeList()
)
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(toRecipient)
val originalMessageId = SignalDatabase.messages.insertMessageOutbox(originalTextMessage, threadId, false, null)
SignalDatabase.messages.markAsSent(originalMessageId, true)
if (content.dataMessage.expireTimer > 0) {
if ((content.dataMessage?.expireTimer ?: 0) > 0) {
SignalDatabase.messages.markExpireStarted(originalMessageId, originalTimestamp)
}
val editMessage = OutgoingMessage(
threadRecipient = toRecipient,
sentTimeMillis = editTimestamp,
body = editedContent.dataMessage.body,
expiresIn = content.dataMessage.expireTimer.seconds.inWholeMilliseconds,
body = editedContent.dataMessage?.body ?: "",
expiresIn = content.dataMessage?.expireTimer?.seconds?.inWholeMilliseconds ?: 0,
isUrgent = true,
isSecure = true,
bodyRanges = editedContent.dataMessage.bodyRangesList.toBodyRangeList(),
bodyRanges = editedContent.dataMessage?.bodyRanges.toBodyRangeList(),
messageToEdit = originalMessageId
)
val editMessageId = SignalDatabase.messages.insertMessageOutbox(editMessage, threadId, false, null)
SignalDatabase.messages.markAsSent(editMessageId, true)
if (content.dataMessage.expireTimer > 0) {
if ((content.dataMessage?.expireTimer ?: 0) > 0) {
SignalDatabase.messages.markExpireStarted(editMessageId, originalTimestamp)
}
testResult.collectLocal()
@ -167,7 +171,7 @@ class EditMessageSyncProcessorTest {
fun runSync(messages: List<TestMessage>) {
messages.forEach { (envelope, content, metadata, serverDeliveredTimestamp) ->
if (content.hasSyncMessage()) {
if (content.syncMessage != null) {
processorV2.process(
envelope,
content,

View file

@ -1,13 +1,13 @@
package org.thoughtcrime.securesms.messages
import androidx.test.ext.junit.runners.AndroidJUnit4
import okio.ByteString.Companion.toByteString
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.GroupReceiptTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.toProtoByteString
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.buildWith
import org.thoughtcrime.securesms.testing.GroupTestingUtils
import org.thoughtcrime.securesms.testing.GroupTestingUtils.asMember
@ -15,8 +15,8 @@ import org.thoughtcrime.securesms.testing.MessageContentFuzzer
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.assertIs
import org.thoughtcrime.securesms.util.MessageTableTestUtils
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.GroupContextV2
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
@ -41,9 +41,9 @@ class MessageContentProcessor__recipientStatusTest {
@Test
fun syncGroupSentTextMessageWithRecipientUpdateFollowup() {
val (groupId, masterKey, groupRecipientId) = GroupTestingUtils.insertGroup(revision = 0, harness.self.asMember(), harness.others[0].asMember(), harness.others[1].asMember())
val groupContextV2 = GroupContextV2.newBuilder().setRevision(0).setMasterKey(masterKey.serialize().toProtoByteString()).build()
val groupContextV2 = GroupContextV2.Builder().revision(0).masterKey(masterKey.serialize().toByteString()).build()
val initialTextMessage = DataMessage.newBuilder().buildWith {
val initialTextMessage = DataMessage.Builder().buildWith {
body = MessageContentFuzzer.string()
groupV2 = groupContextV2
timestamp = envelopeTimestamp

View file

@ -6,7 +6,6 @@ import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.junit.After
import org.junit.Before
import org.junit.Ignore
@ -26,7 +25,7 @@ import org.thoughtcrime.securesms.testing.Entry
import org.thoughtcrime.securesms.testing.FakeClientHelpers
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.testing.awaitFor
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.websocket.WebSocketMessage
import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage
import java.util.regex.Pattern
@ -93,7 +92,7 @@ class MessageProcessingPerformanceTest {
val messageCount = 100
val envelopes = generateInboundEnvelopes(bobClient, messageCount)
val firstTimestamp = envelopes.first().timestamp
val lastTimestamp = envelopes.last().timestamp
val lastTimestamp = envelopes.last().timestamp ?: 0
// Inject the envelopes into the websocket
Thread {
@ -190,7 +189,7 @@ class MessageProcessingPerformanceTest {
path = "/api/v1/message",
id = Random(System.currentTimeMillis()).nextLong(),
headers = listOf("X-Signal-Timestamp: ${this.timestamp}"),
body = this.toByteArray().toByteString()
body = this.encodeByteString()
)
).encodeByteString()
}

View file

@ -1,11 +1,12 @@
package org.thoughtcrime.securesms.messages
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.Envelope
data class TestMessage(
val envelope: SignalServiceProtos.Envelope,
val content: SignalServiceProtos.Content,
val envelope: Envelope,
val content: Content,
val metadata: EnvelopeMetadata,
val serverDeliveredTimestamp: Long
)

View file

@ -5,7 +5,8 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.testing.LogPredicate
import org.thoughtcrime.securesms.util.SignalLocalMetrics
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.Envelope
class TimingMessageContentProcessor(context: Context) : MessageContentProcessor(context) {
companion object {
@ -19,9 +20,9 @@ class TimingMessageContentProcessor(context: Context) : MessageContentProcessor(
fun endTag(timestamp: Long) = "$timestamp end"
}
override fun process(envelope: SignalServiceProtos.Envelope, content: SignalServiceProtos.Content, metadata: EnvelopeMetadata, serverDeliveredTimestamp: Long, processingEarlyContent: Boolean, localMetric: SignalLocalMetrics.MessageReceive?) {
Log.d(TAG, startTag(envelope.timestamp))
override fun process(envelope: Envelope, content: Content, metadata: EnvelopeMetadata, serverDeliveredTimestamp: Long, processingEarlyContent: Boolean, localMetric: SignalLocalMetrics.MessageReceive?) {
Log.d(TAG, startTag(envelope.timestamp!!))
super.process(envelope, content, metadata, serverDeliveredTimestamp, processingEarlyContent, localMetric)
Log.d(TAG, endTag(envelope.timestamp))
Log.d(TAG, endTag(envelope.timestamp!!))
}
}

View file

@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testing.FakeClientHelpers.toEnvelope
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
import org.whispersystems.signalservice.internal.push.Envelope
/**
* Welcome to Alice's Client.

View file

@ -31,11 +31,10 @@ import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.Envelope
import java.util.Optional
import java.util.UUID
import java.util.concurrent.locks.ReentrantLock
import kotlin.UnsupportedOperationException
/**
* Welcome to Bob's Client.
@ -61,7 +60,7 @@ class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair:
}
/** Inspired by SignalServiceMessageSender#getEncryptedMessage */
fun encrypt(now: Long): SignalServiceProtos.Envelope {
fun encrypt(now: Long): Envelope {
val envelopeContent = FakeClientHelpers.encryptedTextMessage(now)
val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, null)
@ -72,10 +71,10 @@ class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair:
}
return cipher.encrypt(getAliceProtocolAddress(), getAliceUnidentifiedAccess(), envelopeContent)
.toEnvelope(envelopeContent.content.get().dataMessage.timestamp, getAliceServiceId())
.toEnvelope(envelopeContent.content.get().dataMessage!!.timestamp!!, getAliceServiceId())
}
fun decrypt(envelope: SignalServiceProtos.Envelope, serverDeliveredTimestamp: Long) {
fun decrypt(envelope: Envelope, serverDeliveredTimestamp: Long) {
val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, UnidentifiedAccessUtil.getCertificateValidator())
cipher.decrypt(envelope, serverDeliveredTimestamp)
}

View file

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.testing
import okio.ByteString.Companion.toByteString
import org.signal.libsignal.internal.Native
import org.signal.libsignal.internal.NativeHandleGuard
import org.signal.libsignal.metadata.certificate.CertificateValidator
@ -9,15 +10,16 @@ import org.signal.libsignal.protocol.ecc.Curve
import org.signal.libsignal.protocol.ecc.ECKeyPair
import org.signal.libsignal.protocol.ecc.ECPublicKey
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.thoughtcrime.securesms.database.model.toProtoByteString
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.buildWith
import org.whispersystems.signalservice.api.crypto.ContentHint
import org.whispersystems.signalservice.api.crypto.EnvelopeContent
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
import org.whispersystems.util.Base64
import java.util.Optional
import java.util.UUID
@ -52,9 +54,9 @@ object FakeClientHelpers {
}
fun encryptedTextMessage(now: Long, message: String = "Test body message"): EnvelopeContent {
val content = SignalServiceProtos.Content.newBuilder().apply {
setDataMessage(
SignalServiceProtos.DataMessage.newBuilder().apply {
val content = Content.Builder().apply {
dataMessage(
DataMessage.Builder().buildWith {
body = message
timestamp = now
}
@ -64,16 +66,16 @@ object FakeClientHelpers {
}
fun OutgoingPushMessage.toEnvelope(timestamp: Long, destination: ServiceId): Envelope {
return Envelope.newBuilder()
.setType(Envelope.Type.valueOf(this.type))
.setSourceDevice(1)
.setTimestamp(timestamp)
.setServerTimestamp(timestamp + 1)
.setDestinationServiceId(destination.toString())
.setServerGuid(UUID.randomUUID().toString())
.setContent(Base64.decode(this.content).toProtoByteString())
.setUrgent(true)
.setStory(false)
return Envelope.Builder()
.type(Envelope.Type.fromValue(this.type))
.sourceDevice(1)
.timestamp(timestamp)
.serverTimestamp(timestamp + 1)
.destinationServiceId(destination.toString())
.serverGuid(UUID.randomUUID().toString())
.content(Base64.decode(this.content).toByteString())
.urgent(true)
.story(false)
.build()
}
}

View file

@ -16,19 +16,19 @@ import kotlin.random.Random
*/
object GroupTestingUtils {
fun member(aci: ACI, revision: Int = 0, role: Member.Role = Member.Role.ADMINISTRATOR): DecryptedMember {
return DecryptedMember.newBuilder()
.setAciBytes(aci.toByteString())
.setJoinedAtRevision(revision)
.setRole(role)
return DecryptedMember.Builder()
.aciBytes(aci.toByteString())
.joinedAtRevision(revision)
.role(role)
.build()
}
fun insertGroup(revision: Int = 0, vararg members: DecryptedMember): TestGroupInfo {
val groupMasterKey = GroupMasterKey(Random.nextBytes(GroupMasterKey.SIZE))
val decryptedGroupState = DecryptedGroup.newBuilder()
.addAllMembers(members.toList())
.setRevision(revision)
.setTitle(MessageContentFuzzer.string())
val decryptedGroupState = DecryptedGroup.Builder()
.members(members.toList())
.revision(revision)
.title(MessageContentFuzzer.string())
.build()
val groupId = SignalDatabase.groups.create(groupMasterKey, decryptedGroupState)!!

View file

@ -1,21 +1,20 @@
package org.thoughtcrime.securesms.testing
import com.google.protobuf.ByteString
import org.thoughtcrime.securesms.database.model.toProtoByteString
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.buildWith
import org.thoughtcrime.securesms.messages.TestMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.api.util.UuidUtil
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage
import org.whispersystems.signalservice.internal.push.AttachmentPointer
import org.whispersystems.signalservice.internal.push.BodyRange
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.push.GroupContextV2
import org.whispersystems.signalservice.internal.push.SyncMessage
import java.util.UUID
import kotlin.random.Random
import kotlin.random.nextInt
@ -35,10 +34,10 @@ object MessageContentFuzzer {
* Create an [Envelope].
*/
fun envelope(timestamp: Long): Envelope {
return Envelope.newBuilder()
.setTimestamp(timestamp)
.setServerTimestamp(timestamp + 5)
.setServerGuidBytes(UuidUtil.toByteString(UUID.randomUUID()))
return Envelope.Builder()
.timestamp(timestamp)
.serverTimestamp(timestamp + 5)
.serverGuid(UUID.randomUUID().toString())
.build()
}
@ -62,20 +61,22 @@ object MessageContentFuzzer {
* - Bold style body ranges
*/
fun fuzzTextMessage(groupContextV2: GroupContextV2? = null): Content {
return Content.newBuilder()
.setDataMessage(
DataMessage.newBuilder().buildWith {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
body = string()
if (random.nextBoolean()) {
expireTimer = random.nextInt(0..28.days.inWholeSeconds.toInt())
}
if (random.nextBoolean()) {
addBodyRanges(
SignalServiceProtos.BodyRange.newBuilder().buildWith {
start = 0
length = 1
style = SignalServiceProtos.BodyRange.Style.BOLD
}
bodyRanges(
listOf(
BodyRange.Builder().buildWith {
start = 0
length = 1
style = BodyRange.Style.BOLD
}
)
)
}
if (groupContextV2 != null) {
@ -95,16 +96,16 @@ object MessageContentFuzzer {
recipientUpdate: Boolean = false
): Content {
return Content
.newBuilder()
.setSyncMessage(
SyncMessage.newBuilder().buildWith {
sent = SyncMessage.Sent.newBuilder().buildWith {
.Builder()
.syncMessage(
SyncMessage.Builder().buildWith {
sent = SyncMessage.Sent.Builder().buildWith {
timestamp = textMessage.timestamp
message = textMessage
isRecipientUpdate = recipientUpdate
addAllUnidentifiedStatus(
unidentifiedStatus(
deliveredTo.map {
SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder().buildWith {
SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder().buildWith {
destinationServiceId = Recipient.resolved(it).requireServiceId().toString()
unidentified = true
}
@ -123,9 +124,9 @@ object MessageContentFuzzer {
* - A message with 0-2 attachment pointers and may contain a text body
*/
fun fuzzMediaMessageWithBody(quoteAble: List<TestMessage> = emptyList()): Content {
return Content.newBuilder()
.setDataMessage(
DataMessage.newBuilder().buildWith {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
if (random.nextBoolean()) {
body = string()
}
@ -133,28 +134,28 @@ object MessageContentFuzzer {
if (random.nextBoolean() && quoteAble.isNotEmpty()) {
body = string()
val quoted = quoteAble.random(random)
quote = DataMessage.Quote.newBuilder().buildWith {
quote = DataMessage.Quote.Builder().buildWith {
id = quoted.envelope.timestamp
authorAci = quoted.metadata.sourceServiceId.toString()
text = quoted.content.dataMessage.body
addAllAttachments(quoted.content.dataMessage.attachmentsList)
addAllBodyRanges(quoted.content.dataMessage.bodyRangesList)
text = quoted.content.dataMessage?.body
attachments(quoted.content.dataMessage?.attachments ?: emptyList())
bodyRanges(quoted.content.dataMessage?.bodyRanges ?: emptyList())
type = DataMessage.Quote.Type.NORMAL
}
}
if (random.nextFloat() < 0.1 && quoteAble.isNotEmpty()) {
val quoted = quoteAble.random(random)
quote = DataMessage.Quote.newBuilder().buildWith {
id = random.nextLong(quoted.envelope.timestamp - 1000000, quoted.envelope.timestamp)
quote = DataMessage.Quote.Builder().buildWith {
id = random.nextLong(quoted.envelope.timestamp!! - 1000000, quoted.envelope.timestamp!!)
authorAci = quoted.metadata.sourceServiceId.toString()
text = quoted.content.dataMessage.body
text = quoted.content.dataMessage?.body
}
}
if (random.nextFloat() < 0.25) {
val total = random.nextInt(1, 2)
(0..total).forEach { _ -> addAttachments(attachmentPointer()) }
attachments((0..total).map { attachmentPointer() })
}
}
)
@ -166,12 +167,12 @@ object MessageContentFuzzer {
* - A reaction to a prior message
*/
fun fuzzMediaMessageNoContent(previousMessages: List<TestMessage> = emptyList()): Content {
return Content.newBuilder()
.setDataMessage(
DataMessage.newBuilder().buildWith {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
if (random.nextFloat() < 0.25) {
val reactTo = previousMessages.random(random)
reaction = DataMessage.Reaction.newBuilder().buildWith {
reaction = DataMessage.Reaction.Builder().buildWith {
emoji = emojis.random(random)
remove = false
targetAuthorAci = reactTo.metadata.sourceServiceId.toString()
@ -187,15 +188,15 @@ object MessageContentFuzzer {
* - A sticker
*/
fun fuzzMediaMessageNoText(previousMessages: List<TestMessage> = emptyList()): Content {
return Content.newBuilder()
.setDataMessage(
DataMessage.newBuilder().buildWith {
return Content.Builder()
.dataMessage(
DataMessage.Builder().buildWith {
if (random.nextFloat() < 0.9) {
sticker = DataMessage.Sticker.newBuilder().buildWith {
sticker = DataMessage.Sticker.Builder().buildWith {
packId = byteString(length = 24)
packKey = byteString(length = 128)
stickerId = random.nextInt()
data = attachmentPointer()
data_ = attachmentPointer()
emoji = emojis.random(random)
}
}
@ -223,14 +224,14 @@ object MessageContentFuzzer {
* Generate a random [ByteString].
*/
fun byteString(length: Int = 512): ByteString {
return random.nextBytes(length).toProtoByteString()
return random.nextBytes(length).toByteString()
}
/**
* Generate a random [AttachmentPointer].
*/
fun attachmentPointer(): AttachmentPointer {
return AttachmentPointer.newBuilder().run {
return AttachmentPointer.Builder().run {
cdnKey = string()
contentType = mediaTypes.random(random)
key = byteString()

View file

@ -1,70 +0,0 @@
package org.thoughtcrime.securesms.testing
import com.google.protobuf.ByteString
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2
import org.whispersystems.signalservice.internal.serialize.protos.AddressProto
import org.whispersystems.signalservice.internal.serialize.protos.MetadataProto
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto
import java.util.UUID
import kotlin.random.Random
class TestProtos private constructor() {
fun address(
uuid: UUID = UUID.randomUUID()
): AddressProto.Builder {
return AddressProto.newBuilder()
.setUuid(ACI.from(uuid).toByteString())
}
fun metadata(
address: AddressProto = address().build()
): MetadataProto.Builder {
return MetadataProto.newBuilder()
.setAddress(address)
}
fun groupContextV2(
revision: Int = 0,
masterKeyBytes: ByteArray = Random.Default.nextBytes(GroupMasterKey.SIZE)
): GroupContextV2.Builder {
return GroupContextV2.newBuilder()
.setRevision(revision)
.setMasterKey(ByteString.copyFrom(masterKeyBytes))
}
fun storyContext(
sentTimestamp: Long = Random.nextLong(),
authorUuid: String = UUID.randomUUID().toString()
): DataMessage.StoryContext.Builder {
return DataMessage.StoryContext.newBuilder()
.setAuthorAci(authorUuid)
.setSentTimestamp(sentTimestamp)
}
fun dataMessage(): DataMessage.Builder {
return DataMessage.newBuilder()
}
fun content(): SignalServiceProtos.Content.Builder {
return SignalServiceProtos.Content.newBuilder()
}
fun serviceContent(
localAddress: AddressProto = address().build(),
metadata: MetadataProto = metadata().build()
): SignalServiceContentProto.Builder {
return SignalServiceContentProto.newBuilder()
.setLocalAddress(localAddress)
.setMetadata(metadata)
}
companion object {
fun <T> build(buildFn: TestProtos.() -> T): T {
return TestProtos().buildFn()
}
}
}

View file

@ -1,10 +1,10 @@
package org.thoughtcrime.securesms.absbackup.backupables
import com.google.protobuf.InvalidProtocolBufferException
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.absbackup.AndroidBackupItem
import org.thoughtcrime.securesms.absbackup.protos.SvrAuthToken
import org.thoughtcrime.securesms.keyvalue.SignalStore
import java.io.IOException
/**
* This backs up the not-secret KBS Auth tokens, which can be combined with a PIN to prove ownership of a phone number in order to complete the registration process.
@ -30,7 +30,7 @@ object SvrAuthTokens : AndroidBackupItem {
val proto = SvrAuthToken.ADAPTER.decode(data)
SignalStore.svr().putAuthTokenList(proto.tokens)
} catch (e: InvalidProtocolBufferException) {
} catch (e: IOException) {
Log.w(TAG, "Cannot restore KbsAuthToken from backup service.")
}
}

View file

@ -13,7 +13,7 @@ import org.whispersystems.signalservice.api.InvalidMessageStructureException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.util.AttachmentPointerUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.DataMessage;
import java.util.LinkedList;
import java.util.List;
@ -152,18 +152,18 @@ public class PointerAttachment extends Attachment {
null));
}
public static Optional<Attachment> forPointer(SignalServiceProtos.DataMessage.Quote.QuotedAttachment quotedAttachment) {
public static Optional<Attachment> forPointer(DataMessage.Quote.QuotedAttachment quotedAttachment) {
SignalServiceAttachment thumbnail;
try {
thumbnail = quotedAttachment.hasThumbnail() ? AttachmentPointerUtil.createSignalAttachmentPointer(quotedAttachment.getThumbnail()) : null;
thumbnail = quotedAttachment.thumbnail != null ? AttachmentPointerUtil.createSignalAttachmentPointer(quotedAttachment.thumbnail) : null;
} catch (InvalidMessageStructureException e) {
return Optional.empty();
}
return Optional.of(new PointerAttachment(quotedAttachment.getContentType(),
return Optional.of(new PointerAttachment(quotedAttachment.contentType,
AttachmentTable.TRANSFER_PROGRESS_PENDING,
thumbnail != null ? thumbnail.asPointer().getSize().orElse(0) : 0,
quotedAttachment.getFileName(),
quotedAttachment.fileName,
thumbnail != null ? thumbnail.asPointer().getCdnNumber() : 0,
thumbnail != null ? thumbnail.asPointer().getRemoteId().toString() : "0",
thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null,

View file

@ -2,19 +2,19 @@ package org.thoughtcrime.securesms.audio;
import androidx.annotation.NonNull;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormData;
import java.util.concurrent.TimeUnit;
import okio.ByteString;
public class AudioFileInfo {
private final long durationUs;
private final byte[] waveFormBytes;
private final float[] waveForm;
public static @NonNull AudioFileInfo fromDatabaseProtobuf(@NonNull AudioWaveFormData audioWaveForm) {
return new AudioFileInfo(audioWaveForm.getDurationUs(), audioWaveForm.getWaveForm().toByteArray());
return new AudioFileInfo(audioWaveForm.durationUs, audioWaveForm.waveForm.toByteArray());
}
AudioFileInfo(long durationUs, byte[] waveFormBytes) {
@ -37,9 +37,9 @@ public class AudioFileInfo {
}
public @NonNull AudioWaveFormData toDatabaseProtobuf() {
return AudioWaveFormData.newBuilder()
.setDurationUs(durationUs)
.setWaveForm(ByteString.copyFrom(waveFormBytes))
.build();
return new AudioWaveFormData.Builder()
.durationUs(durationUs)
.waveForm(ByteString.of(waveFormBytes))
.build();
}
}

View file

@ -22,13 +22,13 @@ public final class AudioHash {
}
public AudioHash(@NonNull AudioWaveFormData audioWaveForm) {
this(Base64.encodeBytes(audioWaveForm.toByteArray()), audioWaveForm);
this(Base64.encodeBytes(audioWaveForm.encode()), audioWaveForm);
}
public static @Nullable AudioHash parseOrNull(@Nullable String hash) {
if (hash == null) return null;
try {
return new AudioHash(hash, AudioWaveFormData.parseFrom(Base64.decode(hash)));
return new AudioHash(hash, AudioWaveFormData.ADAPTER.decode(Base64.decode(hash)));
} catch (IOException e) {
return null;
}

View file

@ -107,7 +107,7 @@ object AudioWaveForms {
private fun generateWaveForm(context: Context, uri: Uri, cacheKey: String, attachmentId: AttachmentId): CacheCheckResult {
try {
val startTime = System.currentTimeMillis()
SignalDatabase.attachments.writeAudioHash(attachmentId, AudioWaveFormData.getDefaultInstance())
SignalDatabase.attachments.writeAudioHash(attachmentId, AudioWaveFormData())
Log.i(TAG, "Starting wave form generation ($cacheKey)")
val fileInfo: AudioFileInfo = AudioWaveFormGenerator.generateWaveForm(context, uri)

View file

@ -101,16 +101,16 @@ object Badges {
@JvmStatic
fun toDatabaseBadge(badge: Badge): BadgeList.Badge {
return BadgeList.Badge.newBuilder()
.setId(badge.id)
.setCategory(badge.category.code)
.setDescription(badge.description)
.setExpiration(badge.expirationTimestamp)
.setVisible(badge.visible)
.setName(badge.name)
.setImageUrl(badge.imageUrl.toString())
.setImageDensity(badge.imageDensity)
.build()
return BadgeList.Badge(
id = badge.id,
category = badge.category.code,
description = badge.description,
expiration = badge.expirationTimestamp,
visible = badge.visible,
name = badge.name,
imageUrl = badge.imageUrl.toString(),
imageDensity = badge.imageDensity
)
}
@JvmStatic

View file

@ -79,12 +79,11 @@ class GiftMessageView @JvmOverloads constructor(
}
actionView.setText(
when (giftBadge.redemptionState ?: GiftBadge.RedemptionState.UNRECOGNIZED) {
when (giftBadge.redemptionState) {
GiftBadge.RedemptionState.PENDING -> R.string.GiftMessageView__redeem
GiftBadge.RedemptionState.STARTED -> R.string.GiftMessageView__redeeming
GiftBadge.RedemptionState.REDEEMED -> R.string.GiftMessageView__redeemed
GiftBadge.RedemptionState.FAILED -> R.string.GiftMessageView__redeem
GiftBadge.RedemptionState.UNRECOGNIZED -> R.string.GiftMessageView__redeem
}
)
}

View file

@ -32,7 +32,7 @@ object Gifts {
): OutgoingMessage {
return OutgoingMessage(
threadRecipient = recipient,
body = Base64.encodeBytes(giftBadge.toByteArray()),
body = Base64.encodeBytes(giftBadge.encode()),
isSecure = true,
sentTimeMillis = sentTimestamp,
expiresIn = expiresIn,

View file

@ -63,7 +63,7 @@ class ViewReceivedGiftBottomSheet : DSLSettingsBottomSheetFragment() {
ViewReceivedGiftBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(ARG_SENT_FROM, messageRecord.fromRecipient.id)
putByteArray(ARG_GIFT_BADGE, messageRecord.giftBadge!!.toByteArray())
putByteArray(ARG_GIFT_BADGE, messageRecord.giftBadge!!.encode())
putLong(ARG_MESSAGE_ID, messageRecord.id)
}
show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)

View file

@ -34,7 +34,7 @@ class ViewSentGiftBottomSheet : DSLSettingsBottomSheetFragment() {
ViewSentGiftBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(ARG_SENT_TO, messageRecord.toRecipient.id)
putByteArray(ARG_GIFT_BADGE, messageRecord.giftBadge!!.toByteArray())
putByteArray(ARG_GIFT_BADGE, messageRecord.giftBadge!!.encode())
}
show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
}
@ -45,7 +45,7 @@ class ViewSentGiftBottomSheet : DSLSettingsBottomSheetFragment() {
get() = requireArguments().getParcelableCompat(ARG_SENT_TO, RecipientId::class.java)!!
private val giftBadge: GiftBadge
get() = GiftBadge.parseFrom(requireArguments().getByteArray(ARG_GIFT_BADGE))
get() = GiftBadge.ADAPTER.decode(requireArguments().getByteArray(ARG_GIFT_BADGE)!!)
private val lifecycleDisposable = LifecycleDisposable()

View file

@ -93,11 +93,11 @@ sealed class CallLogRow {
return FULL
}
if (groupCallUpdateDetails.inCallUuidsList.contains(Recipient.self().requireAci().rawUuid.toString())) {
if (groupCallUpdateDetails.inCallUuids.contains(Recipient.self().requireAci().rawUuid.toString())) {
return LOCAL_USER_JOINED
}
return if (groupCallUpdateDetails.inCallUuidsCount > 0) {
return if (groupCallUpdateDetails.inCallUuids.isNotEmpty()) {
ACTIVE
} else {
NONE

View file

@ -4,6 +4,7 @@ import androidx.annotation.WorkerThread
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import okio.ByteString.Companion.toByteString
import org.signal.core.util.logging.Log
import org.signal.libsignal.protocol.IdentityKeyPair
import org.signal.libsignal.protocol.SignalProtocolAddress
@ -17,7 +18,6 @@ import org.thoughtcrime.securesms.crypto.PreKeyUtil
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.databaseprotos.PendingChangeNumberMetadata
import org.thoughtcrime.securesms.database.model.toProtoByteString
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
import org.thoughtcrime.securesms.keyvalue.CertificateType
@ -42,7 +42,7 @@ import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage
import org.whispersystems.signalservice.internal.push.SyncMessage
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
import org.whispersystems.signalservice.internal.push.WhoAmIResponse
import org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevicesException
@ -367,13 +367,13 @@ class ChangeNumberRepository(
// Device Messages
if (deviceId != primaryDeviceId) {
val pniChangeNumber = SyncMessage.PniChangeNumber.newBuilder()
.setIdentityKeyPair(pniIdentity.serialize().toProtoByteString())
.setSignedPreKey(signedPreKeyRecord.serialize().toProtoByteString())
.setLastResortKyberPreKey(lastResortKyberPreKeyRecord.serialize().toProtoByteString())
.setRegistrationId(pniRegistrationId)
.setNewE164(newE164)
.build()
val pniChangeNumber = SyncMessage.PniChangeNumber(
identityKeyPair = pniIdentity.serialize().toByteString(),
signedPreKey = signedPreKeyRecord.serialize().toByteString(),
lastResortKyberPreKey = lastResortKyberPreKeyRecord.serialize().toByteString(),
registrationId = pniRegistrationId,
newE164 = newE164
)
deviceMessages += messageSender.getEncryptedSyncPniInitializeDeviceMessage(deviceId, pniChangeNumber)
}
@ -391,12 +391,12 @@ class ChangeNumberRepository(
pniRegistrationIds.mapKeys { it.key.toString() }
)
val metadata = PendingChangeNumberMetadata.newBuilder()
.setPreviousPni(SignalStore.account().pni!!.toByteString())
.setPniIdentityKeyPair(pniIdentity.serialize().toProtoByteString())
.setPniRegistrationId(pniRegistrationIds[primaryDeviceId]!!)
.setPniSignedPreKeyId(devicePniSignedPreKeys[primaryDeviceId]!!.keyId)
.build()
val metadata = PendingChangeNumberMetadata(
previousPni = SignalStore.account().pni!!.toByteString(),
pniIdentityKeyPair = pniIdentity.serialize().toByteString(),
pniRegistrationId = pniRegistrationIds[primaryDeviceId]!!,
pniSignedPreKeyId = devicePniSignedPreKeys[primaryDeviceId]!!.keyId
)
return ChangeNumberRequestData(request, metadata)
}

View file

@ -32,7 +32,7 @@ class InternalSettingsRepository(context: Context) {
val title = "Release Note Title"
val bodyText = "Release note body. Aren't I awesome?"
val body = "$title\n\n$bodyText"
val bodyRangeList = BodyRangeList.newBuilder()
val bodyRangeList = BodyRangeList.Builder()
.addStyle(BodyRangeList.BodyRange.Style.BOLD, 0, title.length)
val recipientId = SignalStore.releaseChannelValues().releaseChannelRecipientId!!

View file

@ -10,7 +10,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
import org.thoughtcrime.securesms.database.CallTable
import org.thoughtcrime.securesms.database.MediaTable
@ -152,9 +151,9 @@ class ConversationSettingsRepository(
consumer(
if (groupRecord.isV2Group) {
val decryptedGroup: DecryptedGroup = groupRecord.requireV2GroupProperties().decryptedGroup
val pendingMembers: List<RecipientId> = decryptedGroup.pendingMembersList
.map(DecryptedPendingMember::getServiceIdBytes)
.map(GroupProtoUtil::serviceIdBinaryToRecipientId)
val pendingMembers: List<RecipientId> = decryptedGroup.pendingMembers
.map { m -> m.serviceIdBytes }
.map { s -> GroupProtoUtil.serviceIdBinaryToRecipientId(s) }
val members = mutableListOf<RecipientId>()

View file

@ -9,7 +9,7 @@ import org.whispersystems.signalservice.api.InvalidMessageStructureException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.util.AttachmentPointerUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.DataMessage;
import java.util.ArrayList;
import java.util.LinkedList;
@ -125,47 +125,47 @@ public class ContactModelMapper {
return new Contact(name, sharedContact.getOrganization().orElse(null), phoneNumbers, emails, postalAddresses, avatar);
}
public static Contact remoteToLocal(@NonNull SignalServiceProtos.DataMessage.Contact contact) {
Name name = new Name(contact.getName().getDisplayName(),
contact.getName().getGivenName(),
contact.getName().getFamilyName(),
contact.getName().getPrefix(),
contact.getName().getSuffix(),
contact.getName().getMiddleName());
public static Contact remoteToLocal(@NonNull DataMessage.Contact contact) {
Name name = new Name(contact.name.displayName,
contact.name.givenName,
contact.name.familyName,
contact.name.prefix,
contact.name.suffix,
contact.name.middleName);
List<Phone> phoneNumbers = new ArrayList<>(contact.getNumberCount());
for (SignalServiceProtos.DataMessage.Contact.Phone phone : contact.getNumberList()) {
phoneNumbers.add(new Phone(phone.getValue(),
remoteToLocalType(phone.getType()),
phone.getLabel()));
List<Phone> phoneNumbers = new ArrayList<>(contact.number.size());
for (DataMessage.Contact.Phone phone : contact.number) {
phoneNumbers.add(new Phone(phone.value_,
remoteToLocalType(phone.type),
phone.label));
}
List<Email> emails = new ArrayList<>(contact.getEmailCount());
for (SignalServiceProtos.DataMessage.Contact.Email email : contact.getEmailList()) {
emails.add(new Email(email.getValue(),
remoteToLocalType(email.getType()),
email.getLabel()));
List<Email> emails = new ArrayList<>(contact.email.size());
for (DataMessage.Contact.Email email : contact.email) {
emails.add(new Email(email.value_,
remoteToLocalType(email.type),
email.label));
}
List<PostalAddress> postalAddresses = new ArrayList<>(contact.getAddressCount());
for (SignalServiceProtos.DataMessage.Contact.PostalAddress postalAddress : contact.getAddressList()) {
postalAddresses.add(new PostalAddress(remoteToLocalType(postalAddress.getType()),
postalAddress.getLabel(),
postalAddress.getStreet(),
postalAddress.getPobox(),
postalAddress.getNeighborhood(),
postalAddress.getCity(),
postalAddress.getRegion(),
postalAddress.getPostcode(),
postalAddress.getCountry()));
List<PostalAddress> postalAddresses = new ArrayList<>(contact.address.size());
for (DataMessage.Contact.PostalAddress postalAddress : contact.address) {
postalAddresses.add(new PostalAddress(remoteToLocalType(postalAddress.type),
postalAddress.label,
postalAddress.street,
postalAddress.pobox,
postalAddress.neighborhood,
postalAddress.city,
postalAddress.region,
postalAddress.postcode,
postalAddress.country));
}
Avatar avatar = null;
if (contact.hasAvatar()) {
if (contact.avatar != null) {
try {
SignalServiceAttachmentPointer attachmentPointer = AttachmentPointerUtil.createSignalAttachmentPointer(contact.getAvatar().getAvatar());
SignalServiceAttachmentPointer attachmentPointer = AttachmentPointerUtil.createSignalAttachmentPointer(contact.avatar.avatar);
Attachment attachment = PointerAttachment.forPointer(Optional.of(attachmentPointer.asPointer())).get();
boolean isProfile = contact.getAvatar().getIsProfile();
boolean isProfile = contact.avatar.isProfile;
avatar = new Avatar(null, attachment, isProfile);
} catch (InvalidMessageStructureException e) {
@ -173,7 +173,7 @@ public class ContactModelMapper {
}
}
return new Contact(name, contact.getOrganization(), phoneNumbers, emails, postalAddresses, avatar);
return new Contact(name, contact.organization, phoneNumbers, emails, postalAddresses, avatar);
}
private static Phone.Type remoteToLocalType(SharedContact.Phone.Type type) {
@ -185,7 +185,7 @@ public class ContactModelMapper {
}
}
private static Phone.Type remoteToLocalType(SignalServiceProtos.DataMessage.Contact.Phone.Type type) {
private static Phone.Type remoteToLocalType(DataMessage.Contact.Phone.Type type) {
switch (type) {
case HOME: return Phone.Type.HOME;
case MOBILE: return Phone.Type.MOBILE;
@ -203,7 +203,7 @@ public class ContactModelMapper {
}
}
private static Email.Type remoteToLocalType(SignalServiceProtos.DataMessage.Contact.Email.Type type) {
private static Email.Type remoteToLocalType(DataMessage.Contact.Email.Type type) {
switch (type) {
case HOME: return Email.Type.HOME;
case MOBILE: return Email.Type.MOBILE;
@ -220,7 +220,7 @@ public class ContactModelMapper {
}
}
private static PostalAddress.Type remoteToLocalType(SignalServiceProtos.DataMessage.Contact.PostalAddress.Type type) {
private static PostalAddress.Type remoteToLocalType(DataMessage.Contact.PostalAddress.Type type) {
switch (type) {
case HOME: return PostalAddress.Type.HOME;
case WORK: return PostalAddress.Type.WORK;

View file

@ -1060,10 +1060,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
if (conversationMessage.getBottomButton() != null) {
callToActionStub.get().setVisibility(View.VISIBLE);
callToActionStub.get().setText(conversationMessage.getBottomButton().getLabel());
callToActionStub.get().setText(conversationMessage.getBottomButton().label);
callToActionStub.get().setOnClickListener(v -> {
if (eventListener != null) {
eventListener.onCallToAction(conversationMessage.getBottomButton().getAction());
eventListener.onCallToAction(conversationMessage.getBottomButton().action);
}
});
} else if (callToActionStub.resolved()) {

View file

@ -454,7 +454,7 @@ public final class ConversationUpdateItem extends FrameLayout
if (Util.hasItems(acis)) {
if (acis.contains(SignalStore.account().requireAci())) {
text = R.string.ConversationUpdateItem_return_to_call;
} else if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).getIsCallFull()) {
} else if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).isCallFull) {
text = R.string.ConversationUpdateItem_call_is_full;
} else {
text = R.string.ConversationUpdateItem_join_call;

View file

@ -63,13 +63,13 @@ object MessageStyler {
var bottomButton: BodyRange.Button? = null
messageRanges
.rangesList
.ranges
.filter { r -> r.start >= 0 && r.start < span.length && r.start + r.length >= 0 }
.forEach { range ->
val start = range.start
val end = (range.start + range.length).coerceAtMost(span.length)
if (range.hasStyle()) {
if (range.style != null) {
val styleSpan: Any? = when (range.style) {
BodyRange.Style.BOLD -> boldStyle()
BodyRange.Style.ITALIC -> italicStyle()
@ -90,10 +90,10 @@ object MessageStyler {
span.setSpan(styleSpan, start, end, SPAN_FLAGS)
appliedStyle = true
}
} else if (range.hasLink() && range.link != null) {
} else if (range.link != null) {
span.setSpan(PlaceholderURLSpan(range.link), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
hasLinks = true
} else if (range.hasButton() && range.button != null) {
} else if (range.button != null) {
bottomButton = range.button
}
}
@ -245,7 +245,7 @@ object MessageStyler {
}
if (spanLength > 0 && style != null) {
BodyRange.newBuilder().setStart(spanStart).setLength(spanLength).setStyle(style).build()
BodyRange(start = spanStart, length = spanLength, style = style)
} else {
null
}
@ -255,7 +255,7 @@ object MessageStyler {
}
return if (bodyRanges.isNotEmpty()) {
BodyRangeList.newBuilder().addAllRanges(bodyRanges).build()
BodyRangeList(ranges = bodyRanges)
} else {
null
}

View file

@ -73,20 +73,20 @@ class ChatColors(
}
fun serialize(): ChatColor {
val builder: ChatColor.Builder = ChatColor.newBuilder()
val builder: ChatColor.Builder = ChatColor.Builder()
if (linearGradient != null) {
val gradientBuilder = ChatColor.LinearGradient.newBuilder()
val gradientBuilder = ChatColor.LinearGradient.Builder()
gradientBuilder.rotation = linearGradient.degrees
linearGradient.colors.forEach { gradientBuilder.addColors(it) }
linearGradient.positions.forEach { gradientBuilder.addPositions(it) }
gradientBuilder.colors = linearGradient.colors.toList()
gradientBuilder.positions = linearGradient.positions.toList()
builder.setLinearGradient(gradientBuilder)
builder.linearGradient(gradientBuilder.build())
}
if (singleColor != null) {
builder.setSingleColor(ChatColor.SingleColor.newBuilder().setColor(singleColor))
builder.singleColor(ChatColor.SingleColor.Builder().color(singleColor).build())
}
return builder.build()
@ -142,18 +142,18 @@ class ChatColors(
companion object {
@JvmStatic
fun forChatColor(id: Id, chatColor: ChatColor): ChatColors {
assert(chatColor.hasSingleColor() xor chatColor.hasLinearGradient())
assert((chatColor.singleColor != null) xor (chatColor.linearGradient != null))
return if (chatColor.hasLinearGradient()) {
return if (chatColor.linearGradient != null) {
val linearGradient = LinearGradient(
chatColor.linearGradient.rotation,
chatColor.linearGradient.colorsList.toIntArray(),
chatColor.linearGradient.positionsList.toFloatArray()
chatColor.linearGradient.colors.toIntArray(),
chatColor.linearGradient.positions.toFloatArray()
)
forGradient(id, linearGradient)
} else {
val singleColor = chatColor.singleColor.color
val singleColor = chatColor.singleColor!!.color
forColor(id, singleColor)
}

View file

@ -191,7 +191,7 @@ class DraftRepository(
var updatedText: Spannable? = null
if (textDraft != null && bodyRangesDraft != null) {
val bodyRanges: BodyRangeList = BodyRangeList.parseFrom(Base64.decodeOrThrow(bodyRangesDraft.value))
val bodyRanges: BodyRangeList = BodyRangeList.ADAPTER.decode(Base64.decodeOrThrow(bodyRangesDraft.value))
val mentions: List<Mention> = MentionUtil.bodyRangeListToMentions(bodyRanges)
val updated = MentionUtil.updateBodyAndMentionsWithDisplayNames(context, textDraft.value, mentions)

View file

@ -62,7 +62,7 @@ class DraftViewModel @JvmOverloads constructor(
} else if (mentionRanges == null) {
styleBodyRanges
} else {
styleBodyRanges.toBuilder().addAllRanges(mentionRanges.rangesList).build()
styleBodyRanges.newBuilder().apply { ranges += mentionRanges.ranges }.build()
}
saveDrafts(it.copy(textDraft = text.toTextDraft(), bodyRangesDraft = bodyRanges?.toDraft(), messageEditDraft = Draft(Draft.MESSAGE_EDIT, messageId.serialize())))
@ -84,7 +84,7 @@ class DraftViewModel @JvmOverloads constructor(
} else if (mentionRanges == null) {
styleBodyRanges
} else {
styleBodyRanges.toBuilder().addAllRanges(mentionRanges.rangesList).build()
styleBodyRanges.newBuilder().apply { ranges += mentionRanges.ranges }.build()
}
saveDrafts(it.copy(textDraft = text.toTextDraft(), bodyRangesDraft = bodyRanges?.toDraft()))
@ -148,5 +148,5 @@ private fun String.toTextDraft(): Draft? {
}
private fun BodyRangeList.toDraft(): Draft {
return Draft(Draft.BODY_RANGES, Base64.encodeBytes(toByteArray()))
return Draft(Draft.BODY_RANGES, Base64.encodeBytes(encode()))
}

View file

@ -1787,7 +1787,7 @@ class ConversationFragment :
return
}
if (SignalStore.uiHints().hasNotSeenTextFormattingAlert() && bodyRanges != null && bodyRanges.rangesCount > 0) {
if (SignalStore.uiHints().hasNotSeenTextFormattingAlert() && bodyRanges != null && bodyRanges.ranges.isNotEmpty()) {
Dialogs.showFormattedTextDialog(requireContext()) {
sendMessage(body, mentions, bodyRanges, messageToEdit, quote, scheduledDate, slideDeck, contacts, clearCompose, linkPreviews, preUploadResults, bypassPreSendSafetyNumberCheck, isViewOnce, afterSendComplete)
}

View file

@ -14,12 +14,12 @@ fun BodyRangeList?.adjustBodyRanges(bodyAdjustments: List<BodyAdjustment>): Body
return this
}
val newBodyRanges = rangesList.toMutableList()
val newBodyRanges = ranges.toMutableList()
for (adjustment in bodyAdjustments) {
val adjustmentLength = adjustment.oldLength - adjustment.newLength
rangesList.forEachIndexed { listIndex, range ->
ranges.forEachIndexed { listIndex, range ->
val needsRangeStartsAfterAdjustment = range.start > adjustment.startIndex
val needsRangeCoversAdjustment = range.start <= adjustment.startIndex && range.start + range.length >= adjustment.startIndex + adjustment.oldLength
@ -28,10 +28,10 @@ fun BodyRangeList?.adjustBodyRanges(bodyAdjustments: List<BodyAdjustment>): Body
val newLength: Int? = if (needsRangeCoversAdjustment) newRange.length - adjustmentLength else null
if (newStart != null || newLength != null) {
newBodyRanges[listIndex] = newRange.toBuilder().setStart(newStart ?: newRange.start).setLength(newLength ?: newRange.length).build()
newBodyRanges[listIndex] = newRange.newBuilder().start(newStart ?: newRange.start).length(newLength ?: newRange.length).build()
}
}
}
return BodyRangeList.newBuilder().addAllRanges(newBodyRanges).build()
return BodyRangeList(ranges = newBodyRanges)
}

View file

@ -35,7 +35,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallEvent
import org.whispersystems.signalservice.internal.push.SyncMessage.CallEvent
import java.util.UUID
import java.util.concurrent.TimeUnit
@ -1179,9 +1179,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
}
@JvmStatic
fun from(type: CallEvent.Type): Type? {
fun from(type: CallEvent.Type?): Type? {
return when (type) {
CallEvent.Type.UNKNOWN_TYPE -> null
null, CallEvent.Type.UNKNOWN_TYPE -> null
CallEvent.Type.AUDIO_CALL -> AUDIO_CALL
CallEvent.Type.VIDEO_CALL -> VIDEO_CALL
CallEvent.Type.GROUP_CALL -> GROUP_CALL
@ -1207,9 +1207,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
}
@JvmStatic
fun from(direction: CallEvent.Direction): Direction? {
fun from(direction: CallEvent.Direction?): Direction? {
return when (direction) {
CallEvent.Direction.UNKNOWN_DIRECTION -> null
null, CallEvent.Direction.UNKNOWN_DIRECTION -> null
CallEvent.Direction.INCOMING -> INCOMING
CallEvent.Direction.OUTGOING -> OUTGOING
}
@ -1284,9 +1284,9 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl
}
@JvmStatic
fun from(event: CallEvent.Event): Event? {
fun from(event: CallEvent.Event?): Event? {
return when (event) {
CallEvent.Event.UNKNOWN_ACTION -> null
null, CallEvent.Event.UNKNOWN_ACTION -> null
CallEvent.Event.ACCEPTED -> ACCEPTED
CallEvent.Event.NOT_ACCEPTED -> NOT_ACCEPTED
CallEvent.Event.DELETE -> DELETE

View file

@ -70,7 +70,7 @@ class ChatColorsTable(context: Context, databaseHelper: SignalDatabase) : Databa
val db: SQLiteDatabase = databaseHelper.signalWritableDatabase
val values = ContentValues(1).apply {
put(CHAT_COLORS, chatColors.serialize().toByteArray())
put(CHAT_COLORS, chatColors.serialize().encode())
}
val rowId = db.insert(TABLE_NAME, null, values)
@ -90,7 +90,7 @@ class ChatColorsTable(context: Context, databaseHelper: SignalDatabase) : Databa
val db: SQLiteDatabase = databaseHelper.signalWritableDatabase
val values = ContentValues(1).apply {
put(CHAT_COLORS, chatColors.serialize().toByteArray())
put(CHAT_COLORS, chatColors.serialize().encode())
}
val rowsUpdated = db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(chatColors.id.longValue))
@ -131,6 +131,6 @@ class ChatColorsTable(context: Context, databaseHelper: SignalDatabase) : Databa
private fun Cursor.getId(): Long = CursorUtil.requireLong(this, ID)
private fun Cursor.getChatColors(): ChatColors = ChatColors.forChatColor(
ChatColors.Id.forLongValue(getId()),
ChatColor.parseFrom(CursorUtil.requireBlob(this, CHAT_COLORS))
ChatColor.ADAPTER.decode(CursorUtil.requireBlob(this, CHAT_COLORS))
)
}

View file

@ -695,8 +695,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
Log.w(TAG, "No group entry. Creating restore placeholder for $groupId")
create(
groupMasterKey,
DecryptedGroup.newBuilder()
.setRevision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
DecryptedGroup.Builder()
.revision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
.build(),
true
)
@ -765,7 +765,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
values.put(V2_MASTER_KEY, groupMasterKey.serialize())
values.put(V2_REVISION, groupState.revision)
values.put(V2_DECRYPTED_GROUP, groupState.toByteArray())
values.put(V2_DECRYPTED_GROUP, groupState.encode())
membershipValues.clear()
membershipValues.addAll(groupMembers.toContentValues(groupId))
} else {
@ -790,8 +790,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
writableDatabase.endTransaction()
}
if (groupState != null && groupState.hasDisappearingMessagesTimer()) {
recipients.setExpireMessages(groupRecipientId, groupState.disappearingMessagesTimer.duration)
if (groupState?.disappearingMessagesTimer != null) {
recipients.setExpireMessages(groupRecipientId, groupState.disappearingMessagesTimer!!.duration)
}
if (groupId.isMms || Recipient.resolved(groupRecipientId).isProfileSharing) {
@ -849,8 +849,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
writableDatabase.withinTransaction { db ->
val record = getGroup(groupIdV1).get()
val newMembers: MutableList<RecipientId> = decryptedGroup.membersList.toAciList().toRecipientIds()
val pendingMembers: List<RecipientId> = DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.pendingMembersList).toRecipientIds()
val newMembers: MutableList<RecipientId> = decryptedGroup.members.toAciList().toRecipientIds()
val pendingMembers: List<RecipientId> = DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.pendingMembers).toRecipientIds()
newMembers.addAll(pendingMembers)
val droppedMembers: List<RecipientId> = SetUtil.difference(record.members, newMembers).toList()
@ -895,7 +895,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
val contentValues = ContentValues()
contentValues.put(TITLE, title)
contentValues.put(V2_REVISION, decryptedGroup.revision)
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.toByteArray())
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.encode())
contentValues.put(ACTIVE, if (gv2GroupActive(decryptedGroup)) 1 else 0)
if (existingGroup.isPresent && existingGroup.get().unmigratedV1Members.isNotEmpty() && existingGroup.get().isV2Group) {
@ -903,11 +903,11 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
val change = GroupChangeReconstruct.reconstructGroupChange(existingGroup.get().requireV2GroupProperties().decryptedGroup, decryptedGroup)
val addedMembers: Set<RecipientId> = change.newMembersList.toAciList().toRecipientIds().toSet()
val addedMembers: Set<RecipientId> = change.newMembers.toAciList().toRecipientIds().toSet()
val removedMembers: Set<RecipientId> = DecryptedGroupUtil.removedMembersServiceIdList(change).toRecipientIds().toSet()
val addedInvites: Set<RecipientId> = DecryptedGroupUtil.pendingToServiceIdList(change.newPendingMembersList).toRecipientIds().toSet()
val addedInvites: Set<RecipientId> = DecryptedGroupUtil.pendingToServiceIdList(change.newPendingMembers).toRecipientIds().toSet()
val removedInvites: Set<RecipientId> = DecryptedGroupUtil.removedPendingMembersServiceIdList(change).toRecipientIds().toSet()
val acceptedInvites: Set<RecipientId> = change.promotePendingMembersList.toAciList().toRecipientIds().toSet()
val acceptedInvites: Set<RecipientId> = change.promotePendingMembers.toAciList().toRecipientIds().toSet()
unmigratedV1Members -= addedMembers
unmigratedV1Members -= removedMembers
@ -941,8 +941,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
performMembershipUpdate(database, groupId, groupMembers)
}
if (decryptedGroup.hasDisappearingMessagesTimer()) {
recipients.setExpireMessages(groupRecipientId, decryptedGroup.disappearingMessagesTimer.duration)
if (decryptedGroup.disappearingMessagesTimer != null) {
recipients.setExpireMessages(groupRecipientId, decryptedGroup.disappearingMessagesTimer!!.duration)
}
if (groupId.isMms || Recipient.resolved(groupRecipientId).isProfileSharing) {
@ -1192,18 +1192,18 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
class V2GroupProperties(val groupMasterKey: GroupMasterKey, val groupRevision: Int, val decryptedGroupBytes: ByteArray) {
val decryptedGroup: DecryptedGroup by lazy {
DecryptedGroup.parseFrom(decryptedGroupBytes)
DecryptedGroup.ADAPTER.decode(decryptedGroupBytes)
}
val bannedMembers: Set<ServiceId> by lazy {
DecryptedGroupUtil.bannedMembersToServiceIdSet(decryptedGroup.bannedMembersList)
DecryptedGroupUtil.bannedMembersToServiceIdSet(decryptedGroup.bannedMembers)
}
fun isAdmin(recipient: Recipient): Boolean {
val aci = recipient.aci
return if (aci.isPresent) {
decryptedGroup.membersList.findMemberByAci(aci.get())
decryptedGroup.members.findMemberByAci(aci.get())
.map { it.role == Member.Role.ADMINISTRATOR }
.orElse(false)
} else {
@ -1224,7 +1224,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
var memberLevel: Optional<MemberLevel> = Optional.empty()
if (serviceId is ACI) {
memberLevel = decryptedGroup.membersList.findMemberByAci(serviceId)
memberLevel = decryptedGroup.members.findMemberByAci(serviceId)
.map { member ->
if (member.role == Member.Role.ADMINISTRATOR) {
MemberLevel.ADMINISTRATOR
@ -1235,12 +1235,12 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
}
if (memberLevel.isAbsent()) {
memberLevel = decryptedGroup.pendingMembersList.findPendingByServiceId(serviceId)
memberLevel = decryptedGroup.pendingMembers.findPendingByServiceId(serviceId)
.map { MemberLevel.PENDING_MEMBER }
}
if (memberLevel.isAbsent() && serviceId is ACI) {
memberLevel = decryptedGroup.requestingMembersList.findRequestingByAci(serviceId)
memberLevel = decryptedGroup.requestingMembers.findRequestingByAci(serviceId)
.map { _ -> MemberLevel.REQUESTING_MEMBER }
}
@ -1258,12 +1258,12 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
fun getMemberRecipientIds(memberSet: MemberSet): List<RecipientId> {
val includeSelf = memberSet.includeSelf
val selfAci = SignalStore.account().requireAci()
val recipients: MutableList<RecipientId> = ArrayList(decryptedGroup.membersCount + decryptedGroup.pendingMembersCount)
val recipients: MutableList<RecipientId> = ArrayList(decryptedGroup.members.size + decryptedGroup.pendingMembers.size)
var unknownMembers = 0
var unknownPending = 0
for (aci in decryptedGroup.membersList.toAciListWithUnknowns()) {
for (aci in decryptedGroup.members.toAciListWithUnknowns()) {
if (aci.isUnknown) {
unknownMembers++
} else if (includeSelf || selfAci != aci) {
@ -1272,7 +1272,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
}
if (memberSet.includePending) {
for (serviceId in DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.pendingMembersList)) {
for (serviceId in DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.pendingMembers)) {
if (serviceId.isUnknown) {
unknownPending++
} else if (includeSelf || selfAci != serviceId) {
@ -1290,7 +1290,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
fun getMemberServiceIds(): List<ServiceId> {
return decryptedGroup
.membersList
.members
.asSequence()
.map { ACI.parseOrNull(it.aciBytes) }
.filterNotNull()
@ -1368,8 +1368,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
private fun gv2GroupActive(decryptedGroup: DecryptedGroup): Boolean {
val aci = SignalStore.account().requireAci()
return decryptedGroup.membersList.findMemberByAci(aci).isPresent ||
DecryptedGroupUtil.findPendingByServiceId(decryptedGroup.pendingMembersList, aci).isPresent
return decryptedGroup.members.findMemberByAci(aci).isPresent ||
DecryptedGroupUtil.findPendingByServiceId(decryptedGroup.pendingMembers, aci).isPresent
}
private fun List<ServiceId>.toRecipientIds(): MutableList<RecipientId> {
@ -1412,7 +1412,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT
}
private fun getV2GroupMembers(decryptedGroup: DecryptedGroup, shouldRetry: Boolean): List<RecipientId> {
val ids: List<RecipientId> = decryptedGroup.membersList.toAciList().toRecipientIds()
val ids: List<RecipientId> = decryptedGroup.members.toAciList().toRecipientIds()
return if (RemappedRecords.getInstance().areAnyRemapped(ids)) {
if (shouldRetry) {

View file

@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
public final class MentionUtil {
@ -93,26 +94,29 @@ public final class MentionUtil {
return null;
}
BodyRangeList.Builder builder = BodyRangeList.newBuilder();
for (Mention mention : mentions) {
String uuid = Recipient.resolved(mention.getRecipientId()).requireAci().toString();
builder.addRanges(BodyRangeList.BodyRange.newBuilder()
.setMentionUuid(uuid)
.setStart(mention.getStart())
.setLength(mention.getLength()));
}
BodyRangeList.Builder builder = new BodyRangeList.Builder();
builder.ranges(
mentions.stream()
.map(mention -> {
String uuid = Recipient.resolved(mention.getRecipientId()).requireAci().toString();
return new BodyRangeList.BodyRange.Builder()
.mentionUuid(uuid)
.start(mention.getStart())
.length(mention.getLength())
.build();
})
.collect(Collectors.toList())
);
return builder.build();
}
public static @NonNull List<Mention> bodyRangeListToMentions(@Nullable BodyRangeList bodyRanges) {
if (bodyRanges != null) {
return Stream.of(bodyRanges.getRangesList())
.filter(bodyRange -> bodyRange.getAssociatedValueCase() == BodyRangeList.BodyRange.AssociatedValueCase.MENTIONUUID)
return Stream.of(bodyRanges.ranges)
.filter(bodyRange -> bodyRange.mentionUuid != null)
.map(mention -> {
RecipientId id = Recipient.externalPush(ServiceId.parseOrThrow(mention.getMentionUuid())).getId();
return new Mention(id, mention.getStart(), mention.getLength());
RecipientId id = Recipient.externalPush(ServiceId.parseOrThrow(mention.mentionUuid)).getId();
return new Mention(id, mention.start, mention.length);
})
.toList();
} else {

View file

@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.RecipientAccessList
import org.whispersystems.signalservice.api.crypto.ContentHint
import org.whispersystems.signalservice.api.messages.SendMessageResult
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.Content
/**
* Stores a rolling buffer of all outgoing messages. Used for the retry logic required for sender key.
@ -191,7 +191,7 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal
return -1
}
val content: SignalServiceProtos.Content = results.first { it.isSuccess && it.success.content.isPresent }.success.content.get()
val content: Content = results.first { it.isSuccess && it.success.content.isPresent }.success.content.get()
return insert(recipientDevices, sentTimestamp, content, contentHint, listOf(messageId), urgent)
}
@ -228,14 +228,14 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal
return payloadId
}
private fun insert(recipients: List<RecipientDevice>, dateSent: Long, content: SignalServiceProtos.Content, contentHint: ContentHint, messageIds: List<MessageId>, urgent: Boolean): Long {
private fun insert(recipients: List<RecipientDevice>, dateSent: Long, content: Content, contentHint: ContentHint, messageIds: List<MessageId>, urgent: Boolean): Long {
val db = databaseHelper.signalWritableDatabase
db.beginTransaction()
try {
val payloadValues = ContentValues().apply {
put(MslPayloadTable.DATE_SENT, dateSent)
put(MslPayloadTable.CONTENT, content.toByteArray())
put(MslPayloadTable.CONTENT, content.encode())
put(MslPayloadTable.CONTENT_HINT, contentHint.type)
put(MslPayloadTable.URGENT, urgent.toInt())
}
@ -300,7 +300,7 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal
return MessageLogEntry(
recipientId = RecipientId.from(CursorUtil.requireLong(entryCursor, MslRecipientTable.RECIPIENT_ID)),
dateSent = CursorUtil.requireLong(entryCursor, MslPayloadTable.DATE_SENT),
content = SignalServiceProtos.Content.parseFrom(CursorUtil.requireBlob(entryCursor, MslPayloadTable.CONTENT)),
content = Content.ADAPTER.decode(CursorUtil.requireBlob(entryCursor, MslPayloadTable.CONTENT)),
contentHint = ContentHint.fromType(CursorUtil.requireInt(entryCursor, MslPayloadTable.CONTENT_HINT)),
urgent = entryCursor.requireBoolean(MslPayloadTable.URGENT),
relatedMessages = messageIds

View file

@ -25,7 +25,6 @@ import androidx.annotation.VisibleForTesting
import androidx.core.content.contentValuesOf
import com.google.android.mms.pdu_alt.NotificationInd
import com.google.android.mms.pdu_alt.PduHeaders
import com.google.protobuf.InvalidProtocolBufferException
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
@ -142,7 +141,7 @@ import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.isStory
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage
import org.whispersystems.signalservice.internal.push.SyncMessage
import java.io.Closeable
import java.io.IOException
import java.util.LinkedList
@ -523,8 +522,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val bodyRanges: BodyRangeList? = if (data != null) {
try {
BodyRangeList.parseFrom(data)
} catch (e: InvalidProtocolBufferException) {
BodyRangeList.ADAPTER.decode(data)
} catch (e: IOException) {
Log.w(TAG, "Unable to parse quote body ranges", e)
null
}
@ -541,12 +540,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
if (data != null) {
try {
val bodyRanges = BodyRangeList
.parseFrom(data)
.rangesList
.filter { bodyRange -> bodyRange.associatedValueCase != BodyRangeList.BodyRange.AssociatedValueCase.MENTIONUUID }
.ADAPTER.decode(data)
.ranges
.filter { bodyRange -> bodyRange.mentionUuid == null }
return BodyRangeList.newBuilder().addAllRanges(bodyRanges).build()
} catch (e: InvalidProtocolBufferException) {
return BodyRangeList(ranges = bodyRanges)
} catch (e: IOException) {
Log.w(TAG, "Unable to parse quote body ranges", e)
}
}
@ -847,14 +846,13 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val messageId: MessageId = writableDatabase.withinTransaction { db ->
val self = Recipient.self()
val markRead = joinedUuids.contains(self.requireServiceId().rawUuid) || self.id == sender
val updateDetails: ByteArray = GroupCallUpdateDetails.newBuilder()
.setEraId(eraId)
.setStartedCallUuid(Recipient.resolved(sender).requireServiceId().toString())
.setStartedCallTimestamp(timestamp)
.addAllInCallUuids(joinedUuids.map { it.toString() })
.setIsCallFull(isCallFull)
.build()
.toByteArray()
val updateDetails: ByteArray = GroupCallUpdateDetails(
eraId = eraId,
startedCallUuid = Recipient.resolved(sender).requireServiceId().toString(),
startedCallTimestamp = timestamp,
inCallUuids = joinedUuids.map { it.toString() },
isCallFull = isCallFull
).encode()
val values = contentValuesOf(
FROM_RECIPIENT_ID to sender.serialize(),
@ -893,7 +891,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val updateDetail = GroupCallUpdateDetailsUtil.parse(message.body)
val contentValues = contentValuesOf(
BODY to Base64.encodeBytes(updateDetail.toBuilder().setStartedCallTimestamp(timestamp).build().toByteArray()),
BODY to Base64.encodeBytes(updateDetail.newBuilder().startedCallTimestamp(timestamp).build().encode()),
DATE_SENT to timestamp,
DATE_RECEIVED to timestamp
)
@ -1157,14 +1155,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
fun insertProfileNameChangeMessages(recipient: Recipient, newProfileName: String, previousProfileName: String) {
writableDatabase.withinTransaction { db ->
val groupRecords = groups.getGroupsContainingMember(recipient.id, false)
val profileChangeDetails = ProfileChangeDetails.newBuilder()
.setProfileNameChange(
ProfileChangeDetails.StringChange.newBuilder()
.setNew(newProfileName)
.setPrevious(previousProfileName)
)
.build()
.toByteArray()
val profileChangeDetails = ProfileChangeDetails(profileNameChange = ProfileChangeDetails.StringChange(previous = previousProfileName, newValue = newProfileName))
.encode()
val threadIdsToUpdate = mutableListOf<Long?>().apply {
add(threads.getThreadIdFor(recipient.id))
@ -1294,7 +1286,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
READ to 1,
TYPE to MessageTypes.THREAD_MERGE_TYPE,
THREAD_ID to threadId,
BODY to Base64.encodeBytes(event.toByteArray())
BODY to Base64.encodeBytes(event.encode())
)
.run()
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId)
@ -1313,7 +1305,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
READ to 1,
TYPE to MessageTypes.SESSION_SWITCHOVER_TYPE,
THREAD_ID to threadId,
BODY to Base64.encodeBytes(event.toByteArray())
BODY to Base64.encodeBytes(event.encode())
)
.run()
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId)
@ -2554,15 +2546,15 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
)
} else {
val giftBadge: GiftBadge? = if (body != null && MessageTypes.isGiftBadge(outboxType)) {
GiftBadge.parseFrom(Base64.decode(body))
GiftBadge.ADAPTER.decode(Base64.decode(body))
} else {
null
}
val messageRanges: BodyRangeList? = if (messageRangesData != null) {
try {
BodyRangeList.parseFrom(messageRangesData)
} catch (e: InvalidProtocolBufferException) {
BodyRangeList.ADAPTER.decode(messageRangesData)
} catch (e: IOException) {
Log.w(TAG, "Error parsing message ranges", e)
null
}
@ -2648,15 +2640,15 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
contentValues.put(QUOTE_TYPE, retrieved.quote.type.code)
contentValues.put(QUOTE_MISSING, if (retrieved.quote.isOriginalMissing) 1 else 0)
val quoteBodyRanges: BodyRangeList.Builder = retrieved.quote.bodyRanges?.toBuilder() ?: BodyRangeList.newBuilder()
val quoteBodyRanges: BodyRangeList.Builder = retrieved.quote.bodyRanges?.newBuilder() ?: BodyRangeList.Builder()
val mentionsList = MentionUtil.mentionsToBodyRangeList(retrieved.quote.mentions)
if (mentionsList != null) {
quoteBodyRanges.addAllRanges(mentionsList.rangesList)
quoteBodyRanges.ranges += mentionsList.ranges
}
if (quoteBodyRanges.rangesCount > 0) {
contentValues.put(QUOTE_BODY_RANGES, quoteBodyRanges.build().toByteArray())
if (quoteBodyRanges.ranges.isNotEmpty()) {
contentValues.put(QUOTE_BODY_RANGES, quoteBodyRanges.build().encode())
}
quoteAttachments += retrieved.quote.attachments
@ -2925,12 +2917,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
.run()
.use { cursor ->
if (cursor.moveToFirst()) {
val giftBadge = GiftBadge.parseFrom(Base64.decode(cursor.requireNonNullString(BODY)))
val updatedBadge = giftBadge.toBuilder().setRedemptionState(redemptionState).build()
val giftBadge = GiftBadge.ADAPTER.decode(Base64.decode(cursor.requireNonNullString(BODY)))
val updatedBadge = giftBadge.newBuilder().redemptionState(redemptionState).build()
updated = db
.update(TABLE_NAME)
.values(BODY to Base64.encodeBytes(updatedBadge.toByteArray()))
.values(BODY to Base64.encodeBytes(updatedBadge.encode()))
.where("$ID = ?", messageId)
.run() > 0
@ -3113,18 +3105,18 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val adjustedQuoteBodyRanges = message.outgoingQuote.bodyRanges.adjustBodyRanges(updated.bodyAdjustments)
val quoteBodyRanges: BodyRangeList.Builder = if (adjustedQuoteBodyRanges != null) {
adjustedQuoteBodyRanges.toBuilder()
adjustedQuoteBodyRanges.newBuilder()
} else {
BodyRangeList.newBuilder()
BodyRangeList.Builder()
}
val mentionsList = MentionUtil.mentionsToBodyRangeList(updated.mentions)
if (mentionsList != null) {
quoteBodyRanges.addAllRanges(mentionsList.rangesList)
quoteBodyRanges.ranges += mentionsList.ranges
}
if (quoteBodyRanges.rangesCount > 0) {
contentValues.put(QUOTE_BODY_RANGES, quoteBodyRanges.build().toByteArray())
if (quoteBodyRanges.ranges.isNotEmpty()) {
contentValues.put(QUOTE_BODY_RANGES, quoteBodyRanges.build().encode())
}
if (editedMessage == null) {
@ -3252,7 +3244,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
contentValues.put(BODY, body)
contentValues.put(MENTIONS_SELF, if (mentionsSelf) 1 else 0)
if (messageRanges != null) {
contentValues.put(MESSAGE_RANGES, messageRanges.toByteArray())
contentValues.put(MESSAGE_RANGES, messageRanges.encode())
}
val (messageId, insertedAttachments) = writableDatabase.withinTransaction { db ->
@ -4294,12 +4286,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val bytes: ByteArray? = cursor.requireBlob(EXPORT_STATE)
if (bytes == null) {
MessageExportState.getDefaultInstance()
MessageExportState()
} else {
try {
MessageExportState.parseFrom(bytes)
} catch (e: InvalidProtocolBufferException) {
MessageExportState.getDefaultInstance()
MessageExportState.ADAPTER.decode(bytes)
} catch (e: IOException) {
MessageExportState()
}
}
} ?: throw NoSuchMessageException("The requested message does not exist.")
@ -4333,7 +4325,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
private fun setMessageExportState(messageId: MessageId, messageExportState: MessageExportState) {
writableDatabase
.update(TABLE_NAME)
.values(EXPORT_STATE to messageExportState.toByteArray())
.values(EXPORT_STATE to messageExportState.encode())
.where("$ID = ?", messageId.id)
.run()
}
@ -4573,7 +4565,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun setTimestampReadFromSyncMessageProto(readMessages: List<SyncMessage.Read>, proposedExpireStarted: Long, threadToLatestRead: MutableMap<Long, Long>): Collection<SyncMessageId> {
val reads: List<ReadMessage> = readMessages.map { r -> ReadMessage(ServiceId.parseOrThrow(r.senderAci), r.timestamp) }
val reads: List<ReadMessage> = readMessages.map { r -> ReadMessage(ServiceId.parseOrThrow(r.senderAci!!), r.timestamp!!) }
return setTimestampReadFromSyncMessage(reads, proposedExpireStarted, threadToLatestRead)
}
@ -4920,8 +4912,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
if (data != null) {
try {
bodyRanges[CursorUtil.requireLong(cursor, ID)] = BodyRangeList.parseFrom(data)
} catch (e: InvalidProtocolBufferException) {
bodyRanges[CursorUtil.requireLong(cursor, ID)] = BodyRangeList.ADAPTER.decode(data)
} catch (e: IOException) {
Log.w(TAG, "Unable to parse body ranges for search", e)
}
}
@ -5136,11 +5128,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
override fun getMessageExportStateForCurrentRecord(): MessageExportState {
val messageExportState = CursorUtil.requireBlob(cursor, EXPORT_STATE) ?: return MessageExportState.getDefaultInstance()
val messageExportState = CursorUtil.requireBlob(cursor, EXPORT_STATE) ?: return MessageExportState()
return try {
MessageExportState.parseFrom(messageExportState)
} catch (e: InvalidProtocolBufferException) {
MessageExportState.getDefaultInstance()
MessageExportState.ADAPTER.decode(messageExportState)
} catch (e: IOException) {
MessageExportState()
}
}
@ -5192,7 +5184,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val slideDeck = SlideDeck(MmsNotificationAttachment(status, messageSize))
val giftBadge: GiftBadge? = if (body != null && MessageTypes.isGiftBadge(mailbox)) {
try {
GiftBadge.parseFrom(Base64.decode(body))
GiftBadge.ADAPTER.decode(Base64.decode(body))
} catch (e: IOException) {
Log.w(TAG, "Error parsing gift badge", e)
null
@ -5286,8 +5278,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val messageRanges: BodyRangeList? = if (messageRangesData != null) {
try {
BodyRangeList.parseFrom(messageRangesData)
} catch (e: InvalidProtocolBufferException) {
BodyRangeList.ADAPTER.decode(messageRangesData)
} catch (e: IOException) {
Log.w(TAG, "Error parsing message ranges", e)
null
}
@ -5297,7 +5289,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val giftBadge: GiftBadge? = if (body != null && MessageTypes.isGiftBadge(box)) {
try {
GiftBadge.parseFrom(Base64.decode(body))
GiftBadge.ADAPTER.decode(Base64.decode(body))
} catch (e: IOException) {
Log.w(TAG, "Error parsing gift badge", e)
null

View file

@ -4,8 +4,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mobilecoin.lib.KeyImage;
import com.mobilecoin.lib.Receipt;
import com.mobilecoin.lib.RistrettoPublic;
@ -14,40 +12,44 @@ import com.mobilecoin.lib.exceptions.SerializationException;
import org.thoughtcrime.securesms.payments.proto.PaymentMetaData;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import okio.ByteString;
public final class PaymentMetaDataUtil {
public static PaymentMetaData parseOrThrow(byte[] requireBlob) {
try {
return PaymentMetaData.parseFrom(requireBlob);
} catch (InvalidProtocolBufferException e) {
return PaymentMetaData.ADAPTER.decode(requireBlob);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public static @NonNull PaymentMetaData fromReceipt(@Nullable byte[] receipt) throws SerializationException {
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder();
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = new PaymentMetaData.MobileCoinTxoIdentification.Builder();
if (receipt != null) {
addReceiptData(receipt, builder);
}
return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build();
return new PaymentMetaData.Builder().mobileCoinTxoIdentification(builder.build()).build();
}
public static @NonNull PaymentMetaData fromKeysAndImages(@NonNull List<ByteString> publicKeys, @NonNull List<ByteString> keyImages) {
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder();
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = new PaymentMetaData.MobileCoinTxoIdentification.Builder();
builder.addAllKeyImages(keyImages);
builder.addAllPublicKey(publicKeys);
builder.keyImages(keyImages);
builder.publicKey(publicKeys);
return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build();
return new PaymentMetaData.Builder().mobileCoinTxoIdentification(builder.build()).build();
}
public static @NonNull PaymentMetaData fromReceiptAndTransaction(@Nullable byte[] receipt, @Nullable byte[] transaction) throws SerializationException {
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = PaymentMetaData.MobileCoinTxoIdentification.newBuilder();
PaymentMetaData.MobileCoinTxoIdentification.Builder builder = new PaymentMetaData.MobileCoinTxoIdentification.Builder();
if (transaction != null) {
addTransactionData(transaction, builder);
@ -55,7 +57,7 @@ public final class PaymentMetaDataUtil {
addReceiptData(receipt, builder);
}
return PaymentMetaData.newBuilder().setMobileCoinTxoIdentification(builder).build();
return new PaymentMetaData.Builder().mobileCoinTxoIdentification(builder.build()).build();
}
private static void addReceiptData(@NonNull byte[] receipt, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) throws SerializationException {
@ -66,19 +68,25 @@ public final class PaymentMetaDataUtil {
private static void addTransactionData(@NonNull byte[] transactionBytes, PaymentMetaData.MobileCoinTxoIdentification.Builder builder) throws SerializationException {
Transaction transaction = Transaction.fromBytes(transactionBytes);
Set<KeyImage> keyImages = transaction.getKeyImages();
List<ByteString> newKeyImages = new ArrayList<>(builder.keyImages);
for (KeyImage keyImage : keyImages) {
builder.addKeyImages(ByteString.copyFrom(keyImage.getData()));
newKeyImages.add(ByteString.of(keyImage.getData()));
}
builder.keyImages(newKeyImages);
for (RistrettoPublic publicKey : transaction.getOutputPublicKeys()) {
addPublicKey(builder, publicKey);
}
}
private static void addPublicKey(@NonNull PaymentMetaData.MobileCoinTxoIdentification.Builder builder, @NonNull RistrettoPublic publicKey) {
builder.addPublicKey(ByteString.copyFrom(publicKey.getKeyBytes()));
List<ByteString> publicKeys = new ArrayList<>(builder.publicKey);
publicKeys.add(ByteString.of(publicKey.getKeyBytes()));
builder.publicKey(publicKeys);
}
public static byte[] receiptPublic(@NonNull PaymentMetaData paymentMetaData) {
return Stream.of(paymentMetaData.getMobileCoinTxoIdentification().getPublicKeyList()).single().toByteArray();
return Stream.of(paymentMetaData.mobileCoinTxoIdentification.publicKey).single().toByteArray();
}
}

View file

@ -11,11 +11,12 @@ import androidx.annotation.WorkerThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.google.protobuf.InvalidProtocolBufferException;
import com.mobilecoin.lib.exceptions.SerializationException;
import org.signal.core.util.CursorExtensionsKt;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SQLiteDatabaseExtensionsKt;
import org.signal.core.util.SqlUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageId;
@ -32,12 +33,11 @@ import org.thoughtcrime.securesms.payments.State;
import org.thoughtcrime.securesms.payments.proto.PaymentMetaData;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SqlUtil;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.whispersystems.signalservice.api.payments.Money;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -230,8 +230,8 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData
values.put(NOTE, note);
values.put(DIRECTION, direction.serialize());
values.put(STATE, state.serialize());
values.put(AMOUNT, CryptoValueUtil.moneyToCryptoValue(amount).toByteArray());
values.put(FEE, CryptoValueUtil.moneyToCryptoValue(fee).toByteArray());
values.put(AMOUNT, CryptoValueUtil.moneyToCryptoValue(amount).encode());
values.put(FEE, CryptoValueUtil.moneyToCryptoValue(fee).encode());
if (transaction != null) {
values.put(TRANSACTION, transaction);
} else {
@ -245,9 +245,9 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData
values.putNull(PUBLIC_KEY);
}
if (metaData != null) {
values.put(META_DATA, metaData.toByteArray());
values.put(META_DATA, metaData.encode());
} else {
values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).toByteArray());
values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).encode());
}
values.put(SEEN, seen ? 1 : 0);
@ -469,11 +469,11 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData
values.put(RECEIPT, receipt);
try {
values.put(PUBLIC_KEY, Base64.encodeBytes(PaymentMetaDataUtil.receiptPublic(PaymentMetaDataUtil.fromReceipt(receipt))));
values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).toByteArray());
values.put(META_DATA, PaymentMetaDataUtil.fromReceiptAndTransaction(receipt, transaction).encode());
} catch (SerializationException e) {
throw new IllegalArgumentException(e);
}
values.put(FEE, CryptoValueUtil.moneyToCryptoValue(fee).toByteArray());
values.put(FEE, CryptoValueUtil.moneyToCryptoValue(fee).encode());
database.beginTransaction();
try {
@ -526,7 +526,7 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData
values.put(STATE, state.serialize());
if (amount != null) {
values.put(AMOUNT, CryptoValueUtil.moneyToCryptoValue(amount).toByteArray());
values.put(AMOUNT, CryptoValueUtil.moneyToCryptoValue(amount).encode());
}
if (state == State.FAILED) {
@ -623,9 +623,9 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData
private static @NonNull Money getMoneyValue(@NonNull byte[] blob) {
try {
CryptoValue cryptoValue = CryptoValue.parseFrom(blob);
CryptoValue cryptoValue = CryptoValue.ADAPTER.decode(blob);
return CryptoValueUtil.cryptoValueToMoney(cryptoValue);
} catch (InvalidProtocolBufferException e) {
} catch (IOException e) {
throw new AssertionError(e);
}
}

View file

@ -9,8 +9,7 @@ import android.text.TextUtils
import androidx.annotation.VisibleForTesting
import androidx.core.content.contentValuesOf
import app.cash.exhaustive.Exhaustive
import com.google.protobuf.ByteString
import com.google.protobuf.InvalidProtocolBufferException
import okio.ByteString.Companion.toByteString
import org.signal.core.util.Bitmask
import org.signal.core.util.CursorUtil
import org.signal.core.util.SqlUtil
@ -814,7 +813,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
updateExtras(recipientId) {
it.setHideStory(insert.shouldHideStory())
it.hideStory(insert.shouldHideStory())
}
threadDatabase.applyStorageSyncUpdate(recipientId, insert)
@ -871,7 +870,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
updateExtras(recipientId) {
it.setHideStory(update.new.shouldHideStory())
it.hideStory(update.new.shouldHideStory())
}
threads.applyStorageSyncUpdate(recipientId, update.new)
@ -910,8 +909,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
Log.i(TAG, "Creating restore placeholder for $groupId")
val createdId = groups.create(
masterKey,
DecryptedGroup.newBuilder()
.setRevision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
DecryptedGroup.Builder()
.revision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
.build()
)
@ -921,7 +920,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
groups.setShowAsStoryState(groupId, insert.storySendMode.toShowAsStoryState())
updateExtras(recipient.id) {
it.setHideStory(insert.shouldHideStory())
it.hideStory(insert.shouldHideStory())
}
Log.i(TAG, "Scheduling request for latest group info for $groupId")
@ -943,7 +942,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val recipient = Recipient.externalGroupExact(groupId)
updateExtras(recipient.id) {
it.setHideStory(update.new.shouldHideStory())
it.hideStory(update.new.shouldHideStory())
}
groups.setShowAsStoryState(groupId, update.new.storySendMode.toShowAsStoryState())
@ -1222,7 +1221,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
Log.d(TAG, "No recipients utilizing updated chat color.")
} else {
val values = ContentValues(2).apply {
put(CHAT_COLORS, chatColors.serialize().toByteArray())
put(CHAT_COLORS, chatColors.serialize().encode())
put(CUSTOM_CHAT_COLORS_ID, chatColors.id.longValue)
}
@ -1313,7 +1312,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
fun setColor(id: RecipientId, color: ChatColors) {
val values = ContentValues().apply {
put(CHAT_COLORS, color.serialize().toByteArray())
put(CHAT_COLORS, color.serialize().encode())
put(CUSTOM_CHAT_COLORS_ID, color.id.longValue)
}
if (update(id, values)) {
@ -1427,7 +1426,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
fun setLastSessionResetTime(id: RecipientId, lastResetTime: DeviceLastResetTime) {
val values = ContentValues(1).apply {
put(LAST_SESSION_RESET, lastResetTime.toByteArray())
put(LAST_SESSION_RESET, lastResetTime.encode())
}
update(id, values)
}
@ -1438,28 +1437,25 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
return try {
val serialized = cursor.requireBlob(LAST_SESSION_RESET)
if (serialized != null) {
DeviceLastResetTime.parseFrom(serialized)
DeviceLastResetTime.ADAPTER.decode(serialized)
} else {
DeviceLastResetTime.newBuilder().build()
DeviceLastResetTime()
}
} catch (e: InvalidProtocolBufferException) {
} catch (e: IOException) {
Log.w(TAG, e)
DeviceLastResetTime.newBuilder().build()
DeviceLastResetTime()
}
}
}
return DeviceLastResetTime.newBuilder().build()
return DeviceLastResetTime()
}
fun setBadges(id: RecipientId, badges: List<Badge>) {
val badgeListBuilder = BadgeList.newBuilder()
for (badge in badges) {
badgeListBuilder.addBadges(toDatabaseBadge(badge))
}
val badgeList = BadgeList(badges = badges.map { toDatabaseBadge(it) })
val values = ContentValues(1).apply {
put(BADGES, badgeListBuilder.build().toByteArray())
put(BADGES, badgeList.encode())
}
if (update(id, values)) {
@ -1568,12 +1564,12 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
): Boolean {
val selection = "$ID = ? AND $PROFILE_KEY = ?"
val args = arrayOf(id.serialize(), Base64.encodeBytes(profileKey.serialize()))
val columnData = ExpiringProfileKeyCredentialColumnData.newBuilder()
.setProfileKey(ByteString.copyFrom(profileKey.serialize()))
.setExpiringProfileKeyCredential(ByteString.copyFrom(expiringProfileKeyCredential.serialize()))
val columnData = ExpiringProfileKeyCredentialColumnData.Builder()
.profileKey(profileKey.serialize().toByteString())
.expiringProfileKeyCredential(expiringProfileKeyCredential.serialize().toByteString())
.build()
val values = ContentValues(1).apply {
put(EXPIRING_PROFILE_KEY_CREDENTIAL, Base64.encodeBytes(columnData.toByteArray()))
put(EXPIRING_PROFILE_KEY_CREDENTIAL, Base64.encodeBytes(columnData.encode()))
}
val updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values)
@ -1850,9 +1846,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
private fun setWallpaper(id: RecipientId, wallpaper: Wallpaper?) {
val existingWallpaperUri = getWallpaperUri(id)
val values = ContentValues().apply {
put(WALLPAPER, wallpaper?.toByteArray())
if (wallpaper != null && wallpaper.hasFile()) {
put(WALLPAPER_URI, wallpaper.file.uri)
put(WALLPAPER, wallpaper?.encode())
if (wallpaper?.file_ != null) {
put(WALLPAPER_URI, wallpaper.file_.uri)
} else {
putNull(WALLPAPER_URI)
}
@ -1869,8 +1865,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
fun setDimWallpaperInDarkTheme(id: RecipientId, enabled: Boolean) {
val wallpaper = getWallpaper(id) ?: throw IllegalStateException("No wallpaper set for $id")
val updated = wallpaper.toBuilder()
.setDimLevelInDarkTheme(if (enabled) ChatWallpaper.FIXED_DIM_LEVEL_FOR_DARK_THEME else 0f)
val updated = wallpaper.newBuilder()
.dimLevelInDarkTheme(if (enabled) ChatWallpaper.FIXED_DIM_LEVEL_FOR_DARK_THEME else 0f)
.build()
setWallpaper(id, updated)
@ -1882,8 +1878,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val raw = cursor.requireBlob(WALLPAPER)
return if (raw != null) {
try {
Wallpaper.parseFrom(raw)
} catch (e: InvalidProtocolBufferException) {
Wallpaper.ADAPTER.decode(raw)
} catch (e: IOException) {
null
}
} else {
@ -1898,8 +1894,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
private fun getWallpaperUri(id: RecipientId): Uri? {
val wallpaper = getWallpaper(id)
return if (wallpaper != null && wallpaper.hasFile()) {
Uri.parse(wallpaper.file.uri)
return if (wallpaper != null && wallpaper.file_ != null) {
Uri.parse(wallpaper.file_.uri)
} else {
null
}
@ -2046,13 +2042,13 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
fun setHideStory(id: RecipientId, hideStory: Boolean) {
updateExtras(id) { it.setHideStory(hideStory) }
updateExtras(id) { it.hideStory(hideStory) }
rotateStorageId(id)
StorageSyncHelper.scheduleSyncForDataChange()
}
fun updateLastStoryViewTimestamp(id: RecipientId) {
updateExtras(id) { it.setLastStoryView(System.currentTimeMillis()) }
updateExtras(id) { it.lastStoryView(System.currentTimeMillis()) }
}
fun clearUsernameIfExists(username: String) {
@ -2329,10 +2325,12 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
is PnpOperation.SetPni -> {
affectedIds.add(operation.recipientId)
}
is PnpOperation.Merge -> {
oldIds.add(operation.secondaryId)
affectedIds.add(operation.primaryId)
}
is PnpOperation.SessionSwitchoverInsert -> {}
is PnpOperation.ChangeNumberInsert -> changedNumberId = operation.recipientId
}
@ -2364,6 +2362,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.where("$ID = ?", operation.recipientId)
.run()
}
is PnpOperation.RemovePni -> {
writableDatabase
.update(TABLE_NAME)
@ -2371,6 +2370,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.where("$ID = ?", operation.recipientId)
.run()
}
is PnpOperation.SetAci -> {
writableDatabase
.update(TABLE_NAME)
@ -2382,6 +2382,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.where("$ID = ?", operation.recipientId)
.run()
}
is PnpOperation.SetE164 -> {
writableDatabase
.update(TABLE_NAME)
@ -2389,6 +2390,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.where("$ID = ?", operation.recipientId)
.run()
}
is PnpOperation.SetPni -> {
writableDatabase
.update(TABLE_NAME)
@ -2406,20 +2408,19 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
.where("$ID = ?", operation.recipientId)
.run()
}
is PnpOperation.Merge -> {
val mergeResult: MergeResult = merge(operation.primaryId, operation.secondaryId, inputPni)
hadThreadMerge = hadThreadMerge || mergeResult.neededThreadMerge
}
is PnpOperation.SessionSwitchoverInsert -> {
if (hadThreadMerge) {
Log.d(TAG, "Skipping SSE insert because we already had a thread merge event.")
} else {
val threadId: Long? = threads.getThreadIdFor(operation.recipientId)
if (threadId != null) {
val event = SessionSwitchoverEvent
.newBuilder()
.setE164(operation.e164 ?: "")
.build()
val event = SessionSwitchoverEvent(e164 = operation.e164 ?: "")
try {
SignalDatabase.messages.insertSessionSwitchoverEvent(operation.recipientId, threadId, event)
} catch (e: Exception) {
@ -2471,6 +2472,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
}
}
is PnpOperation.ChangeNumberInsert -> {
if (changeSet.id is PnpIdResolver.PnpNoopId) {
SignalDatabase.messages.insertNumberChangeMessages(changeSet.id.recipientId)
@ -2485,6 +2487,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
is PnpIdResolver.PnpNoopId -> {
changeSet.id.recipientId
}
is PnpIdResolver.PnpInsert -> {
val id: Long = writableDatabase.insert(TABLE_NAME, null, buildContentValuesForNewUser(changeSet.id.e164, changeSet.id.pni, changeSet.id.aci))
RecipientId.from(id)
@ -3094,8 +3097,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val serializedChatColors = cursor.requireBlob(CHAT_COLORS)
var chatColors: ChatColors? = if (serializedChatColors != null) {
try {
forChatColor(forLongValue(customChatColorsId), ChatColor.parseFrom(serializedChatColors))
} catch (e: InvalidProtocolBufferException) {
forChatColor(forLongValue(customChatColorsId), ChatColor.ADAPTER.decode(serializedChatColors))
} catch (e: IOException) {
null
}
} else {
@ -3117,7 +3120,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
val contentValues = ContentValues().apply {
put(CHAT_COLORS, chatColors.serialize().toByteArray())
put(CHAT_COLORS, chatColors.serialize().encode())
put(CUSTOM_CHAT_COLORS_ID, chatColors.id.longValue)
}
db.update(TABLE_NAME, contentValues, "$ID = ?", arrayOf(id.toString()))
@ -3604,7 +3607,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
fun manuallyShowAvatar(recipientId: RecipientId) {
updateExtras(recipientId) { b: RecipientExtras.Builder -> b.setManuallyShownAvatar(true) }
updateExtras(recipientId) { b: RecipientExtras.Builder -> b.manuallyShownAvatar(true) }
}
fun getCapabilities(id: RecipientId): RecipientRecord.Capabilities? {
@ -3629,8 +3632,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
db.query(TABLE_NAME, arrayOf(ID, EXTRAS), ID_WHERE, SqlUtil.buildArgs(recipientId), null, null, null).use { cursor ->
if (cursor.moveToNext()) {
val state = getRecipientExtras(cursor)
val builder = if (state != null) state.toBuilder() else RecipientExtras.newBuilder()
val updatedState = updater.apply(builder).build().toByteArray()
val builder = state?.newBuilder() ?: RecipientExtras.Builder()
val updatedState = updater.apply(builder).build().encode()
val values = ContentValues(1).apply {
put(EXTRAS, updatedState)
}
@ -3782,7 +3785,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
// Thread Merge Event (remaps happen inside ThreadTable#merge)
if (threadMerge.neededMerge) {
val mergeEvent: ThreadMergeEvent.Builder = ThreadMergeEvent.newBuilder()
val mergeEvent: ThreadMergeEvent.Builder = ThreadMergeEvent.Builder()
if (secondaryRecord.e164 != null) {
mergeEvent.previousE164 = secondaryRecord.e164
@ -3807,7 +3810,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
CALL_VIBRATE to if (primaryRecord.callVibrateState != VibrateState.DEFAULT) primaryRecord.callVibrateState.id else secondaryRecord.callVibrateState.id,
NOTIFICATION_CHANNEL to (primaryRecord.notificationChannel ?: secondaryRecord.notificationChannel),
MUTE_UNTIL to if (primaryRecord.muteUntil > 0) primaryRecord.muteUntil else secondaryRecord.muteUntil,
CHAT_COLORS to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.serialize().toByteArray() }.orElse(null),
CHAT_COLORS to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.serialize().encode() }.orElse(null),
AVATAR_COLOR to primaryRecord.avatarColor.serialize(),
CUSTOM_CHAT_COLORS_ID to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.id.longValue }.orElse(null),
MESSAGE_EXPIRATION_TIME to if (primaryRecord.expireMessages > 0) primaryRecord.expireMessages else secondaryRecord.expireMessages,
@ -4094,7 +4097,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
if (expiringProfileKeyCredentialString != null) {
try {
val columnDataBytes = Base64.decode(expiringProfileKeyCredentialString)
val columnData = ExpiringProfileKeyCredentialColumnData.parseFrom(columnDataBytes)
val columnData = ExpiringProfileKeyCredentialColumnData.ADAPTER.decode(columnDataBytes)
if (Arrays.equals(columnData.profileKey.toByteArray(), profileKey)) {
expiringProfileKeyCredential = ExpiringProfileKeyCredential(columnData.expiringProfileKeyCredential.toByteArray())
} else {
@ -4111,8 +4114,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val serializedWallpaper = cursor.requireBlob(WALLPAPER)
val chatWallpaper: ChatWallpaper? = if (serializedWallpaper != null) {
try {
ChatWallpaperFactory.create(Wallpaper.parseFrom(serializedWallpaper))
} catch (e: InvalidProtocolBufferException) {
ChatWallpaperFactory.create(Wallpaper.ADAPTER.decode(serializedWallpaper))
} catch (e: IOException) {
Log.w(TAG, "Failed to parse wallpaper.", e)
null
}
@ -4124,8 +4127,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val serializedChatColors = cursor.requireBlob(CHAT_COLORS)
val chatColors: ChatColors? = if (serializedChatColors != null) {
try {
forChatColor(forLongValue(customChatColorsId), ChatColor.parseFrom(serializedChatColors))
} catch (e: InvalidProtocolBufferException) {
forChatColor(forLongValue(customChatColorsId), ChatColor.ADAPTER.decode(serializedChatColors))
} catch (e: IOException) {
Log.w(TAG, "Failed to parse chat colors.", e)
null
}
@ -4206,15 +4209,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
var badgeList: BadgeList? = null
if (serializedBadgeList != null) {
try {
badgeList = BadgeList.parseFrom(serializedBadgeList)
} catch (e: InvalidProtocolBufferException) {
badgeList = BadgeList.ADAPTER.decode(serializedBadgeList)
} catch (e: IOException) {
Log.w(TAG, e)
}
}
val badges: List<Badge>
if (badgeList != null) {
val protoBadges = badgeList.badgesList
val protoBadges = badgeList.badges
badges = ArrayList(protoBadges.size)
for (protoBadge in protoBadges) {
badges.add(Badges.fromDatabaseBadge(protoBadge))
@ -4254,10 +4257,10 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
private fun getRecipientExtras(cursor: Cursor): RecipientExtras? {
return cursor.optionalBlob(EXTRAS).map { b: ByteArray? ->
return cursor.optionalBlob(EXTRAS).map { b: ByteArray ->
try {
RecipientExtras.parseFrom(b)
} catch (e: InvalidProtocolBufferException) {
RecipientExtras.ADAPTER.decode(b)
} catch (e: IOException) {
Log.w(TAG, e)
throw AssertionError(e)
}

View file

@ -7,7 +7,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessageRe
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SyncMessage
/**
* Represents a list of, or update to a list of, who can access a story through what
@ -84,12 +84,12 @@ data class SentStorySyncManifest(
return SentStorySyncManifest(entries)
}
fun fromRecipientsSet(recipients: List<SignalServiceProtos.SyncMessage.Sent.StoryMessageRecipient>): SentStorySyncManifest {
fun fromRecipientsSet(recipients: List<SyncMessage.Sent.StoryMessageRecipient>): SentStorySyncManifest {
val entries = recipients.toSet().map { recipient ->
Entry(
recipientId = RecipientId.from(ServiceId.parseOrThrow(recipient.destinationServiceId)),
allowedToReply = recipient.isAllowedToReply,
distributionLists = recipient.distributionListIdsList.map { DistributionId.from(it) }
recipientId = RecipientId.from(ServiceId.parseOrThrow(recipient.destinationServiceId!!)),
allowedToReply = recipient.isAllowedToReply!!,
distributionLists = recipient.distributionListIds.map { DistributionId.from(it) }
)
}

View file

@ -14,7 +14,6 @@ import android.preference.PreferenceManager
import android.text.TextUtils
import androidx.core.content.contentValuesOf
import com.annimon.stream.Stream
import com.google.protobuf.InvalidProtocolBufferException
import net.zetetic.database.sqlcipher.SQLiteDatabase
import org.signal.core.util.CursorUtil
import org.signal.core.util.Hex
@ -50,6 +49,7 @@ import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.io.File
import java.io.IOException
import java.util.LinkedList
import java.util.Locale
import java.util.UUID
@ -1375,14 +1375,14 @@ object V149_LegacyMigrations : SignalDatabaseMigration {
continue
}
try {
val hasReceiveLaterThanNotified: Boolean = ReactionList.parseFrom(reactions)
.reactionsList
val hasReceiveLaterThanNotified: Boolean = ReactionList.ADAPTER.decode(reactions)
.reactions
.stream()
.anyMatch { r: ReactionList.Reaction -> r.receivedTime > notifiedTimestamp }
if (!hasReceiveLaterThanNotified) {
smsIds.add(cursor.getLong(cursor.getColumnIndexOrThrow("_id")))
}
} catch (e: InvalidProtocolBufferException) {
} catch (e: IOException) {
Log.e(TAG, e)
}
}
@ -1402,14 +1402,14 @@ object V149_LegacyMigrations : SignalDatabaseMigration {
continue
}
try {
val hasReceiveLaterThanNotified: Boolean = ReactionList.parseFrom(reactions)
.reactionsList
val hasReceiveLaterThanNotified: Boolean = ReactionList.ADAPTER.decode(reactions)
.reactions
.stream()
.anyMatch { r: ReactionList.Reaction -> r.receivedTime > notifiedTimestamp }
if (!hasReceiveLaterThanNotified) {
mmsIds.add(cursor.getLong(cursor.getColumnIndexOrThrow("_id")))
}
} catch (e: InvalidProtocolBufferException) {
} catch (e: IOException) {
Log.e(TAG, e)
}
}
@ -1490,7 +1490,7 @@ object V149_LegacyMigrations : SignalDatabaseMigration {
for (entry: Map.Entry<MaterialColor, ChatColors> in entrySet) {
val whereArgs = SqlUtil.buildArgs(entry.key.serialize())
val values = ContentValues(2)
values.put("chat_colors", entry.value.serialize().toByteArray())
values.put("chat_colors", entry.value.serialize().encode())
values.put("custom_chat_colors_id", entry.value.id.longValue)
db.update("recipient", values, where, whereArgs)
}
@ -2678,9 +2678,9 @@ object V149_LegacyMigrations : SignalDatabaseMigration {
private fun migrateReaction(db: SQLiteDatabase, cursor: Cursor, isMms: Boolean) {
try {
val messageId = CursorUtil.requireLong(cursor, "_id")
val reactionList = ReactionList.parseFrom(CursorUtil.requireBlob(cursor, "reactions"))
val reactionList = ReactionList.ADAPTER.decode(CursorUtil.requireBlob(cursor, "reactions"))
for (reaction in reactionList.reactionsList) {
for (reaction in reactionList.reactions) {
val contentValues = ContentValues().apply {
put("message_id", messageId)
put("is_mms", if (isMms) 1 else 0)
@ -2691,7 +2691,7 @@ object V149_LegacyMigrations : SignalDatabaseMigration {
}
db.insert("reaction", null, contentValues)
}
} catch (e: InvalidProtocolBufferException) {
} catch (e: IOException) {
Log.w(TAG, "Failed to parse reaction!")
}
}

View file

@ -69,7 +69,7 @@ class AvatarPickerDatabase(context: Context, databaseHelper: SignalDatabase) : D
val where = ID_WHERE
val values = ContentValues(1)
values.put(AVATAR, avatar.toProto().toByteArray())
values.put(AVATAR, avatar.toProto().encode())
db.update(TABLE_NAME, values, where, SqlUtil.buildArgs(databaseId.id))
}
@ -96,14 +96,14 @@ class AvatarPickerDatabase(context: Context, databaseHelper: SignalDatabase) : D
if (databaseId is Avatar.DatabaseId.Saved) {
val values = ContentValues(2)
values.put(AVATAR, avatar.toProto().toByteArray())
values.put(AVATAR, avatar.toProto().encode())
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(databaseId.id))
return avatar
} else {
val values = ContentValues(4)
values.put(AVATAR, avatar.toProto().toByteArray())
values.put(AVATAR, avatar.toProto().encode())
if (groupId != null) {
values.put(GROUP_ID, groupId.toString())
@ -126,7 +126,7 @@ class AvatarPickerDatabase(context: Context, databaseHelper: SignalDatabase) : D
while (it.moveToNext()) {
val id = CursorUtil.requireLong(it, ID)
val blob = CursorUtil.requireBlob(it, AVATAR)
val proto = CustomAvatar.parseFrom(blob)
val proto = CustomAvatar.ADAPTER.decode(blob)
results.add(proto.toAvatar(id))
}
}
@ -157,7 +157,7 @@ class AvatarPickerDatabase(context: Context, databaseHelper: SignalDatabase) : D
while (it.moveToNext()) {
val id = CursorUtil.requireLong(it, ID)
val blob = CursorUtil.requireBlob(it, AVATAR)
val proto = CustomAvatar.parseFrom(blob)
val proto = CustomAvatar.ADAPTER.decode(blob)
results.add(proto.toAvatar(id))
}
}
@ -167,18 +167,18 @@ class AvatarPickerDatabase(context: Context, databaseHelper: SignalDatabase) : D
private fun Avatar.toProto(): CustomAvatar {
return when (this) {
is Avatar.Photo -> CustomAvatar.newBuilder().setPhoto(CustomAvatar.Photo.newBuilder().setUri(this.uri.toString())).build()
is Avatar.Text -> CustomAvatar.newBuilder().setText(CustomAvatar.Text.newBuilder().setText(this.text).setColors(this.color.code)).build()
is Avatar.Vector -> CustomAvatar.newBuilder().setVector(CustomAvatar.Vector.newBuilder().setKey(this.key).setColors(this.color.code)).build()
is Avatar.Photo -> CustomAvatar(photo = CustomAvatar.Photo(uri = this.uri.toString()))
is Avatar.Text -> CustomAvatar(text = CustomAvatar.Text(text = this.text, colors = this.color.code))
is Avatar.Vector -> CustomAvatar(vector = CustomAvatar.Vector(key = this.key, colors = this.color.code))
else -> throw AssertionError()
}
}
private fun CustomAvatar.toAvatar(id: Long): Avatar {
return when {
hasPhoto() -> Avatar.Photo(Uri.parse(photo.uri), photo.size, Avatar.DatabaseId.Saved(id))
hasText() -> Avatar.Text(text.text, Avatars.colorMap[text.colors] ?: Avatars.colors[0], Avatar.DatabaseId.Saved(id))
hasVector() -> Avatar.Vector(vector.key, Avatars.colorMap[vector.colors] ?: Avatars.colors[0], Avatar.DatabaseId.Saved(id))
photo != null -> Avatar.Photo(Uri.parse(photo.uri), photo.size, Avatar.DatabaseId.Saved(id))
text != null -> Avatar.Text(text.text, Avatars.colorMap[text.colors] ?: Avatars.colors[0], Avatar.DatabaseId.Saved(id))
vector != null -> Avatar.Vector(vector.key, Avatars.colorMap[vector.colors] ?: Avatars.colors[0], Avatar.DatabaseId.Saved(id))
else -> throw AssertionError()
}
}

View file

@ -5,8 +5,8 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.util.Base64
object BodyRangeListSerializer : StringSerializer<BodyRangeList> {
override fun serialize(data: BodyRangeList): String = Base64.encodeBytes(data.toByteArray())
override fun deserialize(data: String): BodyRangeList = BodyRangeList.parseFrom(Base64.decode(data))
override fun serialize(data: BodyRangeList): String = Base64.encodeBytes(data.encode())
override fun deserialize(data: String): BodyRangeList = BodyRangeList.ADAPTER.decode(Base64.decode(data))
}
fun BodyRangeList.serialize(): String {

View file

@ -2,46 +2,27 @@
package org.thoughtcrime.securesms.database.model
import com.google.protobuf.ByteString
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.BodyRange
import org.whispersystems.signalservice.internal.push.BodyRange
/**
* Collection of extensions to make working with database protos cleaner.
*/
fun ByteArray.toProtoByteString(): ByteString {
return ByteString.copyFrom(this)
}
fun BodyRangeList.Builder.addStyle(style: BodyRangeList.BodyRange.Style, start: Int, length: Int): BodyRangeList.Builder {
addRanges(
BodyRangeList.BodyRange.newBuilder()
.setStyle(style)
.setStart(start)
.setLength(length)
)
ranges += BodyRangeList.BodyRange(style = style, start = start, length = length)
return this
}
fun BodyRangeList.Builder.addLink(link: String, start: Int, length: Int): BodyRangeList.Builder {
addRanges(
BodyRangeList.BodyRange.newBuilder()
.setLink(link)
.setStart(start)
.setLength(length)
)
ranges += BodyRangeList.BodyRange(link = link, start = start, length = length)
return this
}
fun BodyRangeList.Builder.addButton(label: String, action: String, start: Int, length: Int): BodyRangeList.Builder {
addRanges(
BodyRangeList.BodyRange.newBuilder()
.setButton(BodyRangeList.BodyRange.Button.newBuilder().setLabel(label).setAction(action))
.setStart(start)
.setLength(length)
ranges += BodyRangeList.BodyRange(
button = BodyRangeList.BodyRange.Button(label = label, action = action),
start = start,
length = length
)
return this
@ -52,7 +33,7 @@ fun List<BodyRange>?.toBodyRangeList(): BodyRangeList? {
return null
}
val builder = BodyRangeList.newBuilder()
val builder = BodyRangeList.Builder()
for (bodyRange in this) {
var style: BodyRangeList.BodyRange.Style? = null
@ -65,7 +46,7 @@ fun List<BodyRange>?.toBodyRangeList(): BodyRangeList? {
else -> Unit
}
if (style != null) {
builder.addStyle(style, bodyRange.start, bodyRange.length)
builder.addStyle(style, bodyRange.start!!, bodyRange.length!!)
}
}

View file

@ -6,7 +6,6 @@ import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.util.List;
@ -19,14 +18,14 @@ public final class GroupCallUpdateDetailsUtil {
}
public static @NonNull GroupCallUpdateDetails parse(@Nullable String body) {
GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetails.getDefaultInstance();
GroupCallUpdateDetails groupCallUpdateDetails = new GroupCallUpdateDetails();
if (body == null) {
return groupCallUpdateDetails;
}
try {
groupCallUpdateDetails = GroupCallUpdateDetails.parseFrom(Base64.decode(body));
groupCallUpdateDetails = GroupCallUpdateDetails.ADAPTER.decode(Base64.decode(body));
} catch (IOException e) {
Log.w(TAG, "Group call update details could not be read", e);
}
@ -35,14 +34,10 @@ public final class GroupCallUpdateDetailsUtil {
}
public static @NonNull String createUpdatedBody(@NonNull GroupCallUpdateDetails groupCallUpdateDetails, @NonNull List<String> inCallUuids, boolean isCallFull) {
GroupCallUpdateDetails.Builder builder = groupCallUpdateDetails.toBuilder()
.setIsCallFull(isCallFull)
.clearInCallUuids();
GroupCallUpdateDetails.Builder builder = groupCallUpdateDetails.newBuilder()
.isCallFull(isCallFull)
.inCallUuids(inCallUuids);
if (Util.hasItems(inCallUuids)) {
builder.addAllInCallUuids(inCallUuids);
}
return Base64.encodeBytes(builder.build().toByteArray());
return Base64.encodeBytes(builder.build().encode());
}
}

View file

@ -12,8 +12,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DateUtils;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import java.util.ArrayList;
import java.util.List;
@ -53,14 +53,14 @@ public class GroupCallUpdateMessageFactory implements UpdateDescription.Spannabl
}
private @NonNull String createString() {
String time = DateUtils.getTimeString(context, Locale.getDefault(), groupCallUpdateDetails.getStartedCallTimestamp());
String time = DateUtils.getTimeString(context, Locale.getDefault(), groupCallUpdateDetails.startedCallTimestamp);
switch (joinedMembers.size()) {
case 0:
return withTime ? context.getString(R.string.MessageRecord_group_call_s, time)
: context.getString(R.string.MessageRecord_group_call);
case 1:
if (joinedMembers.get(0).toString().equals(groupCallUpdateDetails.getStartedCallUuid())) {
if (joinedMembers.get(0).toString().equals(groupCallUpdateDetails.startedCallUuid)) {
if (Objects.equals(joinedMembers.get(0), selfAci)) {
return withTime ? context.getString(R.string.MessageRecord_you_started_a_group_call_s, time)
: context.getString(R.string.MessageRecord_you_started_a_group_call);

View file

@ -87,7 +87,7 @@ class GroupRecord(
val membershipAdditionAccessControl: GroupAccessControl
get() {
return if (isV2Group) {
if (requireV2GroupProperties().decryptedGroup.accessControl.members == AccessControl.AccessRequired.MEMBER) {
if (requireV2GroupProperties().decryptedGroup.accessControl!!.members == AccessControl.AccessRequired.MEMBER) {
GroupAccessControl.ALL_MEMBERS
} else {
GroupAccessControl.ONLY_ADMINS
@ -105,7 +105,7 @@ class GroupRecord(
val attributesAccessControl: GroupAccessControl
get() {
return if (isV2Group) {
if (requireV2GroupProperties().decryptedGroup.accessControl.attributes == AccessControl.AccessRequired.MEMBER) {
if (requireV2GroupProperties().decryptedGroup.accessControl!!.attributes == AccessControl.AccessRequired.MEMBER) {
GroupAccessControl.ALL_MEMBERS
} else {
GroupAccessControl.ONLY_ADMINS
@ -121,7 +121,7 @@ class GroupRecord(
if (isV2Group && memberLevel(Recipient.self()) == GroupTable.MemberLevel.ADMINISTRATOR) {
requireV2GroupProperties()
.decryptedGroup
.requestingMembersCount
.requestingMembers.size
} else {
0
}
@ -175,7 +175,7 @@ class GroupRecord(
if (isV2Group) {
val serviceId = recipient.serviceId
if (serviceId.isPresent) {
return DecryptedGroupUtil.findPendingByServiceId(requireV2GroupProperties().decryptedGroup.pendingMembersList, serviceId.get())
return DecryptedGroupUtil.findPendingByServiceId(requireV2GroupProperties().decryptedGroup.pendingMembers, serviceId.get())
.isPresent
}
}

View file

@ -12,8 +12,6 @@ import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import com.google.protobuf.ByteString;
import org.signal.core.util.StringUtil;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member;
@ -51,6 +49,8 @@ import java.util.UUID;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import okio.ByteString;
final class GroupsV2UpdateMessageProducer {
@NonNull private final Context context;
@ -72,26 +72,32 @@ final class GroupsV2UpdateMessageProducer {
* <p>
* When the revision of the group is 0, the change is very noisy and only the editor is useful.
*/
UpdateDescription describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange decryptedGroupChange) {
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByServiceId(group.getPendingMembersList(), selfIds.getAci());
if (!selfPending.isPresent() && selfIds.getPni() != null) {
selfPending = DecryptedGroupUtil.findPendingByServiceId(group.getPendingMembersList(), selfIds.getPni());
}
UpdateDescription describeNewGroup(@Nullable DecryptedGroup group, @Nullable DecryptedGroupChange decryptedGroupChange) {
Optional<DecryptedPendingMember> selfPending = Optional.empty();
if (selfPending.isPresent()) {
return updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, selfPending.get().getAddedByAci(), R.drawable.ic_update_group_add_16);
}
ByteString foundingMemberUuid = decryptedGroupChange.getEditorServiceIdBytes();
if (!foundingMemberUuid.isEmpty()) {
if (selfIds.matches(foundingMemberUuid)) {
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_16);
} else {
return updateDescription(R.string.MessageRecord_s_added_you, foundingMemberUuid, R.drawable.ic_update_group_add_16);
if (group != null) {
selfPending = DecryptedGroupUtil.findPendingByServiceId(group.pendingMembers, selfIds.getAci());
if (selfPending.isEmpty() && selfIds.getPni() != null) {
selfPending = DecryptedGroupUtil.findPendingByServiceId(group.pendingMembers, selfIds.getPni());
}
}
if (DecryptedGroupUtil.findMemberByAci(group.getMembersList(), selfIds.getAci()).isPresent()) {
if (selfPending.isPresent()) {
return updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, selfPending.get().addedByAci, R.drawable.ic_update_group_add_16);
}
if (decryptedGroupChange != null) {
ByteString foundingMemberUuid = decryptedGroupChange.editorServiceIdBytes;
if (foundingMemberUuid.size() > 0) {
if (selfIds.matches(foundingMemberUuid)) {
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_16);
} else {
return updateDescription(R.string.MessageRecord_s_added_you, foundingMemberUuid, R.drawable.ic_update_group_add_16);
}
}
}
if (group != null && DecryptedGroupUtil.findMemberByAci(group.members, selfIds.getAci()).isPresent()) {
return updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16);
} else {
return updateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
@ -99,14 +105,14 @@ final class GroupsV2UpdateMessageProducer {
}
List<UpdateDescription> describeChanges(@Nullable DecryptedGroup previousGroupState, @NonNull DecryptedGroupChange change) {
if (DecryptedGroup.getDefaultInstance().equals(previousGroupState)) {
if (new DecryptedGroup().equals(previousGroupState)) {
previousGroupState = null;
}
List<UpdateDescription> updates = new LinkedList<>();
boolean editorUnknown = change.getEditorServiceIdBytes().isEmpty();
ServiceId editorServiceId = editorUnknown ? null : ServiceId.parseOrNull(change.getEditorServiceIdBytes());
boolean editorUnknown = change.editorServiceIdBytes.size() == 0;
ServiceId editorServiceId = editorUnknown ? null : ServiceId.parseOrNull(change.editorServiceIdBytes);
if (editorServiceId == null || editorServiceId.isUnknown()) {
editorUnknown = true;
@ -172,12 +178,12 @@ final class GroupsV2UpdateMessageProducer {
* Handles case of future protocol versions where we don't know what has changed.
*/
private void describeUnknownChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_updated_group, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_16));
updates.add(updateDescription(R.string.MessageRecord_s_updated_group, change.editorServiceIdBytes, R.drawable.ic_update_group_16));
}
}
@ -186,25 +192,25 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeMemberAdditions(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
for (DecryptedMember member : change.getNewMembersList()) {
boolean newMemberIsYou = selfIds.matches(member.getAciBytes());
for (DecryptedMember member : change.newMembers) {
boolean newMemberIsYou = selfIds.matches(member.aciBytes);
if (editorIsYou) {
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group_via_the_group_link), R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_you_added_s, member.getAciBytes(), R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_you_added_s, member.aciBytes, R.drawable.ic_update_group_add_16));
}
} else {
if (newMemberIsYou) {
updates.add(0, updateDescription(R.string.MessageRecord_s_added_you, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_add_16));
updates.add(0, updateDescription(R.string.MessageRecord_s_added_you, change.editorServiceIdBytes, R.drawable.ic_update_group_add_16));
} else {
if (member.getAciBytes().equals(change.getEditorServiceIdBytes())) {
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group_via_the_group_link, member.getAciBytes(), R.drawable.ic_update_group_accept_16));
if (member.aciBytes.equals(change.editorServiceIdBytes)) {
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group_via_the_group_link, member.aciBytes, R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_s, change.getEditorServiceIdBytes(), member.getAciBytes(), R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_s_added_s, change.editorServiceIdBytes, member.aciBytes, R.drawable.ic_update_group_add_16));
}
}
}
@ -212,21 +218,21 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeUnknownEditorMemberAdditions(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember member : change.getNewMembersList()) {
boolean newMemberIsYou = selfIds.matches(member.getAciBytes());
for (DecryptedMember member : change.newMembers) {
boolean newMemberIsYou = selfIds.matches(member.aciBytes);
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, member.getAciBytes(), R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, member.aciBytes, R.drawable.ic_update_group_add_16));
}
}
}
private void describeMemberRemovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
for (ByteString member : change.getDeleteMembersList()) {
for (ByteString member : change.deleteMembers) {
boolean removedMemberIsYou = selfIds.matches(member);
if (editorIsYou) {
@ -237,12 +243,12 @@ final class GroupsV2UpdateMessageProducer {
}
} else {
if (removedMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_removed_you_from_the_group, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_remove_16));
updates.add(updateDescription(R.string.MessageRecord_s_removed_you_from_the_group, change.editorServiceIdBytes, R.drawable.ic_update_group_remove_16));
} else {
if (member.equals(change.getEditorServiceIdBytes())) {
if (member.equals(change.editorServiceIdBytes)) {
updates.add(updateDescription(R.string.MessageRecord_s_left_the_group, member, R.drawable.ic_update_group_leave_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_removed_s, change.getEditorServiceIdBytes(), member, R.drawable.ic_update_group_remove_16));
updates.add(updateDescription(R.string.MessageRecord_s_removed_s, change.editorServiceIdBytes, member, R.drawable.ic_update_group_remove_16));
}
}
}
@ -250,7 +256,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeUnknownEditorMemberRemovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (ByteString member : change.getDeleteMembersList()) {
for (ByteString member : change.deleteMembers) {
boolean removedMemberIsYou = selfIds.matches(member);
if (removedMemberIsYou) {
@ -262,29 +268,29 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeModifyMemberRoles(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
for (DecryptedModifyMemberRole roleChange : change.getModifyMemberRolesList()) {
boolean changedMemberIsYou = selfIds.matches(roleChange.getAciBytes());
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
for (DecryptedModifyMemberRole roleChange : change.modifyMemberRoles) {
boolean changedMemberIsYou = selfIds.matches(roleChange.aciBytes);
if (roleChange.role == Member.Role.ADMINISTRATOR) {
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_made_s_an_admin, roleChange.getAciBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_you_made_s_an_admin, roleChange.aciBytes, R.drawable.ic_update_group_role_16));
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_made_you_an_admin, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_made_you_an_admin, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_made_s_an_admin, change.getEditorServiceIdBytes(), roleChange.getAciBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_made_s_an_admin, change.editorServiceIdBytes, roleChange.aciBytes, R.drawable.ic_update_group_role_16));
}
}
} else {
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_revoked_admin_privileges_from_s, roleChange.getAciBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_you_revoked_admin_privileges_from_s, roleChange.aciBytes, R.drawable.ic_update_group_role_16));
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_admin_privileges, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_admin_privileges, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_admin_privileges_from_s, change.getEditorServiceIdBytes(), roleChange.getAciBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_revoked_admin_privileges_from_s, change.editorServiceIdBytes, roleChange.aciBytes, R.drawable.ic_update_group_role_16));
}
}
}
@ -292,37 +298,37 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeUnknownEditorModifyMemberRoles(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedModifyMemberRole roleChange : change.getModifyMemberRolesList()) {
boolean changedMemberIsYou = selfIds.matches(roleChange.getAciBytes());
for (DecryptedModifyMemberRole roleChange : change.modifyMemberRoles) {
boolean changedMemberIsYou = selfIds.matches(roleChange.aciBytes);
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
if (roleChange.role == Member.Role.ADMINISTRATOR) {
if (changedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_now_an_admin), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_is_now_an_admin, roleChange.getAciBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_is_now_an_admin, roleChange.aciBytes, R.drawable.ic_update_group_role_16));
}
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_an_admin), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_is_no_longer_an_admin, roleChange.getAciBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_is_no_longer_an_admin, roleChange.aciBytes, R.drawable.ic_update_group_role_16));
}
}
}
}
private void describeInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
int notYouInviteCount = 0;
for (DecryptedPendingMember invitee : change.getNewPendingMembersList()) {
boolean newMemberIsYou = selfIds.matches(invitee.getServiceIdBytes());
for (DecryptedPendingMember invitee : change.newPendingMembers) {
boolean newMemberIsYou = selfIds.matches(invitee.serviceIdBytes);
if (newMemberIsYou) {
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_add_16));
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, change.editorServiceIdBytes, R.drawable.ic_update_group_add_16));
} else {
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_invited_s_to_the_group, invitee.getServiceIdBytes(), R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_you_invited_s_to_the_group, invitee.serviceIdBytes, R.drawable.ic_update_group_add_16));
} else {
notYouInviteCount++;
}
@ -330,23 +336,23 @@ final class GroupsV2UpdateMessageProducer {
}
if (notYouInviteCount > 0) {
updates.add(updateDescription(R.plurals.MessageRecord_s_invited_members, notYouInviteCount, change.getEditorServiceIdBytes(), notYouInviteCount, R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.plurals.MessageRecord_s_invited_members, notYouInviteCount, change.editorServiceIdBytes, notYouInviteCount, R.drawable.ic_update_group_add_16));
}
}
private void describeUnknownEditorInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
int notYouInviteCount = 0;
for (DecryptedPendingMember invitee : change.getNewPendingMembersList()) {
boolean newMemberIsYou = selfIds.matches(invitee.getServiceIdBytes());
for (DecryptedPendingMember invitee : change.newPendingMembers) {
boolean newMemberIsYou = selfIds.matches(invitee.serviceIdBytes);
if (newMemberIsYou) {
UUID uuid = UuidUtil.fromByteStringOrUnknown(invitee.getAddedByAci());
UUID uuid = UuidUtil.fromByteStringOrUnknown(invitee.addedByAci);
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_were_invited_to_the_group), R.drawable.ic_update_group_add_16));
} else {
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, invitee.getAddedByAci(), R.drawable.ic_update_group_add_16));
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, invitee.addedByAci, R.drawable.ic_update_group_add_16));
}
} else {
notYouInviteCount++;
@ -359,19 +365,19 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeRevokedInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
int notDeclineCount = 0;
for (DecryptedPendingMemberRemoval invitee : change.getDeletePendingMembersList()) {
boolean decline = invitee.getServiceIdBytes().equals(change.getEditorServiceIdBytes());
for (DecryptedPendingMemberRemoval invitee : change.deletePendingMembers) {
boolean decline = invitee.serviceIdBytes.equals(change.editorServiceIdBytes);
if (decline) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_declined_the_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
}
} else if (selfIds.matches(invitee.getServiceIdBytes())) {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_invitation_to_the_group, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_decline_16));
} else if (selfIds.matches(invitee.serviceIdBytes)) {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_invitation_to_the_group, change.editorServiceIdBytes, R.drawable.ic_update_group_decline_16));
} else {
notDeclineCount++;
}
@ -381,7 +387,7 @@ final class GroupsV2UpdateMessageProducer {
if (editorIsYou) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notDeclineCount, notDeclineCount), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(R.plurals.MessageRecord_s_revoked_invites, notDeclineCount, change.getEditorServiceIdBytes(), notDeclineCount, R.drawable.ic_update_group_decline_16));
updates.add(updateDescription(R.plurals.MessageRecord_s_revoked_invites, notDeclineCount, change.editorServiceIdBytes, notDeclineCount, R.drawable.ic_update_group_decline_16));
}
}
}
@ -389,8 +395,8 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorRevokedInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
int notDeclineCount = 0;
for (DecryptedPendingMemberRemoval invitee : change.getDeletePendingMembersList()) {
boolean inviteeWasYou = selfIds.matches(invitee.getServiceIdBytes());
for (DecryptedPendingMemberRemoval invitee : change.deletePendingMembers) {
boolean inviteeWasYou = selfIds.matches(invitee.serviceIdBytes);
if (inviteeWasYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_an_admin_revoked_your_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
@ -405,10 +411,10 @@ final class GroupsV2UpdateMessageProducer {
}
private void describePromotePending(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
for (DecryptedMember newMember : change.getPromotePendingMembersList()) {
ByteString aci = newMember.getAciBytes();
for (DecryptedMember newMember : change.promotePendingMembers) {
ByteString aci = newMember.aciBytes;
boolean newMemberIsYou = selfIds.matches(aci);
if (editorIsYou) {
@ -419,12 +425,12 @@ final class GroupsV2UpdateMessageProducer {
}
} else {
if (newMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_added_you, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_s_added_you, change.editorServiceIdBytes, R.drawable.ic_update_group_add_16));
} else {
if (aci.equals(change.getEditorServiceIdBytes())) {
if (aci.equals(change.editorServiceIdBytes)) {
updates.add(updateDescription(R.string.MessageRecord_s_accepted_invite, aci, R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, change.getEditorServiceIdBytes(), aci, R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, change.editorServiceIdBytes, aci, R.drawable.ic_update_group_add_16));
}
}
}
@ -432,8 +438,8 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeUnknownEditorPromotePending(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember newMember : change.getPromotePendingMembersList()) {
ByteString aci = newMember.getAciBytes();
for (DecryptedMember newMember : change.promotePendingMembers) {
ByteString aci = newMember.aciBytes;
boolean newMemberIsYou = selfIds.matches(aci);
if (newMemberIsYou) {
@ -445,116 +451,116 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeNewTitle(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.hasNewTitle()) {
String newTitle = StringUtil.isolateBidi(change.getNewTitle().getValue());
if (change.newTitle != null) {
String newTitle = StringUtil.isolateBidi(change.newTitle.value_);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_name_to_s, newTitle), R.drawable.ic_update_group_name_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_name_to_s, change.getEditorServiceIdBytes(), newTitle, R.drawable.ic_update_group_name_16));
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_name_to_s, change.editorServiceIdBytes, newTitle, R.drawable.ic_update_group_name_16));
}
}
}
private void describeNewDescription(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.hasNewDescription()) {
if (change.newDescription != null) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_description), R.drawable.ic_update_group_name_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_description, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_name_16));
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_description, change.editorServiceIdBytes, R.drawable.ic_update_group_name_16));
}
}
}
private void describeUnknownEditorNewTitle(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewTitle()) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_name_has_changed_to_s, StringUtil.isolateBidi(change.getNewTitle().getValue())), R.drawable.ic_update_group_name_16));
if (change.newTitle != null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_name_has_changed_to_s, StringUtil.isolateBidi(change.newTitle.value_)), R.drawable.ic_update_group_name_16));
}
}
private void describeUnknownEditorNewDescription(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewDescription()) {
if (change.newDescription != null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_description_has_changed), R.drawable.ic_update_group_name_16));
}
}
private void describeNewAvatar(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.hasNewAvatar()) {
if (change.newAvatar != null) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_avatar), R.drawable.ic_update_group_avatar_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_avatar, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_avatar_16));
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_avatar, change.editorServiceIdBytes, R.drawable.ic_update_group_avatar_16));
}
}
}
private void describeUnknownEditorNewAvatar(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewAvatar()) {
if (change.newAvatar != null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_group_avatar_has_been_changed), R.drawable.ic_update_group_avatar_16));
}
}
void describeNewTimer(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.hasNewTimer()) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.getNewTimer().getDuration());
if (change.newTimer != null) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.newTimer.duration);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), R.drawable.ic_update_timer_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_set_disappearing_message_time_to_s, change.getEditorServiceIdBytes(), time, R.drawable.ic_update_timer_16));
updates.add(updateDescription(R.string.MessageRecord_s_set_disappearing_message_time_to_s, change.editorServiceIdBytes, time, R.drawable.ic_update_timer_16));
}
}
}
private void describeUnknownEditorNewTimer(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewTimer()) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.getNewTimer().getDuration());
if (change.newTimer != null) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.newTimer.duration);
updates.add(updateDescription(context.getString(R.string.MessageRecord_disappearing_message_time_set_to_s, time), R.drawable.ic_update_timer_16));
}
}
private void describeNewAttributeAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.getNewAttributeAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewAttributeAccess());
if (change.newAttributeAccess != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.newAttributeAccess);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_info_to_s, accessLevel), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_info_to_s, change.getEditorServiceIdBytes(), accessLevel, R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_info_to_s, change.editorServiceIdBytes, accessLevel, R.drawable.ic_update_group_role_16));
}
}
}
private void describeUnknownEditorNewAttributeAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.getNewAttributeAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewAttributeAccess());
if (change.newAttributeAccess != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.newAttributeAccess);
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_info_has_been_changed_to_s, accessLevel), R.drawable.ic_update_group_role_16));
}
}
private void describeNewMembershipAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.getNewMemberAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewMemberAccess());
if (change.newMemberAccess != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.newMemberAccess);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_membership_to_s, accessLevel), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_membership_to_s, change.getEditorServiceIdBytes(), accessLevel, R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_membership_to_s, change.editorServiceIdBytes, accessLevel, R.drawable.ic_update_group_role_16));
}
}
}
private void describeUnknownEditorNewMembershipAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.getNewMemberAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewMemberAccess());
if (change.newMemberAccess != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.newMemberAccess);
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_membership_has_been_changed_to_s, accessLevel), R.drawable.ic_update_group_role_16));
}
}
@ -565,14 +571,15 @@ final class GroupsV2UpdateMessageProducer {
{
AccessControl.AccessRequired previousAccessControl = null;
if (previousGroupState != null) {
previousAccessControl = previousGroupState.getAccessControl().getAddFromInviteLink();
if (previousGroupState != null && previousGroupState.accessControl != null) {
previousAccessControl = previousGroupState.accessControl.addFromInviteLink;
}
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
boolean groupLinkEnabled = false;
switch (change.getNewInviteLinkAccess()) {
//noinspection EnhancedSwitchMigration
switch (change.newInviteLinkAccess) {
case ANY:
groupLinkEnabled = true;
if (editorIsYou) {
@ -583,9 +590,9 @@ final class GroupsV2UpdateMessageProducer {
}
} else {
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_admin_approval_for_the_group_link, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_admin_approval_for_the_group_link, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
}
}
break;
@ -599,9 +606,9 @@ final class GroupsV2UpdateMessageProducer {
}
} else {
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_admin_approval_for_the_group_link, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_admin_approval_for_the_group_link, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
}
}
break;
@ -609,16 +616,16 @@ final class GroupsV2UpdateMessageProducer {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_the_group_link), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_the_group_link, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_the_group_link, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
}
break;
}
if (!groupLinkEnabled && change.getNewInviteLinkPassword().size() > 0) {
if (!groupLinkEnabled && change.newInviteLinkPassword.size() > 0) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_reset_the_group_link), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_reset_the_group_link, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_reset_the_group_link, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
}
}
}
@ -629,11 +636,12 @@ final class GroupsV2UpdateMessageProducer {
{
AccessControl.AccessRequired previousAccessControl = null;
if (previousGroupState != null) {
previousAccessControl = previousGroupState.getAccessControl().getAddFromInviteLink();
if (previousGroupState != null && previousGroupState.accessControl != null) {
previousAccessControl = previousGroupState.accessControl.addFromInviteLink;
}
switch (change.getNewInviteLinkAccess()) {
//noinspection EnhancedSwitchMigration
switch (change.newInviteLinkAccess) {
case ANY:
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_off), R.drawable.ic_update_group_role_16));
@ -653,69 +661,69 @@ final class GroupsV2UpdateMessageProducer {
break;
}
if (change.getNewInviteLinkPassword().size() > 0) {
if (change.newInviteLinkPassword.size() > 0) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_reset), R.drawable.ic_update_group_role_16));
}
}
private void describeRequestingMembers(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
Set<ByteString> deleteRequestingUuids = new HashSet<>(change.getDeleteRequestingMembersList());
Set<ByteString> deleteRequestingUuids = new HashSet<>(change.deleteRequestingMembers);
for (DecryptedRequestingMember member : change.getNewRequestingMembersList()) {
boolean requestingMemberIsYou = selfIds.matches(member.getAciBytes());
for (DecryptedRequestingMember member : change.newRequestingMembers) {
boolean requestingMemberIsYou = selfIds.matches(member.aciBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group), R.drawable.ic_update_group_16));
} else {
if (deleteRequestingUuids.contains(member.getAciBytes())) {
if (deleteRequestingUuids.contains(member.aciBytes)) {
updates.add(updateDescription(R.plurals.MessageRecord_s_requested_and_cancelled_their_request_to_join_via_the_group_link,
change.getDeleteRequestingMembersCount(),
member.getAciBytes(),
change.getDeleteRequestingMembersCount(),
change.deleteRequestingMembers.size(),
member.aciBytes,
change.deleteRequestingMembers.size(),
R.drawable.ic_update_group_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_requested_to_join_via_the_group_link, member.getAciBytes(), R.drawable.ic_update_group_16));
updates.add(updateDescription(R.string.MessageRecord_s_requested_to_join_via_the_group_link, member.aciBytes, R.drawable.ic_update_group_16));
}
}
}
}
private void describeRequestingMembersApprovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedApproveMember requestingMember : change.getPromoteRequestingMembersList()) {
boolean requestingMemberIsYou = selfIds.matches(requestingMember.getAciBytes());
for (DecryptedApproveMember requestingMember : change.promoteRequestingMembers) {
boolean requestingMemberIsYou = selfIds.matches(requestingMember.aciBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_approved_your_request_to_join_the_group, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_accept_16));
updates.add(updateDescription(R.string.MessageRecord_s_approved_your_request_to_join_the_group, change.editorServiceIdBytes, R.drawable.ic_update_group_accept_16));
} else {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, requestingMember.getAciBytes(), R.drawable.ic_update_group_accept_16));
updates.add(updateDescription(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, requestingMember.aciBytes, R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_approved_a_request_to_join_the_group_from_s, change.getEditorServiceIdBytes(), requestingMember.getAciBytes(), R.drawable.ic_update_group_accept_16));
updates.add(updateDescription(R.string.MessageRecord_s_approved_a_request_to_join_the_group_from_s, change.editorServiceIdBytes, requestingMember.aciBytes, R.drawable.ic_update_group_accept_16));
}
}
}
}
private void describeUnknownEditorRequestingMembersApprovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedApproveMember requestingMember : change.getPromoteRequestingMembersList()) {
boolean requestingMemberIsYou = selfIds.matches(requestingMember.getAciBytes());
for (DecryptedApproveMember requestingMember : change.promoteRequestingMembers) {
boolean requestingMemberIsYou = selfIds.matches(requestingMember.aciBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved), R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_approved, requestingMember.getAciBytes(), R.drawable.ic_update_group_accept_16));
updates.add(updateDescription(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_approved, requestingMember.aciBytes, R.drawable.ic_update_group_accept_16));
}
}
}
private void describeRequestingMembersDeletes(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
Set<ByteString> newRequestingUuids = change.getNewRequestingMembersList().stream().map(DecryptedRequestingMember::getAciBytes).collect(Collectors.toSet());
Set<ByteString> newRequestingUuids = change.newRequestingMembers.stream().map(m -> m.aciBytes).collect(Collectors.toSet());
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
for (ByteString requestingMember : change.getDeleteRequestingMembersList()) {
for (ByteString requestingMember : change.deleteRequestingMembers) {
if (newRequestingUuids.contains(requestingMember)) {
continue;
}
@ -729,19 +737,19 @@ final class GroupsV2UpdateMessageProducer {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin), R.drawable.ic_update_group_decline_16));
}
} else {
boolean editorIsCanceledMember = change.getEditorServiceIdBytes().equals(requestingMember);
boolean editorIsCanceledMember = change.editorServiceIdBytes.equals(requestingMember);
if (editorIsCanceledMember) {
updates.add(updateDescription(R.string.MessageRecord_s_canceled_their_request_to_join_the_group, requestingMember, R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_denied_a_request_to_join_the_group_from_s, change.getEditorServiceIdBytes(), requestingMember, R.drawable.ic_update_group_decline_16));
updates.add(updateDescription(R.string.MessageRecord_s_denied_a_request_to_join_the_group_from_s, change.editorServiceIdBytes, requestingMember, R.drawable.ic_update_group_decline_16));
}
}
}
}
private void describeUnknownEditorRequestingMembersDeletes(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (ByteString requestingMember : change.getDeleteRequestingMembersList()) {
for (ByteString requestingMember : change.deleteRequestingMembers) {
boolean requestingMemberIsYou = selfIds.matches(requestingMember);
if (requestingMemberIsYou) {
@ -753,36 +761,36 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeAnnouncementGroupChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
if (change.getNewIsAnnouncementGroup() == EnabledState.ENABLED) {
if (change.newIsAnnouncementGroup == EnabledState.ENABLED) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_allow_only_admins_to_send, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_allow_only_admins_to_send, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
}
} else if (change.getNewIsAnnouncementGroup() == EnabledState.DISABLED) {
} else if (change.newIsAnnouncementGroup == EnabledState.DISABLED) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_allow_all_members_to_send, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_role_16));
updates.add(updateDescription(R.string.MessageRecord_s_allow_all_members_to_send, change.editorServiceIdBytes, R.drawable.ic_update_group_role_16));
}
}
}
private void describeUnknownEditorAnnouncementGroupChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.getNewIsAnnouncementGroup() == EnabledState.ENABLED) {
if (change.newIsAnnouncementGroup == EnabledState.ENABLED) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
} else if (change.getNewIsAnnouncementGroup() == EnabledState.DISABLED) {
} else if (change.newIsAnnouncementGroup == EnabledState.DISABLED) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
}
}
private void describePromotePendingPniAci(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditorServiceIdBytes());
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
for (DecryptedMember newMember : change.getPromotePendingPniAciMembersList()) {
ByteString uuid = newMember.getAciBytes();
for (DecryptedMember newMember : change.promotePendingPniAciMembers) {
ByteString uuid = newMember.aciBytes;
boolean newMemberIsYou = selfIds.matches(uuid);
if (editorIsYou) {
@ -793,12 +801,12 @@ final class GroupsV2UpdateMessageProducer {
}
} else {
if (newMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_added_you, change.getEditorServiceIdBytes(), R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_s_added_you, change.editorServiceIdBytes, R.drawable.ic_update_group_add_16));
} else {
if (uuid.equals(change.getEditorServiceIdBytes())) {
if (uuid.equals(change.editorServiceIdBytes)) {
updates.add(updateDescription(R.string.MessageRecord_s_accepted_invite, uuid, R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, change.getEditorServiceIdBytes(), uuid, R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, change.editorServiceIdBytes, uuid, R.drawable.ic_update_group_add_16));
}
}
}
@ -806,8 +814,8 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeUnknownEditorPromotePendingPniAci(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember newMember : change.getPromotePendingPniAciMembersList()) {
ByteString aci = newMember.getAciBytes();
for (DecryptedMember newMember : change.promotePendingPniAciMembers) {
ByteString aci = newMember.aciBytes;
boolean newMemberIsYou = selfIds.matches(aci);
if (newMemberIsYou) {

View file

@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.database.model
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.crypto.ContentHint
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.Content
/**
* Model class for reading from the [org.thoughtcrime.securesms.database.MessageSendLogTables].
@ -10,7 +10,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos
data class MessageLogEntry(
val recipientId: RecipientId,
val dateSent: Long,
val content: SignalServiceProtos.Content,
val content: Content,
val contentHint: ContentHint,
@get:JvmName("isUrgent")
val urgent: Boolean,

View file

@ -31,8 +31,6 @@ import androidx.annotation.WorkerThread;
import androidx.core.content.ContextCompat;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.signal.core.util.StringUtil;
import org.signal.core.util.logging.Log;
@ -64,8 +62,8 @@ import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
@ -77,6 +75,8 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import okio.ByteString;
/**
* The base class for message record models that are displayed in
* conversations, as opposed to models that are displayed in a thread list.
@ -235,26 +235,26 @@ public abstract class MessageRecord extends DisplayRecord {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_a_message_from_s_couldnt_be_delivered, r.getDisplayName(context)), R.drawable.ic_error_outline_14);
} else if (isThreadMergeEventType()) {
try {
ThreadMergeEvent event = ThreadMergeEvent.parseFrom(Base64.decodeOrThrow(getBody()));
ThreadMergeEvent event = ThreadMergeEvent.ADAPTER.decode(Base64.decodeOrThrow(getBody()));
if (event.getPreviousE164().isEmpty()) {
if (event.previousE164.isEmpty()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_another_chat_has_been_merged, r.getDisplayName(context)), R.drawable.ic_thread_merge_16);
} else {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_their_number_s_has_been_merged, r.getDisplayName(context), PhoneNumberFormatter.prettyPrint(event.getPreviousE164())), R.drawable.ic_thread_merge_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_their_number_s_has_been_merged, r.getDisplayName(context), PhoneNumberFormatter.prettyPrint(event.previousE164)), R.drawable.ic_thread_merge_16);
}
} catch (InvalidProtocolBufferException e) {
} catch (IOException e) {
throw new AssertionError(e);
}
} else if (isSessionSwitchoverEventType()) {
try {
SessionSwitchoverEvent event = SessionSwitchoverEvent.parseFrom(Base64.decodeOrThrow(getBody()));
SessionSwitchoverEvent event = SessionSwitchoverEvent.ADAPTER.decode(Base64.decodeOrThrow(getBody()));
if (event.getE164().isEmpty()) {
if (event.e164.isEmpty()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)), R.drawable.ic_update_safety_number_16);
} else {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_belongs_to_s, PhoneNumberFormatter.prettyPrint(r.requireE164()), r.getDisplayName(context)), R.drawable.ic_update_info_16);
}
} catch (InvalidProtocolBufferException e) {
} catch (IOException e) {
throw new AssertionError(e);
}
} else if (isSmsExportType()) {
@ -282,7 +282,7 @@ public abstract class MessageRecord extends DisplayRecord {
if (decryptedGroupV2Context == null) {
return false;
}
DecryptedGroupChange change = decryptedGroupV2Context.getChange();
DecryptedGroupChange change = decryptedGroupV2Context.change;
return selfCreatedGroup(change);
}
@ -296,7 +296,7 @@ public abstract class MessageRecord extends DisplayRecord {
DecryptedGroupV2Context decryptedGroupV2Context;
try {
byte[] decoded = Base64.decode(getBody());
decryptedGroupV2Context = DecryptedGroupV2Context.parseFrom(decoded);
decryptedGroupV2Context = DecryptedGroupV2Context.ADAPTER.decode(decoded);
} catch (IOException e) {
Log.w(TAG, "GV2 Message update detail could not be read", e);
@ -305,28 +305,29 @@ public abstract class MessageRecord extends DisplayRecord {
return decryptedGroupV2Context;
}
private static boolean selfCreatedGroup(@NonNull DecryptedGroupChange change) {
return change.getRevision() == 0 &&
change.getEditorServiceIdBytes().equals(SignalStore.account().requireAci().toByteString());
private static boolean selfCreatedGroup(@Nullable DecryptedGroupChange change) {
return change != null &&
change.revision == 0 &&
change.editorServiceIdBytes.equals(SignalStore.account().requireAci().toByteString());
}
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull String body, @Nullable Consumer<RecipientId> recipientClickHandler) {
try {
byte[] decoded = Base64.decode(body);
DecryptedGroupV2Context decryptedGroupV2Context = DecryptedGroupV2Context.parseFrom(decoded);
DecryptedGroupV2Context decryptedGroupV2Context = DecryptedGroupV2Context.ADAPTER.decode(decoded);
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, SignalStore.account().getServiceIds(), recipientClickHandler);
if (decryptedGroupV2Context.hasChange() && (decryptedGroupV2Context.getGroupState().getRevision() != 0 || decryptedGroupV2Context.hasPreviousGroupState())) {
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.getPreviousGroupState(), decryptedGroupV2Context.getChange()));
if (decryptedGroupV2Context.change != null && ((decryptedGroupV2Context.groupState != null && decryptedGroupV2Context.groupState.revision != 0) || decryptedGroupV2Context.previousGroupState != null)) {
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.previousGroupState, decryptedGroupV2Context.change));
} else {
List<UpdateDescription> newGroupDescriptions = new ArrayList<>();
newGroupDescriptions.add(updateMessageProducer.describeNewGroup(decryptedGroupV2Context.getGroupState(), decryptedGroupV2Context.getChange()));
newGroupDescriptions.add(updateMessageProducer.describeNewGroup(decryptedGroupV2Context.groupState, decryptedGroupV2Context.change));
if (decryptedGroupV2Context.getChange().hasNewTimer()) {
updateMessageProducer.describeNewTimer(decryptedGroupV2Context.getChange(), newGroupDescriptions);
if (decryptedGroupV2Context.change != null && decryptedGroupV2Context.change.newTimer != null) {
updateMessageProducer.describeNewTimer(decryptedGroupV2Context.change, newGroupDescriptions);
}
if (selfCreatedGroup(decryptedGroupV2Context.getChange())) {
if (selfCreatedGroup(decryptedGroupV2Context.change)) {
newGroupDescriptions.add(staticUpdateDescription(context.getString(R.string.MessageRecord_invite_friends_to_this_group), 0));
}
return UpdateDescription.concatWithNewLines(newGroupDescriptions);
@ -344,11 +345,11 @@ public abstract class MessageRecord extends DisplayRecord {
return null;
}
DecryptedGroup groupState = decryptedGroupV2Context.getGroupState();
boolean invited = DecryptedGroupUtil.findPendingByServiceId(groupState.getPendingMembersList(), SignalStore.account().requireAci()).isPresent();
DecryptedGroup groupState = decryptedGroupV2Context.groupState;
boolean invited = groupState != null && DecryptedGroupUtil.findPendingByServiceId(groupState.pendingMembers, SignalStore.account().requireAci()).isPresent();
if (decryptedGroupV2Context.hasChange()) {
ServiceId changeEditor = ServiceId.parseOrNull(decryptedGroupV2Context.getChange().getEditorServiceIdBytes());
if (decryptedGroupV2Context.change != null) {
ServiceId changeEditor = ServiceId.parseOrNull(decryptedGroupV2Context.change.editorServiceIdBytes);
if (changeEditor != null) {
if (changeEditor instanceof ACI) {
@ -394,12 +395,12 @@ public abstract class MessageRecord extends DisplayRecord {
private @NonNull String getProfileChangeDescription(@NonNull Context context) {
try {
byte[] decoded = Base64.decode(getBody());
ProfileChangeDetails profileChangeDetails = ProfileChangeDetails.parseFrom(decoded);
ProfileChangeDetails profileChangeDetails = ProfileChangeDetails.ADAPTER.decode(decoded);
if (profileChangeDetails.hasProfileNameChange()) {
if (profileChangeDetails.profileNameChange != null) {
String displayName = getFromRecipient().getDisplayName(context);
String newName = StringUtil.isolateBidi(ProfileName.fromSerialized(profileChangeDetails.getProfileNameChange().getNew()).toString());
String previousName = StringUtil.isolateBidi(ProfileName.fromSerialized(profileChangeDetails.getProfileNameChange().getPrevious()).toString());
String newName = StringUtil.isolateBidi(ProfileName.fromSerialized(profileChangeDetails.profileNameChange.newValue).toString());
String previousName = StringUtil.isolateBidi(ProfileName.fromSerialized(profileChangeDetails.profileNameChange.previous).toString());
if (getFromRecipient().isSystemContact()) {
return context.getString(R.string.MessageRecord_changed_their_profile_name_from_to, displayName, previousName, newName);
@ -440,7 +441,7 @@ public abstract class MessageRecord extends DisplayRecord {
public static @NonNull UpdateDescription getGroupCallUpdateDescription(@NonNull Context context, @NonNull String body, boolean withTime) {
GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(body);
List<ACI> joinedMembers = Stream.of(groupCallUpdateDetails.getInCallUuidsList())
List<ACI> joinedMembers = Stream.of(groupCallUpdateDetails.inCallUuids)
.map(UuidUtil::parseOrNull)
.withoutNulls()
.map(ACI::from)
@ -454,15 +455,15 @@ public abstract class MessageRecord extends DisplayRecord {
public boolean isGroupV2DescriptionUpdate() {
DecryptedGroupV2Context decryptedGroupV2Context = getDecryptedGroupV2Context();
if (decryptedGroupV2Context != null) {
return decryptedGroupV2Context.hasChange() && getDecryptedGroupV2Context().getChange().hasNewDescription();
return decryptedGroupV2Context.change != null && decryptedGroupV2Context.change.newDescription != null;
}
return false;
}
public @NonNull String getGroupV2DescriptionUpdate() {
DecryptedGroupV2Context decryptedGroupV2Context = getDecryptedGroupV2Context();
if (decryptedGroupV2Context != null) {
return decryptedGroupV2Context.getChange().hasNewDescription() ? decryptedGroupV2Context.getChange().getNewDescription().getValue() : "";
if (decryptedGroupV2Context != null && decryptedGroupV2Context.change != null) {
return decryptedGroupV2Context.change.newDescription != null ? decryptedGroupV2Context.change.newDescription.value_ : "";
}
return "";
}
@ -474,11 +475,11 @@ public abstract class MessageRecord extends DisplayRecord {
DecryptedGroupV2Context decryptedGroupV2Context = getDecryptedGroupV2Context();
if (decryptedGroupV2Context != null && decryptedGroupV2Context.hasChange()) {
DecryptedGroupChange change = decryptedGroupV2Context.getChange();
if (decryptedGroupV2Context != null && decryptedGroupV2Context.change != null) {
DecryptedGroupChange change = decryptedGroupV2Context.change;
ByteString serviceIdByteString = serviceId.toByteString();
return change.getEditorServiceIdBytes().equals(serviceIdByteString) && change.getNewRequestingMembersList().stream().anyMatch(r -> r.getAciBytes().equals(serviceIdByteString));
return change.editorServiceIdBytes.equals(serviceIdByteString) && change.newRequestingMembers.stream().anyMatch(r -> r.aciBytes.equals(serviceIdByteString));
}
return false;
}
@ -489,11 +490,11 @@ public abstract class MessageRecord extends DisplayRecord {
public boolean isCollapsedGroupV2JoinUpdate(@Nullable ServiceId serviceId) {
DecryptedGroupV2Context decryptedGroupV2Context = getDecryptedGroupV2Context();
if (decryptedGroupV2Context != null && decryptedGroupV2Context.hasChange()) {
DecryptedGroupChange change = decryptedGroupV2Context.getChange();
return change.getNewRequestingMembersCount() > 0 &&
change.getDeleteRequestingMembersCount() > 0 &&
(serviceId == null || change.getEditorServiceIdBytes().equals(serviceId.toByteString()));
if (decryptedGroupV2Context != null && decryptedGroupV2Context.change != null) {
DecryptedGroupChange change = decryptedGroupV2Context.change;
return change.newRequestingMembers.size() > 0 &&
change.deleteRequestingMembers.size() > 0 &&
(serviceId == null || change.editorServiceIdBytes.equals(serviceId.toByteString()));
}
return false;
}
@ -501,14 +502,19 @@ public abstract class MessageRecord extends DisplayRecord {
public static @NonNull String createNewContextWithAppendedDeleteJoinRequest(@NonNull MessageRecord messageRecord, int revision, @NonNull ByteString id) {
DecryptedGroupV2Context decryptedGroupV2Context = messageRecord.getDecryptedGroupV2Context();
if (decryptedGroupV2Context != null && decryptedGroupV2Context.hasChange()) {
DecryptedGroupChange change = decryptedGroupV2Context.getChange();
if (decryptedGroupV2Context != null && decryptedGroupV2Context.change != null) {
DecryptedGroupChange change = decryptedGroupV2Context.change;
return Base64.encodeBytes(decryptedGroupV2Context.toBuilder()
.setChange(change.toBuilder()
.setRevision(revision)
.addDeleteRequestingMembers(id))
.build().toByteArray());
List<ByteString> deleteRequestingMembers = new ArrayList<>(change.deleteRequestingMembers);
deleteRequestingMembers.add(id);
return Base64.encodeBytes(decryptedGroupV2Context.newBuilder()
.change(change.newBuilder()
.revision(revision)
.deleteRequestingMembers(deleteRequestingMembers)
.build())
.build()
.encode());
}
throw new AssertionError("Attempting to modify a message with no change");
@ -553,10 +559,6 @@ public abstract class MessageRecord extends DisplayRecord {
return MessageTypes.isBundleKeyExchange(type);
}
public boolean isContentBundleKeyExchange() {
return MessageTypes.isContentBundleKeyExchange(type);
}
public boolean isRateLimited() {
return MessageTypes.isRateLimited(type);
}
@ -565,10 +567,6 @@ public abstract class MessageRecord extends DisplayRecord {
return MessageTypes.isIdentityUpdate(type);
}
public boolean isCorruptedKeyExchange() {
return MessageTypes.isCorruptedKeyExchange(type);
}
public boolean isBadDecryptType() {
return MessageTypes.isBadDecryptType(type);
}
@ -585,10 +583,6 @@ public abstract class MessageRecord extends DisplayRecord {
return MessageTypes.isSmsExport(type);
}
public boolean isInvalidVersionKeyExchange() {
return MessageTypes.isInvalidVersionKeyExchange(type);
}
public boolean isGroupV1MigrationEvent() {
return MessageTypes.isGroupV1MigrationEvent(type);
}
@ -654,8 +648,7 @@ public abstract class MessageRecord extends DisplayRecord {
}
public boolean equals(Object other) {
return other != null &&
other instanceof MessageRecord &&
return other instanceof MessageRecord &&
((MessageRecord) other).getId() == getId() &&
((MessageRecord) other).isMms() == isMms();
}

View file

@ -156,17 +156,15 @@ class SignalSmsExportReader(
private fun mapExportState(messageExportState: MessageExportState): SmsExportState {
return SmsExportState(
messageId = messageExportState.messageId,
startedRecipients = messageExportState.startedRecipientsList.toSet(),
completedRecipients = messageExportState.completedRecipientsList.toSet(),
startedAttachments = messageExportState.startedAttachmentsList.toSet(),
completedAttachments = messageExportState.completedAttachmentsList.toSet(),
startedRecipients = messageExportState.startedRecipients.toSet(),
completedRecipients = messageExportState.completedRecipients.toSet(),
startedAttachments = messageExportState.startedAttachments.toSet(),
completedAttachments = messageExportState.completedAttachments.toSet(),
progress = messageExportState.progress.let {
when (it) {
MessageExportState.Progress.INIT -> SmsExportState.Progress.INIT
MessageExportState.Progress.STARTED -> SmsExportState.Progress.STARTED
MessageExportState.Progress.COMPLETED -> SmsExportState.Progress.COMPLETED
MessageExportState.Progress.UNRECOGNIZED -> SmsExportState.Progress.INIT
null -> SmsExportState.Progress.INIT
}
}
)

View file

@ -104,13 +104,13 @@ class SignalSmsExportService : SmsExportService() {
override fun onMessageExportStarted(exportableMessage: ExportableMessage) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().setProgress(MessageExportState.Progress.STARTED).build()
it.newBuilder().progress(MessageExportState.Progress.STARTED).build()
}
}
override fun onMessageExportSucceeded(exportableMessage: ExportableMessage) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().setProgress(MessageExportState.Progress.COMPLETED).build()
it.newBuilder().progress(MessageExportState.Progress.COMPLETED).build()
}
SignalDatabase.messages.markMessageExported(exportableMessage.getMessageId())
@ -118,7 +118,7 @@ class SignalSmsExportService : SmsExportService() {
override fun onMessageExportFailed(exportableMessage: ExportableMessage) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().setProgress(MessageExportState.Progress.INIT).build()
it.newBuilder().progress(MessageExportState.Progress.INIT).build()
}
SignalDatabase.messages.markMessageExportFailed(exportableMessage.getMessageId())
@ -126,45 +126,45 @@ class SignalSmsExportService : SmsExportService() {
override fun onMessageIdCreated(exportableMessage: ExportableMessage, messageId: Long) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().setMessageId(messageId).build()
it.newBuilder().messageId(messageId).build()
}
}
override fun onAttachmentPartExportStarted(exportableMessage: ExportableMessage, part: ExportableMessage.Mms.Part) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().addStartedAttachments(part.contentId).build()
it.newBuilder().apply { startedAttachments += part.contentId }.build()
}
}
override fun onAttachmentPartExportSucceeded(exportableMessage: ExportableMessage, part: ExportableMessage.Mms.Part) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().addCompletedAttachments(part.contentId).build()
it.newBuilder().apply { completedAttachments += part.contentId }.build()
}
}
override fun onAttachmentPartExportFailed(exportableMessage: ExportableMessage, part: ExportableMessage.Mms.Part) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
val startedAttachments = it.startedAttachmentsList - part.contentId
it.toBuilder().clearStartedAttachments().addAllStartedAttachments(startedAttachments).build()
val startedAttachments = it.startedAttachments - part.contentId
it.newBuilder().startedAttachments(startedAttachments).build()
}
}
override fun onRecipientExportStarted(exportableMessage: ExportableMessage, recipient: String) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().addStartedRecipients(recipient).build()
it.newBuilder().apply { startedRecipients += recipient }.build()
}
}
override fun onRecipientExportSucceeded(exportableMessage: ExportableMessage, recipient: String) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
it.toBuilder().addCompletedRecipients(recipient).build()
it.newBuilder().apply { completedRecipients += recipient }.build()
}
}
override fun onRecipientExportFailed(exportableMessage: ExportableMessage, recipient: String) {
SignalDatabase.messages.updateMessageExportState(exportableMessage.getMessageId()) {
val startedAttachments = it.startedRecipientsList - recipient
it.toBuilder().clearStartedRecipients().addAllStartedRecipients(startedAttachments).build()
val startedAttachments = it.startedRecipients - recipient
it.newBuilder().startedRecipients(startedAttachments).build()
}
}

View file

@ -24,7 +24,6 @@ enum class TextFont(@DrawableRes val icon: Int, val fallbackFamily: String, val
StoryTextPost.Style.SERIF -> SERIF
StoryTextPost.Style.SCRIPT -> SCRIPT
StoryTextPost.Style.CONDENSED -> CONDENSED
StoryTextPost.Style.UNRECOGNIZED -> REGULAR
}
}
}

View file

@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.getBadge
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import java.io.InputStream
import java.lang.Exception
import java.security.MessageDigest
import java.util.Locale
@ -34,7 +33,7 @@ data class GiftBadgeModel(val giftBadge: GiftBadge) : Key {
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(giftBadge.toByteArray())
messageDigest.update(giftBadge.encode())
}
class Fetcher(

View file

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.groups;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.signal.core.util.DatabaseId;
import org.signal.core.util.Hex;
import org.signal.libsignal.protocol.kdf.HKDFv3;
@ -18,6 +16,8 @@ import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.security.SecureRandom;
import okio.ByteString;
public abstract class GroupId implements DatabaseId {
private static final String ENCODED_SIGNAL_GROUP_V1_PREFIX = "__textsecure_group__!";

View file

@ -7,8 +7,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.google.protobuf.ByteString;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
@ -26,7 +24,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import org.whispersystems.signalservice.internal.push.GroupContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -38,6 +36,8 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import okio.ByteString;
final class GroupManagerV1 {
private static final String TAG = Log.tag(GroupManagerV1.class);
@ -159,13 +159,13 @@ final class GroupManagerV1 {
}
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId.getDecodedId()))
.setType(GroupContext.Type.UPDATE)
.addAllMembersE164(e164Members)
.addAllMembers(uuidMembers);
GroupContext.Builder groupContextBuilder = new GroupContext.Builder()
.id(ByteString.of(groupId.getDecodedId()))
.type(GroupContext.Type.UPDATE)
.membersE164(e164Members)
.members(uuidMembers);
if (groupName != null) groupContextBuilder.setName(groupName);
if (groupName != null) groupContextBuilder.name(groupName);
GroupContext groupContext = groupContextBuilder.build();

View file

@ -9,8 +9,6 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.InvalidInputException;
@ -52,7 +50,6 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
@ -64,9 +61,9 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId.PNI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceIds;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.ConflictException;
@ -91,6 +88,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import okio.ByteString;
final class GroupManagerV2 {
private static final String TAG = Log.tag(GroupManagerV2.class);
@ -227,17 +226,17 @@ final class GroupManagerV2 {
GroupsV2StateProcessor.StateProcessorForGroup stateProcessorForGroup = new GroupsV2StateProcessor(context).forGroup(serviceIds, groupMasterKey);
DecryptedGroup latest = stateProcessorForGroup.getCurrentGroupStateFromServer();
if (latest.getRevision() == 0) {
if (latest.revision == 0) {
return latest;
}
Optional<DecryptedMember> selfInFullMemberList = DecryptedGroupUtil.findMemberByAci(latest.getMembersList(), selfAci);
Optional<DecryptedMember> selfInFullMemberList = DecryptedGroupUtil.findMemberByAci(latest.members, selfAci);
if (!selfInFullMemberList.isPresent()) {
return latest;
}
DecryptedGroup joinedVersion = stateProcessorForGroup.getSpecificVersionFromServer(selfInFullMemberList.get().getJoinedAtRevision());
DecryptedGroup joinedVersion = stateProcessorForGroup.getSpecificVersionFromServer(selfInFullMemberList.get().joinedAtRevision);
if (joinedVersion != null) {
return joinedVersion;
@ -267,7 +266,7 @@ final class GroupManagerV2 {
@WorkerThread
void sendNoopGroupUpdate(@NonNull GroupMasterKey masterKey, @NonNull DecryptedGroup currentState) {
sendGroupUpdateHelper.sendGroupUpdate(masterKey, new GroupMutation(currentState, DecryptedGroupChange.newBuilder().build(), currentState), null);
sendGroupUpdateHelper.sendGroupUpdate(masterKey, new GroupMutation(currentState, new DecryptedGroupChange(), currentState), null);
}
@ -308,16 +307,17 @@ final class GroupManagerV2 {
groupDatabase.onAvatarUpdated(groupId, avatar != null);
SignalDatabase.recipients().setProfileSharing(groupRecipient.getId(), true);
DecryptedGroupChange groupChange = DecryptedGroupChange.newBuilder(GroupChangeReconstruct.reconstructGroupChange(DecryptedGroup.newBuilder().build(), decryptedGroup))
.setEditorServiceIdBytes(selfAci.toByteString())
.build();
DecryptedGroupChange groupChange = GroupChangeReconstruct.reconstructGroupChange(new DecryptedGroup(), decryptedGroup)
.newBuilder()
.editorServiceIdBytes(selfAci.toByteString())
.build();
RecipientAndThread recipientAndThread = sendGroupUpdateHelper.sendGroupUpdate(masterKey, new GroupMutation(null, groupChange, decryptedGroup), null);
return new GroupManager.GroupActionResult(recipientAndThread.groupRecipient,
recipientAndThread.threadId,
decryptedGroup.getMembersCount() - 1,
getPendingMemberRecipientIds(decryptedGroup.getPendingMembersList()));
decryptedGroup.members.size() - 1,
getPendingMemberRecipientIds(decryptedGroup.pendingMembers));
}
}
@ -393,17 +393,16 @@ final class GroupManagerV2 {
{
try {
GroupChange.Actions.Builder change = title != null ? groupOperations.createModifyGroupTitle(title)
: GroupChange.Actions.newBuilder();
: new GroupChange.Actions.Builder();
if (description != null) {
change.setModifyDescription(groupOperations.createModifyGroupDescriptionAction(description));
change.modifyDescription(groupOperations.createModifyGroupDescriptionAction(description).build());
}
if (avatarChanged) {
String cdnKey = avatarBytes != null ? groupsV2Api.uploadAvatar(avatarBytes, groupSecretParams, authorization.getAuthorizationForToday(serviceIds, groupSecretParams))
: "";
change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder()
.setAvatar(cdnKey));
change.modifyAvatar(new GroupChange.Actions.ModifyAvatarAction.Builder().avatar(cdnKey).build());
}
GroupManager.GroupActionResult groupActionResult = commitChangeWithConflictResolution(selfAci, change);
@ -445,7 +444,7 @@ final class GroupManagerV2 {
.map(r -> Recipient.resolved(r).requireAci())
.collect(Collectors.toSet());
return commitChangeWithConflictResolution(selfAci, groupOperations.createRefuseGroupJoinRequest(uuids, true, v2GroupProperties.getDecryptedGroup().getBannedMembersList()));
return commitChangeWithConflictResolution(selfAci, groupOperations.createRefuseGroupJoinRequest(uuids, true, v2GroupProperties.getDecryptedGroup().bannedMembers));
}
@WorkerThread
@ -463,9 +462,9 @@ final class GroupManagerV2 {
{
GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
DecryptedGroup decryptedGroup = groupRecord.requireV2GroupProperties().getDecryptedGroup();
Optional<DecryptedMember> selfMember = DecryptedGroupUtil.findMemberByAci(decryptedGroup.getMembersList(), selfAci);
Optional<DecryptedPendingMember> aciPendingMember = DecryptedGroupUtil.findPendingByServiceId(decryptedGroup.getPendingMembersList(), selfAci);
Optional<DecryptedPendingMember> pniPendingMember = DecryptedGroupUtil.findPendingByServiceId(decryptedGroup.getPendingMembersList(), selfPni);
Optional<DecryptedMember> selfMember = DecryptedGroupUtil.findMemberByAci(decryptedGroup.members, selfAci);
Optional<DecryptedPendingMember> aciPendingMember = DecryptedGroupUtil.findPendingByServiceId(decryptedGroup.pendingMembers, selfAci);
Optional<DecryptedPendingMember> pniPendingMember = DecryptedGroupUtil.findPendingByServiceId(decryptedGroup.pendingMembers, selfPni);
Optional<DecryptedPendingMember> selfPendingMember = Optional.empty();
ServiceId serviceId = selfAci;
@ -478,7 +477,7 @@ final class GroupManagerV2 {
if (selfPendingMember.isPresent()) {
try {
revokeInvites(serviceId, Collections.singleton(new UuidCiphertext(selfPendingMember.get().getServiceIdCipherText().toByteArray())), false);
revokeInvites(serviceId, Collections.singleton(new UuidCiphertext(selfPendingMember.get().serviceIdCipherText.toByteArray())), false);
} catch (InvalidInputException e) {
throw new AssertionError(e);
}
@ -496,7 +495,7 @@ final class GroupManagerV2 {
return commitChangeWithConflictResolution(selfAci,
groupOperations.createRemoveMembersChange(Collections.singleton(aci),
ban,
ban ? v2GroupProperties.getDecryptedGroup().getBannedMembersList()
ban ? v2GroupProperties.getDecryptedGroup().bannedMembers
: Collections.emptyList()),
allowWhenBlocked,
sendToMembers);
@ -518,14 +517,14 @@ final class GroupManagerV2 {
{
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
DecryptedGroup group = groupDatabase.requireGroup(groupId).requireV2GroupProperties().getDecryptedGroup();
Optional<DecryptedMember> selfInGroup = DecryptedGroupUtil.findMemberByAci(group.getMembersList(), selfAci);
Optional<DecryptedMember> selfInGroup = DecryptedGroupUtil.findMemberByAci(group.members, selfAci);
if (!selfInGroup.isPresent()) {
Log.w(TAG, "Self not in group " + groupId);
return null;
}
if (Arrays.equals(profileKey.serialize(), selfInGroup.get().getProfileKey().toByteArray())) {
if (Arrays.equals(profileKey.serialize(), selfInGroup.get().profileKey.toByteArray())) {
Log.i(TAG, "Own Profile Key is already up to date in group " + groupId);
return null;
} else {
@ -548,15 +547,15 @@ final class GroupManagerV2 {
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
{
DecryptedGroup group = groupDatabase.requireGroup(groupId).requireV2GroupProperties().getDecryptedGroup();
Optional<DecryptedMember> selfInGroup = DecryptedGroupUtil.findMemberByAci(group.getMembersList(), selfAci);
Optional<DecryptedMember> selfInGroup = DecryptedGroupUtil.findMemberByAci(group.members, selfAci);
if (selfInGroup.isPresent()) {
Log.w(TAG, "Self already in group");
return null;
}
Optional<DecryptedPendingMember> aciInPending = DecryptedGroupUtil.findPendingByServiceId(group.getPendingMembersList(), selfAci);
Optional<DecryptedPendingMember> pniInPending = DecryptedGroupUtil.findPendingByServiceId(group.getPendingMembersList(), selfPni);
Optional<DecryptedPendingMember> aciInPending = DecryptedGroupUtil.findPendingByServiceId(group.pendingMembers, selfAci);
Optional<DecryptedPendingMember> pniInPending = DecryptedGroupUtil.findPendingByServiceId(group.pendingMembers, selfPni);
GroupCandidate groupCandidate = groupCandidateHelper.recipientIdToCandidate(Recipient.self().getId());
@ -579,9 +578,9 @@ final class GroupManagerV2 {
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
{
ByteString serviceIdByteString = serviceId.toByteString();
boolean rejectJoinRequest = v2GroupProperties.getDecryptedGroup().getRequestingMembersList().stream().anyMatch(m -> m.getAciBytes().equals(serviceIdByteString));
boolean rejectJoinRequest = v2GroupProperties.getDecryptedGroup().requestingMembers.stream().anyMatch(m -> m.aciBytes.equals(serviceIdByteString));
return commitChangeWithConflictResolution(selfAci, groupOperations.createBanServiceIdsChange(Collections.singleton(serviceId), rejectJoinRequest, v2GroupProperties.getDecryptedGroup().getBannedMembersList()));
return commitChangeWithConflictResolution(selfAci, groupOperations.createBanServiceIdsChange(Collections.singleton(serviceId), rejectJoinRequest, v2GroupProperties.getDecryptedGroup().bannedMembers));
}
public GroupManager.GroupActionResult unban(Set<ServiceId> serviceIds)
@ -615,7 +614,7 @@ final class GroupManagerV2 {
if (state != GroupManager.GroupLinkState.DISABLED) {
DecryptedGroup group = groupDatabase.requireGroup(groupId).requireV2GroupProperties().getDecryptedGroup();
if (group.getInviteLinkPassword().isEmpty()) {
if (group.inviteLinkPassword.size() == 0) {
Log.d(TAG, "First time enabling group links for group and password empty, generating");
change = groupOperations.createModifyGroupLinkPasswordAndRightsChange(GroupLinkPassword.createNew().serialize(), access);
}
@ -650,13 +649,13 @@ final class GroupManagerV2 {
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
{
boolean refetchedAddMemberCredentials = false;
change.setSourceServiceId(UuidUtil.toByteString(authServiceId.getRawUuid()));
change.sourceServiceId(UuidUtil.toByteString(authServiceId.getRawUuid()));
for (int attempt = 0; attempt < 5; attempt++) {
try {
return commitChange(change, allowWhenBlocked, sendToMembers);
} catch (GroupPatchNotAcceptedException e) {
if (change.getAddMembersCount() > 0 && !refetchedAddMemberCredentials) {
if (change.addMembers.size() > 0 && !refetchedAddMemberCredentials) {
refetchedAddMemberCredentials = true;
change = refetchAddMemberCredentials(change);
} else {
@ -692,7 +691,7 @@ final class GroupManagerV2 {
}
if (groupUpdateResult.getGroupState() != GroupsV2StateProcessor.GroupState.GROUP_UPDATED) {
int serverRevision = groupUpdateResult.getLatestServer().getRevision();
int serverRevision = groupUpdateResult.getLatestServer().revision;
int localRevision = groupDatabase.requireGroup(groupId).requireV2GroupProperties().getGroupRevision();
int revisionDelta = serverRevision - localRevision;
Log.w(TAG, String.format(Locale.US, "Server is ahead by %d revisions", revisionDelta));
@ -713,7 +712,7 @@ final class GroupManagerV2 {
private GroupChange.Actions.Builder refetchAddMemberCredentials(@NonNull GroupChange.Actions.Builder change) {
try {
List<RecipientId> ids = groupOperations.decryptAddMembers(change.getAddMembersList())
List<RecipientId> ids = groupOperations.decryptAddMembers(change.addMembers)
.stream()
.map(RecipientId::from)
.collect(java.util.stream.Collectors.toList());
@ -738,10 +737,10 @@ final class GroupManagerV2 {
final GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
final GroupTable.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
final int nextRevision = v2GroupProperties.getGroupRevision() + 1;
final GroupChange.Actions changeActions = change.setRevision(nextRevision).build();
final DecryptedGroupChange decryptedChange;
final DecryptedGroup decryptedGroupState;
final DecryptedGroup previousGroupState;
final GroupChange.Actions changeActions = change.revision(nextRevision).build();
final DecryptedGroupChange decryptedChange;
final DecryptedGroup decryptedGroupState;
final DecryptedGroup previousGroupState;
if (!allowWhenBlocked && Recipient.externalGroupExact(groupId).isBlocked()) {
throw new GroupChangeFailedException("Group is blocked.");
@ -763,8 +762,8 @@ final class GroupManagerV2 {
GroupMutation groupMutation = new GroupMutation(previousGroupState, decryptedChange, decryptedGroupState);
RecipientAndThread recipientAndThread = sendGroupUpdateHelper.sendGroupUpdate(groupMasterKey, groupMutation, signedGroupChange, sendToMembers);
int newMembersCount = decryptedChange.getNewMembersCount();
List<RecipientId> newPendingMembers = getPendingMemberRecipientIds(decryptedChange.getNewPendingMembersList());
int newMembersCount = decryptedChange.newMembers.size();
List<RecipientId> newPendingMembers = getPendingMemberRecipientIds(decryptedChange.newPendingMembers);
return new GroupManager.GroupActionResult(recipientAndThread.groupRecipient, recipientAndThread.threadId, newMembersCount, newPendingMembers);
}
@ -826,9 +825,9 @@ final class GroupManagerV2 {
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey));
try {
return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true)
return groupOperations.decryptChange(GroupChange.ADAPTER.decode(signedGroupChange), true)
.orElse(null);
} catch (VerificationFailedException | InvalidGroupStateException | InvalidProtocolBufferException e) {
} catch (VerificationFailedException | InvalidGroupStateException | IOException e) {
Log.w(TAG, "Unable to verify supplied group change", e);
}
}
@ -912,7 +911,7 @@ final class GroupManagerV2 {
@Nullable byte[] avatar)
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception, GroupLinkNotActiveException
{
boolean requestToJoin = joinInfo.getAddFromInviteLink() == AccessControl.AccessRequired.ADMINISTRATOR;
boolean requestToJoin = joinInfo.addFromInviteLink == AccessControl.AccessRequired.ADMINISTRATOR;
boolean alreadyAMember = false;
if (requestToJoin) {
@ -924,7 +923,7 @@ final class GroupManagerV2 {
GroupChange signedGroupChange = null;
DecryptedGroupChange decryptedChange = null;
try {
signedGroupChange = joinGroupOnServer(requestToJoin, joinInfo.getRevision());
signedGroupChange = joinGroupOnServer(requestToJoin, joinInfo.revision);
if (requestToJoin) {
Log.i(TAG, String.format("Successfully requested to join %s on server", groupId));
@ -954,7 +953,7 @@ final class GroupManagerV2 {
if (decryptedChange != null) {
try {
groupsV2StateProcessor.forGroup(SignalStore.account().getServiceIds(), groupMasterKey)
.updateLocalGroupToRevision(decryptedChange.getRevision(), System.currentTimeMillis(), decryptedChange);
.updateLocalGroupToRevision(decryptedChange.revision, System.currentTimeMillis(), decryptedChange);
} catch (GroupNotAMemberException e) {
Log.w(TAG, "Unable to apply join change to existing group", e);
}
@ -968,7 +967,7 @@ final class GroupManagerV2 {
if (decryptedChange != null) {
try {
groupsV2StateProcessor.forGroup(SignalStore.account().getServiceIds(), groupMasterKey)
.updateLocalGroupToRevision(decryptedChange.getRevision(), System.currentTimeMillis(), decryptedChange);
.updateLocalGroupToRevision(decryptedChange.revision, System.currentTimeMillis(), decryptedChange);
} catch (GroupNotAMemberException e) {
Log.w(TAG, "Unable to apply join change to existing group", e);
}
@ -1017,7 +1016,7 @@ final class GroupManagerV2 {
{
try {
new GroupsV2StateProcessor(context).forGroup(serviceIds, groupMasterKey)
.updateLocalGroupToRevision(decryptedChange.getRevision(),
.updateLocalGroupToRevision(decryptedChange.revision,
System.currentTimeMillis(),
decryptedChange);
@ -1050,7 +1049,7 @@ final class GroupManagerV2 {
try {
//noinspection OptionalGetWithoutIsPresent
return groupOperations.decryptChange(signedGroupChange, false).get();
} catch (VerificationFailedException | InvalidGroupStateException | InvalidProtocolBufferException e) {
} catch (VerificationFailedException | InvalidGroupStateException | IOException e) {
Log.w(TAG, e);
throw new GroupChangeFailedException(e);
}
@ -1064,23 +1063,25 @@ final class GroupManagerV2 {
* update message.
*/
private DecryptedGroup createPlaceholderGroup(@NonNull DecryptedGroupJoinInfo joinInfo, boolean requestToJoin) {
DecryptedGroup.Builder group = DecryptedGroup.newBuilder()
.setTitle(joinInfo.getTitle())
.setAvatar(joinInfo.getAvatar())
.setRevision(GroupsV2StateProcessor.PLACEHOLDER_REVISION);
DecryptedGroup.Builder group = new DecryptedGroup.Builder()
.title(joinInfo.title)
.avatar(joinInfo.avatar)
.revision(GroupsV2StateProcessor.PLACEHOLDER_REVISION);
Recipient self = Recipient.self();
ByteString selfAciBytes = selfAci.toByteString();
ByteString profileKey = ByteString.copyFrom(Objects.requireNonNull(self.getProfileKey()));
ByteString profileKey = ByteString.of(Objects.requireNonNull(self.getProfileKey()));
if (requestToJoin) {
group.addRequestingMembers(DecryptedRequestingMember.newBuilder()
.setAciBytes(selfAciBytes)
.setProfileKey(profileKey));
group.requestingMembers(Collections.singletonList(new DecryptedRequestingMember.Builder()
.aciBytes(selfAciBytes)
.profileKey(profileKey)
.build()));
} else {
group.addMembers(DecryptedMember.newBuilder()
.setAciBytes(selfAciBytes)
.setProfileKey(profileKey));
group.members(Collections.singletonList(new DecryptedMember.Builder()
.aciBytes(selfAciBytes)
.profileKey(profileKey)
.build()));
}
return group.build();
@ -1104,7 +1105,7 @@ final class GroupManagerV2 {
GroupChange.Actions.Builder change = requestToJoin ? groupOperations.createGroupJoinRequest(expiringProfileKeyCredential)
: groupOperations.createGroupJoinDirect(expiringProfileKeyCredential);
change.setSourceServiceId(selfAci.toByteString());
change.sourceServiceId(selfAci.toByteString());
return commitJoinChangeWithConflictResolution(currentRevision, change);
}
@ -1114,13 +1115,13 @@ final class GroupManagerV2 {
{
for (int attempt = 0; attempt < 5; attempt++) {
try {
GroupChange.Actions changeActions = change.setRevision(currentRevision + 1)
GroupChange.Actions changeActions = change.revision(currentRevision + 1)
.build();
Log.i(TAG, "Trying to join group at V" + changeActions.getRevision());
Log.i(TAG, "Trying to join group at V" + changeActions.revision);
GroupChange signedGroupChange = commitJoinToServer(changeActions);
Log.i(TAG, "Successfully joined group at V" + changeActions.getRevision());
Log.i(TAG, "Successfully joined group at V" + changeActions.revision);
return signedGroupChange;
} catch (GroupPatchNotAcceptedException e) {
Log.w(TAG, "Patch not accepted", e);
@ -1162,7 +1163,7 @@ final class GroupManagerV2 {
throws IOException, GroupLinkNotActiveException, GroupChangeFailedException
{
try {
int currentRevision = getGroupJoinInfoFromServer(groupMasterKey, password).getRevision();
int currentRevision = getGroupJoinInfoFromServer(groupMasterKey, password).revision;
Log.i(TAG, "Server now on V" + currentRevision);
@ -1176,7 +1177,7 @@ final class GroupManagerV2 {
throws IOException, GroupLinkNotActiveException, GroupChangeFailedException
{
try {
boolean pendingAdminApproval = getGroupJoinInfoFromServer(groupMasterKey, password).getPendingAdminApproval();
boolean pendingAdminApproval = getGroupJoinInfoFromServer(groupMasterKey, password).pendingAdminApproval;
if (pendingAdminApproval) {
Log.i(TAG, "User is already pending admin approval");
@ -1220,7 +1221,7 @@ final class GroupManagerV2 {
DecryptedGroupChange decryptedChange = groupOperations.decryptChange(signedGroupChange, false).get();
DecryptedGroup newGroup = DecryptedGroupUtil.applyWithoutRevisionCheck(decryptedGroup, decryptedChange);
groupDatabase.update(groupId, resetRevision(newGroup, decryptedGroup.getRevision()));
groupDatabase.update(groupId, resetRevision(newGroup, decryptedGroup.revision));
sendGroupUpdateHelper.sendGroupUpdate(groupMasterKey, new GroupMutation(decryptedGroup, decryptedChange, newGroup), signedGroupChange, false);
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
@ -1229,9 +1230,9 @@ final class GroupManagerV2 {
}
private DecryptedGroup resetRevision(DecryptedGroup newGroup, int revision) {
return DecryptedGroup.newBuilder(newGroup)
.setRevision(revision)
.build();
return newGroup.newBuilder()
.revision(revision)
.build();
}
private @NonNull GroupChange commitCancelChangeWithConflictResolution(@NonNull GroupChange.Actions.Builder change)
@ -1241,13 +1242,13 @@ final class GroupManagerV2 {
for (int attempt = 0; attempt < 5; attempt++) {
try {
GroupChange.Actions changeActions = change.setRevision(currentRevision + 1)
GroupChange.Actions changeActions = change.revision(currentRevision + 1)
.build();
Log.i(TAG, "Trying to cancel request group at V" + changeActions.getRevision());
Log.i(TAG, "Trying to cancel request group at V" + changeActions.revision);
GroupChange signedGroupChange = commitJoinToServer(changeActions);
Log.i(TAG, "Successfully cancelled group join at V" + changeActions.getRevision());
Log.i(TAG, "Successfully cancelled group join at V" + changeActions.revision);
return signedGroupChange;
} catch (GroupPatchNotAcceptedException e) {
throw new GroupChangeFailedException(e);

View file

@ -1,13 +1,9 @@
package org.thoughtcrime.securesms.groups;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.google.protobuf.ByteString;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.GroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
@ -20,11 +16,11 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.groupsv2.PartialDecryptedGroup;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.GroupContextV2;
import java.util.List;
import java.util.UUID;
import okio.ByteString;
public final class GroupProtoUtil {
@ -36,12 +32,12 @@ public final class GroupProtoUtil {
{
ByteString bytes = self.toByteString();
for (DecryptedMember decryptedMember : partialDecryptedGroup.getMembersList()) {
if (decryptedMember.getAciBytes().equals(bytes)) {
return decryptedMember.getJoinedAtRevision();
if (decryptedMember.aciBytes.equals(bytes)) {
return decryptedMember.joinedAtRevision;
}
}
for (DecryptedPendingMember decryptedMember : partialDecryptedGroup.getPendingMembersList()) {
if (decryptedMember.getServiceIdBytes().equals(bytes)) {
if (decryptedMember.serviceIdBytes.equals(bytes)) {
// Assume latest, we don't have any information about when pending members were invited
return partialDecryptedGroup.getRevision();
}
@ -53,27 +49,27 @@ public final class GroupProtoUtil {
@NonNull GroupMutation groupMutation,
@Nullable GroupChange signedServerChange)
{
DecryptedGroupChange plainGroupChange = groupMutation.getGroupChange();
DecryptedGroup decryptedGroup = groupMutation.getNewGroupState();
int revision = plainGroupChange != null ? plainGroupChange.getRevision() : decryptedGroup.getRevision();
SignalServiceProtos.GroupContextV2.Builder contextBuilder = SignalServiceProtos.GroupContextV2.newBuilder()
.setMasterKey(ByteString.copyFrom(masterKey.serialize()))
.setRevision(revision);
DecryptedGroupChange plainGroupChange = groupMutation.getGroupChange();
DecryptedGroup decryptedGroup = groupMutation.getNewGroupState();
int revision = plainGroupChange != null ? plainGroupChange.revision : decryptedGroup.revision;
GroupContextV2.Builder contextBuilder = new GroupContextV2.Builder()
.masterKey(ByteString.of(masterKey.serialize()))
.revision(revision);
if (signedServerChange != null) {
contextBuilder.setGroupChange(signedServerChange.toByteString());
contextBuilder.groupChange(signedServerChange.encodeByteString());
}
DecryptedGroupV2Context.Builder builder = DecryptedGroupV2Context.newBuilder()
.setContext(contextBuilder.build())
.setGroupState(decryptedGroup);
DecryptedGroupV2Context.Builder builder = new DecryptedGroupV2Context.Builder()
.context(contextBuilder.build())
.groupState(decryptedGroup);
if (groupMutation.getPreviousGroupState() != null) {
builder.setPreviousGroupState(groupMutation.getPreviousGroupState());
builder.previousGroupState(groupMutation.getPreviousGroupState());
}
if (plainGroupChange != null) {
builder.setChange(plainGroupChange);
builder.change(plainGroupChange);
}
return builder.build();
@ -81,7 +77,7 @@ public final class GroupProtoUtil {
@WorkerThread
public static Recipient pendingMemberToRecipient(@NonNull DecryptedPendingMember pendingMember) {
return pendingMemberServiceIdToRecipient(pendingMember.getServiceIdBytes());
return pendingMemberServiceIdToRecipient(pendingMember.serviceIdBytes);
}
@WorkerThread
@ -110,7 +106,7 @@ public final class GroupProtoUtil {
ByteString aciBytes = aci.toByteString();
for (DecryptedMember member : membersList) {
if (aciBytes.equals(member.getAciBytes())) {
if (aciBytes.equals(member.aciBytes)) {
return true;
}
}

View file

@ -173,10 +173,10 @@ public final class GroupsV1MigrationUtil {
return null;
}
Log.i(TAG, "[Local] Migrating group over to the version we were added to: V" + decryptedGroup.getRevision());
Log.i(TAG, "[Local] Migrating group over to the version we were added to: V" + decryptedGroup.revision);
SignalDatabase.groups().migrateToV2(threadId, gv1Id, decryptedGroup);
Log.i(TAG, "[Local] Applying all changes since V" + decryptedGroup.getRevision());
Log.i(TAG, "[Local] Applying all changes since V" + decryptedGroup.revision);
try {
GroupManager.updateGroupFromServer(context, gv1Id.deriveV2MigrationMasterKey(), LATEST, System.currentTimeMillis(), null);
} catch (GroupChangeBusyException | GroupNotAMemberException e) {

View file

@ -34,7 +34,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public final class LiveGroup {
@ -68,9 +67,9 @@ public final class LiveGroup {
LiveData<GroupTable.V2GroupProperties> v2Properties = Transformations.map(this.groupRecord, GroupRecord::requireV2GroupProperties);
this.groupLink = Transformations.map(v2Properties, g -> {
DecryptedGroup group = g.getDecryptedGroup();
AccessControl.AccessRequired addFromInviteLink = group.getAccessControl().getAddFromInviteLink();
AccessControl.AccessRequired addFromInviteLink = group.accessControl.addFromInviteLink;
if (group.getInviteLinkPassword().isEmpty()) {
if (group.inviteLinkPassword.size() == 0) {
return GroupLinkUrlAndStatus.NONE;
}
@ -107,11 +106,11 @@ public final class LiveGroup {
}
boolean selfAdmin = g.isAdmin(Recipient.self());
List<DecryptedRequestingMember> requestingMembersList = g.requireV2GroupProperties().getDecryptedGroup().getRequestingMembersList();
List<DecryptedRequestingMember> requestingMembersList = g.requireV2GroupProperties().getDecryptedGroup().requestingMembers;
return Stream.of(requestingMembersList)
.map(requestingMember -> {
Recipient recipient = Recipient.externalPush(ServiceId.parseOrThrow(requestingMember.getAciBytes()));
Recipient recipient = Recipient.externalPush(ServiceId.parseOrThrow(requestingMember.aciBytes));
return new GroupMemberEntry.RequestingMember(recipient, selfAdmin);
})
.toList();
@ -157,7 +156,7 @@ public final class LiveGroup {
}
public LiveData<Integer> getPendingMemberCount() {
return Transformations.map(groupRecord, g -> g.isV2Group() ? g.requireV2GroupProperties().getDecryptedGroup().getPendingMembersCount() : 0);
return Transformations.map(groupRecord, g -> g.isV2Group() ? g.requireV2GroupProperties().getDecryptedGroup().pendingMembers.size() : 0);
}
public LiveData<Integer> getPendingAndRequestingMemberCount() {
@ -165,7 +164,7 @@ public final class LiveGroup {
if (g.isV2Group()) {
DecryptedGroup decryptedGroup = g.requireV2GroupProperties().getDecryptedGroup();
return decryptedGroup.getPendingMembersCount() + decryptedGroup.getRequestingMembersCount();
return decryptedGroup.pendingMembers.size() + decryptedGroup.requestingMembers.size();
}
return 0;
});

View file

@ -7,7 +7,6 @@ import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
@ -30,6 +29,8 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import okio.ByteString;
/**
* Repository for modifying the pending members on a single group.
*/
@ -49,17 +50,17 @@ final class PendingMemberInvitesRepository {
public void getInvitees(@NonNull Consumer<InviteeResult> onInviteesLoaded) {
executor.execute(() -> {
GroupTable groupDatabase = SignalDatabase.groups();
GroupTable.V2GroupProperties v2GroupProperties = groupDatabase.getGroup(groupId).get().requireV2GroupProperties();
DecryptedGroup decryptedGroup = v2GroupProperties.getDecryptedGroup();
List<DecryptedPendingMember> pendingMembersList = decryptedGroup.getPendingMembersList();
GroupTable groupDatabase = SignalDatabase.groups();
GroupTable.V2GroupProperties v2GroupProperties = groupDatabase.getGroup(groupId).get().requireV2GroupProperties();
DecryptedGroup decryptedGroup = v2GroupProperties.getDecryptedGroup();
List<DecryptedPendingMember> pendingMembersList = decryptedGroup.pendingMembers;
List<SinglePendingMemberInvitedByYou> byMe = new ArrayList<>(pendingMembersList.size());
List<MultiplePendingMembersInvitedByAnother> byOthers = new ArrayList<>(pendingMembersList.size());
ByteString self = SignalStore.account().requireAci().toByteString();
boolean selfIsAdmin = v2GroupProperties.isAdmin(Recipient.self());
Stream.of(pendingMembersList)
.groupBy(DecryptedPendingMember::getAddedByAci)
.groupBy(m -> m.addedByAci)
.forEach(g ->
{
ByteString inviterAci = g.getKey();
@ -69,7 +70,7 @@ final class PendingMemberInvitesRepository {
for (DecryptedPendingMember pendingMember : invitedMembers) {
try {
Recipient invitee = GroupProtoUtil.pendingMemberToRecipient(pendingMember);
UuidCiphertext uuidCipherText = new UuidCiphertext(pendingMember.getServiceIdCipherText().toByteArray());
UuidCiphertext uuidCipherText = new UuidCiphertext(pendingMember.serviceIdCipherText.toByteArray());
byMe.add(new SinglePendingMemberInvitedByYou(invitee, uuidCipherText));
} catch (InvalidInputException e) {
@ -82,7 +83,7 @@ final class PendingMemberInvitesRepository {
for (DecryptedPendingMember pendingMember : invitedMembers) {
try {
uuidCipherTexts.add(new UuidCiphertext(pendingMember.getServiceIdCipherText().toByteArray()));
uuidCipherTexts.add(new UuidCiphertext(pendingMember.serviceIdCipherText.toByteArray()));
} catch (InvalidInputException e) {
Log.w(TAG, e);
}

View file

@ -18,11 +18,11 @@ public final class GroupDetails {
}
public @NonNull String getGroupName() {
return joinInfo.getTitle();
return joinInfo.title;
}
public @NonNull String getGroupDescription() {
return joinInfo.getDescription();
return joinInfo.description;
}
public @Nullable byte[] getAvatarBytes() {
@ -34,10 +34,10 @@ public final class GroupDetails {
}
public int getGroupMembershipCount() {
return joinInfo.getMemberCount();
return joinInfo.memberCount;
}
public boolean joinRequiresAdminApproval() {
return joinInfo.getAddFromInviteLink() == AccessControl.AccessRequired.ADMINISTRATOR;
return joinInfo.addFromInviteLink == AccessControl.AccessRequired.ADMINISTRATOR;
}
}

View file

@ -86,7 +86,7 @@ final class GroupJoinRepository {
private @Nullable byte[] tryGetAvatarBytes(@NonNull DecryptedGroupJoinInfo joinInfo) {
try {
return AvatarGroupsV2DownloadJob.downloadGroupAvatarBytes(context, groupInviteLinkUrl.getGroupMasterKey(), joinInfo.getAvatar());
return AvatarGroupsV2DownloadJob.downloadGroupAvatarBytes(context, groupInviteLinkUrl.getGroupMasterKey(), joinInfo.avatar);
} catch (IOException e) {
Log.w(TAG, "Failed to get group avatar", e);
return null;

View file

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.groups.v2;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.GroupInviteLink;
@ -15,6 +13,8 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import okio.ByteString;
public final class GroupInviteLinkUrl {
private static final String GROUP_URL_HOST = "signal.group";
@ -27,7 +27,7 @@ public final class GroupInviteLinkUrl {
public static GroupInviteLinkUrl forGroup(@NonNull GroupMasterKey groupMasterKey,
@NonNull DecryptedGroup group)
{
return new GroupInviteLinkUrl(groupMasterKey, GroupLinkPassword.fromBytes(group.getInviteLinkPassword().toByteArray()));
return new GroupInviteLinkUrl(groupMasterKey, GroupLinkPassword.fromBytes(group.inviteLinkPassword.toByteArray()));
}
public static boolean isGroupLink(@NonNull String urlString) {
@ -59,20 +59,19 @@ public final class GroupInviteLinkUrl {
}
byte[] bytes = Base64UrlSafe.decodePaddingAgnostic(encoding);
GroupInviteLink groupInviteLink = GroupInviteLink.parseFrom(bytes);
GroupInviteLink groupInviteLink = GroupInviteLink.ADAPTER.decode(bytes);
//noinspection SwitchStatementWithTooFewBranches
switch (groupInviteLink.getContentsCase()) {
case V1CONTENTS: {
GroupInviteLink.GroupInviteLinkContentsV1 groupInviteLinkContentsV1 = groupInviteLink.getV1Contents();
GroupMasterKey groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey().toByteArray());
GroupLinkPassword password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.getInviteLinkPassword().toByteArray());
if (groupInviteLink.v1Contents != null) {
GroupInviteLink.GroupInviteLinkContentsV1 groupInviteLinkContentsV1 = groupInviteLink.v1Contents;
GroupMasterKey groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.groupMasterKey.toByteArray());
GroupLinkPassword password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.inviteLinkPassword.toByteArray());
return new GroupInviteLinkUrl(groupMasterKey, password);
}
default: throw new UnknownGroupLinkVersionException("Url contains no known group link content");
return new GroupInviteLinkUrl(groupMasterKey, password);
} else {
throw new UnknownGroupLinkVersionException("Url contains no known group link content");
}
} catch (InvalidInputException | IOException e) {
} catch (InvalidInputException | IllegalStateException | IOException e) {
throw new InvalidGroupLinkException(e);
}
}
@ -106,13 +105,14 @@ public final class GroupInviteLinkUrl {
}
protected static @NonNull String createUrl(@NonNull GroupMasterKey groupMasterKey, @NonNull GroupLinkPassword password) {
GroupInviteLink groupInviteLink = GroupInviteLink.newBuilder()
.setV1Contents(GroupInviteLink.GroupInviteLinkContentsV1.newBuilder()
.setGroupMasterKey(ByteString.copyFrom(groupMasterKey.serialize()))
.setInviteLinkPassword(ByteString.copyFrom(password.serialize())))
.build();
GroupInviteLink groupInviteLink = new GroupInviteLink.Builder()
.v1Contents(new GroupInviteLink.GroupInviteLinkContentsV1.Builder()
.groupMasterKey(ByteString.of(groupMasterKey.serialize()))
.inviteLinkPassword(ByteString.of(password.serialize()))
.build())
.build();
String encoding = Base64UrlSafe.encodeBytesWithoutPadding(groupInviteLink.toByteArray());
String encoding = Base64UrlSafe.encodeBytesWithoutPadding(groupInviteLink.encode());
return GROUP_URL_PREFIX + encoding;
}

View file

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.groups.v2;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
@ -12,13 +10,13 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import okio.ByteString;
/**
* Collects profile keys from group states.
@ -42,25 +40,29 @@ public final class ProfileKeySet {
* authoritative.
*/
public void addKeysFromGroupChange(@NonNull DecryptedGroupChange change) {
ServiceId editor = ServiceId.parseOrNull(change.getEditorServiceIdBytes());
ServiceId editor = ServiceId.parseOrNull(change.editorServiceIdBytes);
for (DecryptedMember member : change.getNewMembersList()) {
for (DecryptedMember member : change.newMembers) {
addMemberKey(member, editor);
}
for (DecryptedMember member : change.getPromotePendingMembersList()) {
for (DecryptedMember member : change.promotePendingMembers) {
addMemberKey(member, editor);
}
for (DecryptedMember member : change.getModifiedProfileKeysList()) {
for (DecryptedMember member : change.modifiedProfileKeys) {
addMemberKey(member, editor);
}
for (DecryptedRequestingMember member : change.getNewRequestingMembersList()) {
addMemberKey(editor, member.getAciBytes(), member.getProfileKey());
for (DecryptedRequestingMember member : change.newRequestingMembers) {
addMemberKey(editor, member.aciBytes, member.profileKey);
}
for (DecryptedMember member : change.getPromotePendingPniAciMembersList()) {
for (DecryptedMember member : change.promotePendingPniAciMembers) {
addMemberKey(member, editor);
}
for (DecryptedMember member : change.promotePendingPniAciMembers) {
addMemberKey(member, editor);
}
}
@ -73,13 +75,13 @@ public final class ProfileKeySet {
* gathered from a group state can only be used to fill in gaps in knowledge.
*/
public void addKeysFromGroupState(@NonNull DecryptedGroup group) {
for (DecryptedMember member : group.getMembersList()) {
for (DecryptedMember member : group.members) {
addMemberKey(member, null);
}
}
private void addMemberKey(@NonNull DecryptedMember member, @Nullable ServiceId changeSource) {
addMemberKey(changeSource, member.getAciBytes(), member.getProfileKey());
addMemberKey(changeSource, member.aciBytes, member.profileKey);
}
private void addMemberKey(@Nullable ServiceId changeSource,

View file

@ -43,7 +43,7 @@ final class GlobalGroupState {
int getEarliestRevisionNumber() {
if (localState != null) {
return localState.getRevision();
return localState.revision;
} else {
if (serverHistory.isEmpty()) {
throw new AssertionError();
@ -57,7 +57,7 @@ final class GlobalGroupState {
if (localState == null) {
throw new AssertionError();
}
return localState.getRevision();
return localState.revision;
}
return serverHistory.get(serverHistory.size() - 1).getRevision();
}

View file

@ -70,7 +70,7 @@ final class GroupStateMapper {
final int from = Math.max(0, inputState.getEarliestRevisionNumber());
final int to = Math.min(inputState.getLatestRevisionNumber(), maximumRevisionToApply);
if (current != null && current.getRevision() == PLACEHOLDER_REVISION) {
if (current != null && current.revision == PLACEHOLDER_REVISION) {
Log.i(TAG, "Ignoring place holder group state");
} else {
stateChain.push(current, null);
@ -83,11 +83,11 @@ final class GroupStateMapper {
continue;
}
if (stateChain.getLatestState() == null && entry.getGroup() != null && current != null && current.getRevision() == PLACEHOLDER_REVISION) {
DecryptedGroup previousState = DecryptedGroup.newBuilder(entry.getGroup())
.setTitle(current.getTitle())
.setAvatar(current.getAvatar())
.build();
if (stateChain.getLatestState() == null && entry.getGroup() != null && current != null && current.revision == PLACEHOLDER_REVISION) {
DecryptedGroup previousState = entry.getGroup().newBuilder()
.title(current.title)
.avatar(current.avatar)
.build();
stateChain.push(previousState, null);
}
@ -135,12 +135,12 @@ final class GroupStateMapper {
try {
return DecryptedGroupUtil.applyWithoutRevisionCheck(group, change);
} catch (NotAbleToApplyGroupV2ChangeException e) {
Log.w(TAG, "Unable to apply V" + change.getRevision(), e);
Log.w(TAG, "Unable to apply V" + change.revision, e);
return null;
}
},
(groupB, groupA) -> GroupChangeReconstruct.reconstructGroupChange(groupA, groupB),
(groupA, groupB) -> groupA.getRevision() == groupB.getRevision() && DecryptedGroupUtil.changeIsEmpty(GroupChangeReconstruct.reconstructGroupChange(groupA, groupB))
(groupA, groupB) -> groupA.revision == groupB.revision && DecryptedGroupUtil.changeIsEmpty(GroupChangeReconstruct.reconstructGroupChange(groupA, groupB))
);
}
}

View file

@ -18,8 +18,6 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval;
import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.RecipientTable;
@ -101,9 +99,9 @@ public class GroupsV2StateProcessor {
public GroupsV2StateProcessor(@NonNull Context context) {
this.context = context.getApplicationContext();
this.groupsV2Authorization = ApplicationDependencies.getGroupsV2Authorization();
this.groupsV2Api = ApplicationDependencies.getSignalServiceAccountManager().getGroupsV2Api();
this.recipientTable = SignalDatabase.recipients();
this.groupDatabase = SignalDatabase.groups();
this.groupsV2Api = ApplicationDependencies.getSignalServiceAccountManager().getGroupsV2Api();
this.recipientTable = SignalDatabase.recipients();
this.groupDatabase = SignalDatabase.groups();
}
public StateProcessorForGroup forGroup(@NonNull ServiceIds serviceIds, @NonNull GroupMasterKey groupMasterKey) {
@ -150,9 +148,8 @@ public class GroupsV2StateProcessor {
public static final class StateProcessorForGroup {
private final ServiceIds serviceIds;
private final Context context;
private final GroupTable groupDatabase;
private final GroupsV2Api groupsV2Api;
private final GroupTable groupDatabase;
private final GroupsV2Api groupsV2Api;
private final GroupsV2Authorization groupsV2Authorization;
private final GroupMasterKey masterKey;
private final GroupId.V2 groupId;
@ -160,12 +157,12 @@ public class GroupsV2StateProcessor {
private final ProfileAndMessageHelper profileAndMessageHelper;
private StateProcessorForGroup(@NonNull ServiceIds serviceIds,
@NonNull Context context,
@NonNull GroupTable groupDatabase,
@NonNull GroupsV2Api groupsV2Api,
@NonNull GroupsV2Authorization groupsV2Authorization,
@NonNull GroupMasterKey groupMasterKey,
@NonNull RecipientTable recipientTable)
@NonNull Context context,
@NonNull GroupTable groupDatabase,
@NonNull GroupsV2Api groupsV2Api,
@NonNull GroupsV2Authorization groupsV2Authorization,
@NonNull GroupMasterKey groupMasterKey,
@NonNull RecipientTable recipientTable)
{
this(serviceIds, context, groupDatabase, groupsV2Api, groupsV2Authorization, groupMasterKey, GroupSecretParams.deriveFromMasterKey(groupMasterKey), recipientTable);
}
@ -180,7 +177,6 @@ public class GroupsV2StateProcessor {
@NonNull RecipientTable recipientTable)
{
this.serviceIds = serviceIds;
this.context = context;
this.groupDatabase = groupDatabase;
this.groupsV2Api = groupsV2Api;
this.groupsV2Authorization = groupsV2Authorization;
@ -191,7 +187,6 @@ public class GroupsV2StateProcessor {
}
@VisibleForTesting StateProcessorForGroup(@NonNull ServiceIds serviceIds,
@NonNull Context context,
@NonNull GroupTable groupDatabase,
@NonNull GroupsV2Api groupsV2Api,
@NonNull GroupsV2Authorization groupsV2Authorization,
@ -199,7 +194,6 @@ public class GroupsV2StateProcessor {
@NonNull ProfileAndMessageHelper profileAndMessageHelper)
{
this.serviceIds = serviceIds;
this.context = context;
this.groupDatabase = groupDatabase;
this.groupsV2Api = groupsV2Api;
this.groupsV2Authorization = groupsV2Authorization;
@ -232,18 +226,18 @@ public class GroupsV2StateProcessor {
DecryptedGroupChange decryptedGroupChange = GroupChangeReconstruct.reconstructGroupChange(localState, serverState);
GlobalGroupState inputGroupState = new GlobalGroupState(localState, Collections.singletonList(new ServerGroupLogEntry(serverState, decryptedGroupChange)));
AdvanceGroupStateResult advanceGroupStateResult = GroupStateMapper.partiallyAdvanceGroupState(inputGroupState, serverState.getRevision());
AdvanceGroupStateResult advanceGroupStateResult = GroupStateMapper.partiallyAdvanceGroupState(inputGroupState, serverState.revision);
DecryptedGroup newLocalState = advanceGroupStateResult.getNewGlobalGroupState().getLocalState();
if (newLocalState == null || newLocalState == inputGroupState.getLocalState()) {
info("Local state and server state are equal");
return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, null);
} else {
info("Local state (revision: " + localState.getRevision() + ") does not match server state (revision: " + serverState.getRevision() + "), updating");
info("Local state (revision: " + localState.revision + ") does not match server state (revision: " + serverState.revision + "), updating");
}
updateLocalDatabaseGroupState(inputGroupState, newLocalState);
if (localState.getRevision() == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
if (localState.revision == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
info("Inserting single update message for restore placeholder");
profileAndMessageHelper.insertUpdateMessages(timestamp, null, Collections.singleton(new LocalGroupLogEntry(newLocalState, null)));
} else {
@ -291,8 +285,8 @@ public class GroupsV2StateProcessor {
if (signedGroupChange != null &&
localState != null &&
localState.getRevision() + 1 == signedGroupChange.getRevision() &&
revision == signedGroupChange.getRevision())
localState.revision + 1 == signedGroupChange.revision &&
revision == signedGroupChange.revision)
{
if (notInGroupAndNotBeingAdded(localRecord, signedGroupChange) && notHavingInviteRevoked(signedGroupChange)) {
@ -351,7 +345,7 @@ public class GroupsV2StateProcessor {
}
updateLocalDatabaseGroupState(inputGroupState, newLocalState);
if (localState != null && localState.getRevision() == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
if (localState != null && localState.revision == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
info("Inserting single update message for restore placeholder");
profileAndMessageHelper.insertUpdateMessages(timestamp, null, Collections.singleton(new LocalGroupLogEntry(newLocalState, null)));
} else {
@ -361,7 +355,7 @@ public class GroupsV2StateProcessor {
GlobalGroupState remainingWork = advanceGroupStateResult.getNewGlobalGroupState();
if (remainingWork.getServerHistory().size() > 0) {
info(String.format(Locale.US, "There are more revisions on the server for this group, scheduling for later, V[%d..%d]", newLocalState.getRevision() + 1, remainingWork.getLatestRevisionNumber()));
info(String.format(Locale.US, "There are more revisions on the server for this group, scheduling for later, V[%d..%d]", newLocalState.revision + 1, remainingWork.getLatestRevisionNumber()));
ApplicationDependencies.getJobManager().add(new RequestGroupV2InfoJob(groupId, remainingWork.getLatestRevisionNumber()));
}
@ -371,21 +365,21 @@ public class GroupsV2StateProcessor {
private boolean notInGroupAndNotBeingAdded(@NonNull Optional<GroupRecord> localRecord, @NonNull DecryptedGroupChange signedGroupChange) {
boolean currentlyInGroup = localRecord.isPresent() && localRecord.get().isActive();
boolean addedAsMember = signedGroupChange.getNewMembersList()
boolean addedAsMember = signedGroupChange.newMembers
.stream()
.map(DecryptedMember::getAciBytes)
.map(m -> m.aciBytes)
.map(ACI::parseOrNull)
.filter(Objects::nonNull)
.anyMatch(serviceIds::matches);
boolean addedAsPendingMember = signedGroupChange.getNewPendingMembersList()
boolean addedAsPendingMember = signedGroupChange.newPendingMembers
.stream()
.map(DecryptedPendingMember::getServiceIdBytes)
.map(m -> m.serviceIdBytes)
.anyMatch(serviceIds::matches);
boolean addedAsRequestingMember = signedGroupChange.getNewRequestingMembersList()
boolean addedAsRequestingMember = signedGroupChange.newRequestingMembers
.stream()
.map(DecryptedRequestingMember::getAciBytes)
.map(m -> m.aciBytes)
.map(ACI::parseOrNull)
.filter(Objects::nonNull)
.anyMatch(serviceIds::matches);
@ -394,9 +388,9 @@ public class GroupsV2StateProcessor {
}
private boolean notHavingInviteRevoked(@NonNull DecryptedGroupChange signedGroupChange) {
boolean havingInviteRevoked = signedGroupChange.getDeletePendingMembersList()
boolean havingInviteRevoked = signedGroupChange.deletePendingMembers
.stream()
.map(DecryptedPendingMemberRemoval::getServiceIdBytes)
.map(m -> m.serviceIdBytes)
.anyMatch(serviceIds::matches);
return !havingInviteRevoked;
@ -406,7 +400,7 @@ public class GroupsV2StateProcessor {
* Using network, attempt to bring the local copy of the group up to the revision specified via paging.
*/
private GroupUpdateResult updateLocalGroupFromServerPaged(int revision, DecryptedGroup localState, long timestamp, boolean forceIncludeFirst) throws IOException, GroupNotAMemberException {
boolean latestRevisionOnly = revision == LATEST && (localState == null || localState.getRevision() == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION);
boolean latestRevisionOnly = revision == LATEST && (localState == null || localState.revision == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION);
info("Paging from server revision: " + (revision == LATEST ? "latest" : revision) + ", latestOnly: " + latestRevisionOnly);
@ -421,7 +415,7 @@ public class GroupsV2StateProcessor {
throw new IOException(e);
}
if (localState != null && localState.getRevision() >= latestServerGroup.getRevision() && GroupProtoUtil.isMember(serviceIds.getAci(), localState.getMembersList())) {
if (localState != null && localState.revision >= latestServerGroup.getRevision() && GroupProtoUtil.isMember(serviceIds.getAci(), localState.members)) {
info("Local state is at or later than server");
return new GroupUpdateResult(GroupState.GROUP_CONSISTENT_OR_AHEAD, null);
}
@ -431,16 +425,16 @@ public class GroupsV2StateProcessor {
inputGroupState = new GlobalGroupState(localState, Collections.singletonList(new ServerGroupLogEntry(latestServerGroup.getFullyDecryptedGroup(), null)));
} else {
int revisionWeWereAdded = GroupProtoUtil.findRevisionWeWereAdded(latestServerGroup, serviceIds.getAci());
int logsNeededFrom = localState != null ? Math.max(localState.getRevision(), revisionWeWereAdded) : revisionWeWereAdded;
int logsNeededFrom = localState != null ? Math.max(localState.revision, revisionWeWereAdded) : revisionWeWereAdded;
boolean includeFirstState = forceIncludeFirst ||
localState == null ||
localState.getRevision() < 0 ||
localState.getRevision() == revisionWeWereAdded ||
!GroupProtoUtil.isMember(serviceIds.getAci(), localState.getMembersList()) ||
(revision == LATEST && localState.getRevision() + 1 < latestServerGroup.getRevision());
localState.revision < 0 ||
localState.revision == revisionWeWereAdded ||
!GroupProtoUtil.isMember(serviceIds.getAci(), localState.members) ||
(revision == LATEST && localState.revision + 1 < latestServerGroup.getRevision());
info("Requesting from server currentRevision: " + (localState != null ? localState.getRevision() : "null") +
info("Requesting from server currentRevision: " + (localState != null ? localState.revision : "null") +
" logsNeededFrom: " + logsNeededFrom +
" includeFirstState: " + includeFirstState +
" forceIncludeFirst: " + forceIncludeFirst);
@ -456,10 +450,10 @@ public class GroupsV2StateProcessor {
while (hasMore) {
AdvanceGroupStateResult advanceGroupStateResult = GroupStateMapper.partiallyAdvanceGroupState(inputGroupState, revision);
DecryptedGroup newLocalState = advanceGroupStateResult.getNewGlobalGroupState().getLocalState();
info("Advanced group to revision: " + (newLocalState != null ? newLocalState.getRevision() : "null"));
info("Advanced group to revision: " + (newLocalState != null ? newLocalState.revision : "null"));
if (newLocalState != null && !inputGroupState.hasMore() && !forceIncludeFirst) {
int newLocalRevision = newLocalState.getRevision();
int newLocalRevision = newLocalState.revision;
int requestRevision = (revision == LATEST) ? latestServerGroup.getRevision() : revision;
if (newLocalRevision < requestRevision) {
warn( "Paging again with force first snapshot enabled due to error processing changes. New local revision [" + newLocalRevision + "] hasn't reached our desired level [" + requestRevision + "]");
@ -473,7 +467,7 @@ public class GroupsV2StateProcessor {
updateLocalDatabaseGroupState(inputGroupState, newLocalState);
if (localState == null || localState.getRevision() != GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
if (localState == null || localState.revision != GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
timestamp = profileAndMessageHelper.insertUpdateMessages(timestamp, localState, advanceGroupStateResult.getProcessedLogEntries());
}
@ -491,12 +485,12 @@ public class GroupsV2StateProcessor {
hasMore = inputGroupState.hasMore();
if (hasMore) {
info("Request next page from server revision: " + finalState.getRevision() + " nextPageRevision: " + inputGroupState.getNextPageRevision());
info("Request next page from server revision: " + finalState.revision + " nextPageRevision: " + inputGroupState.getNextPageRevision());
inputGroupState = getFullMemberHistoryPage(finalState, inputGroupState.getNextPageRevision(), false);
}
}
if (localState != null && localState.getRevision() == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
if (localState != null && localState.revision == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
info("Inserting single update message for restore placeholder");
profileAndMessageHelper.insertUpdateMessages(timestamp, null, Collections.singleton(new LocalGroupLogEntry(finalState, null)));
}
@ -504,7 +498,7 @@ public class GroupsV2StateProcessor {
profileAndMessageHelper.persistLearnedProfileKeys(profileKeys);
if (finalGlobalGroupState.getServerHistory().size() > 0) {
info(String.format(Locale.US, "There are more revisions on the server for this group, scheduling for later, V[%d..%d]", finalState.getRevision() + 1, finalGlobalGroupState.getLatestRevisionNumber()));
info(String.format(Locale.US, "There are more revisions on the server for this group, scheduling for later, V[%d..%d]", finalState.revision + 1, finalGlobalGroupState.getLatestRevisionNumber()));
ApplicationDependencies.getJobManager().add(new RequestGroupV2InfoJob(groupId, finalGlobalGroupState.getLatestRevisionNumber()));
}
@ -557,13 +551,13 @@ public class GroupsV2StateProcessor {
.requireV2GroupProperties()
.getDecryptedGroup();
DecryptedGroup simulatedGroupState = DecryptedGroupUtil.removeMember(decryptedGroup, serviceIds.getAci(), decryptedGroup.getRevision() + 1);
DecryptedGroup simulatedGroupState = DecryptedGroupUtil.removeMember(decryptedGroup, serviceIds.getAci(), decryptedGroup.revision + 1);
DecryptedGroupChange simulatedGroupChange = DecryptedGroupChange.newBuilder()
.setEditorServiceIdBytes(ACI.UNKNOWN.toByteString())
.setRevision(simulatedGroupState.getRevision())
.addDeleteMembers(serviceIds.getAci().toByteString())
.build();
DecryptedGroupChange simulatedGroupChange = new DecryptedGroupChange.Builder()
.editorServiceIdBytes(ACI.UNKNOWN.toByteString())
.revision(simulatedGroupState.revision)
.deleteMembers(Collections.singletonList(serviceIds.getAci().toByteString()))
.build();
DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, new GroupMutation(decryptedGroup, simulatedGroupChange, simulatedGroupState), null);
OutgoingMessage leaveMessage = OutgoingMessage.groupUpdateMessage(groupRecipient, decryptedGroupV2Context, System.currentTimeMillis());
@ -605,14 +599,14 @@ public class GroupsV2StateProcessor {
Log.w(TAG, "Group create failed, trying to update");
groupDatabase.update(masterKey, newLocalState);
}
needsAvatarFetch = !TextUtils.isEmpty(newLocalState.getAvatar());
needsAvatarFetch = !TextUtils.isEmpty(newLocalState.avatar);
} else {
groupDatabase.update(masterKey, newLocalState);
needsAvatarFetch = !newLocalState.getAvatar().equals(inputGroupState.getLocalState().getAvatar());
needsAvatarFetch = !newLocalState.avatar.equals(inputGroupState.getLocalState().avatar);
}
if (needsAvatarFetch) {
ApplicationDependencies.getJobManager().add(new AvatarGroupsV2DownloadJob(groupId, newLocalState.getAvatar()));
ApplicationDependencies.getJobManager().add(new AvatarGroupsV2DownloadJob(groupId, newLocalState.avatar));
}
profileAndMessageHelper.determineProfileSharing(inputGroupState, newLocalState);
@ -681,25 +675,25 @@ public class GroupsV2StateProcessor {
void determineProfileSharing(@NonNull GlobalGroupState inputGroupState, @NonNull DecryptedGroup newLocalState) {
if (inputGroupState.getLocalState() != null) {
boolean wasAMemberAlready = DecryptedGroupUtil.findMemberByAci(inputGroupState.getLocalState().getMembersList(), aci).isPresent();
boolean wasAMemberAlready = DecryptedGroupUtil.findMemberByAci(inputGroupState.getLocalState().members, aci).isPresent();
if (wasAMemberAlready) {
return;
}
}
Optional<DecryptedMember> selfAsMemberOptional = DecryptedGroupUtil.findMemberByAci(newLocalState.getMembersList(), aci);
Optional<DecryptedPendingMember> selfAsPendingOptional = DecryptedGroupUtil.findPendingByServiceId(newLocalState.getPendingMembersList(), aci);
Optional<DecryptedMember> selfAsMemberOptional = DecryptedGroupUtil.findMemberByAci(newLocalState.members, aci);
Optional<DecryptedPendingMember> selfAsPendingOptional = DecryptedGroupUtil.findPendingByServiceId(newLocalState.pendingMembers, aci);
if (selfAsMemberOptional.isPresent()) {
DecryptedMember selfAsMember = selfAsMemberOptional.get();
int revisionJoinedAt = selfAsMember.getJoinedAtRevision();
int revisionJoinedAt = selfAsMember.joinedAtRevision;
Optional<Recipient> addedByOptional = Stream.of(inputGroupState.getServerHistory())
.map(ServerGroupLogEntry::getChange)
.filter(c -> c != null && c.getRevision() == revisionJoinedAt)
.filter(c -> c != null && c.revision == revisionJoinedAt)
.findFirst()
.map(c -> Optional.ofNullable(ServiceId.parseOrNull(c.getEditorServiceIdBytes()))
.map(c -> Optional.ofNullable(ServiceId.parseOrNull(c.editorServiceIdBytes))
.map(Recipient::externalPush))
.orElse(Optional.empty());
@ -724,7 +718,7 @@ public class GroupsV2StateProcessor {
Log.w(TAG, "Could not find founding member during gv2 create. Not enabling profile sharing.");
}
} else if (selfAsPendingOptional.isPresent()) {
Optional<Recipient> addedBy = selfAsPendingOptional.flatMap(adder -> Optional.ofNullable(UuidUtil.fromByteStringOrNull(adder.getAddedByAci()))
Optional<Recipient> addedBy = selfAsPendingOptional.flatMap(adder -> Optional.ofNullable(UuidUtil.fromByteStringOrNull(adder.addedByAci))
.map(uuid -> Recipient.externalPush(ACI.from(uuid))));
if (addedBy.isPresent() && addedBy.get().isBlocked()) {
@ -825,14 +819,14 @@ public class GroupsV2StateProcessor {
}
private Optional<ServiceId> getEditor(@NonNull DecryptedGroupV2Context decryptedGroupV2Context) {
DecryptedGroupChange change = decryptedGroupV2Context.getChange();
DecryptedGroupChange change = decryptedGroupV2Context.change;
Optional<ServiceId> changeEditor = DecryptedGroupUtil.editorServiceId(change);
if (changeEditor.isPresent()) {
return changeEditor;
} else {
Optional<DecryptedPendingMember> pending = DecryptedGroupUtil.findPendingByServiceId(decryptedGroupV2Context.getGroupState().getPendingMembersList(), aci);
Optional<DecryptedPendingMember> pending = DecryptedGroupUtil.findPendingByServiceId(decryptedGroupV2Context.groupState.pendingMembers, aci);
if (pending.isPresent()) {
return Optional.ofNullable(ACI.parseOrNull(pending.get().getAddedByAci()));
return Optional.ofNullable(ACI.parseOrNull(pending.get().addedByAci));
}
}
return Optional.empty();

View file

@ -21,7 +21,7 @@ final class LocalGroupLogEntry {
@Nullable private final DecryptedGroupChange change;
LocalGroupLogEntry(@NonNull DecryptedGroup group, @Nullable DecryptedGroupChange change) {
if (change != null && group.getRevision() != change.getRevision()) {
if (change != null && group.revision != change.revision) {
throw new AssertionError();
}

View file

@ -21,7 +21,7 @@ final class ServerGroupLogEntry {
@Nullable private final DecryptedGroupChange change;
ServerGroupLogEntry(@Nullable DecryptedGroup group, @Nullable DecryptedGroupChange change) {
if (change != null && group != null && group.getRevision() != change.getRevision()) {
if (change != null && group != null && group.revision != change.revision) {
Log.w(TAG, "Ignoring change with revision number not matching group");
change = null;
}
@ -43,8 +43,8 @@ final class ServerGroupLogEntry {
}
int getRevision() {
if (group != null) return group.getRevision();
else if (change != null) return change.getRevision();
if (group != null) return group.revision;
else if (change != null) return change.revision;
else throw new AssertionError();
}
}

View file

@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.util.Base64
import org.whispersystems.signalservice.api.crypto.protos.CompleteMessage
import org.whispersystems.signalservice.api.crypto.protos.EnvelopeMetadata
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
import org.whispersystems.signalservice.internal.push.Envelope
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto
/**
@ -60,33 +60,33 @@ class PushProcessMessageJobMigration : JobMigration(10) {
Log.i(TAG, "Migrating PushProcessJob to V2")
val protoBytes: ByteArray = Base64.decode(inputData.getString("message_content"))
val proto = SignalServiceContentProto.parseFrom(protoBytes)
val proto = SignalServiceContentProto.ADAPTER.decode(protoBytes)
val sourceServiceId = ServiceId.parseOrThrow(proto.metadata.address.uuid)
val destinationServiceId = ServiceId.parseOrThrow(proto.metadata.destinationUuid)
val sourceServiceId = ServiceId.parseOrThrow(proto.metadata!!.address!!.uuid!!)
val destinationServiceId = ServiceId.parseOrThrow(proto.metadata!!.destinationUuid!!)
val envelope = Envelope.newBuilder()
.setSourceServiceId(sourceServiceId.toString())
.setSourceDevice(proto.metadata.senderDevice)
.setDestinationServiceId(destinationServiceId.toString())
.setTimestamp(proto.metadata.timestamp)
.setServerGuid(proto.metadata.serverGuid)
.setServerTimestamp(proto.metadata.serverReceivedTimestamp)
val envelope = Envelope.Builder()
.sourceServiceId(sourceServiceId.toString())
.sourceDevice(proto.metadata!!.senderDevice)
.destinationServiceId(destinationServiceId.toString())
.timestamp(proto.metadata!!.timestamp)
.serverGuid(proto.metadata!!.serverGuid)
.serverTimestamp(proto.metadata!!.serverReceivedTimestamp)
val metadata = EnvelopeMetadata(
sourceServiceId = sourceServiceId.toByteArray().toByteString(),
sourceE164 = if (proto.metadata.address.hasE164()) proto.metadata.address.e164 else null,
sourceDeviceId = proto.metadata.senderDevice,
sealedSender = proto.metadata.needsReceipt,
groupId = if (proto.metadata.hasGroupId()) proto.metadata.groupId.toByteArray().toByteString() else null,
sourceE164 = if (proto.metadata?.address?.e164 != null) proto.metadata!!.address!!.e164 else null,
sourceDeviceId = proto.metadata!!.senderDevice!!,
sealedSender = proto.metadata!!.needsReceipt!!,
groupId = if (proto.metadata?.groupId != null) proto.metadata!!.groupId!! else null,
destinationServiceId = destinationServiceId.toByteArray().toByteString()
)
val completeMessage = CompleteMessage(
envelope = envelope.build().toByteArray().toByteString(),
content = proto.content.toByteArray().toByteString(),
envelope = envelope.build().encodeByteString(),
content = proto.content!!.encodeByteString(),
metadata = metadata,
serverDeliveredTimestamp = proto.metadata.serverDeliveredTimestamp
serverDeliveredTimestamp = proto.metadata!!.serverDeliveredTimestamp!!
)
return jobData

View file

@ -8,8 +8,8 @@ import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
@ -55,19 +55,19 @@ public class PushProcessMessageQueueJobMigration extends JobMigration {
String suffix = "";
if (data.getInt("message_state") == 0) {
SignalServiceContentProto proto = SignalServiceContentProto.parseFrom(Base64.decode(data.getString("message_content")));
SignalServiceContentProto proto = SignalServiceContentProto.ADAPTER.decode(Base64.decode(data.getString("message_content")));
if (proto != null && proto.hasContent() && proto.getContent().hasDataMessage() && proto.getContent().getDataMessage().hasGroupV2()) {
if (proto != null && proto.content != null && proto.content.dataMessage != null && proto.content.dataMessage.groupV2 != null) {
Log.i(TAG, "Migrating a group message.");
GroupId groupId = GroupId.v2(new GroupMasterKey(proto.getContent().getDataMessage().getGroupV2().getMasterKey().toByteArray()));
GroupId groupId = GroupId.v2(new GroupMasterKey(proto.content.dataMessage.groupV2.masterKey.toByteArray()));
Recipient recipient = Recipient.externalGroupExact(groupId);
suffix = recipient.getId().toQueueKey();
} else if (proto != null && proto.hasMetadata() && proto.getMetadata().hasAddress()) {
} else if (proto != null && proto.metadata != null && proto.metadata.address != null) {
Log.i(TAG, "Migrating an individual message.");
ServiceId senderServiceId = ServiceId.parseOrThrow(proto.getMetadata().getAddress().getUuid());
String senderE164 = proto.getMetadata().getAddress().getE164();
ServiceId senderServiceId = ServiceId.parseOrThrow(proto.metadata.address.uuid);
String senderE164 = proto.metadata.address.e164;
SignalServiceAddress sender = new SignalServiceAddress(senderServiceId, Optional.ofNullable(senderE164));
suffix = RecipientId.from(sender).toQueueKey();

View file

@ -9,8 +9,8 @@ import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -23,6 +23,8 @@ import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@ -146,26 +148,28 @@ public class AutomaticSessionResetJob extends BaseJob {
}
private long getLastResetTime(@NonNull DeviceLastResetTime resetTimes, int deviceId) {
for (DeviceLastResetTime.Pair pair : resetTimes.getResetTimeList()) {
if (pair.getDeviceId() == deviceId) {
return pair.getLastResetTime();
for (DeviceLastResetTime.Pair pair : resetTimes.resetTime) {
if (pair.deviceId == deviceId) {
return pair.lastResetTime;
}
}
return 0;
}
private @NonNull DeviceLastResetTime setLastResetTime(@NonNull DeviceLastResetTime resetTimes, int deviceId, long time) {
DeviceLastResetTime.Builder builder = DeviceLastResetTime.newBuilder();
DeviceLastResetTime.Builder builder = new DeviceLastResetTime.Builder();
for (DeviceLastResetTime.Pair pair : resetTimes.getResetTimeList()) {
if (pair.getDeviceId() != deviceId) {
builder.addResetTime(pair);
List<DeviceLastResetTime.Pair> newResetTimes = new ArrayList<>(resetTimes.resetTime.size());
for (DeviceLastResetTime.Pair pair : resetTimes.resetTime) {
if (pair.deviceId != deviceId) {
newResetTimes.add(pair);
}
}
builder.addResetTime(DeviceLastResetTime.Pair.newBuilder().setDeviceId(deviceId).setLastResetTime(time));
return builder.build();
newResetTimes.add(new DeviceLastResetTime.Pair.Builder().deviceId(deviceId).lastResetTime(time).build());
return builder.resetTime(newResetTimes).build();
}
public static final class Factory implements Job.Factory<AutomaticSessionResetJob> {

View file

@ -5,7 +5,7 @@
package org.thoughtcrime.securesms.jobs
import com.google.protobuf.ByteString
import okio.ByteString.Companion.toByteString
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
@ -17,8 +17,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate
import java.lang.Exception
import org.whispersystems.signalservice.internal.push.SyncMessage.CallLinkUpdate
import java.util.Optional
import java.util.concurrent.TimeUnit
@ -68,9 +67,7 @@ class CallLinkUpdateSendJob private constructor(
return
}
val callLinkUpdate = CallLinkUpdate.newBuilder()
.setRootKey(ByteString.copyFrom(callLink.credentials.linkKeyBytes))
.build()
val callLinkUpdate = CallLinkUpdate(rootKey = callLink.credentials.linkKeyBytes.toByteString())
ApplicationDependencies.getSignalServiceMessageSender()
.sendSyncMessage(SignalServiceSyncMessage.forCallLinkUpdate(callLinkUpdate), Optional.empty())

View file

@ -5,7 +5,6 @@
package org.thoughtcrime.securesms.jobs
import okio.ByteString
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
@ -13,7 +12,7 @@ import org.thoughtcrime.securesms.jobs.protos.CallLogEventSendJobData
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SyncMessage
import java.util.Optional
import java.util.concurrent.TimeUnit
@ -22,7 +21,7 @@ import java.util.concurrent.TimeUnit
*/
class CallLogEventSendJob private constructor(
parameters: Parameters,
private val callLogEvent: SignalServiceProtos.SyncMessage.CallLogEvent
private val callLogEvent: SyncMessage.CallLogEvent
) : BaseJob(parameters) {
companion object {
@ -37,16 +36,15 @@ class CallLogEventSendJob private constructor(
.setMaxAttempts(Parameters.UNLIMITED)
.addConstraint(NetworkConstraint.KEY)
.build(),
SignalServiceProtos.SyncMessage.CallLogEvent
.newBuilder()
.setTimestamp(timestamp)
.setType(SignalServiceProtos.SyncMessage.CallLogEvent.Type.CLEAR)
.build()
SyncMessage.CallLogEvent(
timestamp = timestamp,
type = SyncMessage.CallLogEvent.Type.CLEAR
)
)
}
override fun serialize(): ByteArray = CallLogEventSendJobData.Builder()
.callLogEvent(ByteString.of(*callLogEvent.toByteArray()))
.callLogEvent(callLogEvent.encodeByteString())
.build()
.encode()
@ -74,7 +72,7 @@ class CallLogEventSendJob private constructor(
override fun create(parameters: Parameters, serializedData: ByteArray?): CallLogEventSendJob {
return CallLogEventSendJob(
parameters,
SignalServiceProtos.SyncMessage.CallLogEvent.parseFrom(
SyncMessage.CallLogEvent.ADAPTER.decode(
CallLogEventSendJobData.ADAPTER.decode(serializedData!!).callLogEvent.toByteArray()
)
)

View file

@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.ringrtc.RemotePeer
import org.thoughtcrime.securesms.service.webrtc.CallEventSyncMessageUtil
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SyncMessage
import java.util.Optional
import java.util.concurrent.TimeUnit
@ -120,7 +120,7 @@ class CallSyncEventJob private constructor(
}
}
private fun createSyncMessage(syncTimestamp: Long, callSyncEvent: CallSyncEventJobRecord, callType: CallTable.Type): SignalServiceProtos.SyncMessage.CallEvent {
private fun createSyncMessage(syncTimestamp: Long, callSyncEvent: CallSyncEventJobRecord, callType: CallTable.Type): SyncMessage.CallEvent {
return when (callSyncEvent.deserializeEvent()) {
CallTable.Event.ACCEPTED -> CallEventSyncMessageUtil.createAcceptedSyncMessage(
remotePeer = RemotePeer(callSyncEvent.deserializeRecipientId(), CallId(callSyncEvent.callId)),

View file

@ -14,9 +14,9 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
@ -254,12 +254,12 @@ public class DonationReceiptRedemptionJob extends BaseJob {
if (MessageRecordUtil.hasGiftBadge(messageRecord)) {
GiftBadge giftBadge = MessageRecordUtil.requireGiftBadge(messageRecord);
if (giftBadge.getRedemptionState() == GiftBadge.RedemptionState.REDEEMED) {
if (giftBadge.redemptionState == GiftBadge.RedemptionState.REDEEMED) {
Log.d(TAG, "Already redeemed this gift badge. Exiting.", true);
return null;
} else {
Log.d(TAG, "Attempting redemption of badge in state " + giftBadge.getRedemptionState().name());
return new ReceiptCredentialPresentation(giftBadge.getRedemptionToken().toByteArray());
Log.d(TAG, "Attempting redemption of badge in state " + giftBadge.redemptionState.name());
return new ReceiptCredentialPresentation(giftBadge.redemptionToken.toByteArray());
}
} else {
Log.d(TAG, "No gift badge on message record. Exiting.", true);

View file

@ -1,6 +1,6 @@
package org.thoughtcrime.securesms.jobs
import com.google.protobuf.ByteString
import okio.ByteString.Companion.toByteString
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.badges.gifts.Gifts
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
@ -63,7 +63,7 @@ class GiftSendJob private constructor(parameters: Parameters, private val recipi
recipient = recipient,
expiresIn = TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong()),
sentTimestamp = System.currentTimeMillis(),
giftBadge = GiftBadge.newBuilder().setRedemptionToken(ByteString.copyFrom(token)).build()
giftBadge = GiftBadge(redemptionToken = token.toByteString())
)
Log.i(TAG, "Sending gift badge to $recipientId...")

View file

@ -4,8 +4,6 @@ import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
@ -18,20 +16,21 @@ import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import okio.ByteString;
/**
* When your profile key changes, this job can be used to update it on a single given group.
* <p>
@ -96,7 +95,7 @@ public final class GroupV2UpdateSelfProfileKeyJob extends BaseJob {
return;
}
ByteString selfProfileKey = ByteString.copyFrom(rawProfileKey);
ByteString selfProfileKey = ByteString.of(rawProfileKey);
long timeSinceLastCheck = System.currentTimeMillis() - SignalStore.misc().getLastGv2ProfileCheckTime();
@ -120,13 +119,13 @@ public final class GroupV2UpdateSelfProfileKeyJob extends BaseJob {
}
ByteString selfUuidBytes = Recipient.self().requireAci().toByteString();
DecryptedMember selfMember = group.get().requireV2GroupProperties().getDecryptedGroup().getMembersList()
DecryptedMember selfMember = group.get().requireV2GroupProperties().getDecryptedGroup().members
.stream()
.filter(m -> m.getAciBytes().equals(selfUuidBytes))
.filter(m -> m.aciBytes.equals(selfUuidBytes))
.findFirst()
.orElse(null);
if (selfMember != null && !selfMember.getProfileKey().equals(selfProfileKey)) {
if (selfMember != null && !selfMember.profileKey.equals(selfProfileKey)) {
Log.w(TAG, "Profile key mismatch for group " + id + " -- enqueueing job");
foundMismatch = true;
ApplicationDependencies.getJobManager().add(GroupV2UpdateSelfProfileKeyJob.withQueueLimits(id));

View file

@ -19,9 +19,9 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.MmsException;
@ -33,7 +33,6 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.ConversationUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
@ -51,8 +50,8 @@ import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredExcepti
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.BodyRange;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage;
import org.whispersystems.signalservice.internal.push.BodyRange;
import org.whispersystems.signalservice.internal.push.DataMessage;
import java.io.FileNotFoundException;
import java.io.IOException;

View file

@ -5,7 +5,7 @@
package org.thoughtcrime.securesms.jobs
import com.google.protobuf.ByteString
import okio.ByteString.Companion.toByteString
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.Job
@ -13,13 +13,14 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate
import org.whispersystems.signalservice.internal.push.SyncMessage.CallLinkUpdate
import java.util.Optional
import kotlin.time.Duration.Companion.days
/**
* Sends a sync message to linked devices when a new call link is created locally.
*/
// TODO [cody] not being created?
class MultiDeviceCallLinkSyncJob private constructor(
parameters: Parameters,
private val callLinkUpdate: CallLinkUpdate
@ -32,10 +33,10 @@ class MultiDeviceCallLinkSyncJob private constructor(
.setLifespan(1.days.inWholeMilliseconds)
.setMaxAttempts(Parameters.UNLIMITED)
.build(),
CallLinkUpdate.newBuilder()
.setRootKey(ByteString.copyFrom(credentials.linkKeyBytes))
.setAdminPassKey(ByteString.copyFrom(credentials.adminPassBytes!!))
.build()
CallLinkUpdate(
rootKey = credentials.linkKeyBytes.toByteString(),
adminPassKey = credentials.adminPassBytes!!.toByteString()
)
)
companion object {
@ -45,7 +46,7 @@ class MultiDeviceCallLinkSyncJob private constructor(
}
override fun serialize(): ByteArray {
return callLinkUpdate.toByteArray()
return callLinkUpdate.encode()
}
override fun getFactoryKey(): String = KEY
@ -72,7 +73,7 @@ class MultiDeviceCallLinkSyncJob private constructor(
class Factory : Job.Factory<MultiDeviceCallLinkSyncJob> {
override fun create(parameters: Parameters, serializedData: ByteArray?): MultiDeviceCallLinkSyncJob {
val data = CallLinkUpdate.parseFrom(serializedData)
val data = CallLinkUpdate.ADAPTER.decode(serializedData!!)
return MultiDeviceCallLinkSyncJob(parameters, data)
}
}

View file

@ -32,7 +32,7 @@ class MultiDeviceContactSyncJob(parameters: Parameters, private val attachmentPo
Parameters.Builder()
.setQueue("MultiDeviceContactSyncJob")
.build(),
AttachmentPointerUtil.createAttachmentPointer(contactsAttachment).toByteArray()
AttachmentPointerUtil.createAttachmentPointer(contactsAttachment).encode()
)
override fun serialize(): ByteArray? {

View file

@ -3,15 +3,13 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.PaymentTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
import org.thoughtcrime.securesms.payments.proto.PaymentMetaData;
@ -28,6 +26,8 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import okio.ByteString;
/**
* Tells a linked device about sent payments.
*/
@ -87,7 +87,7 @@ public final class MultiDeviceOutgoingPaymentSyncJob extends BaseJob {
return;
}
PaymentMetaData.MobileCoinTxoIdentification txoIdentification = payment.getPaymentMetaData().getMobileCoinTxoIdentification();
PaymentMetaData.MobileCoinTxoIdentification txoIdentification = payment.getPaymentMetaData().mobileCoinTxoIdentification;
boolean defrag = payment.isDefrag();
@ -107,13 +107,13 @@ public final class MultiDeviceOutgoingPaymentSyncJob extends BaseJob {
OutgoingPaymentMessage outgoingPaymentMessage = new OutgoingPaymentMessage(uuid,
payment.getAmount().requireMobileCoin(),
payment.getFee().requireMobileCoin(),
ByteString.copyFrom(receipt),
ByteString.of(receipt),
payment.getBlockIndex(),
payment.getTimestamp(),
defrag ? Optional.empty() : Optional.of(payment.getPayee().requirePublicAddress().serialize()),
defrag ? Optional.empty() : Optional.of(payment.getNote()),
txoIdentification.getPublicKeyList(),
txoIdentification.getKeyImagesList());
txoIdentification.publicKey,
txoIdentification.keyImages);
ApplicationDependencies.getSignalServiceMessageSender()

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs
import androidx.annotation.WorkerThread
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import okio.ByteString.Companion.toByteString
import org.signal.core.util.concurrent.safeBlockingGet
import org.signal.core.util.logging.Log
import org.signal.core.util.orNull
@ -15,7 +16,6 @@ import org.signal.libsignal.protocol.util.KeyHelper
import org.signal.libsignal.protocol.util.Medium
import org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNumberRepository
import org.thoughtcrime.securesms.crypto.PreKeyUtil
import org.thoughtcrime.securesms.database.model.toProtoByteString
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
@ -31,7 +31,7 @@ import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import org.whispersystems.signalservice.internal.push.SyncMessage
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
import org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevicesException
import java.io.IOException
@ -212,13 +212,13 @@ class PnpInitializeDevicesJob private constructor(parameters: Parameters) : Base
// Device Messages
if (deviceId != primaryDeviceId) {
val pniChangeNumber = SignalServiceProtos.SyncMessage.PniChangeNumber.newBuilder()
.setIdentityKeyPair(pniIdentity.serialize().toProtoByteString())
.setSignedPreKey(signedPreKeyRecord.serialize().toProtoByteString())
.setLastResortKyberPreKey(lastResortKyberPreKeyRecord.serialize().toProtoByteString())
.setRegistrationId(pniRegistrationId)
.setNewE164(newE164)
.build()
val pniChangeNumber = SyncMessage.PniChangeNumber(
identityKeyPair = pniIdentity.serialize().toByteString(),
signedPreKey = signedPreKeyRecord.serialize().toByteString(),
lastResortKyberPreKey = lastResortKyberPreKeyRecord.serialize().toByteString(),
registrationId = pniRegistrationId,
newE164 = newE164
)
deviceMessages += messageSender.getEncryptedSyncPniInitializeDeviceMessage(deviceId, pniChangeNumber)
}

View file

@ -19,10 +19,10 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobLogger;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.messages.StorySendUtil;
@ -40,7 +40,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessageRecipient;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.BodyRange;
import java.io.IOException;
import java.util.ArrayList;
@ -202,8 +202,8 @@ public final class PushDistributionListSendJob extends PushSendJob {
rotateSenderCertificateIfNecessary();
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
List<SignalServiceProtos.BodyRange> bodyRanges = getBodyRanges(message);
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
List<BodyRange> bodyRanges = getBodyRanges(message);
boolean isRecipientUpdate = Stream.of(SignalDatabase.groupReceipts().getGroupReceiptInfo(messageId))
.anyMatch(info -> info.getStatus() > GroupReceiptTable.STATUS_UNDELIVERED);

View file

@ -8,7 +8,6 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import org.signal.core.util.SetUtil;
import org.signal.core.util.logging.Log;
@ -28,10 +27,10 @@ import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobLogger;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.messages.StorySendUtil;
@ -59,8 +58,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.BodyRange;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2;
import org.whispersystems.signalservice.internal.push.BodyRange;
import org.whispersystems.signalservice.internal.push.GroupContextV2;
import java.io.IOException;
import java.util.ArrayList;
@ -72,6 +71,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import okio.ByteString;
public final class PushGroupSendJob extends PushSendJob {
public static final String KEY = "PushGroupSendJob";
@ -116,8 +117,8 @@ public final class PushGroupSendJob extends PushSendJob {
throw new AssertionError("Not a group!");
}
MessageTable database = SignalDatabase.messages();
OutgoingMessage message = database.getOutgoingMessage(messageId);
MessageTable database = SignalDatabase.messages();
OutgoingMessage message = database.getOutgoingMessage(messageId);
if (message.getScheduledDate() != -1) {
if (!filterAddresses.isEmpty()) {
@ -207,7 +208,8 @@ public final class PushGroupSendJob extends PushSendJob {
}
try {
log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getThreadRecipient().getId() + ", Thread: " + threadId + ", Attachments: " + buildAttachmentString(message.getAttachments()));
log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getThreadRecipient()
.getId() + ", Thread: " + threadId + ", Attachments: " + buildAttachmentString(message.getAttachments()));
if (!groupRecipient.resolve().isProfileSharing() && !database.isGroupQuitMessage(messageId)) {
RecipientUtil.shareProfileIfFirstSecureMessage(groupRecipient);
@ -269,8 +271,8 @@ public final class PushGroupSendJob extends PushSendJob {
List<BodyRange> bodyRanges = getBodyRanges(message);
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
boolean isRecipientUpdate = Stream.of(SignalDatabase.groupReceipts().getGroupReceiptInfo(messageId))
.anyMatch(info -> info.getStatus() > GroupReceiptTable.STATUS_UNDELIVERED);
boolean isRecipientUpdate = Stream.of(SignalDatabase.groupReceipts().getGroupReceiptInfo(messageId))
.anyMatch(info -> info.getStatus() > GroupReceiptTable.STATUS_UNDELIVERED);
if (message.getStoryType().isStory()) {
Optional<GroupRecord> groupRecord = SignalDatabase.groups().getGroup(groupId);
@ -306,10 +308,10 @@ public final class PushGroupSendJob extends PushSendJob {
if (message.isV2Group()) {
MessageGroupContext.GroupV2Properties properties = message.requireGroupV2Properties();
GroupContextV2 groupContext = properties.getGroupContext();
SignalServiceGroupV2.Builder builder = SignalServiceGroupV2.newBuilder(properties.getGroupMasterKey())
.withRevision(groupContext.getRevision());
SignalServiceGroupV2.Builder builder = SignalServiceGroupV2.newBuilder(properties.getGroupMasterKey())
.withRevision(groupContext.revision);
ByteString groupChange = groupContext.getGroupChange();
ByteString groupChange = groupContext.groupChange;
if (groupChange != null) {
builder.withSignedGroupChange(groupChange.toByteArray());
}
@ -320,7 +322,8 @@ public final class PushGroupSendJob extends PushSendJob {
.withExpiration(groupRecipient.getExpiresInSeconds())
.asGroupMessage(group)
.build();
return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), null, destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId), groupDataMessage, message.isUrgent(), false, null);
return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId()
.requireV2(), null, destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId), groupDataMessage, message.isUrgent(), false, null);
} else {
throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!");
}
@ -369,8 +372,8 @@ public final class PushGroupSendJob extends PushSendJob {
}
SignalServiceDataMessage groupMessage = groupMessageBuilder.build();
SignalServiceEditMessage editMessage = originalEditedMessage != null ? new SignalServiceEditMessage(originalEditedMessage.getDateSent(), groupMessage)
: null;
SignalServiceEditMessage editMessage = originalEditedMessage != null ? new SignalServiceEditMessage(originalEditedMessage.getDateSent(), groupMessage)
: null;
Log.i(TAG, JobLogger.format(this, "Beginning message send."));
@ -411,9 +414,9 @@ public final class PushGroupSendJob extends PushSendJob {
MessageTable database = SignalDatabase.messages();
RecipientAccessList accessList = new RecipientAccessList(target);
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(accessList.requireIdByAddress(result.getAddress()))).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null)
.map(result -> new IdentityKeyMismatch(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(accessList.requireIdByAddress(result.getAddress()))).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null)
.map(result -> new IdentityKeyMismatch(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
List<Pair<RecipientId, Boolean>> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(accessList.requireIdByAddress(result.getAddress()), result.getSuccess().isUnidentified())).toList();

View file

@ -8,15 +8,14 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.protobuf.InvalidProtocolBufferException;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
@ -36,7 +35,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.GroupContextV2;
import java.io.IOException;
import java.util.ArrayList;
@ -60,10 +59,10 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
private static final String KEY_TIMESTAMP = "timestamp";
private static final String KEY_GROUP_CONTEXT_V2 = "group_context_v2";
private final List<RecipientId> recipients;
private final int initialRecipientCount;
private final SignalServiceProtos.GroupContextV2 groupContextV2;
private final long timestamp;
private final List<RecipientId> recipients;
private final int initialRecipientCount;
private final GroupContextV2 groupContextV2;
private final long timestamp;
@WorkerThread
public static @NonNull Job create(@NonNull Context context,
@ -71,8 +70,8 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
@NonNull DecryptedGroup decryptedGroup,
@NonNull OutgoingMessage groupMessage)
{
List<ACI> memberAcis = DecryptedGroupUtil.toAciList(decryptedGroup.getMembersList());
List<ServiceId> pendingServiceIds = DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.getPendingMembersList());
List<ACI> memberAcis = DecryptedGroupUtil.toAciList(decryptedGroup.members);
List<ServiceId> pendingServiceIds = DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.pendingMembers);
Stream<ACI> memberServiceIds = Stream.of(memberAcis)
.filter(ACI::isValid)
@ -87,7 +86,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
.collect(Collectors.toSet());
MessageGroupContext.GroupV2Properties properties = groupMessage.requireGroupV2Properties();
SignalServiceProtos.GroupContextV2 groupContext = properties.getGroupContext();
GroupContextV2 groupContext = properties.getGroupContext();
String queue = Recipient.externalGroupExact(groupId).getId().toQueueKey();
@ -105,7 +104,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
private PushGroupSilentUpdateSendJob(@NonNull List<RecipientId> recipients,
int initialRecipientCount,
long timestamp,
@NonNull SignalServiceProtos.GroupContextV2 groupContextV2,
@NonNull GroupContextV2 groupContextV2,
@NonNull Parameters parameters)
{
super(parameters);
@ -121,7 +120,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
return new JsonJobData.Builder().putString(KEY_RECIPIENTS, RecipientId.toSerializedList(recipients))
.putInt(KEY_INITIAL_RECIPIENT_COUNT, initialRecipientCount)
.putLong(KEY_TIMESTAMP, timestamp)
.putString(KEY_GROUP_CONTEXT_V2, Base64.encodeBytes(groupContextV2.toByteArray()))
.putString(KEY_GROUP_CONTEXT_V2, Base64.encodeBytes(groupContextV2.encode()))
.serialize();
}
@ -136,7 +135,7 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
throw new NotPushRegisteredException();
}
GroupId.V2 groupId = GroupId.v2(GroupUtil.requireMasterKey(groupContextV2.getMasterKey().toByteArray()));
GroupId.V2 groupId = GroupId.v2(GroupUtil.requireMasterKey(groupContextV2.masterKey.toByteArray()));
if (Recipient.externalGroupExact(groupId).isBlocked()) {
Log.i(TAG, "Not updating group state for blocked group " + groupId);
@ -201,10 +200,10 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
long timestamp = data.getLong(KEY_TIMESTAMP);
byte[] contextBytes = Base64.decodeOrThrow(data.getString(KEY_GROUP_CONTEXT_V2));
SignalServiceProtos.GroupContextV2 groupContextV2;
GroupContextV2 groupContextV2;
try {
groupContextV2 = SignalServiceProtos.GroupContextV2.parseFrom(contextBytes);
} catch (InvalidProtocolBufferException e) {
groupContextV2 = GroupContextV2.ADAPTER.decode(contextBytes);
} catch (IOException e) {
throw new AssertionError(e);
}

View file

@ -21,8 +21,8 @@ import org.whispersystems.signalservice.api.crypto.protos.CompleteMessage
import org.whispersystems.signalservice.api.groupsv2.NoCredentialForRedemptionTimeException
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.Envelope
import java.io.IOException
import java.util.concurrent.TimeUnit
import org.whispersystems.signalservice.api.crypto.protos.EnvelopeMetadata as EnvelopeMetadataProto
@ -39,8 +39,8 @@ class PushProcessMessageJob private constructor(
override fun serialize(): ByteArray {
return CompleteMessage(
envelope = envelope.toByteArray().toByteString(),
content = content.toByteArray().toByteString(),
envelope = envelope.encodeByteString(),
content = content.encodeByteString(),
metadata = EnvelopeMetadataProto(
sourceServiceId = ByteString.of(*metadata.sourceServiceId.toByteArray()),
sourceE164 = metadata.sourceE164,
@ -76,8 +76,8 @@ class PushProcessMessageJob private constructor(
val completeMessage = CompleteMessage.ADAPTER.decode(data!!)
PushProcessMessageJob(
parameters = parameters,
envelope = Envelope.parseFrom(completeMessage.envelope.toByteArray()),
content = Content.parseFrom(completeMessage.content.toByteArray()),
envelope = Envelope.ADAPTER.decode(completeMessage.envelope.toByteArray()),
content = Content.ADAPTER.decode(completeMessage.content.toByteArray()),
metadata = EnvelopeMetadata(
sourceServiceId = ServiceId.parseOrThrow(completeMessage.metadata.sourceServiceId.toByteArray()),
sourceE164 = completeMessage.metadata.sourceE164,
@ -126,13 +126,13 @@ class PushProcessMessageJob private constructor(
if (groupId.isV2) {
val localRevision = groups.getGroupV2Revision(groupId.requireV2())
if (groupContext.revision > localRevision || GroupsV1MigratedCache.hasV1Group(groupId)) {
if (groupContext.revision!! > localRevision || GroupsV1MigratedCache.hasV1Group(groupId)) {
Log.i(TAG, "Adding network constraint to group-related job.")
requireNetwork = true
}
}
} else if (result.content.hasSyncMessage() && result.content.syncMessage.hasSent() && result.content.syncMessage.sent.hasDestinationServiceId()) {
queueName = getQueueName(RecipientId.from(ServiceId.parseOrThrow(result.content.syncMessage.sent.destinationServiceId)))
} else if (result.content.syncMessage != null && result.content.syncMessage!!.sent != null && result.content.syncMessage!!.sent!!.destinationServiceId != null) {
queueName = getQueueName(RecipientId.from(ServiceId.parseOrThrow(result.content.syncMessage!!.sent!!.destinationServiceId!!)))
} else {
queueName = getQueueName(RecipientId.from(result.metadata.sourceServiceId))
}
@ -145,12 +145,12 @@ class PushProcessMessageJob private constructor(
if (requireNetwork) {
builder.addConstraint(NetworkConstraint.KEY).setLifespan(TimeUnit.DAYS.toMillis(30))
}
PushProcessMessageJob(builder.build(), result.envelope.toBuilder().clearContent().build(), result.content, result.metadata, result.serverDeliveredTimestamp)
PushProcessMessageJob(builder.build(), result.envelope.newBuilder().content(null).build(), result.content, result.metadata, result.serverDeliveredTimestamp)
} else {
try {
messageProcessor.process(result.envelope, result.content, result.metadata, result.serverDeliveredTimestamp, localMetric = localReceiveMetric)
} catch (e: Exception) {
Log.e(TAG, "Failed to process message with timestamp ${result.envelope.timestamp}. Dropping.")
Log.e(TAG, "Failed to process message with timestamp ${result.envelope.timestamp}. Dropping.", e)
}
null
}

View file

@ -68,11 +68,10 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.BodyRange;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -333,7 +332,7 @@ public abstract class PushSendJob extends SendJob {
String quoteBody = message.getOutgoingQuote().getText();
RecipientId quoteAuthor = message.getOutgoingQuote().getAuthor();
List<SignalServiceDataMessage.Mention> quoteMentions = getMentionsFor(message.getOutgoingQuote().getMentions());
List<SignalServiceProtos.BodyRange> bodyRanges = getBodyRanges(message.getOutgoingQuote().getBodyRanges());
List<BodyRange> bodyRanges = getBodyRanges(message.getOutgoingQuote().getBodyRanges());
QuoteModel.Type quoteType = message.getOutgoingQuote().getType();
List<SignalServiceDataMessage.Quote.QuotedAttachment> quoteAttachments = new LinkedList<>();
Optional<Attachment> localQuoteAttachment = message.getOutgoingQuote()
@ -463,7 +462,7 @@ public abstract class PushSendJob extends SendJob {
}
try {
ReceiptCredentialPresentation presentation = new ReceiptCredentialPresentation(giftBadge.getRedemptionToken().toByteArray());
ReceiptCredentialPresentation presentation = new ReceiptCredentialPresentation(giftBadge.redemptionToken.toByteArray());
return new SignalServiceDataMessage.GiftBadge(presentation);
} catch (InvalidInputException invalidInputException) {
@ -471,39 +470,37 @@ public abstract class PushSendJob extends SendJob {
}
}
protected @Nullable List<SignalServiceProtos.BodyRange> getBodyRanges(@NonNull OutgoingMessage message) {
protected @Nullable List<BodyRange> getBodyRanges(@NonNull OutgoingMessage message) {
return getBodyRanges(message.getBodyRanges());
}
protected @Nullable List<SignalServiceProtos.BodyRange> getBodyRanges(@Nullable BodyRangeList bodyRanges) {
if (bodyRanges == null || bodyRanges.getRangesCount() == 0) {
protected @Nullable List<BodyRange> getBodyRanges(@Nullable BodyRangeList bodyRanges) {
if (bodyRanges == null || bodyRanges.ranges.size() == 0) {
return null;
}
return bodyRanges
.getRangesList()
.ranges
.stream()
.map(range -> {
SignalServiceProtos.BodyRange.Builder builder = SignalServiceProtos.BodyRange.newBuilder()
.setStart(range.getStart())
.setLength(range.getLength());
BodyRange.Builder builder = new BodyRange.Builder().start(range.start).length(range.length);
if (range.hasStyle()) {
switch (range.getStyle()) {
if (range.style != null) {
switch (range.style) {
case BOLD:
builder.setStyle(SignalServiceProtos.BodyRange.Style.BOLD);
builder.style(BodyRange.Style.BOLD);
break;
case ITALIC:
builder.setStyle(SignalServiceProtos.BodyRange.Style.ITALIC);
builder.style(BodyRange.Style.ITALIC);
break;
case SPOILER:
builder.setStyle(SignalServiceProtos.BodyRange.Style.SPOILER);
builder.style(BodyRange.Style.SPOILER);
break;
case STRIKETHROUGH:
builder.setStyle(SignalServiceProtos.BodyRange.Style.STRIKETHROUGH);
builder.style(BodyRange.Style.STRIKETHROUGH);
break;
case MONOSPACE:
builder.setStyle(SignalServiceProtos.BodyRange.Style.MONOSPACE);
builder.style(BodyRange.Style.MONOSPACE);
break;
default:
throw new IllegalArgumentException("Unrecognized style");

View file

@ -13,7 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials
import org.thoughtcrime.securesms.service.webrtc.links.ReadCallLinkResult
import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkManager
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate
import org.whispersystems.signalservice.internal.push.SyncMessage.CallLinkUpdate
import java.util.concurrent.TimeUnit
/**
@ -38,7 +38,7 @@ class RefreshCallLinkDetailsJob private constructor(
const val KEY = "RefreshCallLinkDetailsJob"
}
override fun serialize(): ByteArray = callLinkUpdate.toByteArray()
override fun serialize(): ByteArray = callLinkUpdate.encode()
override fun getFactoryKey(): String = KEY
@ -47,7 +47,7 @@ class RefreshCallLinkDetailsJob private constructor(
override fun onRun() {
val manager: SignalCallLinkManager = ApplicationDependencies.getSignalCallManager().callLinkManager
val credentials = CallLinkCredentials(
linkKeyBytes = callLinkUpdate.rootKey.toByteArray(),
linkKeyBytes = callLinkUpdate.rootKey!!.toByteArray(),
adminPassBytes = callLinkUpdate.adminPassKey?.toByteArray()
)
@ -63,7 +63,7 @@ class RefreshCallLinkDetailsJob private constructor(
class Factory : Job.Factory<RefreshCallLinkDetailsJob> {
override fun create(parameters: Parameters, serializedData: ByteArray?): RefreshCallLinkDetailsJob {
val callLinkUpdate = CallLinkUpdate.parseFrom(serializedData)
val callLinkUpdate = CallLinkUpdate.ADAPTER.decode(serializedData!!)
return RefreshCallLinkDetailsJob(parameters, callLinkUpdate)
}
}

View file

@ -3,21 +3,18 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.SignalProtocolAddress;
import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -30,13 +27,16 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content;
import org.whispersystems.signalservice.internal.push.Content;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import okio.ByteString;
/**
* Resends a previously-sent message in response to receiving a retry receipt.
*
@ -111,7 +111,7 @@ public class ResendMessageJob extends BaseJob {
return new JsonJobData.Builder()
.putString(KEY_RECIPIENT_ID, recipientId.serialize())
.putLong(KEY_SENT_TIMESTAMP, sentTimestamp)
.putBlobAsString(KEY_CONTENT, content.toByteArray())
.putBlobAsString(KEY_CONTENT, content.encode())
.putInt(KEY_CONTENT_HINT, contentHint.getType())
.putBoolean(KEY_URGENT, urgent)
.putBlobAsString(KEY_GROUP_ID, groupId != null ? groupId.getDecodedId() : null)
@ -169,9 +169,9 @@ public class ResendMessageJob extends BaseJob {
}
SenderKeyDistributionMessage senderKeyDistributionMessage = messageSender.getOrCreateNewGroupSession(distributionId);
ByteString distributionBytes = ByteString.copyFrom(senderKeyDistributionMessage.serialize());
ByteString distributionBytes = ByteString.of(senderKeyDistributionMessage.serialize());
contentToSend = contentToSend.toBuilder().setSenderKeyDistributionMessage(distributionBytes).build();
contentToSend = contentToSend.newBuilder().senderKeyDistributionMessage(distributionBytes).build();
}
SendMessageResult result = messageSender.resendContent(address, access, sentTimestamp, contentToSend, contentHint, Optional.ofNullable(groupId).map(GroupId::getDecodedId), urgent);
@ -204,8 +204,8 @@ public class ResendMessageJob extends BaseJob {
Content content;
try {
content = Content.parseFrom(data.getStringAsBlob(KEY_CONTENT));
} catch (InvalidProtocolBufferException e) {
content = Content.ADAPTER.decode(data.getStringAsBlob(KEY_CONTENT));
} catch (IOException e) {
throw new AssertionError(e);
}

View file

@ -189,7 +189,7 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool
.forEach { note ->
val title = "${note.translation.title}\n\n"
val body = "$title${note.translation.body}"
val bodyRangeList = BodyRangeList.newBuilder()
val bodyRangeList = BodyRangeList.Builder()
.addStyle(BodyRangeList.BodyRange.Style.BOLD, 0, note.translation.title.length)
if (note.translation.bodyRanges?.isNotEmpty() == true) {

Some files were not shown because too many files have changed in this diff Show more