diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java b/auth/src/main/java/com/firebase/ui/auth/AuthUI.java index f99d0c721..6e283d69e 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java +++ b/auth/src/main/java/com/firebase/ui/auth/AuthUI.java @@ -77,6 +77,7 @@ import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; +import java.util.Locale; import java.util.Set; /** @@ -214,9 +215,9 @@ public static int getDefaultTheme() { * Signs the user in without any UI if possible. If this operation fails, you can safely start a * UI-based sign-in flow knowing it is required. * - * @param context requesting the user be signed in - * @param configs to use for silent sign in. Only Google and email are currently - * supported, the rest will be ignored. + * @param context requesting the user be signed in + * @param configs to use for silent sign in. Only Google and email are currently supported, the + * rest will be ignored. * @return a task which indicates whether or not the user was successfully signed in. */ @NonNull @@ -643,10 +644,140 @@ public PhoneBuilder setDefaultCountryIso(@NonNull String iso) { throw new IllegalStateException("Invalid country iso: " + iso); } - getParams().putString(ExtraConstants.COUNTRY_ISO, iso); + getParams().putString(ExtraConstants.COUNTRY_ISO, + iso.toUpperCase(Locale.getDefault())); + + return this; + } + + + /** + * Sets the country codes available in the country code selector for phone + * authentication. Takes as input a List of both country isos and codes. + * This is not to be called with + * {@link #setBlacklistedCountries(List)}. + * If both are called, an exception will be thrown. + *

+ * Inputting an e-164 country code (e.g. '+1') will include all countries with + * +1 as its code. + * Example input: {'+52', 'us'} + * For a list of country iso or codes, see Alpha-2 isos here: + * https://en.wikipedia.org/wiki/ISO_3166-1 + * and e-164 codes here: https://en.wikipedia.org/wiki/List_of_country_calling_codes + * + * @param whitelistedCountries a case insensitive list of country codes and/or + * isos to be whitelisted + */ + public PhoneBuilder setWhitelistedCountries( + @NonNull List whitelistedCountries) { + if (getParams().containsKey(ExtraConstants.BLACKLISTED_COUNTRIES)) { + throw new RuntimeException( + "You can either whitelist or blacklist country codes for phone " + + "authentication."); + } + addCountriesToBundle(whitelistedCountries, ExtraConstants.WHITELISTED_COUNTRIES); + return this; + } + /** + * Sets the countries to be removed from the country code selector for phone + * authentication. Takes as input a List of both country isos and codes. + * This is not to be called with + * {@link #setWhitelistedCountries(List)}. + * If both are called, an exception will be thrown. + *

+ * Inputting an e-164 country code (e.g. '+1') will include all countries with + * +1 as its code. + * Example input: {'+52', 'us'} + * For a list of country iso or codes, see Alpha-2 codes here: + * https://en.wikipedia.org/wiki/ISO_3166-1 + * and e-164 codes here: https://en.wikipedia.org/wiki/List_of_country_calling_codes + * @param blacklistedCountries a case insensitive list of country codes and/or isos + * to be blacklisted + */ + public PhoneBuilder setBlacklistedCountries( + @NonNull List blacklistedCountries) { + if (getParams().containsKey(ExtraConstants.WHITELISTED_COUNTRIES)) { + throw new RuntimeException( + "You can either whitelist or blacklist country codes for phone " + + "authentication."); + } + addCountriesToBundle(blacklistedCountries, ExtraConstants.BLACKLISTED_COUNTRIES); return this; } + + @Override + public IdpConfig build() { + validateInputs(); + return super.build(); + } + + private void addCountriesToBundle(List CountryIsos, String CountryIsoType) { + ArrayList uppercaseCodes = new ArrayList<>(); + for (String code : CountryIsos) { + uppercaseCodes.add(code.toUpperCase(Locale.getDefault())); + } + + getParams().putStringArrayList(CountryIsoType, uppercaseCodes); + } + + private void validateInputs() { + List whitelistedCountries = getParams().getStringArrayList( + ExtraConstants.WHITELISTED_COUNTRIES); + List blacklistedCountries = getParams().getStringArrayList( + ExtraConstants.BLACKLISTED_COUNTRIES); + + if (whitelistedCountries != null && + blacklistedCountries != null) { + throw new RuntimeException( + "You can either whitelist or blacklist country codes for phone " + + "authentication."); + } else if (whitelistedCountries != null) { + validateCountries(whitelistedCountries); + validateDefaultCountryIso(whitelistedCountries, true); + + } else if (blacklistedCountries != null) { + validateCountries(blacklistedCountries); + validateDefaultCountryIso(blacklistedCountries, false); + } + } + + private void validateCountries(List codes) { + for (String code : codes) { + if (!PhoneNumberUtils.isValidIso(code) && !PhoneNumberUtils.isValid(code)) { + throw new RuntimeException("Invalid input: You must provide a valid " + + "country iso (alpha-2) or code (e-164). e.g. 'us' or '+1'."); + } + } + } + + private void validateDefaultCountryIso(List codes, boolean whitelisted) { + if (getParams().containsKey(ExtraConstants.COUNTRY_ISO) && codes != null) { + String defaultIso = getParams().getString(ExtraConstants.COUNTRY_ISO); + boolean containsIso = containsCountryIso(codes, defaultIso); + if (!containsIso && whitelisted || containsIso && !whitelisted) { + throw new RuntimeException("Invalid default country iso. Make sure it " + + "is either part of the whitelisted list or that you " + + "haven't blacklisted it."); + } + } + } + + private boolean containsCountryIso(List codes, String iso) { + for (String code : codes) { + if (PhoneNumberUtils.isValidIso(code)) { + if (code.equals(iso)) { + return true; + } + } else { + List isos = PhoneNumberUtils.getCountryIsosFromCountryCode(code); + if (isos.contains(iso)) { + return true; + } + } + } + return false; + } } /** @@ -679,8 +810,8 @@ public GoogleBuilder setScopes(@NonNull List scopes) { } /** - * Set the {@link GoogleSignInOptions} to be used for Google sign-in. Standard options - * like requesting the user's email will automatically be added. + * Set the {@link GoogleSignInOptions} to be used for Google sign-in. Standard + * options like requesting the user's email will automatically be added. * * @param options sign-in options */ @@ -784,8 +915,8 @@ private abstract class AuthIntentBuilder { boolean mEnableHints = true; /** - * Specifies the theme to use for the application flow. If no theme is specified, a default - * theme will be used. + * Specifies the theme to use for the application flow. If no theme is specified, a + * default theme will be used. */ @NonNull public T setTheme(@StyleRes int theme) { @@ -825,8 +956,8 @@ public T setPrivacyPolicyUrl(@Nullable String privacyPolicyUrl) { } /** - * Specified the set of supported authentication providers. At least one provider must be - * specified. There may only be one instance of each provider. + * Specified the set of supported authentication providers. At least one provider must + * be specified. There may only be one instance of each provider. *

*

If no providers are explicitly specified by calling this method, then the email * provider is the default supported provider. diff --git a/auth/src/main/java/com/firebase/ui/auth/data/client/CountryListLoadTask.java b/auth/src/main/java/com/firebase/ui/auth/data/client/CountryListLoadTask.java index d2c20589f..119d35127 100644 --- a/auth/src/main/java/com/firebase/ui/auth/data/client/CountryListLoadTask.java +++ b/auth/src/main/java/com/firebase/ui/auth/data/client/CountryListLoadTask.java @@ -19,14 +19,20 @@ package com.firebase.ui.auth.data.client; import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.RestrictTo; import com.firebase.ui.auth.data.model.CountryInfo; +import com.firebase.ui.auth.util.data.PhoneNumberUtils; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; // TODO We need to move away from ListView and AsyncTask in the future and use (say) // RecyclerView and Task/ThreadPoolExecutor . @@ -38,267 +44,78 @@ public interface Listener { void onLoadComplete(List result); } - private static final int MAX_COUNTRIES = 291; - private final Listener mListener; - public CountryListLoadTask(Listener listener) { + + Set whitelistedCountryIsos; + Set blacklistedCountryIsos; + + public CountryListLoadTask(Listener listener, + @Nullable List whitelistedCountries, + @Nullable List blacklistedCountries) { mListener = listener; + + if (whitelistedCountries != null) { + this.whitelistedCountryIsos = convertCodesToIsos(new HashSet<>(whitelistedCountries)); + } else if (blacklistedCountries != null) { + this.blacklistedCountryIsos = convertCodesToIsos(new HashSet<>(blacklistedCountries)); + } + } + + private Set convertCodesToIsos(@NonNull Set codes) { + Set isos = new HashSet<>(); + for (String code : codes) { + if (PhoneNumberUtils.isValid(code)) { + isos.addAll(PhoneNumberUtils.getCountryIsosFromCountryCode(code)); + } else { + isos.add(code); + } + } + return isos; } @Override protected List doInBackground(Void... params) { - final List countryInfoList = new ArrayList<>(MAX_COUNTRIES); - countryInfoList.add(new CountryInfo(new Locale("", "AF"), 93)); - countryInfoList.add(new CountryInfo(new Locale("", "AX"), 358)); - countryInfoList.add(new CountryInfo(new Locale("", "AL"), 355)); - countryInfoList.add(new CountryInfo(new Locale("", "DZ"), 213)); - countryInfoList.add(new CountryInfo(new Locale("", "AS"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "AD"), 376)); - countryInfoList.add(new CountryInfo(new Locale("", "AO"), 244)); - countryInfoList.add(new CountryInfo(new Locale("", "AI"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "AG"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "AR"), 54)); - countryInfoList.add(new CountryInfo(new Locale("", "AM"), 374)); - countryInfoList.add(new CountryInfo(new Locale("", "AW"), 297)); - countryInfoList.add(new CountryInfo(new Locale("", "AC"), 247)); - countryInfoList.add(new CountryInfo(new Locale("", "AU"), 61)); - countryInfoList.add(new CountryInfo(new Locale("", "AT"), 43)); - countryInfoList.add(new CountryInfo(new Locale("", "AZ"), 994)); - countryInfoList.add(new CountryInfo(new Locale("", "BS"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "BH"), 973)); - countryInfoList.add(new CountryInfo(new Locale("", "BD"), 880)); - countryInfoList.add(new CountryInfo(new Locale("", "BB"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "BY"), 375)); - countryInfoList.add(new CountryInfo(new Locale("", "BE"), 32)); - countryInfoList.add(new CountryInfo(new Locale("", "BZ"), 501)); - countryInfoList.add(new CountryInfo(new Locale("", "BJ"), 229)); - countryInfoList.add(new CountryInfo(new Locale("", "BM"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "BT"), 975)); - countryInfoList.add(new CountryInfo(new Locale("", "BO"), 591)); - countryInfoList.add(new CountryInfo(new Locale("", "BA"), 387)); - countryInfoList.add(new CountryInfo(new Locale("", "BW"), 267)); - countryInfoList.add(new CountryInfo(new Locale("", "BR"), 55)); - countryInfoList.add(new CountryInfo(new Locale("", "IO"), 246)); - countryInfoList.add(new CountryInfo(new Locale("", "VG"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "BN"), 673)); - countryInfoList.add(new CountryInfo(new Locale("", "BG"), 359)); - countryInfoList.add(new CountryInfo(new Locale("", "BF"), 226)); - countryInfoList.add(new CountryInfo(new Locale("", "BI"), 257)); - countryInfoList.add(new CountryInfo(new Locale("", "KH"), 855)); - countryInfoList.add(new CountryInfo(new Locale("", "CM"), 237)); - countryInfoList.add(new CountryInfo(new Locale("", "CA"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "CV"), 238)); - countryInfoList.add(new CountryInfo(new Locale("", "BQ"), 599)); - countryInfoList.add(new CountryInfo(new Locale("", "KY"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "CF"), 236)); - countryInfoList.add(new CountryInfo(new Locale("", "TD"), 235)); - countryInfoList.add(new CountryInfo(new Locale("", "CL"), 56)); - countryInfoList.add(new CountryInfo(new Locale("", "CN"), 86)); - countryInfoList.add(new CountryInfo(new Locale("", "CX"), 61)); - countryInfoList.add(new CountryInfo(new Locale("", "CC"), 61)); - countryInfoList.add(new CountryInfo(new Locale("", "CO"), 57)); - countryInfoList.add(new CountryInfo(new Locale("", "KM"), 269)); - countryInfoList.add(new CountryInfo(new Locale("", "CD"), 243)); - countryInfoList.add(new CountryInfo(new Locale("", "CG"), 242)); - countryInfoList.add(new CountryInfo(new Locale("", "CK"), 682)); - countryInfoList.add(new CountryInfo(new Locale("", "CR"), 506)); - countryInfoList.add(new CountryInfo(new Locale("", "CI"), 225)); - countryInfoList.add(new CountryInfo(new Locale("", "HR"), 385)); - countryInfoList.add(new CountryInfo(new Locale("", "CU"), 53)); - countryInfoList.add(new CountryInfo(new Locale("", "CW"), 599)); - countryInfoList.add(new CountryInfo(new Locale("", "CY"), 357)); - countryInfoList.add(new CountryInfo(new Locale("", "CZ"), 420)); - countryInfoList.add(new CountryInfo(new Locale("", "DK"), 45)); - countryInfoList.add(new CountryInfo(new Locale("", "DJ"), 253)); - countryInfoList.add(new CountryInfo(new Locale("", "DM"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "DO"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "TL"), 670)); - countryInfoList.add(new CountryInfo(new Locale("", "EC"), 593)); - countryInfoList.add(new CountryInfo(new Locale("", "EG"), 20)); - countryInfoList.add(new CountryInfo(new Locale("", "SV"), 503)); - countryInfoList.add(new CountryInfo(new Locale("", "GQ"), 240)); - countryInfoList.add(new CountryInfo(new Locale("", "ER"), 291)); - countryInfoList.add(new CountryInfo(new Locale("", "EE"), 372)); - countryInfoList.add(new CountryInfo(new Locale("", "ET"), 251)); - countryInfoList.add(new CountryInfo(new Locale("", "FK"), 500)); - countryInfoList.add(new CountryInfo(new Locale("", "FO"), 298)); - countryInfoList.add(new CountryInfo(new Locale("", "FJ"), 679)); - countryInfoList.add(new CountryInfo(new Locale("", "FI"), 358)); - countryInfoList.add(new CountryInfo(new Locale("", "FR"), 33)); - countryInfoList.add(new CountryInfo(new Locale("", "GF"), 594)); - countryInfoList.add(new CountryInfo(new Locale("", "PF"), 689)); - countryInfoList.add(new CountryInfo(new Locale("", "GA"), 241)); - countryInfoList.add(new CountryInfo(new Locale("", "GM"), 220)); - countryInfoList.add(new CountryInfo(new Locale("", "GE"), 995)); - countryInfoList.add(new CountryInfo(new Locale("", "DE"), 49)); - countryInfoList.add(new CountryInfo(new Locale("", "GH"), 233)); - countryInfoList.add(new CountryInfo(new Locale("", "GI"), 350)); - countryInfoList.add(new CountryInfo(new Locale("", "GR"), 30)); - countryInfoList.add(new CountryInfo(new Locale("", "GL"), 299)); - countryInfoList.add(new CountryInfo(new Locale("", "GD"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "GP"), 590)); - countryInfoList.add(new CountryInfo(new Locale("", "GU"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "GT"), 502)); - countryInfoList.add(new CountryInfo(new Locale("", "GG"), 44)); - countryInfoList.add(new CountryInfo(new Locale("", "GN"), 224)); - countryInfoList.add(new CountryInfo(new Locale("", "GW"), 245)); - countryInfoList.add(new CountryInfo(new Locale("", "GY"), 592)); - countryInfoList.add(new CountryInfo(new Locale("", "HT"), 509)); - countryInfoList.add(new CountryInfo(new Locale("", "HM"), 672)); - countryInfoList.add(new CountryInfo(new Locale("", "HN"), 504)); - countryInfoList.add(new CountryInfo(new Locale("", "HK"), 852)); - countryInfoList.add(new CountryInfo(new Locale("", "HU"), 36)); - countryInfoList.add(new CountryInfo(new Locale("", "IS"), 354)); - countryInfoList.add(new CountryInfo(new Locale("", "IN"), 91)); - countryInfoList.add(new CountryInfo(new Locale("", "ID"), 62)); - countryInfoList.add(new CountryInfo(new Locale("", "IR"), 98)); - countryInfoList.add(new CountryInfo(new Locale("", "IQ"), 964)); - countryInfoList.add(new CountryInfo(new Locale("", "IE"), 353)); - countryInfoList.add(new CountryInfo(new Locale("", "IM"), 44)); - countryInfoList.add(new CountryInfo(new Locale("", "IL"), 972)); - countryInfoList.add(new CountryInfo(new Locale("", "IT"), 39)); - countryInfoList.add(new CountryInfo(new Locale("", "JM"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "JP"), 81)); - countryInfoList.add(new CountryInfo(new Locale("", "JE"), 44)); - countryInfoList.add(new CountryInfo(new Locale("", "JO"), 962)); - countryInfoList.add(new CountryInfo(new Locale("", "KZ"), 7)); - countryInfoList.add(new CountryInfo(new Locale("", "KE"), 254)); - countryInfoList.add(new CountryInfo(new Locale("", "KI"), 686)); - countryInfoList.add(new CountryInfo(new Locale("", "XK"), 381)); - countryInfoList.add(new CountryInfo(new Locale("", "KW"), 965)); - countryInfoList.add(new CountryInfo(new Locale("", "KG"), 996)); - countryInfoList.add(new CountryInfo(new Locale("", "LA"), 856)); - countryInfoList.add(new CountryInfo(new Locale("", "LV"), 371)); - countryInfoList.add(new CountryInfo(new Locale("", "LB"), 961)); - countryInfoList.add(new CountryInfo(new Locale("", "LS"), 266)); - countryInfoList.add(new CountryInfo(new Locale("", "LR"), 231)); - countryInfoList.add(new CountryInfo(new Locale("", "LY"), 218)); - countryInfoList.add(new CountryInfo(new Locale("", "LI"), 423)); - countryInfoList.add(new CountryInfo(new Locale("", "LT"), 370)); - countryInfoList.add(new CountryInfo(new Locale("", "LU"), 352)); - countryInfoList.add(new CountryInfo(new Locale("", "MO"), 853)); - countryInfoList.add(new CountryInfo(new Locale("", "MK"), 389)); - countryInfoList.add(new CountryInfo(new Locale("", "MG"), 261)); - countryInfoList.add(new CountryInfo(new Locale("", "MW"), 265)); - countryInfoList.add(new CountryInfo(new Locale("", "MY"), 60)); - countryInfoList.add(new CountryInfo(new Locale("", "MV"), 960)); - countryInfoList.add(new CountryInfo(new Locale("", "ML"), 223)); - countryInfoList.add(new CountryInfo(new Locale("", "MT"), 356)); - countryInfoList.add(new CountryInfo(new Locale("", "MH"), 692)); - countryInfoList.add(new CountryInfo(new Locale("", "MQ"), 596)); - countryInfoList.add(new CountryInfo(new Locale("", "MR"), 222)); - countryInfoList.add(new CountryInfo(new Locale("", "MU"), 230)); - countryInfoList.add(new CountryInfo(new Locale("", "YT"), 262)); - countryInfoList.add(new CountryInfo(new Locale("", "MX"), 52)); - countryInfoList.add(new CountryInfo(new Locale("", "FM"), 691)); - countryInfoList.add(new CountryInfo(new Locale("", "MD"), 373)); - countryInfoList.add(new CountryInfo(new Locale("", "MC"), 377)); - countryInfoList.add(new CountryInfo(new Locale("", "MN"), 976)); - countryInfoList.add(new CountryInfo(new Locale("", "ME"), 382)); - countryInfoList.add(new CountryInfo(new Locale("", "MS"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "MA"), 212)); - countryInfoList.add(new CountryInfo(new Locale("", "MZ"), 258)); - countryInfoList.add(new CountryInfo(new Locale("", "MM"), 95)); - countryInfoList.add(new CountryInfo(new Locale("", "NA"), 264)); - countryInfoList.add(new CountryInfo(new Locale("", "NR"), 674)); - countryInfoList.add(new CountryInfo(new Locale("", "NP"), 977)); - countryInfoList.add(new CountryInfo(new Locale("", "NL"), 31)); - countryInfoList.add(new CountryInfo(new Locale("", "NC"), 687)); - countryInfoList.add(new CountryInfo(new Locale("", "NZ"), 64)); - countryInfoList.add(new CountryInfo(new Locale("", "NI"), 505)); - countryInfoList.add(new CountryInfo(new Locale("", "NE"), 227)); - countryInfoList.add(new CountryInfo(new Locale("", "NG"), 234)); - countryInfoList.add(new CountryInfo(new Locale("", "NU"), 683)); - countryInfoList.add(new CountryInfo(new Locale("", "NF"), 672)); - countryInfoList.add(new CountryInfo(new Locale("", "KP"), 850)); - countryInfoList.add(new CountryInfo(new Locale("", "MP"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "NO"), 47)); - countryInfoList.add(new CountryInfo(new Locale("", "OM"), 968)); - countryInfoList.add(new CountryInfo(new Locale("", "PK"), 92)); - countryInfoList.add(new CountryInfo(new Locale("", "PW"), 680)); - countryInfoList.add(new CountryInfo(new Locale("", "PS"), 970)); - countryInfoList.add(new CountryInfo(new Locale("", "PA"), 507)); - countryInfoList.add(new CountryInfo(new Locale("", "PG"), 675)); - countryInfoList.add(new CountryInfo(new Locale("", "PY"), 595)); - countryInfoList.add(new CountryInfo(new Locale("", "PE"), 51)); - countryInfoList.add(new CountryInfo(new Locale("", "PH"), 63)); - countryInfoList.add(new CountryInfo(new Locale("", "PL"), 48)); - countryInfoList.add(new CountryInfo(new Locale("", "PT"), 351)); - countryInfoList.add(new CountryInfo(new Locale("", "PR"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "QA"), 974)); - countryInfoList.add(new CountryInfo(new Locale("", "RE"), 262)); - countryInfoList.add(new CountryInfo(new Locale("", "RO"), 40)); - countryInfoList.add(new CountryInfo(new Locale("", "RU"), 7)); - countryInfoList.add(new CountryInfo(new Locale("", "RW"), 250)); - countryInfoList.add(new CountryInfo(new Locale("", "BL"), 590)); - countryInfoList.add(new CountryInfo(new Locale("", "SH"), 290)); - countryInfoList.add(new CountryInfo(new Locale("", "KN"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "LC"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "MF"), 590)); - countryInfoList.add(new CountryInfo(new Locale("", "PM"), 508)); - countryInfoList.add(new CountryInfo(new Locale("", "VC"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "WS"), 685)); - countryInfoList.add(new CountryInfo(new Locale("", "SM"), 378)); - countryInfoList.add(new CountryInfo(new Locale("", "ST"), 239)); - countryInfoList.add(new CountryInfo(new Locale("", "SA"), 966)); - countryInfoList.add(new CountryInfo(new Locale("", "SN"), 221)); - countryInfoList.add(new CountryInfo(new Locale("", "RS"), 381)); - countryInfoList.add(new CountryInfo(new Locale("", "SC"), 248)); - countryInfoList.add(new CountryInfo(new Locale("", "SL"), 232)); - countryInfoList.add(new CountryInfo(new Locale("", "SG"), 65)); - countryInfoList.add(new CountryInfo(new Locale("", "SX"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "SK"), 421)); - countryInfoList.add(new CountryInfo(new Locale("", "SI"), 386)); - countryInfoList.add(new CountryInfo(new Locale("", "SB"), 677)); - countryInfoList.add(new CountryInfo(new Locale("", "SO"), 252)); - countryInfoList.add(new CountryInfo(new Locale("", "ZA"), 27)); - countryInfoList.add(new CountryInfo(new Locale("", "GS"), 500)); - countryInfoList.add(new CountryInfo(new Locale("", "KR"), 82)); - countryInfoList.add(new CountryInfo(new Locale("", "SS"), 211)); - countryInfoList.add(new CountryInfo(new Locale("", "ES"), 34)); - countryInfoList.add(new CountryInfo(new Locale("", "LK"), 94)); - countryInfoList.add(new CountryInfo(new Locale("", "SD"), 249)); - countryInfoList.add(new CountryInfo(new Locale("", "SR"), 597)); - countryInfoList.add(new CountryInfo(new Locale("", "SJ"), 47)); - countryInfoList.add(new CountryInfo(new Locale("", "SZ"), 268)); - countryInfoList.add(new CountryInfo(new Locale("", "SE"), 46)); - countryInfoList.add(new CountryInfo(new Locale("", "CH"), 41)); - countryInfoList.add(new CountryInfo(new Locale("", "SY"), 963)); - countryInfoList.add(new CountryInfo(new Locale("", "TW"), 886)); - countryInfoList.add(new CountryInfo(new Locale("", "TJ"), 992)); - countryInfoList.add(new CountryInfo(new Locale("", "TZ"), 255)); - countryInfoList.add(new CountryInfo(new Locale("", "TH"), 66)); - countryInfoList.add(new CountryInfo(new Locale("", "TG"), 228)); - countryInfoList.add(new CountryInfo(new Locale("", "TK"), 690)); - countryInfoList.add(new CountryInfo(new Locale("", "TO"), 676)); - countryInfoList.add(new CountryInfo(new Locale("", "TT"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "TN"), 216)); - countryInfoList.add(new CountryInfo(new Locale("", "TR"), 90)); - countryInfoList.add(new CountryInfo(new Locale("", "TM"), 993)); - countryInfoList.add(new CountryInfo(new Locale("", "TC"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "TV"), 688)); - countryInfoList.add(new CountryInfo(new Locale("", "VI"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "UG"), 256)); - countryInfoList.add(new CountryInfo(new Locale("", "UA"), 380)); - countryInfoList.add(new CountryInfo(new Locale("", "AE"), 971)); - countryInfoList.add(new CountryInfo(new Locale("", "GB"), 44)); - countryInfoList.add(new CountryInfo(new Locale("", "US"), 1)); - countryInfoList.add(new CountryInfo(new Locale("", "UY"), 598)); - countryInfoList.add(new CountryInfo(new Locale("", "UZ"), 998)); - countryInfoList.add(new CountryInfo(new Locale("", "VU"), 678)); - countryInfoList.add(new CountryInfo(new Locale("", "VA"), 379)); - countryInfoList.add(new CountryInfo(new Locale("", "VE"), 58)); - countryInfoList.add(new CountryInfo(new Locale("", "VN"), 84)); - countryInfoList.add(new CountryInfo(new Locale("", "WF"), 681)); - countryInfoList.add(new CountryInfo(new Locale("", "EH"), 212)); - countryInfoList.add(new CountryInfo(new Locale("", "YE"), 967)); - countryInfoList.add(new CountryInfo(new Locale("", "ZM"), 260)); - countryInfoList.add(new CountryInfo(new Locale("", "ZW"), 263)); + Map countryInfoMap = PhoneNumberUtils.getImmutableCountryIsoMap(); + List countryInfoList = getAvailableCountryIsos(countryInfoMap); Collections.sort(countryInfoList); return countryInfoList; } + public List getAvailableCountryIsos(Map countryInfoMap) { + // We consider all countries to be whitelisted if there are no whitelisted + // or blacklisted countries given as input. + if (whitelistedCountryIsos == null && blacklistedCountryIsos == null) { + this.whitelistedCountryIsos = new HashSet<>(countryInfoMap.keySet()); + } + + List countryInfoList = new ArrayList<>(); + + // At this point either whitelistedCountryIsos or blacklistedCountryIsos is null. + // We assume no countries are to be excluded. Here, we correct this assumption based on the + // contents of either lists. + Set excludedCountries = new HashSet<>(); + if (whitelistedCountryIsos == null) { + // Exclude all countries in the blacklistedCountryIsos list. + excludedCountries.addAll(blacklistedCountryIsos); + } else { + // Exclude all countries that are not present in the whitelistedCountryIsos list. + excludedCountries.addAll(countryInfoMap.keySet()); + excludedCountries.removeAll(whitelistedCountryIsos); + } + + // Once we know which countries need to be excluded, we loop through the country isos, + // skipping those that have been excluded. + for (String countryIso : countryInfoMap.keySet()) { + if (!excludedCountries.contains(countryIso)) { + countryInfoList.add(new CountryInfo(new Locale("", countryIso), + countryInfoMap.get(countryIso))); + } + } + + return countryInfoList; + } + @Override public void onPostExecute(List result) { if (mListener != null) { diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java index 5d80c5de1..9ae55b75c 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java @@ -50,6 +50,9 @@ public final class CountryListSpinner extends AppCompatEditText implements private String mSelectedCountryName; private CountryInfo mSelectedCountryInfo; + private List whitelistedCountries; + private List blacklistedCountries; + public CountryListSpinner(Context context) { this(context, null, android.R.attr.spinnerStyle); } @@ -116,7 +119,7 @@ public void setSelectedForCountry(final Locale locale, String countryCode) { } public CountryInfo getSelectedCountryInfo() { - return mSelectedCountryInfo; + return mSelectedCountryInfo; } @Override @@ -145,7 +148,9 @@ public void onClick(View view) { } private void loadCountryList() { - new CountryListLoadTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new CountryListLoadTask(this, + whitelistedCountries, + blacklistedCountries).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void executeUserClickListener(View view) { @@ -154,6 +159,14 @@ private void executeUserClickListener(View view) { } } + public void setWhitelistedCountries(List whitelistedCountries) { + this.whitelistedCountries = whitelistedCountries; + } + + public void setBlacklistedCountries(List blacklistedCountries) { + this.blacklistedCountries = blacklistedCountries; + } + @Override public void onLoadComplete(List result) { mCountryListAdapter.setData(result); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/VerifyPhoneNumberFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/VerifyPhoneNumberFragment.java index 721632025..c59734443 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/VerifyPhoneNumberFragment.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/VerifyPhoneNumberFragment.java @@ -219,6 +219,14 @@ private String getPseudoValidPhoneNumber() { } private void setupCountrySpinner() { + Bundle params = getArguments().getBundle(ExtraConstants.PARAMS); + if (params != null) { + mCountryListSpinner.setWhitelistedCountries( + params.getStringArrayList(ExtraConstants.WHITELISTED_COUNTRIES)); + mCountryListSpinner.setBlacklistedCountries( + params.getStringArrayList(ExtraConstants.BLACKLISTED_COUNTRIES)); + } + //clear error when spinner is clicked on mCountryListSpinner.setOnClickListener(new View.OnClickListener() { @Override diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java b/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java index 7e2035a0f..a3f8ab7e5 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java @@ -37,6 +37,9 @@ public final class ExtraConstants { public static final String COUNTRY_ISO = "extra_country_iso"; public static final String NATIONAL_NUMBER = "extra_national_number"; + public static final String WHITELISTED_COUNTRIES = "whitelisted_countries"; + public static final String BLACKLISTED_COUNTRIES = "blacklisted_countries"; + private ExtraConstants() { throw new AssertionError("No instance for you!"); } diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java index 704bb3060..53f84e8a4 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java @@ -51,8 +51,8 @@ public final class PhoneNumberUtils { private static final SparseArray> COUNTRY_TO_REGION_CODES = createCountryCodeToRegionCodeMap(); - private static final Map COUNTRY_TO_ISO_CODES = - Collections.unmodifiableMap(createCountryCodeByIsoMap()); + + private static Map COUNTRY_TO_ISO_CODES; /** * This method works as follow:

  1. When the android version is LOLLIPOP or greater, the @@ -151,10 +151,20 @@ public static PhoneNumber getPhoneNumber( @Nullable public static Integer getCountryCode(String countryIso) { + if (COUNTRY_TO_ISO_CODES == null) { + initCountryCodeByIsoMap(); + } return countryIso == null ? null : COUNTRY_TO_ISO_CODES.get(countryIso.toUpperCase(Locale.getDefault())); } + public static Map getImmutableCountryIsoMap() { + if (COUNTRY_TO_ISO_CODES == null) { + initCountryCodeByIsoMap(); + } + return COUNTRY_TO_ISO_CODES; + } + private static String getCountryIsoForCountryCode(String countryCode) { List countries = COUNTRY_TO_REGION_CODES.get(Integer.parseInt(countryCode)); if (countries != null) { @@ -163,6 +173,12 @@ private static String getCountryIsoForCountryCode(String countryCode) { return DEFAULT_LOCALE.getCountry(); } + @Nullable + public static List getCountryIsosFromCountryCode(String countryCode) { + return !isValid(countryCode) ? null : + COUNTRY_TO_REGION_CODES.get(Integer.parseInt(countryCode.substring(1))); + } + /** * Country code extracted using shortest matching prefix like libPhoneNumber. See: * https://github.com/googlei18n/libphonenumber/blob/master/java/libphonenumber/src/com @@ -181,7 +197,6 @@ private static String getCountryCodeForPhoneNumber(String normalizedPhoneNumber) return potentialCountryCode; } } - return null; } @@ -433,7 +448,7 @@ private static SparseArray> createCountryCodeToRegionCodeMap() { return map; } - private static Map createCountryCodeByIsoMap() { + private static void initCountryCodeByIsoMap() { Map map = new HashMap<>(MAX_COUNTRIES); for (int i = 0; i < COUNTRY_TO_REGION_CODES.size(); i++) { @@ -457,6 +472,6 @@ private static Map createCountryCodeByIsoMap() { map.put("GS", 500); map.put("XK", 381); - return map; + COUNTRY_TO_ISO_CODES = Collections.unmodifiableMap(map); } } diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java index d189f6b30..480e04fee 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java @@ -20,6 +20,7 @@ import com.firebase.ui.auth.data.client.CountryListLoadTask; import com.firebase.ui.auth.data.model.CountryInfo; +import com.firebase.ui.auth.util.data.PhoneNumberUtils; import org.junit.Before; import org.junit.Test; @@ -295,13 +296,13 @@ public class CountryListLoadTaskTests { @Before public void setUp() { - // Create task and mock dependencies mListener = mock(CountryListLoadTask.Listener.class); - mTask = new CountryListLoadTask(mListener); } @Test - public void testExecute() { + public void testExecute_withoutUserInput_expectAllCountriesAdded() { + mTask = new CountryListLoadTask(mListener, null, null); + mTask.execute(); try { @@ -315,9 +316,110 @@ public void testExecute() { } } + + @Test + public void testExecute_withUserWhitelistInput_expectUserCountriesAddedOnly() { + List whitelistedCountryIsos = new ArrayList<>(); + whitelistedCountryIsos.add("US"); + + List expectedOutput = new ArrayList<>(); + expectedOutput.add(new CountryInfo(new Locale("", "US"), 1)); + + mTask = new CountryListLoadTask(mListener, whitelistedCountryIsos, null); + + mTask.execute(); + + try { + final List result = mTask.get(); + Collections.sort(expectedOutput); + assertEquals(expectedOutput, result); + } catch (InterruptedException e) { + fail("Should not throw InterruptedException"); + } catch (ExecutionException e) { + fail("Should not throw ExecutionException"); + } + } + + @Test + public void testExecute_withUserBlacklistInput_expectUserCountriesRemoved() { + List blacklistedCountryIsos = new ArrayList<>(); + blacklistedCountryIsos.add("US"); + + List expectedOutput = new ArrayList<>(COUNTRY_LIST); + expectedOutput.remove(new CountryInfo(new Locale("", "US"), 1)); + + mTask = new CountryListLoadTask(mListener, null, blacklistedCountryIsos); + + mTask.execute(); + + try { + final List result = mTask.get(); + Collections.sort(expectedOutput); + assertEquals(expectedOutput, result); + } catch (InterruptedException e) { + fail("Should not throw InterruptedException"); + } catch (ExecutionException e) { + fail("Should not throw ExecutionException"); + } + } + + @Test + public void testExecute_withCodeWhitelistInput_expectUserCodeCountriesAddedOnly() { + List whitelistedCountries = new ArrayList<>(); + whitelistedCountries.add("+1"); + + List expectedOutput = new ArrayList<>(); + for (String iso : PhoneNumberUtils.getCountryIsosFromCountryCode("+1")) { + expectedOutput.add(new CountryInfo(new Locale("", iso), 1)); + } + + mTask = new CountryListLoadTask(mListener, whitelistedCountries, null); + + mTask.execute(); + + try { + final List result = mTask.get(); + Collections.sort(expectedOutput); + assertEquals(expectedOutput, result); + } catch (InterruptedException e) { + fail("Should not throw InterruptedException"); + } catch (ExecutionException e) { + fail("Should not throw ExecutionException"); + } + } + + @Test + public void testExecute_withCodeBlacklistInput_expectUserCodeCountriesRemoved() { + List blacklistedCountries = new ArrayList<>(); + blacklistedCountries.add("+1"); + + List excludedCountries = new ArrayList<>(); + for (String iso : PhoneNumberUtils.getCountryIsosFromCountryCode("+1")) { + excludedCountries.add(new CountryInfo(new Locale("", iso), 1)); + } + + List expectedOutput = new ArrayList<>(COUNTRY_LIST); + expectedOutput.removeAll(excludedCountries); + + mTask = new CountryListLoadTask(mListener, null, blacklistedCountries); + + mTask.execute(); + + try { + final List result = mTask.get(); + Collections.sort(expectedOutput); + assertEquals(expectedOutput, result); + } catch (InterruptedException e) { + fail("Should not throw InterruptedException"); + } catch (ExecutionException e) { + fail("Should not throw ExecutionException"); + } + } + + @Test public void testOnPostExecute_nullListener() { - mTask = new CountryListLoadTask(null); + mTask = new CountryListLoadTask(null, null, null); try { mTask.onPostExecute(COUNTRY_LIST); } catch (NullPointerException ex) { @@ -327,6 +429,7 @@ public void testOnPostExecute_nullListener() { @Test public void testOnPostExecute() { + mTask = new CountryListLoadTask(mListener, null, null); mTask.onPostExecute(COUNTRY_LIST); verify(mListener).onLoadComplete(COUNTRY_LIST); }