Improve and centralize e164 utils.

This commit is contained in:
Greyson Parrelli
2025-03-03 10:42:21 -05:00
parent 0fdcc1c027
commit 9c473fb570
99 changed files with 748 additions and 1826 deletions

View File

@@ -40,9 +40,9 @@ import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.phonenumbers.NumberUtil
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.util.SignalE164Util
import org.thoughtcrime.securesms.util.UsernameUtil.isValidUsernameForSearch
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
@@ -556,7 +556,7 @@ class Recipient(
}
if (name.isBlank() && e164Value.isNotNullOrBlank()) {
name = PhoneNumberFormatter.prettyPrint(e164Value)
name = SignalE164Util.prettyPrint(e164Value)
}
if (name.isBlank() && emailValue != null) {
@@ -587,7 +587,7 @@ class Recipient(
}
if (name.isBlank() && e164Value.isNotNullOrBlank()) {
name = PhoneNumberFormatter.prettyPrint(e164Value)
name = SignalE164Util.prettyPrint(e164Value)
}
if (name.isBlank()) {
@@ -901,6 +901,37 @@ class Recipient(
return externalPush(serviceId, null)
}
/**
* Returns a fully-populated [Recipient] based off of a ServiceId and phone number, creating one
* in the database if necessary. We want both piece of information so we're able to associate them
* both together, depending on which are available.
*
* In particular, while we may eventually get the ACI of a user created via a phone number
* (through a directory sync), the only way we can store the phone number is by retrieving it from
* sent messages and whatnot. So we should store it when available.
*/
@JvmStatic
@WorkerThread
private fun externalPush(serviceId: ServiceId, e164: String?): Recipient {
if (ACI.UNKNOWN == serviceId || PNI.UNKNOWN == serviceId) {
throw AssertionError()
}
val recipientId = RecipientId.from(SignalServiceAddress(serviceId, e164))
val resolved = resolved(recipientId)
if (resolved.id != recipientId) {
Log.w(TAG, "Resolved $recipientId, but got back a recipient with ${resolved.id}")
}
if (!resolved.isRegistered) {
Log.w(TAG, "External push was locally marked unregistered. Marking as registered.")
SignalDatabase.recipients.markRegistered(recipientId, serviceId)
}
return resolved
}
/**
* Create a recipient with a full (ACI, PNI, E164) tuple. It is assumed that the association between the PNI and serviceId is trusted.
* That means it must be from either storage service (with the verified field set) or a PNI verification message.
@@ -927,39 +958,6 @@ class Recipient(
return resolved
}
/**
* Returns a fully-populated [Recipient] based off of a ServiceId and phone number, creating one
* in the database if necessary. We want both piece of information so we're able to associate them
* both together, depending on which are available.
*
* In particular, while we may eventually get the ACI of a user created via a phone number
* (through a directory sync), the only way we can store the phone number is by retrieving it from
* sent messages and whatnot. So we should store it when available.
*/
@JvmStatic
@WorkerThread
fun externalPush(serviceId: ServiceId?, e164: String?): Recipient {
if (ACI.UNKNOWN == serviceId || PNI.UNKNOWN == serviceId) {
throw AssertionError()
}
val recipientId = RecipientId.from(SignalServiceAddress(serviceId, e164))
val resolved = resolved(recipientId)
if (resolved.id != recipientId) {
Log.w(TAG, "Resolved $recipientId, but got back a recipient with ${resolved.id}")
}
if (!resolved.isRegistered && serviceId != null) {
Log.w(TAG, "External push was locally marked unregistered. Marking as registered.")
SignalDatabase.recipients.markRegistered(recipientId, serviceId)
} else if (!resolved.isRegistered) {
Log.w(TAG, "External push was locally marked unregistered, but we don't have an ACI, so we can't do anything.", Throwable())
}
return resolved
}
/**
* A safety wrapper around [.external] for when you know you're using an
* identifier for a system contact, and therefore always want to prevent interpreting it as a
@@ -969,13 +967,14 @@ class Recipient(
*/
@JvmStatic
@WorkerThread
fun externalContact(identifier: String): Recipient {
fun externalContact(identifier: String): Recipient? {
val id: RecipientId = if (UuidUtil.isUuid(identifier)) {
throw AssertionError("UUIDs are not valid system contact identifiers!")
} else if (NumberUtil.isValidEmail(identifier)) {
SignalDatabase.recipients.getOrInsertFromEmail(identifier)
} else {
SignalDatabase.recipients.getOrInsertFromE164(identifier)
val e164 = SignalE164Util.formatAsE164(identifier) ?: return null
SignalDatabase.recipients.getOrInsertFromE164(e164)
}
return resolved(id)
@@ -1027,10 +1026,13 @@ class Recipient(
* If the identifier is a UUID of a Signal user, prefer using
* [.externalPush] or its overload, as this will let us associate
* the phone number with the recipient.
*
* Important: If the identifier cannot be considered a valid UUID, groupId, email, or phone number,
* this will return null.
*/
@JvmStatic
@WorkerThread
fun external(context: Context, identifier: String): Recipient {
fun external(identifier: String): Recipient? {
val serviceId = ServiceId.parseOrNull(identifier, logFailures = false)
val id: RecipientId = if (serviceId != null) {
@@ -1042,7 +1044,7 @@ class Recipient(
} else if (isValidUsernameForSearch(identifier)) {
throw IllegalArgumentException("Creating a recipient based on username alone is not supported!")
} else {
val e164 = PhoneNumberFormatter.get(context).format(identifier)
val e164: String = SignalE164Util.formatAsE164(identifier) ?: return null
SignalDatabase.recipients.getOrInsertFromE164(e164)
}

View File

@@ -5,13 +5,12 @@
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 org.thoughtcrime.securesms.util.SignalE164Util
import java.io.IOException
/**
@@ -29,10 +28,10 @@ object RecipientRepository {
*/
@WorkerThread
@JvmStatic
fun lookupNewE164(context: Context, inputE164: String): LookupResult {
val e164 = PhoneNumberFormatter.get(context).format(inputE164)
fun lookupNewE164(inputE164: String): LookupResult {
val e164 = SignalE164Util.formatAsE164(inputE164)
if (!NumberUtil.isVisuallyValidNumber(e164)) {
if (e164 == null || !NumberUtil.isVisuallyValidNumber(e164)) {
return LookupResult.InvalidEntry
}

View File

@@ -51,10 +51,10 @@ import org.thoughtcrime.securesms.components.emoji.EmojiTextView
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
import org.thoughtcrime.securesms.conversation.v2.UnverifiedProfileNameBottomSheet
import org.thoughtcrime.securesms.nicknames.ViewNoteSheet
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.stories.settings.my.SignalConnectionsBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.SignalE164Util
import org.thoughtcrime.securesms.util.viewModel
/**
@@ -103,7 +103,7 @@ class AboutSheet : ComposeBottomSheetDialogFragment() {
hasAvatar = recipient.get().profileAvatarFileDetails.hasFile(),
recipientForAvatar = recipient.get(),
formattedE164 = if (recipient.get().hasE164 && recipient.get().shouldShowE164) {
PhoneNumberFormatter.get(requireContext()).prettyPrintFormat(recipient.get().requireE164())
SignalE164Util.prettyPrint(recipient.get().requireE164())
} else {
null
},

View File

@@ -68,6 +68,7 @@ import org.signal.core.ui.Previews
import org.signal.core.ui.Scaffolds
import org.signal.core.ui.TextFields
import org.signal.core.ui.theme.SignalTheme
import org.signal.core.util.E164Util
import org.signal.core.util.getParcelableExtraCompat
import org.thoughtcrime.securesms.PassphraseRequiredActivity
import org.thoughtcrime.securesms.R
@@ -80,7 +81,6 @@ import org.thoughtcrime.securesms.registration.ui.countrycode.CountryCodeSelectS
import org.thoughtcrime.securesms.registration.ui.countrycode.CountryCodeState
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
import org.thoughtcrime.securesms.util.viewModel
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter
import org.signal.core.ui.R as CoreUiR
/**
@@ -193,9 +193,9 @@ class FindByActivity : PassphraseRequiredActivity() {
} else {
val formattedNumber = remember(state.userEntry) {
val cleansed = state.userEntry.removePrefix(state.selectedCountry.countryCode.toString())
PhoneNumberFormatter.formatE164(state.selectedCountry.countryCode.toString(), cleansed)
E164Util.formatAsE164WithCountryCodeForDisplay(state.selectedCountry.countryCode.toString(), cleansed)
}
stringResource(id = R.string.FindByActivity__s_is_not_a_valid_phone_number, formattedNumber)
stringResource(id = R.string.FindByActivity__s_is_not_a_valid_phone_number, state.userEntry)
}
Dialogs.SimpleAlertDialog(
@@ -232,7 +232,7 @@ class FindByActivity : PassphraseRequiredActivity() {
} else {
val formattedNumber = remember(state.userEntry) {
val cleansed = state.userEntry.removePrefix(state.selectedCountry.countryCode.toString())
PhoneNumberFormatter.formatE164(state.selectedCountry.countryCode.toString(), cleansed)
E164Util.formatAsE164WithCountryCodeForDisplay(state.selectedCountry.countryCode.toString(), cleansed)
}
stringResource(id = R.string.FindByActivity__s_is_not_a_signal_user_would, formattedNumber)
}

View File

@@ -81,7 +81,7 @@ class FindByViewModel(
val e164 = "+$countryCode$nationalNumber"
return when (val result = RecipientRepository.lookupNewE164(context, e164)) {
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)