Where am I? - Get country
Asked Answered
V

10

142

An android mobile actually does know quite well where it is - but is there a way of retrieving the country by something like a country code?

No need of knowing the exact GPS position - the country is sufficient

I first thought of using the time zone, but actually I need more information than that since it makes a difference if the location is New York or Lima.

The background of the question: I have an application that uses temperature values, and I'd like to set the default unit either to Celsius or Fahrenheit, depending on whether the location is US or outside

Vargueno answered 7/9, 2010 at 14:57 Comment(1)
-1: You definitely don't want to know where the user is. You want to know the user's locale setting. The accepted answer answers just that, but is otherwise totally inappropriate here, because search will point people here who actually want to know where the user really is.Stringhalt
L
106

This will get the country code set for the phone (phones language, NOT user location):

 String locale = context.getResources().getConfiguration().locale.getCountry(); 

can also replace getCountry() with getISO3Country() to get a 3 letter ISO code for the country. This will get the country name:

 String locale = context.getResources().getConfiguration().locale.getDisplayCountry();

This seems easier than the other methods and rely upon the localisation settings on the phone, so if a US user is abroad they probably still want Fahrenheit and this will work :)

Editors note: This solution has nothing to do with the location of the phone. It is constant. When you travel to Germany locale will NOT change. In short: locale != location.

