How do I get someone's location every few seconds? - android

I am trying to make a simple app that revolves around tracking someone's speed. I have read through Android Studio's Location guide, however it doesn't show how to get someone's speed, or get someone's location at an even interval.
Currently, I have this snippet of code set up:
if (OldLocation != null) {
OldLocation = NewLocation;
NewLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
} else {
OldLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
try {Thread.sleep(1000);} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
NewLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
float distance = OldLocation.distanceTo(NewLocation);
float speed = distance / 1000;
I am currently using Thread.sleep to do this, though I think there would be a better way to request updates periodically.
Can anyone show me how?

That is the worst way you can possible do it. For a dozen reasons you should almost never use getLastKnownLocation- and getting continual updates is definitely NOT one of those places to do it. Instead, use LocationManager.requestLocationUpdates(). You provide it a callback object and it calls you when an updated position is available.
Also, use LocationManager or the Google Play Fused Location. There's no good reason to mix and match.

Related

Android device is stable-at one place only but still getting km traveled as more than 2Km sometime

I am developing an App into which i am taking user current location using FusedLocationProviderClient. something like below code.
I am not able to understand why it takes current location as different location sometimes its 2+KM far away from current location and device is just stable at one place
private void getLastLocation() {
try {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLocation = task.getResult();
onNewLocation(mLocation);
} else {
Log.e(TAG, "Failed to get location.");
mLocation = null;
}
}
});
} catch (SecurityException unlikely) {
//Log.e(TAG, "Lost location permission." + unlikely);
}
Above code is in a foreground service... Now issue is when user device is stable yet after 5-6 hrs running the app it returns different different current location even though device is at same place(no movement )
Actual result : it should not give me any km traveled by user.
my KM traveled by user calculation is something like below code
double distanceInMeters = currentLocation.distanceTo(priviusLocation);
if (distanceInMeters > getSharedPref.getDefaultDistance() && (mLocation.getAccuracy() < Config.ACCURACY ) //Config.ACCURACY==>70{
// if (distanceInMeters > getSharedPref.getDefaultDistance()) {//getSharedPref.getDefaultDistance()==>10
if (mLocation.getLatitude() > 0) {
getSharedPref.setPreviousLocation(String.valueOf(mLocation.getLatitude()), String.valueOf(mLocation.getLongitude()));
writeDataInLogFile("this set to previous ", String.valueOf(mLocation.getLatitude()) + " getLongitude " + String.valueOf(mLocation.getLongitude()));
}
data.setGPS_Km_Travelled(String.format("%.2f", distanceInMeters / 1000));
data.setGPS_Is_Loc_Changed("1");
}
The way the fused location provider works is that it combines data from a device's GPS as well as network information from Google using the device's cellular network and WiFi information as well as perhaps other information from your device.
What sometimes happens is that when you leave a device in an area where there is little to no GPS connectivity, the user's position is inferred using network information from the device. This is sometimes as accurate as 30m but sometimes the position given by the fused location provider can be as inaccurate as a few kilometers.
Due to this your app may think the device has moved since the current location has changed even though the physical device never moved.
In order to fix this it's necessary for you to implement an algorithm to filter received positions based on certain criteria. For example, if the position has an 'accuracy' value higher than maybe up to 100m then it's not even worth considering as a valid location since the user might actually be up to 3km away from the location the api is returning.
Also, if the user's current location is too far away from the last location then the you should check whether the new location can be trusted as being accurate (a person can't usually move at a speed of more than a few km/h)
Good luck with your endeavours!

Android: getting location using GPS provider returns a wrong location

