I have spent quite some time debugging an issue where a phone number that was entered, formatted and validated on an Android device was rejected by the backend server because it could not be used to send an SMS.
It turns out formatting and validating phone numbers can be a bit tricky, depending on the phone number and the specific locale of the device.
What was happening?
The user enters his phone number into an EditText that has TextWatcher attached to it.
The TextWatcher is a standard Android PhoneNumberFormattingTextWatcher
and automatically formats the phone number while typing.
When the user is done, the number is reformatted in the international E164 format using PhoneNumberUtils.formatNumberToE164
before it is sent to the backend.
Note that both the PhoneNumberFormattingTextWatcher()
and PhoneNumberUtils.formatNumberToE164()
require a country code to figure out if a phone number is valid and how it should be formatted.
When a Belgian user types the phone number 0475873235, he will see 0475 87 32 35, and the backend will receive the full E164 number +32475873235. This is a valid mobile number in Belgium and can be used to send an SMS.
This example works fine when the locale of the user’s device is set to the Belgian locale nl_BE
.
Unfortunately things start to go wrong when the user has set his phone to nl_NL
instead of nl_BE
.
Both locales are largely the same (e.g. the UI is shown in Dutch, date and amounts are formatted the same way,…) but they do phone number formatting differently.
On an nl_NL
device, when the user types the phone number 0475873235, he will still see 0475 87 32 35 because that phone number is also valid in the Netherlands.
But this time the backend will receive the full E164 number +31475873235, with the international prefix for the Netherlands: 31.
Even though this is a valid number in the Netherlands, it is not a valid mobile number and can not be used to send an SMS. The server encounters a problem and returns an error.
Locale | User types | User sees | Backend gets | Backend sends SMS |
---|---|---|---|---|
en_US | 0475873235 | Invalid number | N/A | N/A |
nl_BE | 0475873235 | 0475 87 32 35 | +32475873235 | Success |
nl_NL | 0475873235 | 0475 87 32 35 | +31475873235 | Failure |
The solution
Since quite a few people in Belgium have set up their phone as nl_NL
instead of nl_BE
, it’s best to avoid Locale.getDefault().getCountry()
.
Instead I decided to use the country code provided by TelephonyManager.getNetworkCountryIso()
. This returns the country code of the current registered operator of the cell tower nearby.
Note that this code has a fallback to Locale.getDefault().getCountry()
when the operator’s country code is not available.