Limulus answered 7/9, 2010 at 15:47 Comment(14)
This won't work in countries for which Android does not have a Locale. For example, in Switzerland, the language is likely to be set to German or French. This method will give you Germany or France, not Switzerland. Better to use the LocationManager or TelephonyManager approach.Exurbanite
Doesn't work well. I have to differentiate user country for all the Latin American countries, all the testing phones have returned USA even when the phone is in English or Spanish lang.Bouilli
This does not answer the title of the question. I may have the phone set to English and be about anywhere in the world (my native language is not English, but I do have my phone set to (British) English. It however does answer the actual question, because user wants US units if he is USAian, wherever he actually happens to be at the moment.Stringhalt
This won't work in countries such as Switzerland like Mathewl noted.Sushi
Not the feasible solution to get Country nameAmphidiploid
This is not a good questions. Precisely if I want to know the country is because I want to know if the user has move or if it´s not in the usual location...Archdeaconry
It's a good answer in general but it only returns the country of the locale, not the actual country.Its
This code is returning US but it's not giving the current country name/codeNursemaid
This code get phone configuration not user locationHaunt
This doesn't work all the time. I'm getting an empty string in some devicesPilgarlic
context.getResources().getConfiguration().locale is deprecatedJacksmelt
Unreliable, not universally valid and simply bad practice. Please do not use this code.Philous
this doesn't work, it return the code based on the language of the deviceKolivas
Thanks. i tried this one also but it always return "USInterline
U
151
/**
 * Get ISO 3166-1 alpha-2 country code for this device (or null if not available)
 * @param context Context reference to get the TelephonyManager instance from
 * @return country code or null
 */
public static String getUserCountry(Context context) {
    try {
        final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        final String simCountry = tm.getSimCountryIso();
        if (simCountry != null && simCountry.length() == 2) { // SIM country code is available
            return simCountry.toLowerCase(Locale.US);
        }
        else if (tm.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { // device is not 3G (would be unreliable)
            String networkCountry = tm.getNetworkCountryIso();
            if (networkCountry != null && networkCountry.length() == 2) { // network country code is available
                return networkCountry.toLowerCase(Locale.US);
            }
        }
    }
    catch (Exception e) { }
    return null;
}
Underset answered 16/10, 2013 at 22:48 Comment(9)
Only works on phones or other devices with a sim card. When using on a WIFI tablet, you will get null. So its usefulness is limited.Foreignism
@Foreignism This is all you can get. So it's actually quite useful, the problem is only that you have no other reliable data sources if you have a WiFi-only tablet.Underset
Note that if a user from USA is on holiday in France, getSimCountryIso will say us and getNetworkCountryIso will say frLicensee
You may want to change the toLowerCase() call to toUpperCase().Allover
I need Code not code Name; Like for usa +1 not the USHeadmistress
Please do not catch (Exception e).Laurettelauri
If you don't need null you may return a country from current locale instead in order to receive at leas anythingOden
@LeoDroidcoder The country from the current locale may be much less precise (or not even accurate at all). This weakens the guarantee that the method gives you, because you don't know if you've just got a country code from the SIM card or a less precise piece of information from the locale. If you're okay with that, and keep that in mind, your solution is fine.Underset
Keep in mind that getSimCountryIso & getNetworkCountryIso may return an empty string.Colligate
L
106

This will get the country code set for the phone (phones language, NOT user location):

 String locale = context.getResources().getConfiguration().locale.getCountry(); 

can also replace getCountry() with getISO3Country() to get a 3 letter ISO code for the country. This will get the country name:

 String locale = context.getResources().getConfiguration().locale.getDisplayCountry();

This seems easier than the other methods and rely upon the localisation settings on the phone, so if a US user is abroad they probably still want Fahrenheit and this will work :)

Editors note: This solution has nothing to do with the location of the phone. It is constant. When you travel to Germany locale will NOT change. In short: locale != location.

Limulus answered 7/9, 2010 at 15:47 Comment(14)
This won't work in countries for which Android does not have a Locale. For example, in Switzerland, the language is likely to be set to German or French. This method will give you Germany or France, not Switzerland. Better to use the LocationManager or TelephonyManager approach.Exurbanite
Doesn't work well. I have to differentiate user country for all the Latin American countries, all the testing phones have returned USA even when the phone is in English or Spanish lang.Bouilli
This does not answer the title of the question. I may have the phone set to English and be about anywhere in the world (my native language is not English, but I do have my phone set to (British) English. It however does answer the actual question, because user wants US units if he is USAian, wherever he actually happens to be at the moment.Stringhalt
This won't work in countries such as Switzerland like Mathewl noted.Sushi
Not the feasible solution to get Country nameAmphidiploid
This is not a good questions. Precisely if I want to know the country is because I want to know if the user has move or if it´s not in the usual location...Archdeaconry
It's a good answer in general but it only returns the country of the locale, not the actual country.Its
This code is returning US but it's not giving the current country name/codeNursemaid
This code get phone configuration not user locationHaunt
This doesn't work all the time. I'm getting an empty string in some devicesPilgarlic
context.getResources().getConfiguration().locale is deprecatedJacksmelt
Unreliable, not universally valid and simply bad practice. Please do not use this code.Philous
this doesn't work, it return the code based on the language of the deviceKolivas
Thanks. i tried this one also but it always return "USInterline
C
83

Use this link http://ip-api.com/json ,this will provide all the information as json. From this json you can get the country easily. This site works using your current IP,it automatically detects the IP and sendback details.

Docs http://ip-api.com/docs/api:json Hope it helps.

Example json

{
  "status": "success",
  "country": "United States",
  "countryCode": "US",
  "region": "CA",
  "regionName": "California",
  "city": "San Francisco",
  "zip": "94105",
  "lat": "37.7898",
  "lon": "-122.3942",
  "timezone": "America/Los_Angeles",
  "isp": "Wikimedia Foundation",
  "org": "Wikimedia Foundation",
  "as": "AS14907 Wikimedia US network",
  "query": "208.80.152.201"
}

note : As this is a 3rd party solution, only use if others didn't work.

Canon answered 5/11, 2014 at 9:34 Comment(13)
I'm not sure this is exactly what the OP wanted, but I really like the answer anyway.Ruthenian
Like the answer since this would work on tablets (without sim) as well. Telephony solution works just on phones with active sims. Dual sim phones is another problem scenario as they might be from different countries. So probably it makes more sense to rely on IP solution mentioned above.Balmuth
Even though, as you pointed out, this may not be the best approach to the problem, it actually provides a valid solution. +1 from me and thank you!Asphyxiant
It's always bad to use third parties, you can never know how stable there are. for instance - Parse.Scrotum
Yeah. That's why this is not a recommended solution. But is an easy approach.Canon
but it has limitations. their system will automatically ban any IP addresses doing over 150 requests per minute.Fluorosis
I like the approach but how can i test this??Couching
What do you mean by test? you can implement this as normal webservices and parse the data. But as this is a 3rd party solution, you must try all other options mentioned above.Canon
This is what I wantedMaddock
It's nice but you need internet connection for this to work.Vitiated
Thanks, it works, but has limitations. They do not allow commercial use of the free endpoint, their endpoints are limited to 45 HTTP requests per minute from an IP address (else requests will be throttled (HTTP 429) until your rate limit window is reset).Paxon
How they know if we use free tier as commercial use?Lianne
This wont work for people using VPNs, but still not a bad backup strategyGayl
V
66

Actually I just found out that there is even one more way of getting a country code, using the getSimCountryIso() method of TelephoneManager:

TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String countryCode = tm.getSimCountryIso();

Since it is the sim code it also should not change when traveling to other countries.

Vargueno answered 7/9, 2010 at 16:23 Comment(1)
This might not work when the device doesn't have SIM card, for example tabletTegucigalpa
H
41

First, get the LocationManager. Then, call LocationManager.getLastKnownPosition. Then create a GeoCoder and call GeoCoder.getFromLocation. Do this is in a separate thread!! This will give you a list of Address objects. Call Address.getCountryName and you got it.

Keep in mind that the last known position can be a bit stale, so if the user just crossed the border, you may not know about it for a while.

Hook answered 7/9, 2010 at 15:0 Comment(5)
For this application changing the default unit would not be desired when moving countries as the user's preference of Celsius or Fahrenheit will not change as he ventures to new lands.Limulus
I need to know in which country my user is for an API call. The call returns locations in the surrounding area. Therefore I am only interested in the real country code the user is in right now. Just to give an insight what the above approach could be needed for.Crosslegged
This is highly under rated ans, but the fact is this is the most reliable solution. Most of the ans above don't work for wifi network. The one provided by @Canon is looking good but the api provided is not for commercial use.Rayraya
Use with caution. Your device will give you an location even if not networked, but the Geocoder will typically return a null for the addresses array if wifi is off, raising an exception that you will need to try/catch.Apolitical
But one catch is we need to request runtime permission for accessing COARSE_LOCATION right?Velodrome
Z
17

You could use getNetworkCountryIso() from TelephonyManager to get the country the phone is currently in (although apparently this is unreliable on CDMA networks).

Zane answered 7/9, 2010 at 15:7 Comment(3)
that sounds pretty straight forward and worked well at the emulator in first instance - could you just explain why it's unreliable on CDMA networks?Vargueno
ah okay, I got it - because the documentation says so :)Vargueno
Because of the application this wouldn't be the best solution as when a user travels abroad their default unit would change. It would make more sense to have a static default unit based on the phones locale settings, which then can of course be changed in a setting somewhere.Limulus
S
17

Here is a complete solution based on the LocationManager and as fallbacks the TelephonyManager and the Network Provider's locations. I used the above answer from @Marco W. for the fallback part(great answer as itself!).

Note: the code contains PreferencesManager, this is a helper class that saves and loads data from SharedPrefrences. I'm using it to save the country to S"P, I'm only getting the country if it is empty. For my product I don't really care for all the edge cases(user travels abroad and so on).

public static String getCountry(Context context) {
    String country = PreferencesManager.getInstance(context).getString(COUNTRY);
    if (country != null) {
        return country;
    }

    LocationManager locationManager = (LocationManager) PiplApp.getInstance().getSystemService(Context.LOCATION_SERVICE);
    if (locationManager != null) {
        Location location = locationManager
                .getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if (location == null) {
            location = locationManager
                    .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        }
        if (location == null) {
            log.w("Couldn't get location from network and gps providers")
            return
        }
        Geocoder gcd = new Geocoder(context, Locale.getDefault());
        List<Address> addresses;
        try {
            addresses = gcd.getFromLocation(location.getLatitude(),
                    location.getLongitude(), 1);

            if (addresses != null && !addresses.isEmpty()) {
                country = addresses.get(0).getCountryName();
                if (country != null) {
                    PreferencesManager.getInstance(context).putString(COUNTRY, country);
                    return country;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    country = getCountryBasedOnSimCardOrNetwork(context);
    if (country != null) {
        PreferencesManager.getInstance(context).putString(COUNTRY, country);
        return country;
    }
    return null;
}


/**
 * Get ISO 3166-1 alpha-2 country code for this device (or null if not available)
 *
 * @param context Context reference to get the TelephonyManager instance from
 * @return country code or null
 */
private static String getCountryBasedOnSimCardOrNetwork(Context context) {
    try {
        final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        final String simCountry = tm.getSimCountryIso();
        if (simCountry != null && simCountry.length() == 2) { // SIM country code is available
            return simCountry.toLowerCase(Locale.US);
        } else if (tm.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { // device is not 3G (would be unreliable)
            String networkCountry = tm.getNetworkCountryIso();
            if (networkCountry != null && networkCountry.length() == 2) { // network country code is available
                return networkCountry.toLowerCase(Locale.US);
            }
        }
    } catch (Exception e) {
    }
    return null;
}
Scrotum answered 22/5, 2016 at 11:28 Comment(8)
Sorry, but your answer really needs to be rewritten. The code contains lots of unusable code.Glamorous
@Glamorous or you didn't understand the logic. Please give me 1 example of unused code in this snippetScrotum
Of course I undestood the logic and please don't take it the wrong way. I ment that PipplApp, PrefernecesManager etc could have been removed and the offered solution could be a bit more clear to the readers.Glamorous
@Glamorous about the PiplApp I agree 100% with you, I just remove itScrotum
There is country = addresses.get(0).getCountryName(); in your code, which will put full name of country into variable country. At same time in method getCountryBasedOnSimCardOrNetwork(Context context) you are returning the ISO code of country. I believe that you wanted to write country = addresses.get(0).getCountryCode(); :-)Philous
@Scrotum The location that you set with the network provider is never usedLycopodium
@Lycopodium it is used..it assigns value to location variable which later on in the code is being usedScrotum
@Scrotum but that's the thing. Strictly looking at the given method above, location is a local variable to the method. Therefore it cannot be used later on in the code. For further clarification, I am not talking about the first assignation of the variable. I am talking about the assignation in the if condition. You check if location is null. If it is, attempt to set it again. Then only in the else you apply some logic with the location. therefore, if the first assignation of location resulted in a null, the second assignation may work but will never be usedLycopodium
E
6
String locale = context.getResources().getConfiguration().locale.getCountry(); 

Is deprecated. Use this instead:

Locale locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    locale = context.getResources().getConfiguration().getLocales().get(0);
} else {
    locale = context.getResources().getConfiguration().locale;
}
Elouise answered 21/4, 2017 at 21:51 Comment(1)
Does it require permission and what country does it show?Paxon
C
6

For some devices, if the default language is set different (an indian can set English (US)) then

context.getResources().getConfiguration().locale.getDisplayCountry();

will give wrong value .So this method is non reliable

Also, getNetworkCountryIso() method of TelephonyManager will not work on devices which don't have SIM card (WIFI tablets).

If a device doesn't have SIM then we can use Time Zone to get the country. For countries like India, this method will work

sample code used to check the country is India or not (Time zone id : asia/calcutta)

private void checkCountry() {


    TelephonyManager telMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    if (telMgr == null)
        return;

    int simState = telMgr.getSimState();

    switch (simState) {
        //if sim is not available then country is find out using timezone id
        case TelephonyManager.SIM_STATE_ABSENT:
            TimeZone tz = TimeZone.getDefault();
            String timeZoneId = tz.getID();
            if (timeZoneId.equalsIgnoreCase(Constants.INDIA_TIME_ZONE_ID)) {
               //do something
            } else {
               //do something
            }
            break;

            //if sim is available then telephony manager network country info is used
        case TelephonyManager.SIM_STATE_READY:

           TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
            if (tm != null) {
                String countryCodeValue = tm.getNetworkCountryIso();
                //check if the network country code is "in"
                if (countryCodeValue.equalsIgnoreCase(Constants.NETWORK_INDIA_CODE)) {
                   //do something
                }

                else {
                   //do something
                }

            }
            break;

    }
}
Contretemps answered 26/10, 2018 at 9:10 Comment(2)
nice idea but Time zone is not a country specific feature you know that right? For every 15 degree of meridians there is another time zone etc.Shaker
@Gunhan, the time zones returned by Android mostly don't cross the country borders. See the list on wikipedia. However, there are some exceptions that make the mapping trickyRowden
G
6

Java (which is what Android uses) allows for one to retrieve the current TZ (Timezone) database name. Although your question mentioned that time zones may not be specific enough, using this method, you can get the country (and even city in some cases) of the user without needing location permissions.

A sample TZ Database Name:

Europe/Zurich reveals that the user is in Switzerland, while Asia/Seoul shows that the user is in South Korea.
(the user may not be in Zurich or Seoul respectively though, maybe in other states/provinces)
Here is a list of all available TZ Database Time Zones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

So you can get the TZ Database name using:

public String usersCountryByTzDbName() {
    return ZoneId.systemDefault().getId();
}

You can map these to the countries of your choice. The advantages of this method:

  • Unlike context.getResources().getConfiguration().locale.getCountry() like others have suggested, this would work regardless of the user's locale. Imagine if the user lived in Japan and set the language to en_US, you'd detect that the user is in USA instead of Japan.
  • Works on Wi-Fi only devices (which would not work if you used the telephony manager API)

Reference: Java 8 - tz database time zones
Note that according to this SO answer, TZ Database Time Zones may change from time to time, so you should expect new timezones that you have not previously encountered. Also, if the user happens to travel to another country, the other country would be reflected in this method.

Thanks!

Grownup answered 16/3, 2022 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.