mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-30 01:43:46 +01:00
Remove unnecessary leading digits in phone number.
This commit is contained in:
committed by
Michelle Tang
parent
dfe6ae812d
commit
7a2c084b49
+32
-3
@@ -117,7 +117,8 @@ class PhoneNumberEntryViewModel(
|
||||
stateEmitter(applyPhoneNumberChanged(state, event.oldValue, event.newValue))
|
||||
}
|
||||
is PhoneNumberEntryScreenEvents.NextClicked -> {
|
||||
stateEmitter(state.copy(dialogs = state.dialogs.copy(confirmNumber = true)))
|
||||
val normalized = state.withNormalizedNationalNumber()
|
||||
stateEmitter(normalized.copy(dialogs = normalized.dialogs.copy(confirmNumber = true)))
|
||||
}
|
||||
is PhoneNumberEntryScreenEvents.PhoneNumberCancelled -> {
|
||||
stateEmitter(state.copy(dialogs = state.dialogs.copy(confirmNumber = false)))
|
||||
@@ -275,8 +276,8 @@ class PhoneNumberEntryViewModel(
|
||||
inputState: PhoneNumberEntryState,
|
||||
parentEventEmitter: (RegistrationFlowEvent) -> Unit
|
||||
): PhoneNumberEntryState {
|
||||
val e164 = "+${inputState.countryCode}${inputState.nationalNumber}"
|
||||
var state = inputState.copy()
|
||||
var state = inputState.withNormalizedNationalNumber()
|
||||
val e164 = "+${state.countryCode}${state.nationalNumber}"
|
||||
|
||||
// If the user selected a restore option before entering their phone number, navigate to the restore flow
|
||||
if (state.pendingRestoreOption != null) {
|
||||
@@ -891,6 +892,34 @@ class PhoneNumberEntryViewModel(
|
||||
return (new.length - prefix - suffix).coerceAtLeast(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips a redundant national trunk prefix from the national number (e.g. the leading 0 a Dutch user habitually
|
||||
* types in "0612345678"), which would otherwise produce a wrong E164 when naively concatenated with the country
|
||||
* code (+310612345678 instead of +31612345678). Leading zeros that are a significant part of the number (e.g.
|
||||
* Italian landlines) are preserved.
|
||||
*/
|
||||
private fun PhoneNumberEntryState.withNormalizedNationalNumber(): PhoneNumberEntryState {
|
||||
if (countryCode.isEmpty() || nationalNumber.isEmpty()) {
|
||||
return this
|
||||
}
|
||||
|
||||
val parsedNumber = try {
|
||||
phoneNumberUtil.parse("+$countryCode$nationalNumber", null)
|
||||
} catch (_: NumberParseException) {
|
||||
return this
|
||||
}
|
||||
|
||||
val significantNumber = phoneNumberUtil.getNationalSignificantNumber(parsedNumber)
|
||||
if (significantNumber == nationalNumber || parsedNumber.countryCode.toString() != countryCode) {
|
||||
return this
|
||||
}
|
||||
|
||||
return copy(
|
||||
nationalNumber = significantNumber,
|
||||
formattedNumber = formatNumber(significantNumber)
|
||||
).withNumberValidity()
|
||||
}
|
||||
|
||||
/**
|
||||
* Recomputes [PhoneNumberEntryState.isNumberPossible] and [PhoneNumberEntryState.isNumberInvalid] from the current
|
||||
* country code and national number. Should be applied to any state that changes either of those fields.
|
||||
|
||||
+67
@@ -485,6 +485,73 @@ class PhoneNumberEntryViewModelTest {
|
||||
assertThat(state.nationalNumber).isEqualTo("15123456789")
|
||||
}
|
||||
|
||||
// ==================== Trunk Prefix Normalization Tests ====================
|
||||
|
||||
@Test
|
||||
fun `NextClicked strips a redundant leading trunk prefix before showing the confirmation dialog`() = runTest {
|
||||
// Dutch users habitually type their number with the leading national '0' (e.g. 0612345678), which must not end
|
||||
// up in the E164 (+31612345678, not +310612345678).
|
||||
val initialState = PhoneNumberEntryState(regionCode = "NL", countryCode = "31", nationalNumber = "0612345678", formattedNumber = "06 12345678")
|
||||
|
||||
viewModel.applyEvent(initialState, PhoneNumberEntryScreenEvents.NextClicked, parentEventEmitter, stateEmitter)
|
||||
|
||||
assertThat(emittedStates).hasSize(1)
|
||||
val result = emittedStates.last()
|
||||
assertThat(result.nationalNumber).isEqualTo("612345678")
|
||||
assertThat(result.countryCode).isEqualTo("31")
|
||||
assertThat(result.isNumberPossible).isTrue()
|
||||
assertThat(result.dialogs.confirmNumber).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NextClicked leaves a number without a trunk prefix unchanged`() = runTest {
|
||||
val initialState = PhoneNumberEntryState(regionCode = "US", countryCode = "1", nationalNumber = "5551234567", formattedNumber = "(555) 123-4567")
|
||||
|
||||
viewModel.applyEvent(initialState, PhoneNumberEntryScreenEvents.NextClicked, parentEventEmitter, stateEmitter)
|
||||
|
||||
assertThat(emittedStates).hasSize(1)
|
||||
val result = emittedStates.last()
|
||||
assertThat(result.nationalNumber).isEqualTo("5551234567")
|
||||
assertThat(result.formattedNumber).isEqualTo("(555) 123-4567")
|
||||
assertThat(result.dialogs.confirmNumber).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NextClicked preserves a leading zero that is a significant part of the number`() = runTest {
|
||||
// Italian landlines include the leading zero as part of the number itself, so it must not be stripped.
|
||||
val initialState = PhoneNumberEntryState(regionCode = "IT", countryCode = "39", nationalNumber = "0612345678", formattedNumber = "06 1234 5678")
|
||||
|
||||
viewModel.applyEvent(initialState, PhoneNumberEntryScreenEvents.NextClicked, parentEventEmitter, stateEmitter)
|
||||
|
||||
assertThat(emittedStates).hasSize(1)
|
||||
val result = emittedStates.last()
|
||||
assertThat(result.nationalNumber).isEqualTo("0612345678")
|
||||
assertThat(result.dialogs.confirmNumber).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PhoneNumberConfirmed submits the E164 without a redundant leading trunk prefix`() = runTest {
|
||||
val sessionMetadata = createSessionMetadata(requestedInformation = emptyList())
|
||||
|
||||
coEvery { mockRepository.createSession(any()) } returns
|
||||
RequestResult.Success(sessionMetadata)
|
||||
coEvery { mockRepository.requestVerificationCode(any(), any(), any()) } returns
|
||||
RequestResult.Success(sessionMetadata)
|
||||
|
||||
val initialState = PhoneNumberEntryState(
|
||||
regionCode = "NL",
|
||||
countryCode = "31",
|
||||
nationalNumber = "0612345678"
|
||||
)
|
||||
|
||||
viewModel.applyEvent(initialState, PhoneNumberEntryScreenEvents.PhoneNumberConfirmed, parentEventEmitter, stateEmitter)
|
||||
|
||||
coVerify(exactly = 1) { mockRepository.createSession("+31612345678") }
|
||||
assertThat(emittedStates.last().nationalNumber).isEqualTo("612345678")
|
||||
assertThat(emittedEvents.filterIsInstance<RegistrationFlowEvent.E164Chosen>())
|
||||
.isEqualTo(listOf(RegistrationFlowEvent.E164Chosen("+31612345678")))
|
||||
}
|
||||
|
||||
// ==================== FullPhoneNumberEntered Tests ====================
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user