Class PhoneNumberField

All Implemented Interfaces:
Animation, Editable, StyleListener, Iterable<Component>

public class PhoneNumberField extends Container

A phone number entry field: a country selector holding the calling code and a field for the rest of the number, producing one E.164 string.

Example
PhoneNumberField phone = new PhoneNumberField();
form.add(phone);
sendButton.addActionListener(e -> requestCode(phone.getE164()));

The selector starts on the country the device is in and opens a searchable list of every calling code. The number field carries TextArea#PHONENUMBER, so it gets the phone keypad and the platform offers the device's own number where it knows it.

The value

#getE164() returns the number in the one format a server can act on -- a leading "+", the calling code, then the national number, digits only:

Everything that is not a digit is dropped, so the separators a user reaches for make no difference:

phone.setCountry(PhoneNumberField.findCountry("IL"));
// user types 50-123-4567, or 50 123 4567, or (50) 1234567
phone.getE164(); // "+972501234567"

A number that already carries its own calling code is used as it stands, and the selector is not applied to it. Pasting is one way that happens; platform autofill is the other, since it offers the device's own number in exactly that form:

// Israel selected, the user pastes +1 415 555 0100
phone.getE164(); // "+14155550100", not the selection with that appended

A national trunk prefix is a digit, and it is kept:

// the same user typing the number the way they say it out loud
// user types 050-123-4567
phone.getE164(); // "+9720501234567" -- the leading 0 is still there

That is not an oversight, and it is the one thing to handle before sending. "0" is a trunk prefix in Israel and part of the number in Italy, and telling them apart is a per-country rule this field does not carry, so stripping one here would corrupt numbers in the countries where it belongs. Normalizing is left to the service that sends the message, which has the rules and can refuse what it cannot make sense of.

What this field does carry is the shape of E.164 -- at most fifteen digits, and the calling code separated from the rest -- so #isValid() is a sanity check rather than a verdict.

Country names

Names are English, and each is looked up first as "Country." plus the ISO code in the theme's resource bundle, so an application that ships translations gets them without replacing the list. An application with its own list entirely passes it to #setCountries(Country[]).

Styling

The field uses the UIID "PhoneNumberField", the country selector "PhoneNumberCountry" and the number field "PhoneNumberText".

  • Constructor Details

    • PhoneNumberField

      public PhoneNumberField()
      Builds a field defaulting to the country the device reports, falling back to the first entry when the device reports one that is not in the list.
  • Method Details

    • getCountries

      public PhoneNumberField.Country[] getCountries()

      The list this field offers, defaulting to every known country.

      Returns

      the countries offered by the selector

    • setCountries

      public void setCountries(PhoneNumberField.Country[] countries)

      Narrows or replaces the list this field offers. An application serving three countries has no reason to show two hundred.

      Parameters
      • countries: the countries to offer, or null to restore the full list
    • getCountry

      public PhoneNumberField.Country getCountry()
      The selected country, never null.
    • setCountry

      public void setCountry(PhoneNumberField.Country c)

      Selects a country, which changes the calling code the value is built from without touching the number that was typed.

      The country has to be one this field offers. Selecting one that is not leaves the selector showing a country the list it opens does not contain, and the field submitting a calling code the user was never given the chance to choose -- a mistake worth hearing about where it is made rather than in a support ticket about numbers from the wrong country.

      The object itself is kept rather than replaced by the equal one from the list. Countries are equal by ISO code, so an application that supplies its own entry for a country -- a different name, or a calling code it has reason to override -- keeps what it passed.

      Parameters
      • c: the country; ignored when null
      Throws
      • IllegalArgumentException: when the country is not one this field offers; narrow or widen the list with #setCountries(Country[]) first
    • getNationalNumber

      public String getNationalNumber()
      The national part as typed, digits only.
    • getE164

      public String getE164()

      The number in E.164 form -- "+", the calling code, then the national number -- or null when nothing has been typed.

      Returns

      the E.164 number, or null when the national part is empty

    • setE164

      public void setE164(String e164)

      Sets the field from an E.164 number, selecting the country whose calling code the number starts with and putting the rest in the number field.

      Several countries share a calling code (+1 covers the United States, Canada and much of the Caribbean, which the North American area code tells apart and this field does not), and the number alone does not say which. The currently selected country is kept when its code matches, and otherwise the first country listed for that code is selected.

      Parameters
      • e164: the number, with or without the leading "+"; null clears the field
    • isValid

      public boolean isValid()

      A sanity check on the shape of the number: a national part that is present and short enough to leave the whole number inside E.164's fifteen digit limit. It is not a check that the number exists, which only the service that sends the message can answer.

      Returns

      true when the number could be an E.164 number

    • getNumberField

      public TextField getNumberField()
      The field holding the national part, exposed for theming and for listening to what is typed.
    • getCountryButton

      public Button getCountryButton()
      The button that opens the country list, exposed for theming.
    • addDataChangedListener

      public void addDataChangedListener(DataChangedListener l)

      Adds a listener notified as the number is typed.

      Parameters
      • l: the listener
    • removeDataChangedListener

      public void removeDataChangedListener(DataChangedListener l)

      Removes a previously-registered listener.

      Parameters
      • l: the listener
    • getAllCountries

      public static PhoneNumberField.Country[] getAllCountries()

      Every country with a numbering plan of its own, ordered by English name.

      A handful of ISO 3166 regions are deliberately absent -- see the note on the table itself -- because they have no calling code assigned to them. An application that needs one supplies its own list.

    • findCountry

      public static PhoneNumberField.Country findCountry(String isoCode)

      Looks a country up by its two letter ISO 3166 code.

      Parameters
      • isoCode: the code, case insensitive
      Returns

      the country, or null when the code is not one this list carries