Вопрос

In an application that I'm currently working on there is a huge need to determine user country as fast as possible and as reliable as possible. I have decided to rely on three ways for finding user country; each one has its advantages and disadvantages:

  1. Android inner methods to get the SIM country.
  2. GeoCoding.
  3. IP to Location API.

Here are the three pieces of code:

1. Android inner methods to get the SIM country:

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
             CupsLog.d(TAG, "getUserCountry, simCountry: " + simCountry.toLowerCase(Locale.US));
            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
                 CupsLog.d(TAG, "getUserCountry, networkCountry: " + networkCountry.toLowerCase(Locale.US));
                return networkCountry.toLowerCase(Locale.US);

            }
        }
    }
    catch (Exception e) { }
    return null;
}

2. GeoCoding:

  public static void getCountryCode(final Location location, final Context context) {
   CupsLog.d(TAG, "getCountryCode");

   AsyncTask<Void, Void, String> countryCodeTask = new AsyncTask<Void, Void, String>() {

       final float latitude = (float) location.getLatitude();
       final float longitude = (float) location.getLongitude();
       List<Address> addresses = null;
       Geocoder gcd = new Geocoder(context);
       String code = null;

       @Override
       protected String doInBackground(Void... params) {
           CupsLog.d(TAG, "doInBackground");
           try {
               addresses = gcd.getFromLocation(latitude, longitude, 10);
               for (int i=0; i < addresses.size(); i++)
               {
                   if (addresses.get(i) != null && addresses.get(i).getCountryCode() != null)
                   {
                       code = addresses.get(i).getCountryCode();
                   }
               } 
           } catch (IOException e) {
               CupsLog.d(TAG, "IOException" + e);
           }
           return code;
       }

       @Override
       protected void onPostExecute(String code) 
       {
           if (code != null)
           {
               CupsLog.d(TAG, "getCountryCode :" + code.toLowerCase());
               UserLocation.instance.setCountryCode(code.toLowerCase());
               CookieUtil.formatLangueageAndLocationCookie();
               CookieUtil.instance.instantateCookieUtil(context);
           }
       }
   };

   if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
       CupsLog.d(TAG, "countryCodeTask.execute();");
       countryCodeTask.execute();
   } else {
       CupsLog.d(TAG, "countryCodeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);");
       countryCodeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
   }
}

3. IP to Location API:

 private void getUserCountryByIp() {

        AsyncHttpClient client = new AsyncHttpClient();
        client.setCookieStore(CookieUtil.instance.getPersistentCookieStore());

        String userCountryApi = "http://ip-api.com/json";
        CupsLog.i(TAG, "country uri: " + userCountryApi);


        client.get(userCountryApi, new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(JSONObject orderResponseJSON) {
                CupsLog.i(TAG, "onSuccess(JSONObject res)");
                try 
                {
                    CupsLog.i(TAG, "JsonResponse: "+ orderResponseJSON.toString(3));
                    String tempString = orderResponseJSON.getString("countryCode");
                    if (tempString != null)
                    {
                        //countryCodeFromIpApi = tempString.toLowerCase();
                        UserLocation.instance.setCountryCode(tempString.toLowerCase());
                        CookieUtil.formatLangueageAndLocationCookie();
                        CookieUtil.instance.instantateCookieUtil(LoginActivity.this);
                        isGotCountryFromIp = true;
                    }
                } catch (JSONException e) {
                    CupsLog.i(TAG, "JSONException: " + e);
                }
            }

            @Override
            public void onFailure(Throwable arg0, JSONObject orderResponseJSON) {
                CupsLog.i(TAG, "onFailure");
                try {
                    CupsLog.i(TAG, "JsonResponse: "+ orderResponseJSON.toString(3));
                } catch (JSONException e) {
                    CupsLog.i(TAG, "JSONException: " + e);
                }
            }

            @Override
            public void onFinish() {
                CupsLog.i(TAG, "onFinish");
                super.onFinish();
            }
        });
    }

Now I have those 3 methods that are working great, my problem is more of a Java problem. The first method give me the result right away, while the two others (2,3) are potentiality long running tasks. what more is that the first option is the most not reliable one, as users can travel to different countries with the SIM card. The second is more reliable but still sometimes does not returns an country (depending on the location of the user). The third one is the one that I found to be the most reliable one, but the most long as well.

The question: knowing this information, how would I construct a method that uses those 3 methods in the right order for reliability stand point and considering the long running tasks factor?

Это было полезно?

Решение

Unfortunately there is no really reliable way to determine the physical location of a user (e.g. his/her cellphone) because:

  • SIM card might be bought and/or manufactured in other country;
  • Geocoding (which is AFAIU based on GPS/GLONASS coordinates) might give wrong (~10m) results or no results at all if user disabled it or no satellites are visible (underground, for example);
  • Resolving country by IP might also give you wrong results, for example because of using VPN;

So my advice would be to ask user, which country he is currently in and willing to "tell" you so.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top