Convert SignalService, Database, Group, Payment, and other remaining protos to wire.
This commit is contained in:
parent
a6b7d0bcc5
commit
efbd5cab85
267 changed files with 7100 additions and 7214 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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)!!
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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!!))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)!!
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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!!
|
||||
|
|
|
@ -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>()
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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__!";
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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...")
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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? {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue