Onde estou? - Obter país

126

Um celular Android realmente sabe muito bem onde está - mas existe uma maneira de recuperar o país com algo como um código de país?

Não é necessário saber a posição exata do GPS - o país é suficiente

Primeiro pensei em usar o fuso horário, mas na verdade preciso de mais informações, pois isso faz diferença se o local for Nova York ou Lima.

O pano de fundo da pergunta: eu tenho um aplicativo que usa valores de temperatura e gostaria de definir a unidade padrão como Celsius ou Fahrenheit, dependendo se o local é norte-americano ou externo

DonGru
fonte
14
-1: você definitivamente não quer saber onde o usuário está . Você deseja conhecer a configuração de localidade do usuário. A resposta aceita responde exatamente isso, mas de outra forma é totalmente inapropriada aqui, porque a pesquisa indicará pessoas que realmente querem saber onde o usuário está.
Jan Janec

Respostas:

96

Isso obterá o código do país definido para o telefone (idioma do telefone, NÃO localização do usuário):

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

também pode substituir getCountry () por getISO3Country () para obter um código ISO de três letras para o país. Isso obterá o nome do país :

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

Isso parece mais fácil do que os outros métodos e depende das configurações de localização do telefone; portanto, se um usuário americano estiver no exterior, provavelmente ainda desejará Fahrenheit e isso funcionará :)

Nota dos editores : Esta solução não tem nada a ver com a localização do telefone. É constante. Quando você viaja para a Alemanha, a localidade NÃO muda. Em resumo: locale! = Local.

stealthcopter
fonte
83
Isso não funcionará em países para os quais o Android não possui um código de idioma. Por exemplo, na Suíça, é provável que o idioma seja definido como alemão ou francês. Este método fornecerá a você Alemanha ou França, não a Suíça. Melhor usar a abordagem LocationManager ou TelephonyManager.
MathewI
5
Não funciona bem. Eu tenho que diferenciar o país do usuário para todos os países da América Latina, todos os telefones de teste retornaram aos EUA, mesmo quando o telefone está em inglês ou espanhol.
Htafoya 26/06
21
Isso não responde ao título da pergunta. I pode ter o conjunto de telefone para Inglês e estar em qualquer lugar no mundo (minha língua nativa é não Inglês, mas eu faço tem meu telefone definido para (British) Inglês. É no entanto não responder a questão real , porque o usuário quer unidades nos se ele é USAian, onde quer que ele realmente passa a ser no momento.
Jan Hudec
2
Não é a solução viável para obter o nome do país
Aamirkhan
2
É uma boa resposta em geral, mas retorna apenas o país da localidade, não o 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;
}
caw
fonte
14
Funciona apenas em telefones ou outros dispositivos com um cartão SIM. Ao usar um tablet WIFI, você será nulo. Portanto, sua utilidade é limitada.
Bram
1
@Ram Isso é tudo que você pode obter. Portanto, é realmente bastante útil, o problema é apenas que você não possui outras fontes de dados confiáveis ​​se tiver um tablet somente para WiFi.
caw
11
Observe que, se um usuário dos EUA estiver de férias na França, getSimCountryIsodirá use getNetworkCountryIsodirá #fr
Tim #
1
Você pode alterar a toLowerCase()chamada para toUpperCase().
toobsco42
Preciso de código, não código Nome; Como nos EUA +1, não nos EUA
Shihab Uddin
67

Use este link http://ip-api.com/json , isso fornecerá todas as informações como json. A partir deste json, você pode obter o país facilmente. Este site funciona usando seu IP atual, ele detecta automaticamente os detalhes de IP e de retorno.

Documentos http://ip-api.com/docs/api:json Espero que ajude.

Exemplo 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 é uma solução de terceiros, use somente se outras pessoas não funcionarem.

shine_joseph
fonte
1
Não sei se é exatamente isso que o OP queria, mas realmente gosto da resposta.
precisa saber é o seguinte
1
Como a resposta, já que isso também funcionaria em tablets (sem sim). A solução de telefonia funciona apenas em telefones com sims ativos. Os telefones dual sim são outro cenário problemático, pois podem ser de diferentes países. Portanto, provavelmente faz mais sentido confiar na solução IP mencionada acima.
Manish Kataria
1
Mesmo que, como você apontou, essa não seja a melhor abordagem para o problema, ele realmente fornece uma solução válida. +1 de mim e obrigado!
Matteo
1
É sempre ruim usar terceiros, você nunca pode saber o quão estável existe. por exemplo - Parse.
Nativ
1
mas tem limitações. seu sistema banirá automaticamente qualquer endereço IP que faça mais de 150 solicitações por minuto.
Ram Mandal
62

Na verdade, acabei de descobrir que há mais uma maneira de obter um código de país, usando o método getSimCountryIso () de TelephoneManager:

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

Por ser o código sim, ele também não deve ser alterado quando se viaja para outros países.

DonGru
fonte
43
Isso pode não funcionar quando o dispositivo não tiver cartão SIM, por exemplo tablet
thomasdao
38

