¿Dónde estoy? - Obtener país

126

Un móvil Android realmente sabe muy bien dónde está, pero ¿hay alguna forma de recuperar el país mediante un código de país?

No es necesario conocer la posición exacta del GPS: el país es suficiente

Primero pensé en usar la zona horaria, pero en realidad necesito más información que eso, ya que hace una diferencia si la ubicación es Nueva York o Lima.

El trasfondo de la pregunta: tengo una aplicación que utiliza valores de temperatura y me gustaría establecer la unidad predeterminada en grados Celsius o Fahrenheit, dependiendo de si la ubicación es de EE. UU.

DonGru
fuente
14
-1: Usted definitivamente no quieren saber dónde está el usuario es . Desea conocer la configuración regional del usuario. La respuesta aceptada responde exactamente eso, pero por lo demás es totalmente inapropiado aquí, porque la búsqueda apuntará a las personas que realmente quieren saber dónde está realmente el usuario.
Jan Hudec

Respuestas:

96

Esto obtendrá el código de país establecido para el teléfono (idioma del teléfono, NO ubicación del usuario):

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

también puede reemplazar getCountry () con getISO3Country () para obtener un código ISO de 3 letras para el país. Esto obtendrá el nombre del país :

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

Esto parece más fácil que los otros métodos y depende de la configuración de localización en el teléfono, por lo que si un usuario de EE. UU. Está en el extranjero, probablemente todavía quieran Fahrenheit y esto funcionará :)

Nota del editor : esta solución no tiene nada que ver con la ubicación del teléfono. Es constante. Cuando viaje a Alemania, la ubicación NO cambiará. En resumen: locale! = Ubicación.

Stealthcopter
fuente
83
Esto no funcionará en países para los que Android no tiene una configuración regional. Por ejemplo, en Suiza, es probable que el idioma esté configurado en alemán o francés. Este método te dará Alemania o Francia, no Suiza. Es mejor usar el enfoque LocationManager o TelephonyManager.
Mathewi
55
No funciona bien Tengo que diferenciar el país de usuario para todos los países latinoamericanos, todos los teléfonos de prueba han regresado a EE. UU. Incluso cuando el teléfono está en inglés o español.
htafoya
21
Esto no responde el título de la pregunta. Puede que tenga el sistema de teléfono de Inglés y estar en cualquier lugar en el mundo (mi lengua materna es no Inglés, pero hacer que mi sistema de teléfono a (británico) Inglés. No obstante, no responde a la pregunta real , porque el usuario quiere unidades de Estados Unidos si es estadounidense, donde sea que esté en este momento.
Jan Hudec
2
No es la solución factible para obtener el nombre del país
Aamirkhan
2
Es una buena respuesta en general, pero solo devuelve el país de la localidad, no el país real.
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;
}
graznar
fuente
14
Solo funciona en teléfonos u otros dispositivos con una tarjeta SIM. Cuando se utiliza en una tableta WIFI, obtendrá nulo. Entonces su utilidad es limitada.
Bram
1
@Bram Esto es todo lo que puedes conseguir. Por lo tanto, en realidad es bastante útil, el problema es solo que no tiene otras fuentes de datos confiables si tiene una tableta solo con WiFi.
caw
11
Tenga en cuenta que si un usuario de EE. UU. Está de vacaciones en Francia, getSimCountryIsodirá usy getNetworkCountryIsodiráfr
Tim
1
Es posible que desee cambiar la toLowerCase()llamada a toUpperCase().
toobsco42
Necesito Código, no Código Nombre; Al igual que para los EE.UU. 1 no los EE.UU.
Shihab Uddin
67

Utilice este enlace http://ip-api.com/json , esto proporcionará toda la información como json. Desde este json puedes obtener el país fácilmente. Este sitio funciona utilizando su IP actual, detecta automáticamente la IP y los detalles de devolución.

Docs http://ip-api.com/docs/api:json Espero que ayude.

Ejemplo 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"
}

nota : Como esta es una solución de terceros, solo úsela si otras no funcionaron.

brillo_joseph
fuente
1
No estoy seguro de que esto sea exactamente lo que quería el OP, pero realmente me gusta la respuesta de todos modos.
JohnnyLambada
1
Me gusta la respuesta ya que esto también funcionaría en tabletas (sin sim). La solución de telefonía funciona solo en teléfonos con sims activos. Los teléfonos con doble sim es otro escenario problemático, ya que pueden ser de diferentes países. Entonces, probablemente tenga más sentido confiar en la solución IP mencionada anteriormente.
Manish Kataria
1
Aunque, como usted señaló, este puede no ser el mejor enfoque para el problema, en realidad proporciona una solución válida. ¡+1 de mi parte y gracias!
Matteo
1
Siempre es malo usar terceros, nunca se puede saber qué tan estables son. por ejemplo, Parse.
Nativ
1
Pero tiene limitaciones. su sistema prohibirá automáticamente cualquier dirección IP que realice más de 150 solicitudes por minuto.
Ram Mandal
62

En realidad, acabo de descubrir que hay incluso una forma más de obtener un código de país, utilizando el método getSimCountryIso () de TelephoneManager:

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

Dado que es el código sim, tampoco debería cambiar cuando se viaja a otros países.

DonGru
fuente
43
Esto podría no funcionar cuando el dispositivo no tiene tarjeta SIM, por ejemplo tableta
thomasdao
38

