kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add support for rendering session switchover events.
rodzic
03c68375db
commit
9e056e5dd0
|
@ -43,10 +43,7 @@
|
||||||
</JavaCodeStyleSettings>
|
</JavaCodeStyleSettings>
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
<value>
|
<value />
|
||||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
|
||||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
|
||||||
</value>
|
|
||||||
</option>
|
</option>
|
||||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||||
|
|
|
@ -346,6 +346,32 @@ class RecipientTableTest_getAndPossiblyMerge {
|
||||||
expectThreadMergeEvent(E164_A)
|
expectThreadMergeEvent(E164_A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("merge, e164 & pni & aci, all provided, no threads") {
|
||||||
|
given(E164_A, null, null, createThread = false)
|
||||||
|
given(null, PNI_A, null, createThread = false)
|
||||||
|
given(null, null, ACI_A, createThread = false)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectDeleted()
|
||||||
|
expectDeleted()
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("merge, e164 & pni & aci, all provided, pni session no threads") {
|
||||||
|
given(E164_A, null, null, createThread = false)
|
||||||
|
given(null, PNI_A, null, createThread = true, pniSession = true)
|
||||||
|
given(null, null, ACI_A, createThread = false)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectDeleted()
|
||||||
|
expectDeleted()
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectSessionSwitchoverEvent(E164_A)
|
||||||
|
}
|
||||||
|
|
||||||
test("merge, e164 & pni, no aci provided") {
|
test("merge, e164 & pni, no aci provided") {
|
||||||
given(E164_A, null, null)
|
given(E164_A, null, null)
|
||||||
given(null, PNI_A, null)
|
given(null, PNI_A, null)
|
||||||
|
@ -382,7 +408,7 @@ class RecipientTableTest_getAndPossiblyMerge {
|
||||||
expectThreadMergeEvent("")
|
expectThreadMergeEvent("")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("merge, e164 & pni, aci provided, existing pni session") {
|
test("merge, e164 & pni, aci provided, existing pni session, thread merge shadows") {
|
||||||
given(E164_A, null, null)
|
given(E164_A, null, null)
|
||||||
given(null, PNI_A, null, pniSession = true)
|
given(null, PNI_A, null, pniSession = true)
|
||||||
|
|
||||||
|
@ -392,6 +418,17 @@ class RecipientTableTest_getAndPossiblyMerge {
|
||||||
expectDeleted()
|
expectDeleted()
|
||||||
|
|
||||||
expectThreadMergeEvent("")
|
expectThreadMergeEvent("")
|
||||||
|
}
|
||||||
|
|
||||||
|
test("merge, e164 & pni, aci provided, existing pni session, no thread merge") {
|
||||||
|
given(E164_A, null, null, createThread = true)
|
||||||
|
given(null, PNI_A, null, createThread = false, pniSession = true)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
expectDeleted()
|
||||||
|
|
||||||
expectSessionSwitchoverEvent(E164_A)
|
expectSessionSwitchoverEvent(E164_A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +444,7 @@ class RecipientTableTest_getAndPossiblyMerge {
|
||||||
expectThreadMergeEvent("")
|
expectThreadMergeEvent("")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("merge, e164+pni & aci") {
|
test("merge, e164+pni & aci, no pni session") {
|
||||||
given(E164_A, PNI_A, null)
|
given(E164_A, PNI_A, null)
|
||||||
given(null, null, ACI_A)
|
given(null, null, ACI_A)
|
||||||
|
|
||||||
|
@ -419,6 +456,52 @@ class RecipientTableTest_getAndPossiblyMerge {
|
||||||
expectThreadMergeEvent(E164_A)
|
expectThreadMergeEvent(E164_A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("merge, e164+pni & aci, pni session, thread merge shadows") {
|
||||||
|
given(E164_A, PNI_A, null, pniSession = true)
|
||||||
|
given(null, null, ACI_A)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectDeleted()
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectThreadMergeEvent(E164_A)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("merge, e164+pni & aci, pni session, no thread merge") {
|
||||||
|
given(E164_A, PNI_A, null, createThread = true, pniSession = true)
|
||||||
|
given(null, null, ACI_A, createThread = false)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectDeleted()
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectSessionSwitchoverEvent(E164_A)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("merge, e164+pni & aci, pni session, no thread merge, pni verified") {
|
||||||
|
given(E164_A, PNI_A, null, createThread = true, pniSession = true)
|
||||||
|
given(null, null, ACI_A, createThread = false)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A, pniVerified = true)
|
||||||
|
|
||||||
|
expectDeleted()
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("merge, e164+pni & aci, pni session, pni verified") {
|
||||||
|
given(E164_A, PNI_A, null, pniSession = true)
|
||||||
|
given(null, null, ACI_A)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A, pniVerified = true)
|
||||||
|
|
||||||
|
expectDeleted()
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectThreadMergeEvent(E164_A)
|
||||||
|
}
|
||||||
|
|
||||||
test("merge, e164+pni & e164+pni+aci, change number") {
|
test("merge, e164+pni & e164+pni+aci, change number") {
|
||||||
given(E164_A, PNI_A, null)
|
given(E164_A, PNI_A, null)
|
||||||
given(E164_B, PNI_B, ACI_A)
|
given(E164_B, PNI_B, ACI_A)
|
||||||
|
@ -758,8 +841,8 @@ class RecipientTableTest_getAndPossiblyMerge {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
fun process(e164: String?, pni: PNI?, aci: ACI?, changeSelf: Boolean = false) {
|
fun process(e164: String?, pni: PNI?, aci: ACI?, changeSelf: Boolean = false, pniVerified: Boolean = false) {
|
||||||
outputRecipientId = SignalDatabase.recipients.getAndPossiblyMerge(serviceId = aci ?: pni, pni = pni, e164 = e164, pniVerified = false, changeSelf = changeSelf)
|
outputRecipientId = SignalDatabase.recipients.getAndPossiblyMerge(serviceId = aci ?: pni, pni = pni, e164 = e164, pniVerified = pniVerified, changeSelf = changeSelf)
|
||||||
generatedIds += outputRecipientId
|
generatedIds += outputRecipientId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,843 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.database
|
|
||||||
|
|
||||||
import androidx.core.content.contentValuesOf
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
|
||||||
import org.thoughtcrime.securesms.testing.SignalDatabaseRule
|
|
||||||
import org.thoughtcrime.securesms.util.Util
|
|
||||||
import org.whispersystems.signalservice.api.push.ACI
|
|
||||||
import org.whispersystems.signalservice.api.push.PNI
|
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId
|
|
||||||
import java.lang.AssertionError
|
|
||||||
import java.lang.IllegalStateException
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class RecipientTableTest_processPnpTupleToChangeSet {
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
@JvmField
|
|
||||||
val databaseRule = SignalDatabaseRule(deleteAllThreadsOnEachRun = false)
|
|
||||||
|
|
||||||
private lateinit var db: RecipientTable
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
db = SignalDatabase.recipients
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noMatch_e164Only() {
|
|
||||||
val changeSet = db.processPnpTupleToChangeSet(E164_A, null, null, pniVerified = false)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpInsert(E164_A, null, null)
|
|
||||||
),
|
|
||||||
changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noMatch_e164AndPni() {
|
|
||||||
val changeSet = db.processPnpTupleToChangeSet(E164_A, PNI_A, null, pniVerified = false)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpInsert(E164_A, PNI_A, null)
|
|
||||||
),
|
|
||||||
changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noMatch_aciOnly() {
|
|
||||||
val changeSet = db.processPnpTupleToChangeSet(null, null, ACI_A, pniVerified = false)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpInsert(null, null, ACI_A)
|
|
||||||
),
|
|
||||||
changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalStateException::class)
|
|
||||||
fun noMatch_noData() {
|
|
||||||
db.processPnpTupleToChangeSet(null, null, null, pniVerified = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noMatch_allFields() {
|
|
||||||
val changeSet = db.processPnpTupleToChangeSet(E164_A, PNI_A, ACI_A, pniVerified = false)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpInsert(E164_A, PNI_A, ACI_A)
|
|
||||||
),
|
|
||||||
changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun fullMatch() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, PNI_A, ACI_A),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyE164Matches() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, null, null),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetPni(result.id, PNI_A),
|
|
||||||
PnpOperation.SetAci(result.id, ACI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyE164Matches_pniChanges_noAciProvided_existingPniSession() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, PNI_B, null, pniSession = true),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetPni(result.id, PNI_A),
|
|
||||||
PnpOperation.SessionSwitchoverInsert(result.id, E164_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyE164Matches_pniChanges_noAciProvided_noPniSession() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, PNI_B, null),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetPni(result.id, PNI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun e164AndPniMatches_noExistingSession() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, PNI_A, null),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetAci(result.id, ACI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun e164AndPniMatches_existingPniSession() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, PNI_A, null, pniSession = true),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetAci(result.id, ACI_A),
|
|
||||||
PnpOperation.SessionSwitchoverInsert(result.id, E164_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun e164AndAciMatches() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_A, null, ACI_A),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetPni(result.id, PNI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyPniMatches_noExistingSession() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(null, PNI_A, null),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A),
|
|
||||||
PnpOperation.SetAci(result.id, ACI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyPniMatches_existingPniSession() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(null, PNI_A, null, pniSession = true),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A),
|
|
||||||
PnpOperation.SetAci(result.id, ACI_A),
|
|
||||||
PnpOperation.SessionSwitchoverInsert(result.id, E164_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyPniMatches_existingPniSession_changeNumber() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_B, PNI_A, null, pniSession = true),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A),
|
|
||||||
PnpOperation.SetAci(result.id, ACI_A),
|
|
||||||
PnpOperation.ChangeNumberInsert(
|
|
||||||
recipientId = result.id,
|
|
||||||
oldE164 = E164_B,
|
|
||||||
newE164 = E164_A
|
|
||||||
),
|
|
||||||
PnpOperation.SessionSwitchoverInsert(result.id, E164_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun pniAndAciMatches() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(null, PNI_A, ACI_A),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun pniAndAciMatches_changeNumber() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_B, PNI_A, ACI_A),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A),
|
|
||||||
PnpOperation.ChangeNumberInsert(
|
|
||||||
recipientId = result.id,
|
|
||||||
oldE164 = E164_B,
|
|
||||||
newE164 = E164_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyAciMatches() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(null, null, ACI_A),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A),
|
|
||||||
PnpOperation.SetPni(result.id, PNI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onlyAciMatches_changeNumber() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
Input(E164_B, null, ACI_A),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.id),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.SetE164(result.id, E164_A),
|
|
||||||
PnpOperation.SetPni(result.id, PNI_A),
|
|
||||||
PnpOperation.ChangeNumberInsert(
|
|
||||||
recipientId = result.id,
|
|
||||||
oldE164 = E164_B,
|
|
||||||
newE164 = E164_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164Only_pniOnly_aciOnly() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, null, null),
|
|
||||||
Input(null, PNI_A, null),
|
|
||||||
Input(null, null, ACI_A)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.thirdId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.firstId,
|
|
||||||
secondaryId = result.secondId
|
|
||||||
),
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.thirdId,
|
|
||||||
secondaryId = result.firstId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164Only_pniOnly_noAciProvided() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, null, null),
|
|
||||||
Input(null, PNI_A, null)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.firstId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.firstId,
|
|
||||||
secondaryId = result.secondId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164Only_pniOnly_aciProvidedButNoAciRecord() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, null, null),
|
|
||||||
Input(null, PNI_A, null)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.firstId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.firstId,
|
|
||||||
secondaryId = result.secondId
|
|
||||||
),
|
|
||||||
PnpOperation.SetAci(
|
|
||||||
recipientId = result.firstId,
|
|
||||||
aci = ACI_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164Only_pniAndE164_noAciProvided() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, null, null),
|
|
||||||
Input(E164_B, PNI_A, null)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.firstId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.secondId),
|
|
||||||
PnpOperation.SetPni(
|
|
||||||
recipientId = result.firstId,
|
|
||||||
pni = PNI_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_pniOnly_noAciProvided() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, PNI_B, null),
|
|
||||||
Input(null, PNI_A, null)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.firstId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.firstId),
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.firstId,
|
|
||||||
secondaryId = result.secondId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_e164AndPni_noAciProvided_noSessions() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, PNI_B, null),
|
|
||||||
Input(E164_B, PNI_A, null)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.firstId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.secondId),
|
|
||||||
PnpOperation.SetPni(result.firstId, PNI_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_e164AndPni_noAciProvided_sessionsExist() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, PNI_B, null, pniSession = true),
|
|
||||||
Input(E164_B, PNI_A, null, pniSession = true)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, null),
|
|
||||||
Output(E164_A, PNI_A, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.firstId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.secondId),
|
|
||||||
PnpOperation.SetPni(result.firstId, PNI_A),
|
|
||||||
PnpOperation.SessionSwitchoverInsert(result.secondId, E164_A),
|
|
||||||
PnpOperation.SessionSwitchoverInsert(result.firstId, E164_A)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_aciOnly() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, PNI_A, null),
|
|
||||||
Input(null, null, ACI_A)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.secondId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.secondId,
|
|
||||||
secondaryId = result.firstId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_aciOnly_e164RecordHasSeparateE164() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_B, PNI_A, null),
|
|
||||||
Input(null, null, ACI_A)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.secondId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.firstId),
|
|
||||||
PnpOperation.SetPni(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
pni = PNI_A
|
|
||||||
),
|
|
||||||
PnpOperation.SetE164(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
e164 = E164_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_aciOnly_e164RecordHasSeparateE164_changeNumber() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_B, PNI_A, null),
|
|
||||||
Input(E164_C, null, ACI_A)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.secondId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.firstId),
|
|
||||||
PnpOperation.SetPni(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
pni = PNI_A
|
|
||||||
),
|
|
||||||
PnpOperation.SetE164(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
e164 = E164_A
|
|
||||||
),
|
|
||||||
PnpOperation.ChangeNumberInsert(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
oldE164 = E164_C,
|
|
||||||
newE164 = E164_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_e164AndPniAndAci_changeNumber() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, PNI_A, null),
|
|
||||||
Input(E164_B, PNI_B, ACI_A)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.secondId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemovePni(result.secondId),
|
|
||||||
PnpOperation.RemoveE164(result.secondId),
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.secondId,
|
|
||||||
secondaryId = result.firstId
|
|
||||||
),
|
|
||||||
PnpOperation.ChangeNumberInsert(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
oldE164 = E164_B,
|
|
||||||
newE164 = E164_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun merge_e164AndPni_e164Aci_changeNumber() {
|
|
||||||
val result = applyAndAssert(
|
|
||||||
listOf(
|
|
||||||
Input(E164_A, PNI_A, null),
|
|
||||||
Input(E164_B, null, ACI_A)
|
|
||||||
),
|
|
||||||
Update(E164_A, PNI_A, ACI_A),
|
|
||||||
Output(E164_A, PNI_A, ACI_A)
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
PnpChangeSet(
|
|
||||||
id = PnpIdResolver.PnpNoopId(result.secondId),
|
|
||||||
operations = linkedSetOf(
|
|
||||||
PnpOperation.RemoveE164(result.secondId),
|
|
||||||
PnpOperation.Merge(
|
|
||||||
primaryId = result.secondId,
|
|
||||||
secondaryId = result.firstId
|
|
||||||
),
|
|
||||||
PnpOperation.ChangeNumberInsert(
|
|
||||||
recipientId = result.secondId,
|
|
||||||
oldE164 = E164_B,
|
|
||||||
newE164 = E164_A
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
result.changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun insert(e164: String?, pni: PNI?, aci: ACI?): RecipientId {
|
|
||||||
val id: Long = SignalDatabase.rawDatabase.insert(
|
|
||||||
RecipientTable.TABLE_NAME,
|
|
||||||
null,
|
|
||||||
contentValuesOf(
|
|
||||||
RecipientTable.PHONE to e164,
|
|
||||||
RecipientTable.SERVICE_ID to (aci ?: pni)?.toString(),
|
|
||||||
RecipientTable.PNI_COLUMN to pni?.toString(),
|
|
||||||
RecipientTable.REGISTERED to RecipientTable.RegisteredState.REGISTERED.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return RecipientId.from(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun insertMockSessionFor(account: ServiceId, address: ServiceId) {
|
|
||||||
SignalDatabase.rawDatabase.insert(
|
|
||||||
SessionTable.TABLE_NAME,
|
|
||||||
null,
|
|
||||||
contentValuesOf(
|
|
||||||
SessionTable.ACCOUNT_ID to account.toString(),
|
|
||||||
SessionTable.ADDRESS to address.toString(),
|
|
||||||
SessionTable.DEVICE to 1,
|
|
||||||
SessionTable.RECORD to Util.getSecretBytes(32)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Input(val e164: String?, val pni: PNI?, val aci: ACI?, val pniSession: Boolean = false, val aciSession: Boolean = false)
|
|
||||||
data class Update(val e164: String?, val pni: PNI?, val aci: ACI?, val pniVerified: Boolean = false)
|
|
||||||
data class Output(val e164: String?, val pni: PNI?, val aci: ACI?)
|
|
||||||
data class PnpMatchResult(val ids: List<RecipientId>, val changeSet: PnpChangeSet) {
|
|
||||||
val id
|
|
||||||
get() = if (ids.size == 1) {
|
|
||||||
ids[0]
|
|
||||||
} else {
|
|
||||||
throw IllegalStateException("There are multiple IDs, but you assumed 1!")
|
|
||||||
}
|
|
||||||
|
|
||||||
val firstId
|
|
||||||
get() = ids[0]
|
|
||||||
|
|
||||||
val secondId
|
|
||||||
get() = ids[1]
|
|
||||||
|
|
||||||
val thirdId
|
|
||||||
get() = ids[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyAndAssert(input: Input, update: Update, output: Output): PnpMatchResult {
|
|
||||||
return applyAndAssert(listOf(input), update, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method that will call insert your recipients, call [RecipientTable.processPnpTupleToChangeSet] with your params,
|
|
||||||
* and then verify your output matches what you expect.
|
|
||||||
*
|
|
||||||
* It results the inserted ID's and changeset for additional verification.
|
|
||||||
*
|
|
||||||
* But basically this is here to make the tests more readable. It gives you a clear list of:
|
|
||||||
* - input
|
|
||||||
* - update
|
|
||||||
* - output
|
|
||||||
*
|
|
||||||
* that you can spot check easily.
|
|
||||||
*
|
|
||||||
* Important: The output will only include records that contain fields from the input. That means
|
|
||||||
* for:
|
|
||||||
*
|
|
||||||
* Input: E164_B, PNI_A, null
|
|
||||||
* Update: E164_A, PNI_A, null
|
|
||||||
*
|
|
||||||
* You will get:
|
|
||||||
* Output: E164_A, PNI_A, null
|
|
||||||
*
|
|
||||||
* Even though there was an update that will also result in the row (E164_B, null, null)
|
|
||||||
*/
|
|
||||||
private fun applyAndAssert(input: List<Input>, update: Update, output: Output): PnpMatchResult {
|
|
||||||
val ids = input.map { insert(it.e164, it.pni, it.aci) }
|
|
||||||
|
|
||||||
input
|
|
||||||
.filter { it.pniSession }
|
|
||||||
.forEach { insertMockSessionFor(databaseRule.localAci, it.pni!!) }
|
|
||||||
|
|
||||||
input
|
|
||||||
.filter { it.aciSession }
|
|
||||||
.forEach { insertMockSessionFor(databaseRule.localAci, it.aci!!) }
|
|
||||||
|
|
||||||
val byE164 = update.e164?.let { db.getByE164(it).orElse(null) }
|
|
||||||
val byPniSid = update.pni?.let { db.getByServiceId(it).orElse(null) }
|
|
||||||
val byAciSid = update.aci?.let { db.getByServiceId(it).orElse(null) }
|
|
||||||
|
|
||||||
val data = PnpDataSet(
|
|
||||||
e164 = update.e164,
|
|
||||||
pni = update.pni,
|
|
||||||
aci = update.aci,
|
|
||||||
byE164 = byE164,
|
|
||||||
byPniSid = byPniSid,
|
|
||||||
byPniOnly = update.pni?.let { db.getByPni(it).orElse(null) },
|
|
||||||
byAciSid = byAciSid,
|
|
||||||
e164Record = byE164?.let { db.getRecord(it) },
|
|
||||||
pniSidRecord = byPniSid?.let { db.getRecord(it) },
|
|
||||||
aciSidRecord = byAciSid?.let { db.getRecord(it) }
|
|
||||||
)
|
|
||||||
val changeSet = db.processPnpTupleToChangeSet(update.e164, update.pni, update.aci, pniVerified = update.pniVerified)
|
|
||||||
|
|
||||||
val finalData = data.perform(changeSet.operations)
|
|
||||||
|
|
||||||
val finalRecords = setOfNotNull(finalData.e164Record, finalData.pniSidRecord, finalData.aciSidRecord)
|
|
||||||
assertEquals("There's still multiple records in the resulting record set! $finalRecords", 1, finalRecords.size)
|
|
||||||
|
|
||||||
finalRecords.firstOrNull { record -> record.e164 == output.e164 && record.pni == output.pni && record.serviceId == (output.aci ?: output.pni) }
|
|
||||||
?: throw AssertionError("Expected output was not found in the result set! Expected: $output")
|
|
||||||
|
|
||||||
return PnpMatchResult(
|
|
||||||
ids = ids,
|
|
||||||
changeSet = changeSet
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val ACI_A = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
|
|
||||||
val ACI_B = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
|
|
||||||
|
|
||||||
val PNI_A = PNI.from(UUID.fromString("154b8d92-c960-4f6c-8385-671ad2ffb999"))
|
|
||||||
val PNI_B = PNI.from(UUID.fromString("ba92b1fb-cd55-40bf-adda-c35a85375533"))
|
|
||||||
|
|
||||||
const val E164_A = "+12221234567"
|
|
||||||
const val E164_B = "+13331234567"
|
|
||||||
const val E164_C = "+14441234567"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,6 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager
|
||||||
import org.whispersystems.signalservice.api.push.TrustStore
|
import org.whispersystems.signalservice.api.push.TrustStore
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl
|
import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl
|
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl
|
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl
|
||||||
|
@ -66,15 +65,13 @@ class InstrumentationApplicationDependencyProvider(application: Application, def
|
||||||
0 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
0 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||||
2 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT))
|
2 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT))
|
||||||
),
|
),
|
||||||
arrayOf(SignalContactDiscoveryUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
|
||||||
arrayOf(SignalKeyBackupServiceUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
arrayOf(SignalKeyBackupServiceUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||||
arrayOf(SignalStorageUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
arrayOf(SignalStorageUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||||
arrayOf(SignalCdsiUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
arrayOf(SignalCdsiUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||||
emptyList(),
|
emptyList(),
|
||||||
Optional.of(SignalServiceNetworkAccess.DNS),
|
Optional.of(SignalServiceNetworkAccess.DNS),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS),
|
Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS)
|
||||||
true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
serviceNetworkAccessMock = mock {
|
serviceNetworkAccessMock = mock {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import androidx.lifecycle.ViewModelProvider
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.signal.core.util.Hex
|
import org.signal.core.util.Hex
|
||||||
import org.signal.core.util.concurrent.SignalExecutors
|
import org.signal.core.util.concurrent.SignalExecutors
|
||||||
|
import org.signal.core.util.isAbsent
|
||||||
|
import org.signal.libsignal.zkgroup.profiles.ProfileKey
|
||||||
import org.thoughtcrime.securesms.MainActivity
|
import org.thoughtcrime.securesms.MainActivity
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||||
|
@ -220,7 +222,7 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||||
sectionHeaderPref(DSLSettingsText.from("PNP"))
|
sectionHeaderPref(DSLSettingsText.from("PNP"))
|
||||||
|
|
||||||
clickPref(
|
clickPref(
|
||||||
title = DSLSettingsText.from("Split contact"),
|
title = DSLSettingsText.from("Split and create threads"),
|
||||||
summary = DSLSettingsText.from("Splits this contact into two recipients and two threads so that you can test merging them together. This will remain the 'primary' recipient."),
|
summary = DSLSettingsText.from("Splits this contact into two recipients and two threads so that you can test merging them together. This will remain the 'primary' recipient."),
|
||||||
onClick = {
|
onClick = {
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
@ -257,6 +259,41 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
clickPref(
|
||||||
|
title = DSLSettingsText.from("Split without creating threads"),
|
||||||
|
summary = DSLSettingsText.from("Splits this contact into two recipients so you can test merging them together. This will become the PNI-based recipient. Another recipient will be made with this ACI and profile key. Doing a CDS refresh should allow you to see a Session Switchover Event, as long as you had a session with this PNI."),
|
||||||
|
isEnabled = FeatureFlags.phoneNumberPrivacy(),
|
||||||
|
onClick = {
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle("Are you sure?")
|
||||||
|
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
if (recipient.pni.isAbsent()) {
|
||||||
|
Toast.makeText(context, "Recipient doesn't have a PNI! Can't split.", Toast.LENGTH_SHORT).show()
|
||||||
|
return@setPositiveButton
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipient.serviceId.isAbsent()) {
|
||||||
|
Toast.makeText(context, "Recipient doesn't have a serviceId! Can't split.", Toast.LENGTH_SHORT).show()
|
||||||
|
return@setPositiveButton
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalDatabase.recipients.debugRemoveAci(recipient.id)
|
||||||
|
|
||||||
|
val aciRecipientId: RecipientId = SignalDatabase.recipients.getAndPossiblyMergePnpVerified(recipient.requireServiceId(), null, null)
|
||||||
|
|
||||||
|
recipient.profileKey?.let { profileKey ->
|
||||||
|
SignalDatabase.recipients.setProfileKey(aciRecipientId, ProfileKey(profileKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalDatabase.recipients.debugClearProfileData(recipient.id)
|
||||||
|
|
||||||
|
Toast.makeText(context, "Done! Split the ACI and profile key off into $aciRecipientId", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ import android.graphics.Typeface;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.style.CharacterStyle;
|
import android.text.style.CharacterStyle;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
|
@ -73,6 +72,7 @@ import org.thoughtcrime.securesms.database.model.UpdateDescription;
|
||||||
import org.thoughtcrime.securesms.glide.GlideLiveDataTarget;
|
import org.thoughtcrime.securesms.glide.GlideLiveDataTarget;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
@ -85,7 +85,6 @@ import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -637,6 +636,14 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
||||||
return emphasisAdded(context, "", defaultTint);
|
return emphasisAdded(context, "", defaultTint);
|
||||||
} else if (MessageTypes.isBadDecryptType(thread.getType())) {
|
} else if (MessageTypes.isBadDecryptType(thread.getType())) {
|
||||||
return emphasisAdded(context, context.getString(R.string.ThreadRecord_delivery_issue), defaultTint);
|
return emphasisAdded(context, context.getString(R.string.ThreadRecord_delivery_issue), defaultTint);
|
||||||
|
} else if (MessageTypes.isThreadMergeType(thread.getType())) {
|
||||||
|
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_history_has_been_merged), defaultTint);
|
||||||
|
} else if (MessageTypes.isSessionSwitchoverType(thread.getType())) {
|
||||||
|
if (thread.getRecipient().getE164().isPresent()) {
|
||||||
|
return emphasisAdded(context, context.getString(R.string.ThreadRecord_s_belongs_to_s, PhoneNumberFormatter.prettyPrint(thread.getRecipient().requireE164()), thread.getRecipient().getDisplayName(context)), defaultTint);
|
||||||
|
} else {
|
||||||
|
return emphasisAdded(context, context.getString(R.string.ThreadRecord_safety_number_changed), defaultTint);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ThreadTable.Extra extra = thread.getExtra();
|
ThreadTable.Extra extra = thread.getExtra();
|
||||||
if (extra != null && extra.isViewOnce()) {
|
if (extra != null && extra.isViewOnce()) {
|
||||||
|
|
|
@ -2406,6 +2406,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
fun writePnpChangeSetToDisk(changeSet: PnpChangeSet, inputPni: PNI?): RecipientId {
|
fun writePnpChangeSetToDisk(changeSet: PnpChangeSet, inputPni: PNI?): RecipientId {
|
||||||
|
var hadThreadMerge = false
|
||||||
for (operation in changeSet.operations) {
|
for (operation in changeSet.operations) {
|
||||||
@Exhaustive
|
@Exhaustive
|
||||||
when (operation) {
|
when (operation) {
|
||||||
|
@ -2465,16 +2466,21 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
is PnpOperation.Merge -> {
|
is PnpOperation.Merge -> {
|
||||||
merge(operation.primaryId, operation.secondaryId, inputPni)
|
val mergeResult: MergeResult = merge(operation.primaryId, operation.secondaryId, inputPni)
|
||||||
|
hadThreadMerge = hadThreadMerge || mergeResult.neededThreadMerge
|
||||||
}
|
}
|
||||||
is PnpOperation.SessionSwitchoverInsert -> {
|
is PnpOperation.SessionSwitchoverInsert -> {
|
||||||
val threadId: Long? = threads.getThreadIdFor(operation.recipientId)
|
if (hadThreadMerge) {
|
||||||
if (threadId != null) {
|
Log.d(TAG, "Skipping SSE insert because we already had a thread merge event.")
|
||||||
val event = SessionSwitchoverEvent
|
} else {
|
||||||
.newBuilder()
|
val threadId: Long? = threads.getThreadIdFor(operation.recipientId)
|
||||||
.setE164(operation.e164 ?: "")
|
if (threadId != null) {
|
||||||
.build()
|
val event = SessionSwitchoverEvent
|
||||||
SignalDatabase.messages.insertSessionSwitchoverEvent(operation.recipientId, threadId, event)
|
.newBuilder()
|
||||||
|
.setE164(operation.e164 ?: "")
|
||||||
|
.build()
|
||||||
|
SignalDatabase.messages.insertSessionSwitchoverEvent(operation.recipientId, threadId, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PnpOperation.ChangeNumberInsert -> {
|
is PnpOperation.ChangeNumberInsert -> {
|
||||||
|
@ -2622,6 +2628,14 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pniVerified && fullData.pniSidRecord != null && finalData.aciSidRecord != null && sessions.hasAnySessionFor(fullData.pniSidRecord.serviceId.toString())) {
|
||||||
|
breadCrumbs += "FinalUpdateSSE"
|
||||||
|
operations += PnpOperation.SessionSwitchoverInsert(
|
||||||
|
recipientId = primaryId,
|
||||||
|
e164 = finalData.e164
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return PnpChangeSet(
|
return PnpChangeSet(
|
||||||
id = PnpIdResolver.PnpNoopId(primaryId),
|
id = PnpIdResolver.PnpNoopId(primaryId),
|
||||||
operations = operations,
|
operations = operations,
|
||||||
|
@ -3644,7 +3658,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
* Merges one ACI recipient with an E164 recipient. It is assumed that the E164 recipient does
|
* Merges one ACI recipient with an E164 recipient. It is assumed that the E164 recipient does
|
||||||
* *not* have an ACI.
|
* *not* have an ACI.
|
||||||
*/
|
*/
|
||||||
private fun merge(primaryId: RecipientId, secondaryId: RecipientId, newPni: PNI? = null): RecipientId {
|
private fun merge(primaryId: RecipientId, secondaryId: RecipientId, newPni: PNI? = null): MergeResult {
|
||||||
ensureInTransaction()
|
ensureInTransaction()
|
||||||
val db = writableDatabase
|
val db = writableDatabase
|
||||||
val primaryRecord = getRecord(primaryId)
|
val primaryRecord = getRecord(primaryId)
|
||||||
|
@ -3656,7 +3670,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
}
|
}
|
||||||
|
|
||||||
// Threads
|
// Threads
|
||||||
val threadMerge = threads.merge(primaryId, secondaryId)
|
val threadMerge: ThreadTable.MergeResult = threads.merge(primaryId, secondaryId)
|
||||||
threads.setLastScrolled(threadMerge.threadId, 0)
|
threads.setLastScrolled(threadMerge.threadId, 0)
|
||||||
threads.update(threadMerge.threadId, false, false)
|
threads.update(threadMerge.threadId, false, false)
|
||||||
|
|
||||||
|
@ -3721,7 +3735,11 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
}
|
}
|
||||||
|
|
||||||
db.update(TABLE_NAME, uuidValues, ID_WHERE, SqlUtil.buildArgs(primaryId))
|
db.update(TABLE_NAME, uuidValues, ID_WHERE, SqlUtil.buildArgs(primaryId))
|
||||||
return primaryId
|
|
||||||
|
return MergeResult(
|
||||||
|
finalId = primaryId,
|
||||||
|
neededThreadMerge = threadMerge.neededMerge
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureInTransaction() {
|
private fun ensureInTransaction() {
|
||||||
|
@ -3845,6 +3863,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
* get them back through CDS).
|
* get them back through CDS).
|
||||||
*/
|
*/
|
||||||
fun debugClearServiceIds(recipientId: RecipientId? = null) {
|
fun debugClearServiceIds(recipientId: RecipientId? = null) {
|
||||||
|
check(FeatureFlags.internalUser())
|
||||||
|
|
||||||
writableDatabase
|
writableDatabase
|
||||||
.update(TABLE_NAME)
|
.update(TABLE_NAME)
|
||||||
.values(
|
.values(
|
||||||
|
@ -3868,6 +3888,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
* Should only be used for debugging! A very destructive action that clears all known profile keys and credentials.
|
* Should only be used for debugging! A very destructive action that clears all known profile keys and credentials.
|
||||||
*/
|
*/
|
||||||
fun debugClearProfileData(recipientId: RecipientId? = null) {
|
fun debugClearProfileData(recipientId: RecipientId? = null) {
|
||||||
|
check(FeatureFlags.internalUser())
|
||||||
|
|
||||||
writableDatabase
|
writableDatabase
|
||||||
.update(TABLE_NAME)
|
.update(TABLE_NAME)
|
||||||
.values(
|
.values(
|
||||||
|
@ -3896,6 +3918,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
* Should only be used for debugging! Clears the E164 and PNI from a recipient.
|
* Should only be used for debugging! Clears the E164 and PNI from a recipient.
|
||||||
*/
|
*/
|
||||||
fun debugClearE164AndPni(recipientId: RecipientId) {
|
fun debugClearE164AndPni(recipientId: RecipientId) {
|
||||||
|
check(FeatureFlags.internalUser())
|
||||||
|
|
||||||
writableDatabase
|
writableDatabase
|
||||||
.update(TABLE_NAME)
|
.update(TABLE_NAME)
|
||||||
.values(
|
.values(
|
||||||
|
@ -3905,7 +3929,28 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
.where(ID_WHERE, recipientId)
|
.where(ID_WHERE, recipientId)
|
||||||
.run()
|
.run()
|
||||||
|
|
||||||
Recipient.live(recipientId).refresh()
|
ApplicationDependencies.getRecipientCache().clear()
|
||||||
|
RecipientId.clearCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should only be used for debugging! Clears the ACI from a contact.
|
||||||
|
* Only works if the recipient has a PNI.
|
||||||
|
*/
|
||||||
|
fun debugRemoveAci(recipientId: RecipientId) {
|
||||||
|
check(FeatureFlags.internalUser())
|
||||||
|
|
||||||
|
writableDatabase.execSQL(
|
||||||
|
"""
|
||||||
|
UPDATE $TABLE_NAME
|
||||||
|
SET $SERVICE_ID = $PNI_COLUMN
|
||||||
|
WHERE $ID = ? AND $PNI_COLUMN NOT NULL
|
||||||
|
""".toSingleLine(),
|
||||||
|
SqlUtil.buildArgs(recipientId)
|
||||||
|
)
|
||||||
|
|
||||||
|
ApplicationDependencies.getRecipientCache().clear()
|
||||||
|
RecipientId.clearCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRecord(context: Context, cursor: Cursor): RecipientRecord {
|
fun getRecord(context: Context, cursor: Cursor): RecipientRecord {
|
||||||
|
@ -4131,6 +4176,11 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class MergeResult(
|
||||||
|
val finalId: RecipientId,
|
||||||
|
val neededThreadMerge: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
inner class BulkOperationsHandle internal constructor(private val database: SQLiteDatabase) {
|
inner class BulkOperationsHandle internal constructor(private val database: SQLiteDatabase) {
|
||||||
private val pendingRecipients: MutableSet<RecipientId> = mutableSetOf()
|
private val pendingRecipients: MutableSet<RecipientId> = mutableSetOf()
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
|
||||||
import org.thoughtcrime.securesms.emoji.EmojiSource;
|
import org.thoughtcrime.securesms.emoji.EmojiSource;
|
||||||
import org.thoughtcrime.securesms.emoji.JumboEmoji;
|
import org.thoughtcrime.securesms.emoji.JumboEmoji;
|
||||||
|
@ -235,6 +236,18 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
} else if (isSessionSwitchoverEventType()) {
|
||||||
|
try {
|
||||||
|
SessionSwitchoverEvent event = SessionSwitchoverEvent.parseFrom(Base64.decodeOrThrow(getBody()));
|
||||||
|
|
||||||
|
if (event.getE164().isEmpty()) {
|
||||||
|
return fromRecipient(getIndividualRecipient(), 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(getIndividualRecipient(), 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) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
} else if (isSmsExportType()) {
|
} else if (isSmsExportType()) {
|
||||||
int messageResource = SignalStore.misc().getSmsExportPhase().isSmsSupported() ? R.string.MessageRecord__you_will_no_longer_be_able_to_send_sms_messages_from_signal_soon
|
int messageResource = SignalStore.misc().getSmsExportPhase().isSmsSupported() ? R.string.MessageRecord__you_will_no_longer_be_able_to_send_sms_messages_from_signal_soon
|
||||||
: R.string.MessageRecord__you_can_no_longer_send_sms_messages_in_signal;
|
: R.string.MessageRecord__you_can_no_longer_send_sms_messages_in_signal;
|
||||||
|
@ -551,6 +564,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
return MessageTypes.isThreadMergeType(type);
|
return MessageTypes.isThreadMergeType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSessionSwitchoverEventType() {
|
||||||
|
return MessageTypes.isSessionSwitchoverType(type);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSmsExportType() {
|
public boolean isSmsExportType() {
|
||||||
return MessageTypes.isSmsExport(type);
|
return MessageTypes.isSmsExport(type);
|
||||||
}
|
}
|
||||||
|
@ -575,7 +592,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
|
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
|
||||||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
|
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
|
||||||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
|
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
|
||||||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType() ||
|
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() ||
|
||||||
isPaymentsRequestToActivate() || isPaymentsActivated();
|
isPaymentsRequestToActivate() || isPaymentsActivated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.thoughtcrime.securesms.messages.MessageDecryptionUtil;
|
||||||
import org.thoughtcrime.securesms.messages.MessageDecryptionUtil.DecryptionResult;
|
import org.thoughtcrime.securesms.messages.MessageDecryptionUtil.DecryptionResult;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
|
@ -33,7 +32,6 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServicePniSignatureMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServicePniSignatureMessage;
|
||||||
import org.whispersystems.signalservice.api.push.PNI;
|
import org.whispersystems.signalservice.api.push.PNI;
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -114,10 +112,6 @@ public final class PushDecryptMessageJob extends BaseJob {
|
||||||
if (result.getContent().getSenderKeyDistributionMessage().isPresent()) {
|
if (result.getContent().getSenderKeyDistributionMessage().isPresent()) {
|
||||||
handleSenderKeyDistributionMessage(result.getContent().getSender(), result.getContent().getSenderDevice(), result.getContent().getSenderKeyDistributionMessage().get());
|
handleSenderKeyDistributionMessage(result.getContent().getSender(), result.getContent().getSenderDevice(), result.getContent().getSenderKeyDistributionMessage().get());
|
||||||
}
|
}
|
||||||
result.getContent();
|
|
||||||
if (envelope.hasReportingToken()) {
|
|
||||||
SignalDatabase.recipients().setReportingToken(RecipientId.from(result.getContent().getSender()), envelope.getReportingToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FeatureFlags.phoneNumberPrivacy() && result.getContent().getPniSignatureMessage().isPresent()) {
|
if (FeatureFlags.phoneNumberPrivacy() && result.getContent().getPniSignatureMessage().isPresent()) {
|
||||||
handlePniSignatureMessage(result.getContent().getSender(), result.getContent().getSenderDevice(), result.getContent().getPniSignatureMessage().get());
|
handlePniSignatureMessage(result.getContent().getSender(), result.getContent().getSenderDevice(), result.getContent().getPniSignatureMessage().get());
|
||||||
|
@ -125,6 +119,10 @@ public final class PushDecryptMessageJob extends BaseJob {
|
||||||
Log.w(TAG, "Ignoring PNI signature because the feature flag is disabled!");
|
Log.w(TAG, "Ignoring PNI signature because the feature flag is disabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (envelope.hasReportingToken()) {
|
||||||
|
SignalDatabase.recipients().setReportingToken(RecipientId.from(result.getContent().getSender()), envelope.getReportingToken());
|
||||||
|
}
|
||||||
|
|
||||||
jobs.add(new PushProcessMessageJob(result.getContent(), smsMessageId, envelope.getTimestamp()));
|
jobs.add(new PushProcessMessageJob(result.getContent(), smsMessageId, envelope.getTimestamp()));
|
||||||
} else if (result.getException() != null && result.getState() != MessageState.NOOP) {
|
} else if (result.getException() != null && result.getState() != MessageState.NOOP) {
|
||||||
jobs.add(new PushProcessMessageJob(result.getState(), result.getException(), smsMessageId, envelope.getTimestamp()));
|
jobs.add(new PushProcessMessageJob(result.getState(), result.getException(), smsMessageId, envelope.getTimestamp()));
|
||||||
|
|
|
@ -1042,7 +1042,11 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode() {
|
public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode() {
|
||||||
return unidentifiedAccessMode;
|
if (getPni().isPresent() && getPni().equals(getServiceId())) {
|
||||||
|
return UnidentifiedAccessMode.DISABLED;
|
||||||
|
} else {
|
||||||
|
return unidentifiedAccessMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable ChatWallpaper getWallpaper() {
|
public @Nullable ChatWallpaper getWallpaper() {
|
||||||
|
|
|
@ -1962,6 +1962,10 @@
|
||||||
<string name="ThreadRecord_payment">Payment</string>
|
<string name="ThreadRecord_payment">Payment</string>
|
||||||
<!-- Displayed in the conversation list when your only message in a conversation is a scheduled send. -->
|
<!-- Displayed in the conversation list when your only message in a conversation is a scheduled send. -->
|
||||||
<string name="ThreadRecord_scheduled_message">Scheduled message</string>
|
<string name="ThreadRecord_scheduled_message">Scheduled message</string>
|
||||||
|
<!-- Displayed in the conversation list when your message history has been merged -->
|
||||||
|
<string name="ThreadRecord_message_history_has_been_merged">Your message history has been merged</string>
|
||||||
|
<!-- Displayed in the conversation list when identities have been merged. The first placeholder is a phone number, and the second is a person's name -->
|
||||||
|
<string name="ThreadRecord_s_belongs_to_s">%1$s belongs to %2$s</string>
|
||||||
|
|
||||||
<!-- UpdateApkReadyListener -->
|
<!-- UpdateApkReadyListener -->
|
||||||
<string name="UpdateApkReadyListener_Signal_update">Signal update</string>
|
<string name="UpdateApkReadyListener_Signal_update">Signal update</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue