Getting this error when trying to get location from latlong using Geocoder.
Error
Caused by java.io.IOException: grpc failed
at android.location.Geocoder.getFromLocation(Geocoder.java:136)
at com.example.myApp.test.Fragments.DashboardFragment$sendLocationData$1.onSuccess(DashboardFragment.kt:676)
at com.example.myApp.test.Fragments.DashboardFragment$sendLocationData$1.onSuccess(DashboardFragment.kt:81)
at com.google.android.gms.tasks.zzj.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
Code
punch_button?.setOnClickListener {
createLocationRequest()
}
protected fun createLocationRequest() {
mLocationRequest = LocationRequest()
mLocationRequest?.interval = 10
mLocationRequest?.fastestInterval = 50
mLocationRequest?.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest!!)
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(p0: LocationResult?) {
super.onLocationResult(p0)
mCurrentLocation = p0?.lastLocation
}
}
val client = LocationServices.getSettingsClient(context)
val task = client.checkLocationSettings(builder.build())
task.addOnSuccessListener(OnSuccessListener<LocationSettingsResponse> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
askForPermission();
}
}) }
private fun askForPermission() {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION)) {
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 123);
} else {
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 123);
}
} else {
sendLocationData()
}
}
#SuppressLint("MissingPermission")
fun sendLocationData() {
try{
Log.e("lat", mCurrentLocation?.latitude.toString())
//mCurrentLocation?.latitude !=null && mCurrentLocation?.latitude !=null
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null)
mFusedLocationClient.lastLocation.addOnSuccessListener({ location ->
if (location != null) {
val address: List<Address> = geocoder?.getFromLocation(location.latitude, location.longitude, 1)!!
}
})
}catch(e:Exception){
Log.e("tag","error")
}
}
This is a know bug reported to Google, but unfortunately not solved yet. This bug happens for real devices and emulators. You can see the thread about this issue here:
https://issuetracker.google.com/issues/64418751
https://issuetracker.google.com/issues/64247769
A workround to try solved this bug in your case is try to use Geocoding API web service:
https://github.com/googlemaps/google-maps-services-java
Or you can try just catch the except and handle the exception like this:
try{
geocoder = new Geocoder(this, Locale.getDefault())
// ... your code that throws the exception here
}catch(e: IOException){
Log.e("Error", "grpc failed: " + e.message, e)
// ... retry again your code that throws the exeception
}
I faced this issue while getting Addresses List using geocoder.getFromLocation() method, the problem is that getting addresses list from latitude,longitude takes time or on some slow devices it takes more time and, also sometimes it give me the java-io-ioexception-grpc-failed.
I fix this problem using Rx Java.
public class AsyncGeocoder {
private final Geocoder geocoder;
public AsyncGeocoder(Context context) {
geocoder = new Geocoder(context);
}
public Disposable reverseGeocode(double lat, double lng, Callback callback) {
return Observable.fromCallable(() -> {
try {
return geocoder.getFromLocation(lat, lng, 1);
} catch (Exception e) {
AppLogger.d("throwable,", new Gson().toJson(e));
e.printStackTrace();
}
return false;
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
//Use result for something
AppLogger.d("throwable,", new Gson().toJson(result));
callback.success((Address) ((ArrayList) result).get(0));
}, throwable -> AppLogger.d("throwable,", new Gson().toJson(throwable)));
}
public interface Callback {
void success(Address address);
void failure(Throwable e);
}
}
Calling Position
mViewModel.getLocation(asyncGeocoder, getLat(), getLng(), this);
ViewModel Method
public void getLocation(AsyncGeocoder geocoder, Double lat, Double lng, AsyncGeocoder.Callback callback) {
getCompositeDisposable().add(geocoder.reverseGeocode(lat, lng, callback));
}
Related
Iam using easebuzz payment gateway and it was working fine two days ago, but now
I got stuck in payments and im getting an error but easebuzz is not showing any error its in the screen of Transaction is Processing...
after selecting payment option(ex: Bank) it will process the payment and next it is redirecting to Transaction processing page and it got stuck in there after waiting for long time still its not showing any response but only a error log in the terminal. it was working fine few days before i didnt changed any payment code.ihave Updated the flutter version but i dont thing flutter version cant do anything because My previous projects that built few months before also not working due to this issue in the easebuzz ,
error log
W/System.err(30454): org.json.JSONException: No value for error_status W/System.err(30454): at org.json.JSONObject.get(JSONObject.java:399) W/System.err(30454): at org.json.JSONObject.getString(JSONObject.java:560) W/System.err(30454): at com.easebuzz.payment.kit.PWEBankPageActivity$PWEPaymentStatus$1.run(PWEBankPageActivity.java:1016) W/System.err(30454): at android.os.Handler.handleCallback(Handler.java:883) W/System.err(30454): at android.os.Handler.dispatchMessage(Handler.java:100) W/System.err(30454): at android.os.Looper.loop(Looper.java:224) W/System.err(30454): at android.app.ActivityThread.main(ActivityThread.java:7590) W/System.err(30454): at java.lang.reflect.Method.invoke(Native Method) W/System.err(30454): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) W/System.err(30454): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
This is the Javacode that is given by easebuzz
MainActivity.java
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "easebuzz";
MethodChannel.Result channel_result;
boolean start_payment = true;
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
start_payment = true;
new MethodChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
channel_result = result;
if (call.method.equals("payWithEasebuzz")) {
if (start_payment) {
start_payment = false;
startPayment(call.arguments);
}
}
}
});
}
private void startPayment(Object arguments) {
try {
System.out.print(":::::::::::::Payment started 0::::::::::::::");
Gson gson = new Gson();
System.out.print(":::::::::::::Payment started 1::::::::::::::");
JSONObject parameters = new JSONObject(gson.toJson(arguments));
System.out.print(":::::::::::::Payment started ::::::::::::::");
Intent intentProceed = new Intent(getBaseContext(), PWECouponsActivity.class);
intentProceed.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intentProceed.putExtra("access_key", parameters.getString("access_key"));
intentProceed.putExtra("pay_mode", parameters.getString("pay_mode"));
startActivityForResult(intentProceed, PWEStaticDataModel.PWE_REQUEST_CODE);
} catch (Exception e) {
start_payment = true;
Map error_map = new HashMap<>();
Map error_desc_map = new HashMap<>();
String error_desc = "exception occured:" + e.getMessage();
error_desc_map.put("error", "Exception");
error_desc_map.put("error_msg", error_desc);
error_map.put("result", PWEStaticDataModel.TXN_FAILED_CODE);
error_map.put("payment_response", error_desc_map);
channel_result.success(error_map);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data != null) {
if (requestCode == PWEStaticDataModel.PWE_REQUEST_CODE) {
start_payment = true;
JSONObject response = new JSONObject();
Map error_map = new HashMap<>();
if (data != null) {
String result = data.getStringExtra("result");
String payment_response = data.getStringExtra("payment_response");
System.out.print(":::::::::::::Payment response ::::::::::::::");
System.out.print(payment_response);
try {
JSONObject obj = new JSONObject(payment_response);
response.put("result", result);
response.put("payment_response", obj);
channel_result.success(JsonConverter.convertToMap(response));
} catch (Exception e) {
Map error_desc_map = new HashMap<>();
error_desc_map.put("error", result);
error_desc_map.put("error_msg", payment_response);
error_map.put("result", result);
error_map.put("payment_response", error_desc_map);
channel_result.success(error_map);
}
} else {
Map error_desc_map = new HashMap<>();
String error_desc = "Empty payment response";
error_desc_map.put("error", "Empty error");
error_desc_map.put("error_msg", error_desc);
error_map.put("result", "payment_failed");
error_map.put("payment_response", error_desc_map);
channel_result.success(error_map);
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
}
I'm trying to launch context from android MainActivity class to flutter.
code :
val authResult = ComponentActivity().registerForActivityResult(PianoIdAuthResultContract()) { r ->
when (r) {
null -> { /* user cancelled Authorization process */ }
is PianoIdAuthSuccessResult -> {
val token = r.token
val isNewUserRegistered = r.isNewUser
if (token != null) {
if (token.emailConfirmationRequired) {
// process enabled Double opt-in
}
}
// process successful authorization
}
is PianoIdAuthFailureResult -> {
val e = r.exception
// Authorization failed, check e.cause for details
}
}
}
and then calling the method launch
code :
try{
authResult.launch(PianoId.signIn());
}catch (e : Exception){
val text = e.message
val duration = Toast.LENGTH_SHORT
val toast = Toast.makeText(applicationContext, text, duration)
toast.show()
}
and then I call this method from flutter by creating a channel between flutter and android and invoke it :
signInChannel.invokeMethod('testSignIn');
when I press the sign in button it shows me this exception :
Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference
I too was searching for the solution. what i did was extend MainActivity with FlutterFragmentActivity and pass this to method channel handler.
public class MainActivity extends FlutterFragmentActivity{
...
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine)
{
handlePickerMethodChannel(flutterEngine);
}
private void handlePickerMethodChannel(FlutterEngine flutterEngine) {
PickerMethodChannelHandler PickerMethodChannelHandler = new PickerMethodChannelHandler(new WeakReference<>(this));
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), PHOTO_PICKER_METHOD_CHANNEL)
.setMethodCallHandler(pickerMethodChannelHandler);
}
}
class PickerMethodChannelHandler(
private val activity: WeakReference<Activity>,
) : MethodChannel.MethodCallHandler {
private val pickMedia = (activity.get() as ComponentActivity).registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
Log.d("PhotoPicker", "Selected URI: $uri")
} else {
Log.d("PhotoPicker", "No media selected")
}
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"pickMedia" -> pickMedia(call,result)
else -> result.notImplemented()
}
}
private fun pickMedia(call: MethodCall, result: MethodChannel.Result) {
val context = activity.get() as ComponentActivity
Log.i("PICK_MEDIA","PICK ${context != null}")
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo))
}
}
It worked
I'm using android lollipop in PAX A920 device (SDK Version 22). I get a warning message like this when build:
NetworkInfo in android.net has been deprecated
WifiConfiguration in android.net has been deprecated
I'm confused because I saw an example of sdk 22 in C:\Users\{YOUR_ACCOUNT}\AppData\Local\Android\Sdk\sources\android-22\com\android\connectivitymanagertestto access wifi using that method, but why does it appear deprecated?
What method is the same to replace the deprecated methods?
public boolean isWifiConnected() {
boolean isWifiConnected = false;
try {
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (networkInfo != null) {
isWifiConnected = networkInfo.getState() == CONNECTED;
}
Log.i(TAG, "wifi adapter is connected? " + isWifiConnected);
} catch (Exception ex) {
ex.printStackTrace();
}
return isWifiConnected;
}
public void removeNetwork() {
List<WifiConfiguration> wifiCfgList = wifiManager.getConfiguredNetworks();
if (wifiCfgList.size() > 0) {
for (WifiConfiguration item : wifiCfgList) {
if (item != null) {
wifiManager.removeNetwork(item.networkId);
wifiManager.saveConfiguration();
}
}
}
}
Thanks for point out.
Deprecated classes were repaced with ConnectivityManager system service and NetworkCallbacks: https://developer.android.com/training/monitoring-device-state/connectivity-status-type
Even though the official code example shows how to get NetworkInfo from ConnectivityManager there is a highlighted note:
Here is a sample code of how you could get the current network state and also receive updates as time goes by. This is a partially stripped-down solution that I would use in production. If you connect it with RxJava or RxKotlin you could create an observable that will hold network state and that will be updated when overridden method of NetworkCallback get called.
Note regarding Java: public class-level variables are made public just for brevity. I'd instead create a few getters for these to access the value behind these variables.
Feel free to ask questions.
Java
class NetworkReachabilityService {
public NetworkType networkType;
public NetworkState networkState = NetworkState.Unavailable;
private ConnectivityManager connectivityManager;
private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(#NonNull Network network) {
super.onAvailable(network);
updateAvailability(connectivityManager.getNetworkCapabilities(network));
}
#Override
public void onLosing(#NonNull Network network, int maxMsToLive) {
super.onLosing(network, maxMsToLive);
networkState = NetworkState.Losing;
}
#Override
public void onLost(#NonNull Network network) {
super.onLost(network);
networkState = NetworkState.Lost;
}
#Override
public void onUnavailable() {
super.onUnavailable();
networkState = NetworkState.Unavailable;
}
#Override
public void onCapabilitiesChanged(#NonNull Network network, #NonNull NetworkCapabilities networkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities);
updateAvailability(networkCapabilities);
}
};
public NetworkReachabilityService(Context context) {
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
private void updateAvailability(NetworkCapabilities networkCapabilities) {
if (networkCapabilities == null) {
networkState = NetworkState.Unavailable;
return;
}
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
networkType = NetworkType.CELL;
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
networkType = NetworkType.WiFi;
}
networkState = NetworkState.Available;
}
public void resumeListeningNetworkChanges() {
pauseListeningNetworkChanges();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.registerDefaultNetworkCallback(networkCallback);
} else {
connectivityManager.registerNetworkCallback(
new NetworkRequest.Builder().build(),
networkCallback
);
}
}
public void pauseListeningNetworkChanges() {
try {
connectivityManager.unregisterNetworkCallback(networkCallback);
} catch (IllegalArgumentException exception) {
// Usually happens only once if: "NetworkCallback was not registered"
}
}
private enum NetworkState {
Available, Unavailable, Connecting, Losing, Lost
}
private enum NetworkType {
WiFi, CELL, OTHER
}
}
Kotlin
sealed class NetworkState {
data class Available(val type: NetworkType) : NetworkState()
object Unavailable : NetworkState()
object Connecting : NetworkState()
object Losing : NetworkState()
object Lost : NetworkState()
}
sealed class NetworkType {
object WiFi : NetworkType()
object CELL : NetworkType()
object OTHER : NetworkType()
}
class NetworkReachabilityService private constructor(context: Context) {
private val connectivityManager: ConnectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
// There are more functions to override!
override fun onLost(network: Network) {
super.onLost(network)
networkState = NetworkState.Lost
}
override fun onUnavailable() {
super.onUnavailable()
networkState = NetworkState.Unavailable
}
override fun onLosing(network: Network, maxMsToLive: Int) {
super.onLosing(network, maxMsToLive)
networkState = NetworkState.Losing
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
updateAvailability(connectivityManager.getNetworkCapabilities(network))
}
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, networkCapabilities)
updateAvailability(networkCapabilities)
}
}
var networkState: NetworkState = NetworkState.Unavailable
private set
private fun updateAvailability(networkCapabilities: NetworkCapabilities?) {
if (networkCapabilities == null) {
networkState = NetworkState.Unavailable
return
}
var networkType: NetworkType = NetworkType.OTHER
if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
networkType = NetworkType.CELL
}
if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
networkType = NetworkType.WiFi
}
networkState = NetworkState.Available(networkType)
}
fun pauseListeningNetworkChanges() {
try {
connectivityManager.unregisterNetworkCallback(networkCallback)
} catch (e: IllegalArgumentException) {
// Usually happens only once if: "NetworkCallback was not registered"
}
}
fun resumeListeningNetworkChanges() {
pauseListeningNetworkChanges()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.registerDefaultNetworkCallback(networkCallback)
} else {
connectivityManager.registerNetworkCallback(
NetworkRequest.Builder().build(),
networkCallback
)
}
}
}
To start receiving network state updates call resumeListeningNetworkChanges and to stop pauseListeningNetworkChanges respectively.
Update: how to switch between deprecated and new API
Note that even when you use this solution you will anyway have a message that the certain code you use is deprecated! It is completely fine and is not considered as an error as long as you provide an implementation that can switch between new API and old, deprecated API.
Here is an approximate solution. Since the new classes were added in API level 29 we must use Build.VERSION_CODES.Q because it is an integer with the value 29.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Use new API here
} else {
// Use old API here
}
I want to return a string array from Async class back to the activity that is calling this asynchronous class that is job is to do the reverse geocoding.
So, from my activity I call the constructor of the class like this:
Double[] lat_long = new Double[] { Double.parseDouble(map_lat), Double.parseDouble(map_long) };
ReverseGeocodingTask reverseGeocoding = new ReverseGeocodingTask(getActivity().getApplicationContext());
reverseGeocoding.execute(lat_long);
And this is the code of the class:
class ReverseGeocodingTask extends AsyncTask<Double, Void, List<String>> {
public static List<String> LIST = new ArrayList<String>();
Context mContext;
public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
#Override
protected List<String> doInBackground(Double... params) {
Geocoder gc= new Geocoder(mContext, Locale.getDefault());
List<Address> addrList = null;
double latitude = params[0].doubleValue();
double longitude = params[1].doubleValue();
Log.d("LATLONG", latitude + ":" + longitude);
try {
addrList = gc.getFromLocation(latitude, longitude, 1);
if (addrList.size() > 0) {
//format location info
Address address = addrList.get(0);
LIST.add(address.getLocality());
LIST.add(address.getSubAdminArea());
LIST.add(address.getCountryName());
Log.d("LIST", LIST.get(0));
}
else{
Log.d("addrList SIZE", "=0");
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
return LIST;
}
#Override
protected void onPostExecute(List<String> result) {
if (result != null) {
Log.d("ON POST", result.get(0));
}
}
}
This is the logcat:
02-28 19:20:04.323 12275-14109/guide_me_for_all.guide_me_for_all D/LATLONG﹕ 34.681377999999995:33.039339
02-28 19:20:05.434 12275-14109/guide_me_for_all.guide_me_for_all D/addrList SIZE﹕ =0
I get correctly the latitude and longitude point as you can see from the Log.d(), BUT getFromLocation.size() is always 0.
This may be a problem with your GeoCoder service. If you're backend service for the device is not present or has other problems, you will get this response.
use isPresent to check if an implementation is present.
Also, see this post here:
Geocoder.getFromLocation throws IOException on Android emulator
And the docs mention that you need a backend service:
http://developer.android.com/reference/android/location/Geocoder.html
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:
Android inner methods to get the SIM country.
GeoCoding.
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.