How can I get my Android device country code without using GPS?
AndroidLocaleAndroid Problem Overview
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 or country name?
There isn't any need to know the exact GPS position - the country code or name is sufficient, and I am using this code:
String locale = context.getResources().getConfiguration().locale.getCountry(Locale.getDefault());
System.out.println("country = "+locale);
It gives me the code "US", but my device kept in India. Is there a way to find the device's current country code without using GPS or a network provider?
Because I am using a tablet.
Android Solutions
Solution 1 - Android
You can simply use this code,
TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
String countryCodeValue = tm.getNetworkCountryIso();
This will return 'US' if your current connected network is in the United States. This works without a SIM card even.
Solution 2 - Android
You shouldn't be passing anything in to getCountry()
. Remove Locale.getDefault()
:
String locale = context.getResources().getConfiguration().locale.getCountry();
Solution 3 - Android
Use the link http://ip-api.com/json. This will provide all the information as JSON. From this JSON content you can get the country easily. This site works using your current IP address. It automatically detects the IP address and sendback details.
This is what I got:
{
"as": "AS55410 C48 Okhla Industrial Estate, New Delhi-110020",
"city": "Kochi",
"country": "India",
"countryCode": "IN",
"isp": "Vodafone India",
"lat": 9.9667,
"lon": 76.2333,
"org": "Vodafone India",
"query": "123.63.81.162",
"region": "KL",
"regionName": "Kerala",
"status": "success",
"timezone": "Asia/Kolkata",
"zip": ""
}
N.B. - As this is a third-party API, do not use it as the primary solution. And also I am not sure whether it's free or not.
Solution 4 - Android
The checked answer has deprecated code. You need to implement this:
String locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
locale = context.getResources().getConfiguration().getLocales().get(0).getCountry();
} else {
locale = context.getResources().getConfiguration().locale.getCountry();
}
Solution 5 - Android
I have created a utility function (tested once on a device where I was getting an incorrect country code based on locale).
Reference: CountryCodePicker.java
fun getDetectedCountry(context: Context, defaultCountryIsoCode: String): String {
detectSIMCountry(context)?.let {
return it
}
detectNetworkCountry(context)?.let {
return it
}
detectLocaleCountry(context)?.let {
return it
}
return defaultCountryIsoCode
}
private fun detectSIMCountry(context: Context): String? {
try {
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
Log.d(TAG, "detectSIMCountry: ${telephonyManager.simCountryIso}")
return telephonyManager.simCountryIso
}
catch (e: Exception) {
e.printStackTrace()
}
return null
}
private fun detectNetworkCountry(context: Context): String? {
try {
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
Log.d(TAG, "detectNetworkCountry: ${telephonyManager.networkCountryIso}")
return telephonyManager.networkCountryIso
}
catch (e: Exception) {
e.printStackTrace()
}
return null
}
private fun detectLocaleCountry(context: Context): String? {
try {
val localeCountryISO = context.resources.configuration.locales[0].country
Log.d(TAG, "detectLocaleCountry: $localeCountryISO")
return localeCountryISO
}
catch (e: Exception) {
e.printStackTrace()
}
return null
}
Solution 6 - Android
Here is a complete example. It tries to get the country code from TelephonyManager (from SIM or CDMA devices), and if not available, tries to get it from the local configuration.
private static String getDeviceCountryCode(Context context) {
String countryCode;
// Try to get country code from TelephonyManager service
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if(tm != null) {
// Query first getSimCountryIso()
countryCode = tm.getSimCountryIso();
if (countryCode != null && countryCode.length() == 2)
return countryCode.toLowerCase();
if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
// Special case for CDMA Devices
countryCode = getCDMACountryIso();
}
else {
// For 3G devices (with SIM) query getNetworkCountryIso()
countryCode = tm.getNetworkCountryIso();
}
if (countryCode != null && countryCode.length() == 2)
return countryCode.toLowerCase();
}
// If network country not available (tablets maybe), get country code from Locale class
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
countryCode = context.getResources().getConfiguration().getLocales().get(0).getCountry();
}
else {
countryCode = context.getResources().getConfiguration().locale.getCountry();
}
if (countryCode != null && countryCode.length() == 2)
return countryCode.toLowerCase();
// General fallback to "us"
return "us";
}
@SuppressLint("PrivateApi")
private static String getCDMACountryIso() {
try {
// Try to get country code from SystemProperties private class
Class<?> systemProperties = Class.forName("android.os.SystemProperties");
Method get = systemProperties.getMethod("get", String.class);
// Get homeOperator that contain MCC + MNC
String homeOperator = ((String) get.invoke(systemProperties,
"ro.cdma.home.operator.numeric"));
// First three characters (MCC) from homeOperator represents the country code
int mcc = Integer.parseInt(homeOperator.substring(0, 3));
// Mapping just countries that actually use CDMA networks
switch (mcc) {
case 330: return "PR";
case 310: return "US";
case 311: return "US";
case 312: return "US";
case 316: return "US";
case 283: return "AM";
case 460: return "CN";
case 455: return "MO";
case 414: return "MM";
case 619: return "SL";
case 450: return "KR";
case 634: return "SD";
case 434: return "UZ";
case 232: return "AT";
case 204: return "NL";
case 262: return "DE";
case 247: return "LV";
case 255: return "UA";
}
}
catch (ClassNotFoundException ignored) {
}
catch (NoSuchMethodException ignored) {
}
catch (IllegalAccessException ignored) {
}
catch (InvocationTargetException ignored) {
}
catch (NullPointerException ignored) {
}
return null;
}
Also another idea is to try an API request like in this answer.
Solution 7 - Android
For some devices, if the default language is set different (an Indian can set English (US)) then
context.getResources().getConfiguration().locale.getDisplayCountry();
will give the wrong value. So this method is not reliable.
Also, getNetworkCountryIso() method of TelephonyManager will not work on devices which don't have SIM card (Wi-Fi tablets).
If a device doesn't have a SIM card then we can use the 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.
Use the below values in your constants file
(Constants.INDIA_TIME_ZONE_ID: "asia/calcutta", Constants.NETWORK_INDIA_CODE :"in")
And in your activity, add the following code:
private void checkCountry() {
TelephonyManager telMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (telMgr == null)
return;
int simState = telMgr.getSimState();
switch (simState) {
// If a SIM card is not available then the
// country is found using the 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 a SIM card is available then the telephony
// manager network country information is used
case TelephonyManager.SIM_STATE_READY:
if (telMgr != 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;
}
}
Solution 8 - Android
If you wish to get the country code without asking for any permission, you can choose a tricky way.
The method simply uses an API to get the country code, and there aren't any third-party libraries to depend on. We can create one for us.
Here I have used Google Cloud Functions to write an API and it is so effortless.
Step 1: Create a Google Cloud Account, and set up billing (the free tier is enough)
Step 2: Create a cloud function to get the geo location
Copy this basic function to the code editor of index.js:
const cors = require('cors')
function _geolocation(req, res) {
const data = {
country_code: req.headers["x-appengine-country"],
region: req.headers["x-appengine-region"],
city: req.headers["x-appengine-city"],
cityLatLong: req.headers["x-appengine-citylatlong"],
userIP: req.headers["x-appengine-user-ip"]
}
res.json(data)
};
exports.geolocation = (req, res) => {
const corsHandler = cors({ origin: true })
return corsHandler(req, res, function() {
return _geolocation(req, res);
});
};
Also we need to copy the package.json definition:
{
"name": "gfc-geolocation",
"version": "0.0.1",
"dependencies": {
"cors": "^2.8.4"
}
}
Step 3: finish, and get the URL similar to: "https://us-central1-geolocation-mods-sdde.cloudfunctions.net/geolocation"
Step 4: parse the JSON response and get the country code
The response will look like:
{
"country": "IN",
"region": "kl",
"city": "kochi",
"cityLatLong": "9.9312,76.2673",
"userIP": "xx.xx.xx.xx"
}
Thanks and credits go to the Medium article: Free IP-based Geolocation with Google Cloud Functions
Solution 9 - Android
There isn't any need to call any API. You can get the country code from your device where it is located. Just use this function:
fun getUserCountry(context: Context): String? {
try {
val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val simCountry = tm.simCountryIso
if (simCountry != null && simCountry.length == 2) { // SIM country code is available
return simCountry.toLowerCase(Locale.US)
}
else if (tm.phoneType != TelephonyManager.PHONE_TYPE_CDMA) { // Device is not 3G (would be unreliable)
val networkCountry = tm.networkCountryIso
if (networkCountry != null && networkCountry.length == 2) { // network country code is available
return networkCountry.toLowerCase(Locale.US)
}
}
}
catch (e: Exception) {
}
return null
}
Solution 10 - Android
> To get country code, we can also perform an HTTP call (Here, I use a third-party link, you can also buy the services to get extra information in JSON https://ipinfo.io/ ). Here, when we call the API service, they trace our IP and provide information accordingly.
<uses-permission android:name="android.permission.INTERNET" />
public String makeServiceCall() {
String response = null;
String reqUrl = "https://ipinfo.io/country";
try {
URL url = new URL(reqUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
// read the response
InputStream in = new BufferedInputStream(conn.getInputStream());
response = convertStreamToString(in);
} catch (MalformedURLException e) {
Log.e(TAG, "MalformedURLException: " + e.getMessage());
} catch (ProtocolException e) {
Log.e(TAG, "ProtocolException: " + e.getMessage());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.getMessage());
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
return response;
}