Где я? - Получить страну

126

Мобильный телефон Android действительно хорошо знает, где он находится, но есть ли способ получить страну по чему-то вроде кода страны?

Нет необходимости знать точное местоположение по GPS - достаточно страны

Сначала я подумал об использовании часового пояса, но на самом деле мне нужно больше информации, так как это имеет значение, если местоположение - Нью-Йорк или Лима.

Предыстория вопроса: у меня есть приложение, которое использует значения температуры, и я хотел бы установить единицу измерения по умолчанию: Цельсий или Фаренгейт, в зависимости от того, находится ли местоположение в США или за его пределами.

DonGru
источник
14
-1: Вы определенно не хотите знать , где пользователь находится . Вы хотите знать настройки локали пользователя. Принятый ответ отвечает именно на это, но в остальном он здесь совершенно неуместен, потому что поиск направит сюда людей, которые действительно хотят знать, где на самом деле находится пользователь.
Ян Худек

Ответы:

96

В результате будет установлен код страны для телефона (язык телефона, а НЕ местоположение пользователя):

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

также можно заменить getCountry () на getISO3Country (), чтобы получить трехбуквенный код ISO для страны. Это получит название страны :

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

Это кажется проще, чем другие методы, и полагаться на настройки локализации на телефоне, поэтому, если пользователь из США находится за границей, он, вероятно, все еще хочет Фаренгейта, и это сработает :)

Примечание редакции : это решение не имеет ничего общего с расположением телефона. Это постоянно. Когда вы поедете в Германию, регион НЕ изменится. Вкратце: locale! = Location.

