Always perform CDSI lookups when starting new chats.

This commit is contained in:
Greyson Parrelli
2024-03-06 12:11:52 -05:00
committed by Alex Hart
parent 184c1b67cc
commit 2e4ac7ede1
15 changed files with 264 additions and 126 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.recipients
import android.content.Context
import androidx.annotation.WorkerThread
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.phonenumbers.NumberUtil
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import java.io.IOException
/**
* We operate on recipients many places, but sometimes we find ourselves performing the same recipient-related operations in several locations.
* This is meant to be a place to put those common operations.
*/
object RecipientRepository {
private val TAG = Log.tag(RecipientRepository::class.java)
/**
* Attempts to lookup a potentially-new recipient by their e164.
* We will check locally first for a potential match, but may end up hitting the network.
* This will not create a new recipient if we could not find it in the CDSI directory.
*/
@WorkerThread
@JvmStatic
fun lookupNewE164(context: Context, inputE164: String): LookupResult {
val e164 = PhoneNumberFormatter.get(context).format(inputE164)
if (!NumberUtil.isVisuallyValidNumber(e164)) {
return LookupResult.InvalidEntry
}
val matchingFullRecipientId = SignalDatabase.recipients.getByE164IfRegisteredAndDiscoverable(e164)
if (matchingFullRecipientId != null) {
Log.i(TAG, "Already have a full, discoverable recipient for $e164. $matchingFullRecipientId")
return LookupResult.Success(matchingFullRecipientId)
}
Log.i(TAG, "Need to lookup up $e164 with CDSI.")
return try {
val result = ContactDiscovery.lookupE164(e164)
if (result == null) {
LookupResult.NotFound()
} else {
LookupResult.Success(result.recipientId)
}
} catch (e: IOException) {
return LookupResult.NetworkError
}
}
sealed interface LookupResult {
data class Success(val recipientId: RecipientId) : LookupResult
object InvalidEntry : LookupResult
data class NotFound(val recipientId: RecipientId = RecipientId.UNKNOWN) : LookupResult
object NetworkError : LookupResult
}
}

View File

@@ -84,7 +84,6 @@ import org.thoughtcrime.securesms.components.settings.app.usernamelinks.main.Use
import org.thoughtcrime.securesms.invites.InviteActions
import org.thoughtcrime.securesms.permissions.compose.Permissions
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberVisualTransformation
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.registration.util.CountryPrefix
import org.thoughtcrime.securesms.util.viewModel
@@ -161,6 +160,7 @@ class FindByActivity : PassphraseRequiredActivity() {
FindByResult.InvalidEntry -> navController.navigate("invalid-entry")
is FindByResult.NotFound -> navController.navigate("not-found/${result.recipientId.toLong()}")
is FindByResult.NetworkError -> navController.navigate("network-error")
}
}
},
@@ -220,6 +220,16 @@ class FindByActivity : PassphraseRequiredActivity() {
)
}
dialog(
route = "network-error"
) {
Dialogs.SimpleMessageDialog(
message = getString(R.string.FindByActivity__network_error_dialog),
dismiss = getString(android.R.string.ok),
onDismiss = { navController.popBackStack() }
)
}
dialog(
route = "not-found/{recipientId}",
arguments = listOf(navArgument("recipientId") { type = NavType.LongType })
@@ -260,15 +270,10 @@ class FindByActivity : PassphraseRequiredActivity() {
dismiss = dismiss,
onConfirm = {
if (state.mode == FindByMode.PHONE_NUMBER) {
val recipientId = navBackStackEntry.arguments?.getLong("recipientId")?.takeIf { it > 0 }?.let { RecipientId.from(it) } ?: RecipientId.UNKNOWN
if (recipientId != RecipientId.UNKNOWN) {
InviteActions.inviteUserToSignal(
context,
Recipient.resolved(recipientId),
null,
this@FindByActivity::startActivity
)
}
InviteActions.inviteUserToSignal(
context,
this@FindByActivity::startActivity
)
}
},
onDismiss = { navController.popBackStack() }
@@ -429,6 +434,10 @@ private fun Content(
}
}
if (state.isLookupInProgress) {
Dialogs.IndeterminateProgressDialog()
}
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}

View File

@@ -11,4 +11,5 @@ sealed interface FindByResult {
data class Success(val recipientId: RecipientId) : FindByResult
object InvalidEntry : FindByResult
data class NotFound(val recipientId: RecipientId = RecipientId.UNKNOWN) : FindByResult
object NetworkError : FindByResult
}

View File

@@ -14,14 +14,11 @@ import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import org.signal.core.util.concurrent.safeBlockingGet
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
import org.thoughtcrime.securesms.phonenumbers.NumberUtil
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientRepository
import org.thoughtcrime.securesms.registration.util.CountryPrefix
import org.thoughtcrime.securesms.util.UsernameUtil
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter
import java.util.concurrent.TimeUnit
class FindByViewModel(
mode: FindByMode
@@ -86,40 +83,13 @@ class FindByViewModel(
val countryCode = stateSnapshot.selectedCountryPrefix.digits
val nationalNumber = stateSnapshot.userEntry.removePrefix(countryCode.toString())
val e164 = "$countryCode$nationalNumber"
val e164 = "+$countryCode$nationalNumber"
if (!NumberUtil.isVisuallyValidNumber(e164)) {
return FindByResult.InvalidEntry
}
val recipient = try {
Recipient.external(context, e164)
} catch (e: Exception) {
return FindByResult.InvalidEntry
}
return if (!recipient.isRegistered || !recipient.hasServiceId()) {
try {
ContactDiscovery.refresh(context, recipient, false, TimeUnit.SECONDS.toMillis(10))
val resolved = Recipient.resolved(recipient.id)
if (!resolved.isRegistered) {
if (PhoneNumberFormatter.isValidNumber(nationalNumber, countryCode.toString())) {
FindByResult.NotFound(recipient.id)
} else {
FindByResult.InvalidEntry
}
} else {
FindByResult.Success(recipient.id)
}
} catch (e: Exception) {
if (PhoneNumberFormatter.isValidNumber(nationalNumber, countryCode.toString())) {
FindByResult.NotFound(recipient.id)
} else {
FindByResult.InvalidEntry
}
}
} else {
FindByResult.Success(recipient.id)
return when (val result = RecipientRepository.lookupNewE164(context, e164)) {
RecipientRepository.LookupResult.InvalidEntry -> FindByResult.InvalidEntry
RecipientRepository.LookupResult.NetworkError -> FindByResult.NetworkError
is RecipientRepository.LookupResult.NotFound -> FindByResult.NotFound(result.recipientId)
is RecipientRepository.LookupResult.Success -> FindByResult.Success(result.recipientId)
}
}
}