Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,5 @@ class FullAccessContactReader @Inject constructor(
return Result.success(result)
}

private fun normalizeToE164(rawNumber: String): String? {
val cleaned = rawNumber.filter { it.isDigit() || it == '+' }
if (cleaned.isBlank()) return null

val withPlus = if (cleaned.startsWith("+")) cleaned else "+$cleaned"
if (!phoneUtils.isPhoneNumberValid(withPlus)) return null

val countryCode = phoneUtils.getCountryCode(withPlus.removePrefix("+"))
val locale = phoneUtils.countryLocales.find { it.countryCode == countryCode }
?: phoneUtils.defaultCountryLocale

val result = phoneUtils.cleanNumber(withPlus.removePrefix("+${locale.phoneCode}"), locale)
return result.ifBlank { null }
}
private fun normalizeToE164(rawNumber: String): String? = phoneUtils.toE164(rawNumber)
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,5 @@ class PickerContactReader @Inject constructor(
return Result.success(result)
}

private fun normalizeToE164(rawNumber: String): String? {
val cleaned = rawNumber.filter { it.isDigit() || it == '+' }
if (cleaned.isBlank()) return null

val withPlus = if (cleaned.startsWith("+")) cleaned else "+$cleaned"
if (!phoneUtils.isPhoneNumberValid(withPlus)) return null

val countryCode = phoneUtils.getCountryCode(withPlus.removePrefix("+"))
val locale = phoneUtils.countryLocales.find { it.countryCode == countryCode }
?: phoneUtils.defaultCountryLocale

val result = phoneUtils.cleanNumber(withPlus.removePrefix("+${locale.phoneCode}"), locale)
return result.ifBlank { null }
}
private fun normalizeToE164(rawNumber: String): String? = phoneUtils.toE164(rawNumber)
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,21 @@ class PhoneUtils @Inject constructor(
}
}

fun toE164(rawNumber: String): String? {
val cleaned = rawNumber.filter { it.isDigit() || it == '+' }
if (cleaned.isBlank()) return null

return try {
val parsed = phoneNumberUtil.parse(cleaned, defaultCountryLocale.countryCode)
if (!phoneNumberUtil.isValidNumber(parsed)) return null
val type = phoneNumberUtil.getNumberType(parsed)
if (type == PhoneNumberUtil.PhoneNumberType.UNKNOWN) return null
phoneNumberUtil.format(parsed, PhoneNumberUtil.PhoneNumberFormat.E164)
} catch (_: NumberParseException) {
null
}
}

fun toFlagEmoji(country: String): String {
// 1. It first checks if the string consists of only 2 characters: ISO 3166-1 alpha-2 two-letter country codes (https://en.wikipedia.org/wiki/Regional_Indicator_Symbol).
if (country.length != 2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(RobolectricTestRunner::class)
Expand Down Expand Up @@ -138,6 +139,78 @@ class PhoneUtilsTest {

// endregion

// region toE164

@Test
fun `toE164 normalizes international number with plus`() {
val mockNumber = mockk<io.michaelrocks.libphonenumber.android.Phonenumber.PhoneNumber>(relaxed = true)
every { mockPhoneNumberUtil.parse("+12025551234", "US") } returns mockNumber
every { mockPhoneNumberUtil.isValidNumber(mockNumber) } returns true
every { mockPhoneNumberUtil.getNumberType(mockNumber) } returns PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE
every { mockPhoneNumberUtil.format(mockNumber, PhoneNumberUtil.PhoneNumberFormat.E164) } returns "+12025551234"

assertEquals("+12025551234", phoneUtils.toE164("+12025551234"))
}

@Test
fun `toE164 normalizes national number without country code`() {
val mockNumber = mockk<io.michaelrocks.libphonenumber.android.Phonenumber.PhoneNumber>(relaxed = true)
every { mockPhoneNumberUtil.parse("2025551234", "US") } returns mockNumber
every { mockPhoneNumberUtil.isValidNumber(mockNumber) } returns true
every { mockPhoneNumberUtil.getNumberType(mockNumber) } returns PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE
every { mockPhoneNumberUtil.format(mockNumber, PhoneNumberUtil.PhoneNumberFormat.E164) } returns "+12025551234"

assertEquals("+12025551234", phoneUtils.toE164("2025551234"))
}

@Test
fun `toE164 strips formatting characters`() {
val mockNumber = mockk<io.michaelrocks.libphonenumber.android.Phonenumber.PhoneNumber>(relaxed = true)
every { mockPhoneNumberUtil.parse("+12025551234", "US") } returns mockNumber
every { mockPhoneNumberUtil.isValidNumber(mockNumber) } returns true
every { mockPhoneNumberUtil.getNumberType(mockNumber) } returns PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE
every { mockPhoneNumberUtil.format(mockNumber, PhoneNumberUtil.PhoneNumberFormat.E164) } returns "+12025551234"

assertEquals("+12025551234", phoneUtils.toE164("+1 (202) 555-1234"))
}

@Test
fun `toE164 returns null for blank input`() {
assertNull(phoneUtils.toE164(""))
assertNull(phoneUtils.toE164(" "))
}

@Test
fun `toE164 returns null for invalid number`() {
val mockNumber = mockk<io.michaelrocks.libphonenumber.android.Phonenumber.PhoneNumber>(relaxed = true)
every { mockPhoneNumberUtil.parse("123", "US") } returns mockNumber
every { mockPhoneNumberUtil.isValidNumber(mockNumber) } returns false

assertNull(phoneUtils.toE164("123"))
}

@Test
fun `toE164 returns null for unparseable number`() {
every { mockPhoneNumberUtil.parse("abc", "US") } throws io.michaelrocks.libphonenumber.android.NumberParseException(
io.michaelrocks.libphonenumber.android.NumberParseException.ErrorType.NOT_A_NUMBER,
"not a number"
)

assertNull(phoneUtils.toE164("abc"))
}

@Test
fun `toE164 returns null for UNKNOWN number type`() {
val mockNumber = mockk<io.michaelrocks.libphonenumber.android.Phonenumber.PhoneNumber>(relaxed = true)
every { mockPhoneNumberUtil.parse("5551234567", "US") } returns mockNumber
every { mockPhoneNumberUtil.isValidNumber(mockNumber) } returns true
every { mockPhoneNumberUtil.getNumberType(mockNumber) } returns PhoneNumberUtil.PhoneNumberType.UNKNOWN

assertNull(phoneUtils.toE164("5551234567"))
}

// endregion

// region formatNumber

@Test
Expand Down
Loading