stealthcopter
источник
83
Это не будет работать в странах, для которых Android не имеет локали. Например, в Швейцарии язык, скорее всего, будет установлен на немецкий или французский. Этот метод даст вам Германию или Францию, а не Швейцарию. Лучше использовать подход LocationManager или TelephonyManager.
MathewI
5
Не работает. Я должен различать страну пользователя для всех стран Латинской Америки, все тестовые телефоны вернулись в США, даже если телефон находится на английском или испанском языке.
htafoya
21
Это не отвечает на заголовок вопроса. У меня может быть телефон, настроенный на английский язык, и я могу находиться в любой точке мира (мой родной язык не английский, но у меня есть телефон, настроенный на (британский) английский). Однако он отвечает на фактический вопрос , потому что пользователю нужны единицы США, если он американец, где бы он ни находился в данный момент.
Ян Худек
2
Не подходящее решение для получения названия страны
Аамирхан
2
В целом это хороший ответ, но он возвращает только страну региона, а не фактическую страну.
dst
138
/**
 * 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;
}
каркать
источник
14
Работает только на телефонах или других устройствах с сим-картой. При использовании на планшете WIFI вы получите null. Так что его полезность ограничена.
Брэм
1
@Bram Это все, что ты можешь получить. Так что на самом деле это довольно полезно, проблема только в том, что у вас нет других надежных источников данных, если у вас есть планшет только с Wi-Fi.
caw
11
Обратите внимание: если пользователь из США находится в отпуске во Франции, getSimCountryIsoон скажет usи getNetworkCountryIsoскажетfr
Тим
1
Вы можете изменить toLowerCase()вызов на toUpperCase().
toobsco42
Мне нужен код, а не код Имя; Как для США +1 не США
Шихаб Уддин
67

Используйте эту ссылку http://ip-api.com/json , она предоставит всю информацию в формате json. Из этого json вы можете легко получить страну. Этот сайт работает с вашим текущим IP, он автоматически определяет IP и детали обратной отправки.

Документы http://ip-api.com/docs/api:json Надеюсь, это поможет.

Пример 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"
}

Примечание . Поскольку это решение стороннего производителя, используйте его только в том случае, если другие не работают.

shine_joseph
источник
1
Я не уверен, что это именно то, что хотел OP, но мне все равно нравится ответ.
JohnnyLambada
1
Как и ответ, так как это будет работать и на планшетах (без SIM-карты). Решение для телефонии работает только на телефонах с активными симками. Телефоны с двумя SIM-картами - еще один сценарий проблемы, поскольку они могут быть из разных стран. Так что, вероятно, имеет смысл полагаться на IP-решение, упомянутое выше.
Маниш Катария,
1
Хотя, как вы отметили, это может быть не лучший подход к проблеме, на самом деле он обеспечивает правильное решение. +1 от меня и спасибо!
Маттео
1
Использовать третьи стороны всегда плохо, никогда не знаешь, насколько они стабильны. например - Разбор.
Натив
1
но у него есть ограничения. их система автоматически запрещает любые IP-адреса, выполняющие более 150 запросов в минуту.
Ram Mandal
62

На самом деле я только что узнал, что есть еще один способ получить код страны, используя метод getSimCountryIso () TelephoneManager:

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

Поскольку это сим-код, он также не должен изменяться при поездках в другие страны.

DonGru
источник
43
Это может не сработать, если в устройстве нет SIM-карты, например в планшете
thomasdao
38

Сначала получите LocationManager. Тогда позвони LocationManager.getLastKnownPosition. Затем создайте GeoCoder и позвоните GeoCoder.getFromLocation. Сделайте это в отдельной ветке !! Это даст вам список Addressобъектов. Звоните, Address.getCountryNameи все готово.

Имейте в виду, что последняя известная позиция может быть немного устаревшей, поэтому, если пользователь только что пересек границу, вы можете не знать об этом некоторое время.

EboMike
источник
Для этого приложения изменение единицы измерения по умолчанию было бы нежелательным при перемещении страны, поскольку предпочтение пользователя в градусах Цельсия или Фаренгейта не изменится, когда он отправится в новые страны.
Stealthcopter
Мне нужно знать, в какой стране находится мой пользователь для вызова API. Вызов возвращает местоположения в окрестностях. Поэтому меня интересует только реальный код страны, в которой сейчас находится пользователь. Просто чтобы понять, для чего может понадобиться описанный выше подход.
vinzenzweber 07
Это очень недооценено, но факт в том, что это самое надежное решение. Большинство ответов выше не работают в сети Wi-Fi. Тот, который предоставлен @shine_joseph, выглядит хорошо, но предоставленный api не предназначен для коммерческого использования.
Gem
Используйте с осторожностью. Ваше устройство предоставит вам местоположение, даже если оно не подключено к сети, но геокодер обычно возвращает ноль для массива адресов, если Wi-Fi отключен, вызывая исключение, которое вам нужно будет попробовать / поймать.
Майк Кричли
17

Вот полное решение, основанное на LocationManager и в качестве запасных вариантов TelephonyManager и местоположения сетевого провайдера. Я использовал приведенный выше ответ от @Marco W. для резервной части (отличный ответ сам по себе!).

Примечание: код содержит PreferencesManager, это вспомогательный класс, который сохраняет и загружает данные из SharedPrefrences. Я использую его, чтобы сохранить страну в S'P, я получаю страну только в том случае, если она пуста. Что касается моего продукта, меня не волнуют все крайние случаи (пользователь уезжает за границу и так далее).

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;
}
Натив
источник
4
Извините, но ваш ответ действительно нужно переписать. Код содержит много неиспользуемого кода.
ichalos
@ichalos или вы не поняли логики. Пожалуйста , дайте мне 1 пример неиспользуемого кода в этом фрагменте
Натив
2
Конечно, я понимаю логику и, пожалуйста, не пойму неправильно. Я считаю, что PipplApp, PrefernecesManager и т. Д. Могли быть удалены, и предлагаемое решение могло быть немного более понятным для читателей.
ichalos
@ichalos насчет PiplApp Я согласен с вами на 100%, я просто удалил его
Натив
Есть country = addresses.get(0).getCountryName();в вашем коде, который поместит в переменную полное название страны country. В то же время в методе getCountryBasedOnSimCardOrNetwork(Context context)вы возвращаете ISO-код страны. Я считаю, что вы хотели написать country = addresses.get(0).getCountryCode();:-)
Фирзен
15

Вы можете использовать getNetworkCountryIso()from,TelephonyManager чтобы получить страну, в которой в настоящее время находится телефон (хотя, очевидно, это ненадежно в сетях CDMA).

Дэйв Уэбб
источник
это звучит довольно прямолинейно и хорошо работает в эмуляторе в первую очередь - не могли бы вы просто объяснить, почему он ненадежен в сетях CDMA?
DonGru 07
ах, ладно, я понял - потому что так написано в документации :)
DonGru 07
Из-за приложения это не будет лучшим решением, поскольку, когда пользователь выезжает за границу, его единица измерения по умолчанию изменится. Было бы разумнее иметь статическую единицу измерения по умолчанию на основе настроек локали телефона, которые затем, конечно, можно где-нибудь изменить.
Stealthcopter
5
String locale = context.getResources().getConfiguration().locale.getCountry(); 

Устарела. Используйте вместо этого:

Locale locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    locale = context.getResources().getConfiguration().getLocales().get(0);
} else {
    locale = context.getResources().getConfiguration().locale;
}
4gus71n
источник
1
Требуется ли разрешение и в какой стране указано?
CoolMind
4

Для некоторых устройств, если установлен другой язык по умолчанию (индиец может установить английский (США)), тогда

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

даст неправильное значение. Таким образом, этот метод ненадежен

Кроме того, метод getNetworkCountryIso () в TelephonyManager не будет работать на устройствах, у которых нет SIM-карты (планшеты WIFI).

Если на устройстве нет SIM-карты, мы можем использовать часовой пояс, чтобы получить страну. Для таких стран, как Индия, этот метод подойдет

образец кода, используемый для проверки, является ли страна Индия или нет (идентификатор часового пояса: 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;

    }
}
Прибыль
источник
1
Хорошая идея, но часовой пояс не зависит от страны, вы же знаете? Для каждых 15 градусов меридианов существует другой часовой пояс и т. Д.
Гунхан,
3

Используя GPS с широтой и долготой, мы можем получить код страны.

Если мы используем телефонию, это не сработает, если мы не используем сим-карту, и в локали, в зависимости от языка, код страны отображается неправильно.

MainActivity.java:

    GPSTracker gpsTrack;
    public static double latitude = 0;
    public static double longitude = 0;

    gpsTrack = new GPSTracker(TabHomeActivity.this);

        if (gpsTrack.canGetLocation()) {
            latitude = gpsParty.getLatitude();
            longitude = gpsParty.getLongitude();

            Log.e("GPSLat", "" + latitude);
            Log.e("GPSLong", "" + longitude);

        } else {
            gpsTrack.showSettingsAlert();

            Log.e("ShowAlert", "ShowAlert");

        }

        countryCode = getAddress(TabHomeActivity.this, latitude, longitude);

        Log.e("countryCode", ""+countryCode);

   public String getAddress(Context ctx, double latitude, double longitude) {
    String region_code = null;
    try {
        Geocoder geocoder = new Geocoder(ctx, Locale.getDefault());
        List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);
        if (addresses.size() > 0) {
            Address address = addresses.get(0);


            region_code = address.getCountryCode();


        }
    } catch (IOException e) {
        Log.e("tag", e.getMessage());
    }

    return region_code;
}

GPSTracker.java:

import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;

public class GPSTracker extends Service implements LocationListener {

    private final Context mContext;

    // flag for GPS status
    boolean isGPSEnabled = false;

    // flag for network status
    boolean isNetworkEnabled = false;

    // flag for GPS status
    boolean canGetLocation = false;

    Location location; // location
    double latitude; // latitude
    double longitude; // longitude

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

    // Declaring a Location Manager
    protected LocationManager locationManager;

    public GPSTracker(Context context) {
        this.mContext = context;
        getLocation();
    }

    public Location getLocation() {
        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(LOCATION_SERVICE);

            // getting GPS status
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            isNetworkEnabled = locationManager
                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // no network provider is enabled
            } else {
                this.canGetLocation = true;
                // First get location from Network Provider
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                    Log.d("Network", "Network");
                    if (locationManager != null) {
                        location = locationManager
                                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(
                                LocationManager.GPS_PROVIDER,
                                MIN_TIME_BW_UPDATES,
                                MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        Log.d("GPS Enabled", "GPS Enabled");
                        if (locationManager != null) {
                            location = locationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return location;
    }

    /**
     * Stop using GPS listener
     * Calling this function will stop using GPS in your app
     * */
    public void stopUsingGPS(){
        if(locationManager != null){
            locationManager.removeUpdates(GPSTracker.this);
        }
    }

    /**
     * Function to get latitude
     * */
    public double getLatitude(){
        if(location != null){
            latitude = location.getLatitude();
        }

        // return latitude
        return latitude;
    }

    /**
     * Function to get longitude
     * */
    public double getLongitude(){
        if(location != null){
            longitude = location.getLongitude();
        }

        // return longitude
        return longitude;
    }

    /**
     * Function to check GPS/wifi enabled
     * @return boolean
     * */
    public boolean canGetLocation() {
        return this.canGetLocation;
    }



    public void showSettingsAlert() {
        final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setMessage("Your GPS seems to be disabled, do you want to enable it?")
                .setCancelable(false)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(@SuppressWarnings("unused") final DialogInterface dialog, @SuppressWarnings("unused") final int id) {
                        mContext.startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                    }
                })
                .setNegativeButton("No", new DialogInterface.OnClickListener() {
                    public void onClick(final DialogInterface dialog, @SuppressWarnings("unused") final int id) {
                        dialog.cancel();
                    }
                });
        final AlertDialog alert = builder.create();
        alert.show();
    }

    @Override
    public void onLocationChanged(Location location) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

}