Primeiro, obtenha o LocationManager. Então ligue LocationManager.getLastKnownPosition. Em seguida, crie um GeoCoder e ligue GeoCoder.getFromLocation. Faça isso em um thread separado !! Isso fornecerá uma lista de Addressobjetos. Ligue Address.getCountryNamee você conseguiu.

Lembre-se de que a última posição conhecida pode ser um pouco obsoleta; portanto, se o usuário cruzar a fronteira, talvez você não saiba disso por um tempo.

EboMike
fonte
Para esta aplicação, não seria desejável alterar a unidade padrão ao mover países, pois a preferência do usuário por Celsius ou Fahrenheit não mudará conforme ele se aventurar em novos terrenos.
Stealthcopter
Preciso saber em qual país meu usuário está para uma chamada de API. A chamada retorna locais na área circundante. Portanto, estou interessado apenas no código do país real no qual o usuário está agora. Apenas para dar uma ideia de como seria possível a abordagem acima.
vinzenzweber
Isso é altamente subestimado, mas o fato é que essa é a solução mais confiável. A maioria dos ans acima não funciona para a rede wifi. O fornecido por @shine_joseph está bonito, mas a API fornecida não é para uso comercial.
Gem
Use com cuidado. Seu dispositivo fornecerá um local mesmo que não esteja em rede, mas o geocoder normalmente retornará um nulo para a matriz de endereços se o wifi estiver desativado, gerando uma exceção que você precisará tentar / capturar.
Mike Critchley
17

Aqui está uma solução completa baseada no LocationManager e como substitutos dos locais do TelephonyManager e do provedor de rede. Eu usei a resposta acima de @Marco W. na parte de fallback (ótima resposta como ela mesma!).

Nota: o código contém PreferencesManager, esta é uma classe auxiliar que salva e carrega dados de SharedPrefrences. Estou usando-o para salvar o país em S "P, só estou conseguindo o país se estiver vazio. Para o meu produto, eu realmente não me importo com todos os casos extremos (o usuário viaja para o exterior e assim por diante).

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
fonte
4
Desculpe, mas sua resposta realmente precisa ser reescrita. O código contém muitos códigos inutilizáveis.
ichalos
@ichalos ou você não entendeu a lógica. Por favor me dê um exemplo de código não utilizado neste trecho
Nativ
2
É claro que eu não entendo a lógica e, por favor, não leve a mal. Eu acredito que o PipplApp, o PrefernecesManager, etc, poderiam ter sido removidos e a solução oferecida poderia ser um pouco mais clara para os leitores.
Ichalos #
@ichalos sobre o PiplApp Concordo 100% com você, eu apenas removê-lo
Nativ
country = addresses.get(0).getCountryName();em seu código, o que vai colocar nome completo do país na variável country. Ao mesmo tempo, no método, getCountryBasedOnSimCardOrNetwork(Context context)você está retornando o código ISO do país. Eu acredito que você queria escrever country = addresses.get(0).getCountryCode();:-)
Firzen
15

Você pode usar getNetworkCountryIso()deTelephonyManager para obter o país em que o telefone está atualmente (embora aparentemente isso não seja confiável nas redes CDMA).

Dave Webb
fonte
isso parece bastante direto e funcionou bem no emulador em primeira instância - você poderia explicar por que não é confiável nas redes CDMA?
DonGru
ah ok, eu consegui-lo - porque a documentação diz isso :)
DonGru
Por causa do aplicativo, essa não seria a melhor solução, pois quando um usuário viaja para o exterior, sua unidade padrão mudaria. Seria mais sensato ter uma unidade estática padrão com base nas configurações de localidade dos telefones, que obviamente podem ser alteradas em uma configuração em algum lugar.
Stealthcopter
5
String locale = context.getResources().getConfiguration().locale.getCountry(); 

Está obsoleto. Use isso:

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
fonte
1
Requer permissão e que país mostra?
precisa saber é o seguinte
4

Para alguns dispositivos, se o idioma padrão estiver definido diferente (um indiano pode definir inglês (EUA)),

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

dará valor errado. Portanto, este método não é confiável

Além disso, o método getNetworkCountryIso () do TelephonyManager não funcionará em dispositivos que não possuem cartão SIM (tablets WIFI).

Se um dispositivo não tiver SIM, podemos usar o fuso horário para obter o país. Para países como a Índia, esse método funcionará

o código de exemplo usado para verificar se o país é Índia ou não (ID de fuso horário: asia / calcutá)

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;

    }
}
Margem
fonte
1
boa idéia, mas o fuso horário não é um recurso específico do país, você sabe disso, certo? Para cada 15 graus de meridianos, há outro fuso horário etc.
Gunhan 13/02/19
3

Usando GPS com latitude e longitude, podemos obter o código do país.

Se usarmos telefonia, não funcionará se não estivermos usando o cartão SIM e no local, com base no idioma, mostrará o código do país de maneira incorreta.

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

}

Registro:

E / countryCode: IN

Edit: Use Fused Location Provider para obter atualizações de latitude e longitude para obter melhores resultados.

Steve
fonte
O que é gpsParty ()? Ele não está definido
Nikola C
-2

Eu usei GEOIP db e criei uma função. Você pode consumir esse link diretamente 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"}]}

você pode baixar o arquivo autoload.php e .mmdb em 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
fonte