mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Centralize recipient lookup in RecipientRepository.
This commit is contained in:
committed by
Cody Henthorne
parent
2e1291b3c3
commit
0e46ab33e8
@@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallUiState.CallType
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallUiState.UserMessage
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientLookupFailureMessage
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPicker
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPickerCallbacks
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPickerScaffold
|
||||
@@ -218,19 +219,14 @@ private fun UserMessagesHost(
|
||||
when (userMessage) {
|
||||
null -> {}
|
||||
|
||||
is UserMessage.Info.NetworkError -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NetworkFailure__network_error_check_your_connection_and_try_again),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
is UserMessage.RecipientLookupFailed -> {
|
||||
RecipientLookupFailureMessage(
|
||||
failure = userMessage.failure,
|
||||
onDismissed = { onDismiss(userMessage) }
|
||||
)
|
||||
}
|
||||
|
||||
is UserMessage.Info.RecipientNotSignalUser -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NewConversationActivity__s_is_not_a_signal_user, userMessage.phone.displayText),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
|
||||
is UserMessage.Info.UserAlreadyInAnotherCall -> LaunchedEffect(userMessage) {
|
||||
is UserMessage.UserAlreadyInAnotherCall -> LaunchedEffect(userMessage) {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = context.getString(R.string.CommunicationActions__you_are_already_in_a_call)
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallUiState.CallType
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallUiState.UserMessage.Info
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallUiState.UserMessage
|
||||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
@@ -65,33 +65,17 @@ class NewCallViewModel : ViewModel() {
|
||||
}
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = true) }
|
||||
|
||||
val lookupResult = withContext(Dispatchers.IO) {
|
||||
RecipientRepository.lookupNewE164(inputE164 = phone.value)
|
||||
}
|
||||
|
||||
when (lookupResult) {
|
||||
is RecipientRepository.LookupResult.Success -> {
|
||||
val recipient = withContext(Dispatchers.IO) {
|
||||
Recipient.resolved(lookupResult.recipientId)
|
||||
}
|
||||
when (val lookupResult = RecipientRepository.lookup(phone)) {
|
||||
is RecipientRepository.PhoneLookupResult.Found -> {
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = false) }
|
||||
openCall(recipient)
|
||||
openCall(recipient = lookupResult.recipient)
|
||||
}
|
||||
|
||||
is RecipientRepository.LookupResult.NotFound, is RecipientRepository.LookupResult.InvalidEntry -> {
|
||||
is RecipientRepository.LookupResult.Failure -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.RecipientNotSignalUser(phone)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is RecipientRepository.LookupResult.NetworkError -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.NetworkError
|
||||
userMessage = UserMessage.RecipientLookupFailed(failure = lookupResult)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -120,7 +104,7 @@ class NewCallViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun showUserAlreadyInACall() {
|
||||
internalUiState.update { it.copy(userMessage = Info.UserAlreadyInAnotherCall) }
|
||||
internalUiState.update { it.copy(userMessage = UserMessage.UserAlreadyInAnotherCall) }
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
@@ -153,11 +137,8 @@ data class NewCallUiState(
|
||||
val userMessage: UserMessage? = null
|
||||
) {
|
||||
sealed interface UserMessage {
|
||||
sealed interface Info : UserMessage {
|
||||
data class RecipientNotSignalUser(val phone: PhoneNumber) : Info
|
||||
data object UserAlreadyInAnotherCall : Info
|
||||
data object NetworkError : Info
|
||||
}
|
||||
data object UserAlreadyInAnotherCall : UserMessage
|
||||
data class RecipientLookupFailed(val failure: RecipientRepository.LookupResult.Failure) : UserMessage
|
||||
}
|
||||
|
||||
sealed interface CallType {
|
||||
|
||||
@@ -81,13 +81,14 @@ object ContactDiscovery {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
@Throws(IOException::class)
|
||||
@WorkerThread
|
||||
fun refresh(context: Context, recipients: List<Recipient>, notifyOfNewUsers: Boolean) {
|
||||
fun refresh(context: Context, recipients: List<Recipient>, notifyOfNewUsers: Boolean, timeoutMs: Long? = null) {
|
||||
refreshRecipients(
|
||||
context = context,
|
||||
descriptor = "refresh-multiple",
|
||||
refresh = { ContactDiscoveryRefreshV2.refresh(context, recipients) },
|
||||
refresh = { ContactDiscoveryRefreshV2.refresh(context, recipients, timeoutMs = timeoutMs) },
|
||||
removeSystemContactLinksIfMissing = false,
|
||||
notifyOfNewUsers = notifyOfNewUsers
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.conversation.NewConversationUiState.UserMessag
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientLookupFailureMessage
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPicker
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPickerCallbacks
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPickerScaffold
|
||||
@@ -351,17 +352,12 @@ private fun UserMessagesHost(
|
||||
onDismiss(userMessage)
|
||||
}
|
||||
|
||||
is UserMessage.Info.NetworkError -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NetworkFailure__network_error_check_your_connection_and_try_again),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
|
||||
is UserMessage.Info.RecipientNotSignalUser -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NewConversationActivity__s_is_not_a_signal_user, userMessage.phone.displayText),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
is UserMessage.Info.RecipientLookupFailed -> {
|
||||
RecipientLookupFailureMessage(
|
||||
failure = userMessage.failure,
|
||||
onDismissed = { onDismiss(userMessage) }
|
||||
)
|
||||
}
|
||||
|
||||
is UserMessage.Info.UserAlreadyInAnotherCall -> LaunchedEffect(userMessage) {
|
||||
snackbarHostState.showSnackbar(
|
||||
|
||||
@@ -65,13 +65,11 @@ class NewConversationViewModel : ViewModel() {
|
||||
viewModelScope.launch {
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = true) }
|
||||
|
||||
val lookupResult = withContext(Dispatchers.IO) {
|
||||
RecipientRepository.lookupNewE164(inputE164 = phone.value)
|
||||
}
|
||||
when (val lookupResult = RecipientRepository.lookup(phone)) {
|
||||
is RecipientRepository.PhoneLookupResult.Found -> {
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = false) }
|
||||
|
||||
when (lookupResult) {
|
||||
is RecipientRepository.LookupResult.Success -> {
|
||||
val recipient = Recipient.resolved(lookupResult.recipientId)
|
||||
val recipient = lookupResult.recipient
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = false) }
|
||||
|
||||
if (recipient.isRegistered && recipient.hasServiceId) {
|
||||
@@ -81,20 +79,11 @@ class NewConversationViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
is RecipientRepository.LookupResult.NotFound, is RecipientRepository.LookupResult.InvalidEntry -> {
|
||||
is RecipientRepository.LookupResult.Failure -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.RecipientNotSignalUser(phone)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is RecipientRepository.LookupResult.NetworkError -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.NetworkError
|
||||
userMessage = Info.RecipientLookupFailed(failure = lookupResult)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -180,9 +169,8 @@ data class NewConversationUiState(
|
||||
sealed interface Info : UserMessage {
|
||||
data class RecipientRemoved(val recipient: Recipient) : Info
|
||||
data class RecipientBlocked(val recipient: Recipient) : Info
|
||||
data class RecipientNotSignalUser(val phone: PhoneNumber) : Info
|
||||
data class RecipientLookupFailed(val failure: RecipientRepository.LookupResult.Failure) : Info
|
||||
data object UserAlreadyInAnotherCall : Info
|
||||
data object NetworkError : Info
|
||||
}
|
||||
|
||||
sealed interface Prompt : UserMessage {
|
||||
|
||||
@@ -123,9 +123,9 @@ public class AddMembersActivity extends PushContactSelectionActivity implements
|
||||
if (result instanceof RecipientRepository.LookupResult.Success) {
|
||||
enableDone();
|
||||
callback.accept(true);
|
||||
} else if (result instanceof RecipientRepository.LookupResult.NotFound || result instanceof RecipientRepository.LookupResult.InvalidEntry) {
|
||||
} else if (result instanceof RecipientRepository.PhoneLookupResult.NotFound || result instanceof RecipientRepository.PhoneLookupResult.InvalidPhone) {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setMessage(getString(R.string.NewConversationActivity__s_is_not_a_signal_user, number))
|
||||
.setMessage(getString(R.string.RecipientLookup_error__s_is_not_a_signal_user, number))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
callback.accept(false);
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupUiState.NavTarget
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupUiState.UserMessage
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.details.AddGroupDetailsActivity
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientLookupFailureMessage
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPicker
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPickerCallbacks
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientPickerScaffold
|
||||
@@ -275,31 +276,15 @@ private fun UserMessagesHost(
|
||||
userMessage: UserMessage?,
|
||||
onDismiss: (UserMessage) -> Unit
|
||||
) {
|
||||
val context: Context = LocalContext.current
|
||||
when (userMessage) {
|
||||
null -> {}
|
||||
|
||||
is UserMessage.Info.NetworkError -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NetworkFailure__network_error_check_your_connection_and_try_again),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
|
||||
is UserMessage.Info.RecipientNotSignalUser -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NewConversationActivity__s_is_not_a_signal_user, userMessage.phone.displayText),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
|
||||
is UserMessage.Info.RecipientsNotSignalUsers -> Dialogs.SimpleMessageDialog(
|
||||
message = pluralStringResource(
|
||||
id = R.plurals.CreateGroupActivity_not_signal_users,
|
||||
count = userMessage.recipients.size,
|
||||
userMessage.recipients.joinToString(", ") { it.getDisplayName(context) }
|
||||
),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismiss(userMessage) }
|
||||
)
|
||||
is UserMessage.RecipientLookupFailed -> {
|
||||
RecipientLookupFailureMessage(
|
||||
failure = userMessage.failure,
|
||||
onDismissed = { onDismiss(userMessage) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,30 +7,22 @@ package org.thoughtcrime.securesms.groups.ui.creategroup
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.signal.core.util.Stopwatch
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.contacts.SelectedContact
|
||||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupUiState.NavTarget
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupUiState.UserMessage.Info
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupUiState.UserMessage
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.PhoneNumber
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.recipients.RecipientRepository
|
||||
import org.thoughtcrime.securesms.recipients.ui.RecipientSelection
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import java.io.IOException
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class CreateGroupViewModel : ViewModel() {
|
||||
companion object {
|
||||
@@ -52,31 +44,17 @@ class CreateGroupViewModel : ViewModel() {
|
||||
private suspend fun recipientExists(phone: PhoneNumber): Boolean {
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = true) }
|
||||
|
||||
val lookupResult = withContext(Dispatchers.IO) {
|
||||
RecipientRepository.lookupNewE164(inputE164 = phone.value)
|
||||
}
|
||||
|
||||
return when (lookupResult) {
|
||||
is RecipientRepository.LookupResult.Success -> {
|
||||
return when (val lookupResult = RecipientRepository.lookup(phone)) {
|
||||
is RecipientRepository.PhoneLookupResult.Found -> {
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = false) }
|
||||
true
|
||||
}
|
||||
|
||||
is RecipientRepository.LookupResult.NotFound, is RecipientRepository.LookupResult.InvalidEntry -> {
|
||||
is RecipientRepository.LookupResult.Failure -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.RecipientNotSignalUser(phone)
|
||||
)
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
is RecipientRepository.LookupResult.NetworkError -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.NetworkError
|
||||
userMessage = UserMessage.RecipientLookupFailed(failure = lookupResult)
|
||||
)
|
||||
}
|
||||
false
|
||||
@@ -108,58 +86,31 @@ class CreateGroupViewModel : ViewModel() {
|
||||
|
||||
fun continueToGroupDetails() {
|
||||
viewModelScope.launch {
|
||||
val stopwatch = Stopwatch(title = "Recipient Refresh")
|
||||
internalUiState.update { it.copy(isLookingUpRecipient = true) }
|
||||
|
||||
val selectedRecipients = uiState.value.newSelections.asRecipients(stopwatch)
|
||||
stopwatch.split(label = "registered")
|
||||
stopwatch.stop(tag = TAG)
|
||||
|
||||
val notSignalUsers = selectedRecipients.filter { !it.isRegistered || !it.hasServiceId }
|
||||
if (notSignalUsers.isNotEmpty()) {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = Info.RecipientsNotSignalUsers(recipients = notSignalUsers)
|
||||
)
|
||||
val selectedRecipientIds = uiState.value.newSelections.map { it.orCreateRecipientId }
|
||||
when (val lookupResult = RecipientRepository.lookup(recipientIds = selectedRecipientIds)) {
|
||||
is RecipientRepository.IdLookupResult.FoundAll -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
pendingDestination = NavTarget.AddGroupDetails(recipientIds = selectedRecipientIds)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
pendingDestination = NavTarget.AddGroupDetails(recipientIds = selectedRecipients.map(Recipient::id))
|
||||
)
|
||||
|
||||
is RecipientRepository.LookupResult.Failure -> {
|
||||
internalUiState.update {
|
||||
it.copy(
|
||||
isLookingUpRecipient = false,
|
||||
userMessage = UserMessage.RecipientLookupFailed(failure = lookupResult)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<SelectedContact>.asRecipients(stopwatch: Stopwatch): List<Recipient> {
|
||||
val selectedRecipientIds: List<RecipientId> = this.map { it.orCreateRecipientId }
|
||||
|
||||
val recipientsNeedingRegistrationCheck = Recipient
|
||||
.resolvedList(selectedRecipientIds)
|
||||
.also { stopwatch.split(label = "resolve") }
|
||||
.filter { !it.isRegistered || !it.hasServiceId }
|
||||
.toSet()
|
||||
|
||||
Log.d(TAG, "Need to do ${recipientsNeedingRegistrationCheck.size} registration checks.")
|
||||
recipientsNeedingRegistrationCheck.forEach { recipient ->
|
||||
try {
|
||||
ContactDiscovery.refresh(
|
||||
context = AppDependencies.application,
|
||||
recipient = recipient,
|
||||
notifyOfNewUsers = false,
|
||||
timeoutMs = 10.seconds.inWholeMilliseconds
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Failed to refresh registered status for ${recipient.id}", e)
|
||||
}
|
||||
}
|
||||
|
||||
return Recipient.resolvedList(selectedRecipientIds)
|
||||
}
|
||||
|
||||
fun clearUserMessage() {
|
||||
internalUiState.update { it.copy(userMessage = null) }
|
||||
}
|
||||
@@ -181,11 +132,7 @@ data class CreateGroupUiState(
|
||||
val userMessage: UserMessage? = null
|
||||
) {
|
||||
sealed interface UserMessage {
|
||||
sealed interface Info : UserMessage {
|
||||
data class RecipientNotSignalUser(val phone: PhoneNumber) : Info
|
||||
data class RecipientsNotSignalUsers(val recipients: List<Recipient>) : Info
|
||||
data object NetworkError : Info
|
||||
}
|
||||
data class RecipientLookupFailed(val failure: RecipientRepository.LookupResult.Failure) : UserMessage
|
||||
}
|
||||
|
||||
sealed interface NavTarget {
|
||||
|
||||
@@ -6,12 +6,16 @@
|
||||
package org.thoughtcrime.securesms.recipients
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.phonenumbers.NumberUtil
|
||||
import org.thoughtcrime.securesms.util.SignalE164Util
|
||||
import java.io.IOException
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* We operate on recipients many places, but sometimes we find ourselves performing the same recipient-related operations in several locations.
|
||||
@@ -21,24 +25,68 @@ object RecipientRepository {
|
||||
|
||||
private val TAG = Log.tag(RecipientRepository::class.java)
|
||||
|
||||
/**
|
||||
* Validates whether the provided [PhoneNumber] is a registered Signal user. Checks locally first, then queries the CDSI directory if needed.
|
||||
*/
|
||||
suspend fun lookup(phone: PhoneNumber): PhoneLookupResult {
|
||||
return withContext(Dispatchers.IO) {
|
||||
lookupNewE164(inputE164 = phone.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether the provided [RecipientId]s are registered Signal users.
|
||||
*/
|
||||
suspend fun lookup(recipientIds: List<RecipientId>): IdLookupResult {
|
||||
val recipientsNeedingRegistrationCheck = Recipient
|
||||
.resolvedList(recipientIds)
|
||||
.filter { !it.isRegistered || !it.hasServiceId }
|
||||
.toSet()
|
||||
|
||||
Log.d(TAG, "Need to do ${recipientsNeedingRegistrationCheck.size} registration checks.")
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
ContactDiscovery.refresh(
|
||||
context = AppDependencies.application,
|
||||
recipients = recipientsNeedingRegistrationCheck.toList(),
|
||||
notifyOfNewUsers = false,
|
||||
timeoutMs = 30.seconds.inWholeMilliseconds
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Failed to refresh registered status for ${recipientsNeedingRegistrationCheck.size} recipients", e)
|
||||
}
|
||||
}
|
||||
|
||||
val allRecipients = Recipient.resolvedList(recipientIds)
|
||||
val (registeredRecipients, unregisteredRecipients) = allRecipients.partition { it.isRegistered && it.hasServiceId }
|
||||
return if (unregisteredRecipients.isNotEmpty()) {
|
||||
Log.w(TAG, "Found ${unregisteredRecipients.size} non-Signal users: ${unregisteredRecipients.joinToString(", ") { it.id.toString() }}")
|
||||
IdLookupResult.FoundSome(found = registeredRecipients, notFound = unregisteredRecipients)
|
||||
} else {
|
||||
IdLookupResult.FoundAll(registeredRecipients)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Deprecated("Use [RecipientRepository.lookup(PhoneNumber)] instead.")
|
||||
@WorkerThread
|
||||
@JvmStatic
|
||||
fun lookupNewE164(inputE164: String): LookupResult {
|
||||
fun lookupNewE164(inputE164: String): PhoneLookupResult {
|
||||
val e164 = SignalE164Util.formatAsE164(inputE164)
|
||||
|
||||
if (e164 == null || !NumberUtil.isVisuallyValidNumber(e164)) {
|
||||
return LookupResult.InvalidEntry
|
||||
return PhoneLookupResult.InvalidPhone(inputE164)
|
||||
}
|
||||
|
||||
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)
|
||||
return PhoneLookupResult.Found(recipient = Recipient.resolved(matchingFullRecipientId), phone = PhoneNumber(e164))
|
||||
}
|
||||
|
||||
Log.i(TAG, "Need to lookup up $e164 with CDSI.")
|
||||
@@ -46,19 +94,32 @@ object RecipientRepository {
|
||||
return try {
|
||||
val result = ContactDiscovery.lookupE164(e164)
|
||||
if (result == null) {
|
||||
LookupResult.NotFound()
|
||||
PhoneLookupResult.NotFound(PhoneNumber(e164))
|
||||
} else {
|
||||
LookupResult.Success(result.recipientId)
|
||||
PhoneLookupResult.Found(recipient = Recipient.resolved(result.recipientId), phone = PhoneNumber(e164))
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
} catch (_: 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
|
||||
sealed interface Success : LookupResult
|
||||
sealed interface Failure : LookupResult
|
||||
|
||||
data object NetworkError : PhoneLookupResult, IdLookupResult, Failure
|
||||
}
|
||||
|
||||
sealed interface PhoneLookupResult : LookupResult {
|
||||
sealed interface NoResult : PhoneLookupResult, LookupResult.Failure
|
||||
|
||||
data class Found(val recipient: Recipient, val phone: PhoneNumber) : PhoneLookupResult, LookupResult.Success
|
||||
data class NotFound(val phone: PhoneNumber) : NoResult
|
||||
data class InvalidPhone(val invalidValue: String) : NoResult
|
||||
}
|
||||
|
||||
sealed interface IdLookupResult : LookupResult {
|
||||
data class FoundAll(val found: List<Recipient>) : IdLookupResult, LookupResult.Success
|
||||
data class FoundSome(val found: List<Recipient>, val notFound: List<Recipient>) : IdLookupResult, LookupResult.Failure
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.recipients.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import org.signal.core.ui.compose.Dialogs
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.recipients.RecipientRepository
|
||||
import org.thoughtcrime.securesms.recipients.RecipientRepository.IdLookupResult
|
||||
import org.thoughtcrime.securesms.recipients.RecipientRepository.PhoneLookupResult
|
||||
|
||||
/**
|
||||
* Handles displaying a message to the user when a recipient lookup fails.
|
||||
*/
|
||||
@Composable
|
||||
fun RecipientLookupFailureMessage(
|
||||
failure: RecipientRepository.LookupResult.Failure,
|
||||
onDismissed: () -> Unit
|
||||
) {
|
||||
val context: Context = LocalContext.current
|
||||
|
||||
when (failure) {
|
||||
is PhoneLookupResult.NotFound,
|
||||
is PhoneLookupResult.InvalidPhone -> {
|
||||
val phoneDisplayText: String = when (failure) {
|
||||
is PhoneLookupResult.NotFound -> failure.phone.displayText
|
||||
is PhoneLookupResult.InvalidPhone -> failure.invalidValue
|
||||
}
|
||||
|
||||
Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.RecipientLookup_error__s_is_not_a_signal_user, phoneDisplayText),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismissed() }
|
||||
)
|
||||
}
|
||||
|
||||
is IdLookupResult.FoundSome -> Dialogs.SimpleMessageDialog(
|
||||
message = pluralStringResource(
|
||||
id = R.plurals.RecipientLookup_error__not_signal_users,
|
||||
count = failure.notFound.size,
|
||||
failure.notFound.joinToString(", ") { it.getDisplayName(context) }
|
||||
),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismissed() }
|
||||
)
|
||||
|
||||
is RecipientRepository.LookupResult.NetworkError -> Dialogs.SimpleMessageDialog(
|
||||
message = stringResource(R.string.NetworkFailure__network_error_check_your_connection_and_try_again),
|
||||
dismiss = stringResource(android.R.string.ok),
|
||||
onDismiss = { onDismissed() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository
|
||||
import org.thoughtcrime.securesms.recipients.PhoneNumber
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientRepository
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
@@ -71,19 +72,18 @@ class FindByViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun performPhoneLookup(): FindByResult {
|
||||
private suspend fun performPhoneLookup(): FindByResult {
|
||||
val stateSnapshot = state.value
|
||||
val countryCode = stateSnapshot.selectedCountry.countryCode
|
||||
val nationalNumber = stateSnapshot.userEntry.removePrefix(countryCode.toString())
|
||||
|
||||
val e164 = "+$countryCode$nationalNumber"
|
||||
|
||||
return when (val result = RecipientRepository.lookupNewE164(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)
|
||||
return when (val result = RecipientRepository.lookup(PhoneNumber(e164))) {
|
||||
is RecipientRepository.PhoneLookupResult.InvalidPhone -> FindByResult.InvalidEntry
|
||||
is RecipientRepository.PhoneLookupResult.NotFound -> FindByResult.NotFound()
|
||||
is RecipientRepository.PhoneLookupResult.Found -> FindByResult.Success(result.recipient.id)
|
||||
is RecipientRepository.LookupResult.NetworkError -> FindByResult.NetworkError
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -452,7 +452,7 @@ public class CommunicationActions {
|
||||
startConversation(activity, recipient, null);
|
||||
} else {
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(activity.getString(R.string.NewConversationActivity__s_is_not_a_signal_user, e164))
|
||||
.setMessage(activity.getString(R.string.RecipientLookup_error__s_is_not_a_signal_user, e164))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user