Журнал:

E / countryCode: IN

Изменить: используйте Fused Location Provider, чтобы получить обновление широты и долготы для лучшего результата.

Стив
источник
Что такое gpsParty ()? Это не определено
Nikola C
-2

Я использовал GEOIP db и создал функцию. Вы можете использовать эту ссылку напрямую http://jamhubsoftware.com/geoip/getcountry.php

{"country":["India"],"isoCode":["IN"],"names":[{"de":"Indien","en":"India","es":"India","fr":"Inde","ja":"\u30a4\u30f3\u30c9","pt-BR":"\u00cdndia","ru":"\u0418\u043d\u0434\u0438\u044f","zh-CN":"\u5370\u5ea6"}]}

вы можете скачать файлы autoload.php и .mmdb с https://dev.maxmind.com/geoip/geoip2/geolite2/

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$ip_address = $_SERVER['REMOTE_ADDR'];
//$ip_address = '3.255.255.255';

require_once 'vendor/autoload.php';

use GeoIp2\Database\Reader;

// This creates the Reader object, which should be reused across
// lookups.
$reader = new Reader('/var/www/html/geoip/GeoLite2-City.mmdb');

// Replace "city" with the appropriate method for your database, e.g.,
// "country".
$record = $reader->city($ip_address);

//print($record->country->isoCode . "\n"); // 'US'
//print($record->country->name . "\n"); // 'United States'
$rows['country'][] = $record->country->name;
$rows['isoCode'][] = $record->country->isoCode;
$rows['names'][] = $record->country->names;
print json_encode($rows);
//print($record->country->names['zh-CN'] . "\n"); // '美国'
//
//print($record->mostSpecificSubdivision->name . "\n"); // 'Minnesota'
//print($record->mostSpecificSubdivision->isoCode . "\n"); // 'MN'
//
//print($record->city->name . "\n"); // 'Minneapolis'
//
//print($record->postal->code . "\n"); // '55455'
//
//print($record->location->latitude . "\n"); // 44.9733
//print($record->location->longitude . "\n"); // -93.2323
?>
Arun-р
источник