I'm implementing an app that gets your location when you open it. It only gets it when the activity is created, so I don't need a LocationListener. I'm testing it in two real devices.
First I try to get the location using GPS_PROVIDER. If the result is NULL, I use NETWORK_PROVIDER. Since I'm testing it inside a building is to be expected that I won't be able to get the location using the GPS provider.
In one of the devices the location using GPS_PROVIDER is NULL, and the NETWORK_PROVIDER returns the correct location. But in the other device the location using GPS_PROVIDER is not NULL, but it's wrong (it's in a different city than the one I'm currently am!). This is the code I'm using:
private void getPosition() {
locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
myloc = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(myloc == null)
myloc = locManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(myloc != null){
Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault());
try{
List<Address> addresses = geoCoder.getFromLocation(myloc.getLatitude(), myloc.getLongitude(), 1);
if(addresses.size()>0){
String add = "";
for(int i=0; i<addresses.get(0).getMaxAddressLineIndex(); i++)
add += addresses.get(0).getAddressLine(i)+"\n";
placeEdit.setText(add);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
How can I know the location returned by the GPS provider is wrong? Should I look the network provider first and if it's null use the gps? This app is supposed to be used outside, so I'd like to use the gps first since it's more accurate.
Thanks!
As state in Android Documentation
getLastKnownLocation(String provider) Returns a Location
indicating the data from the last known location fix obtained from the
given provider.
So when your device obtained position 2h earlier and in the meantime never get fresh fix, Location object return that old position. I suggest you to use LocationListener but with thread that will stop listening after some period of time - this approach prevent device from long fix-searching in case of bad view to satellites or when you are inside a building. If LocationListener obtain position use it, if not - use NETWORK_PROVIDER.

Android: requestLocationUpdates updates location at most every 45 seconds

Background
I am writing an Android app whose main function is tracking the user's location and making an alert when the user gets near some point. Therefore I need to update the user's location at regular intervals, and these intervals should get smaller as the user comes closer to the target. So when the user is within, say, 1 km of the target, I want the location to be updated every 20 seconds and so on, until the user arrives.
Problem
When I test it (provider = LocationManager.NETWORK_PROVIDER), a call to requestLocationUpdates(provider, minTime, minDistance, locationListener) with any minTime < 45000 has the same effect as minTime = 45000, i.e. I get updates with an interval of exactly 45 seconds.
I know the minimum time parameter is only a "hint", but it is not taken as a hint by my app. I get updates with the interval specified until that interval passes below 45 seconds. It seems as though a minimum time of 45 seconds between location updates is hardcoded into Android, but that would be kind of odd. Plus I have never heard of this problem before, and I have not been able to find it addressed here on Stackoverflow.
Because I am not able to get frequent updates, my workaround (for now) is to manually call requestLocationUpdates whenever a new location is needed, and then just use the first available location. To do this at small intervals I use handler.postDelayed(myRunnable, updateInterval) to delay the calls, and myRunnable then takes care of calling requestLocationUpdates. However, this method only works about 50 (apparently random) percent of the time.
Does anybody know of the problem, and is there a way to fix it? Or is my only option to set minTime = 0 and just hope for the best?
Source code
Here is the source code for myRunnable, whose run() method I manually call regularly with handler.postDelayed(myRunnable, updateInterval):
public class MyRunnable implements Runnable {
private LocationManager manager;
private LocationListener listener;
#Override
public void run() {
// This is called everytime a new update is requested
// so that only one request is running at a time.
removeUpdates();
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
listener = new LocationListener() {
#Override
public void onLocationChanged(Location loc) {
location = loc;
latitude = loc.getLatitude();
longitude = loc.getLongitude();
accuracy = Math.round(loc.getAccuracy());
handler.sendMessage(Message.obtain(handler, KEY_MESSAGE_LOCATION_CHANGED));
checkForArrival();
}
// Other overrides are empty.
};
if(!arrived)
manager.requestLocationUpdates(provider, updateInterval, 0, listener);
}
/**
* Removes location updates from the LocationListener.
*/
public void removeUpdates() {
if(!(manager == null || listener == null))
manager.removeUpdates(listener);
}
// Another method for "cleaning up" when the user has arrived.
}
And here is my handler:
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case KEY_MESSAGE_LOCATION_CHANGED:
if(myRunnable != null) {
myRunnable.removeUpdates();
handler.postDelayed(myRunnable, updateInterval);
}
break;
}
}
};
Additional info
The whole location updating thing runs in a service.
I have read the doc several times, Google'd the problem, and tried various other workarounds. Nothing quite does it.
I have logged the damn out of this thing, and the only exciting thing to see is a big fat "ignore" to my frequent location requests. All the right methods are called.
Any help will be very much appreciated!
You are completely right, the minimum time 45 seconds is harcoded in Android.
This seems to be a NetworkLocationProvider class source code, when it was still in Android core:
http://www.netmite.com/android/mydroid/frameworks/base/location/java/com/android/internal/location/NetworkLocationProvider.java
Look at the variable:
private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 45 * 1000; // 45 seconds
And the method:
#Override
public void setMinTime(long minTime) {
if (minTime < MIN_TIME_BETWEEN_WIFI_REPORTS) {
mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS;
} else {
mWifiScanFrequency = minTime;
}
super.setMinTime(minTime);
}
Now NetworkLocationProvider is out of the Android core, you can find it in NetworkLocation.apk in /system/app
You can find an explanation of why is out of the core here:
https://groups.google.com/forum/?fromgroups=#!topic/android-platform/10Yr0r2myGA
But 45 seconds min time seems to still be there.
Look at this NetworkProvider decompilation:
http://android.fjfalcon.com/xt720/miui-trans/apk-decompiled/NetworkLocation/smali/com/google/android/location/NetworkLocationProvider.smali
.line 149
const-wide/32 v4, 0xafc8
iput-wide v4, p0, Lcom/google/android/location/NetworkLocationProvider;->mWifiScanFrequency:J
As you might guess if you convert 0xafc8 to decimal you get 45000 milliseconds
I haven't found an explanation of why 45 seconds. I suppose there will be reasons like avoiding service overloading or other uses they don't want.
In fact, there is a 100 request courtesy limit to Geolocation API:
https://developers.google.com/maps/documentation/business/geolocation/#usage_limits
But they don't seem to respect this rule in Google Maps app. If you open it and you only active network location you can notice that yout location is updated much more frequently than 45 seconds.
I noticed this line suspiciously frequent (33 times a second) in logcat when Google Maps is open:
02-20 17:12:08.204: V/LocationManagerService(1733): getAllProviders
I guess Google Maps is also calling removeUpdates() and requestLocationUpdates() again to obtain a new position.
So I think there is no fix and this is the best you can do if you want to get network locations over one in 45 seconds.
You can set the minTime to any value. However, you will only get an update once a new location is available. The network only updates every 45 sec or so on every phone I own. This seems to be a limitation of the Network Provider. If you want more frequent updates use the GPS provider. Depending on the GPS hardware you should get a maximum update rate around 4Hz.
I was having a similar issue. I put a call to locationManager.requestSingleUpdate() at the end of onLocationChanged() and it forced back to back updates. You could set a delay command then execute requestSingleUpdate, making sure to register the containing locationListener.
I was trying to create a GPS clock but the updates were inconsistent updating anywhere from 1-5 seconds or so. but it might work for another application.