Primero, obtenga el LocationManager. Entonces llama LocationManager.getLastKnownPosition. Luego cree un GeoCoder y llame GeoCoder.getFromLocation. ¡Esto está en un hilo separado! Esto te dará una lista de Addressobjetos. Llama Address.getCountryNamey lo tienes.

Tenga en cuenta que la última posición conocida puede ser un poco obsoleta, por lo que si el usuario acaba de cruzar el borde, es posible que no lo sepa por un tiempo.

EboMike
fuente
Para esta aplicación, no sería deseable cambiar la unidad predeterminada al mover países, ya que la preferencia del usuario de Celsius o Fahrenheit no cambiará a medida que se aventura a nuevas tierras.
stealthcopter
Necesito saber en qué país está mi usuario para una llamada API. La llamada devuelve ubicaciones en el área circundante. Por lo tanto, solo estoy interesado en el código de país real en el que se encuentra el usuario en este momento. Solo para dar una idea de para qué podría ser necesario el enfoque anterior.
vinzenzweber
Esto está altamente subestimado, pero el hecho es que esta es la solución más confiable. La mayoría de las respuestas anteriores no funcionan para la red wifi. El proporcionado por @shine_joseph se ve bien, pero la API proporcionada no es para uso comercial.
Gema
Usar con precaución. Su dispositivo le proporcionará una ubicación incluso si no está en red, pero el Geocoder generalmente devolverá un valor nulo para la matriz de direcciones si el wifi está apagado, lo que genera una excepción que deberá probar / detectar.
Mike Critchley
17

Aquí hay una solución completa basada en el LocationManager y, como respaldo, las ubicaciones de TelephonyManager y del proveedor de red. Utilicé la respuesta anterior de @Marco W. para la parte alternativa (¡gran respuesta en sí misma!).

Nota: el código contiene PreferencesManager, esta es una clase auxiliar que guarda y carga datos de SharedPrefrences. Lo estoy usando para guardar el país en S "P, solo obtengo el país si está vacío. Para mi producto, realmente no me importan todos los casos extremos (el usuario viaja al extranjero, etc.).

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;
}
Nativ
fuente
44
Lo sentimos, pero tu respuesta realmente necesita ser reescrita. El código contiene muchos códigos inutilizables.
ichalos
@ichalos o no entendiste la lógica. Por favor, dame 1 ejemplo de código no utilizado en este fragmento
Nativ
2
Por supuesto, no entendí la lógica y, por favor, no lo tomes a mal. Mencioné que PipplApp, PrefernecesManager, etc. podrían haberse eliminado y la solución ofrecida podría ser un poco más clara para los lectores.
ichalos
@ichalos sobre el PiplApp Estoy 100% de acuerdo con usted, solo lo
elimino
Hay country = addresses.get(0).getCountryName();en su código, que pondrá el nombre completo del país en variable country. Al mismo tiempo, en el método getCountryBasedOnSimCardOrNetwork(Context context), está devolviendo el código ISO del país. Creo que querías escribir country = addresses.get(0).getCountryCode();:-)
Firzen
15

Puede usar getNetworkCountryIso()desdeTelephonyManager para obtener el país en el que se encuentra actualmente el teléfono (aunque aparentemente esto no es confiable en las redes CDMA).

Dave Webb
fuente
eso suena bastante sencillo y funcionó bien en el emulador en primera instancia, ¿podría explicar por qué no es confiable en las redes CDMA?
DonGru
ah bien, lo tengo - porque la documentación lo dice :)
DonGru
Debido a la aplicación, esta no sería la mejor solución, ya que cuando un usuario viaja al extranjero, su unidad predeterminada cambiaría. Tendría más sentido tener una unidad predeterminada estática basada en la configuración regional del teléfono, que luego, por supuesto, se puede cambiar en algún lugar.
stealthcopter
5
String locale = context.getResources().getConfiguration().locale.getCountry(); 

Es obsoleto. Use esto en su lugar:

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
fuente
1
¿Requiere permiso y qué país muestra?
CoolMind
4

Para algunos dispositivos, si el idioma predeterminado es diferente (un indio puede configurar el inglés (EE. UU.)), Entonces

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

dará un valor incorrecto. Así que este método no es confiable

Además, el método getNetworkCountryIso () de TelephonyManager no funcionará en dispositivos que no tengan tarjeta SIM (tabletas WIFI).

Si un dispositivo no tiene SIM, podemos usar la zona horaria para obtener el país. Para países como India, este método funcionará

el código de muestra utilizado para verificar si el país es India o no (Id. de zona horaria: 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;

    }
}
Margen
fuente
1
Buena idea, pero la zona horaria no es una característica específica del país, ¿lo sabes? Por cada 15 grados de meridianos hay otra zona horaria, etc.
Gunhan
3

Usando GPS con latitud y longitud, podemos obtener el código del país.

Si usamos la telefonía, no funcionará si no estamos usando la tarjeta SIM y en la configuración regional, según el idioma, muestra el código del país de manera incorrecta.

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;
    }

}

Iniciar sesión:

E / countryCode: IN

Editar: use el proveedor de ubicación fusionada para obtener la actualización de latitud y longitud para un mejor resultado.

Steve
fuente
¿Qué es gpsParty ()? No está definido
Nikola C
-2

Usé GEOIP db y creé una función. Puede consumir este enlace directamente 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"}]}

puede descargar el archivo autoload.php y .mmdb desde 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-r
fuente