android speed using COARSE

hi to all
how can i get the speed using the ACCESS_COARSE_LOCATION in android
thank you
Hi are you referring to Location.getSpeed() I believe that is an optional field and probably up to the device to actually support it. First off try this
Criteria criteria = new Criteria();
criteria.setSpeedRequired(true);
LocationProvider[] providers = mLocationManager.getProviders(criteria, false);
if (providers == null || providers.length == 0) {
//Sorry it doesn't do it by default.
}
Luckily Math and Science will help us :) Just calculate the speed as you get updates from your LocationProviders. Make sure to calculate the distance between the lat,longs over times as the difference of the recorded times stamps of two Location updates.
...
onLocationChanged(Location location) {
if (mOldLocation != null) {
float distance = getDistance(mOldLocation, location);
long time = location.getTime() - mOldLocation.getTime();
mSpeed = distance/time;
}
mOldLocation = location;
}
It won't necessarily be greatly accurate (probably really noisy) but it's better to make lemonade than to have rotted fruit ... hopefully this helps. Make sure to only compare deltas across the same provider too. You will probably find yourself tweaking the manual calculation because the devices providers are very diverse and don't guarantee that time always moves forward.
Most likely, you cannot. Location technology that is "coarse" only knows where you are within several hundred meters. Your speed could vary wildly. If you need to know your speed, you will most likely need a "fine" location provider.
Or, better yet, use the Criteria object to stipulate that you need to know the device's speed, and let it guide you to the best provider to use.

Android: GPS fallback from fine to coarse

Greetings,
Does anyone know how I can get coarse GPS coordinates when I don't have a fix and get fine GPS coordinates when I have a fix?
I've tried googling for some sample code to no avail.
I did find this: http://www.android10.org/index.php/articleslocationmaps/226-android-location-providers-gps-network-passive
But I don't know how to implement the fallback to coarse/upgrade to fine.
I hope someone can help. Thanks in advance,
You can find an excellent introduction to the subject in the documentation. The basic idea is that you enable listening for updates from different providers. When a new location is received, you compare it to the previous stored location (a sample function is provided in the above link).
A location object has an getAccuracy that you can use to measure its accuracy. You should also set up a timer so that you know how long has passed after a location provider has provided an update. If more than two minutes have passed after GPS provider has given you an update, then start listening for network updates. While listening for network updates, if GPS gives you a new update, then switch to fine location.
You can get the last know location using the code below. It gets the location providers and loops over the array backwards. i.e starts with GPS, if no GPS then gets network location. You can call this method whenever you need to get the location.
private double[] getGPS() {
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
List<String> providers = lm.getProviders(true);
/* Loop over the array backwards, and if you get an accurate location, then break out the loop*/
Location l = null;
for (int i=providers.size()-1; i>=0; i--) {
l = lm.getLastKnownLocation(providers.get(i));
if (l != null) break;
}
double[] gps = new double[2];
if (l != null) {
gps[0] = l.getLatitude();
gps[1] = l.getLongitude();
}
return gps;
}

Categories

Resources