My app use Google Play service API to get the user location and check if the device is inside or outside a particular area of 50 mt of radius.
The app use PRIORITY_HIGH_ACCURACY and a Interval of 1 minute.
So I create GoogleApiClient:
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
In the onConnected callback:
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(60000);
mLocationRequest.setFastestInterval(10000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
and
MyLocationListener.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mLocationListener);
My app also filter locations based on accuracy (if accuracy > 200 mt I discard it) and time (if it is too old I discard it).
The app works fine except in some particular areas where sometimes it returns wrong locations update about 500 meters from the real location, and those wrong locations are always near the same place, some step away from a Cell Tower.
In those areas I get a combination of wrong and correct locations and my app think that the device is sometimes inside and sometimes outside the area of interest.
When I get a location update I wait for other 3 in a row to confirm the position. Also I filter location if exactly the same as the previous. This means that when I m getting those errors I'm receiving 4 wrong locations in a row each one slightly different from the others.
Is there a way to prevent this behavior? Can this be caused by the Cell Tower?
There's always a chance of it being wrong. An accuracy of 200m doesn't mean its within 200m- it means there's a 67% chance you're within 200m. There's still a 1/3 chance you aren't.
Since most of the Google Play location providers are fused (use GPS and wifi/cell data), yes being very close to a tower could screw with the data.
Change setInterval(60000) to setInterval(20000) or to 5000.
Since the interval is one minute, you won't get updates even if the user moves. That's why there is an inaccuracy.
According to Google, setInterval(long) means - set the interval in which you want to get locations. setFastestInterval(long) means - if a location is available sooner you can get it (i.e. another app is using the location services. Means, if no other apps are using the location service, you will get updates only after a minute).
Related
In my application, I have a requirement where I need to track the user's current place and notify the user if he/she stays for some time.
So to do this, I am using Google's place SDK to get current place detail, reference link
But the problem is output.
If I use Location service with priority PRIORITY_HIGH_ACCURACY or PRIORITY_BALANCED_POWER_ACCURACY, the output of Place SDK is different for the same location.
My Location Request is as below:
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// or
// mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(1000);
As mentioned in above link, we don't have any option to pass current latitude-longitude, as SDK itself manage it.
So can anyone help me how I can get much accurate output from Place SDK.
Sample code of background service
as the documentation states, you probably should migrate to GoogleApi Client
while the two different results returned, should have a different PlaceLikelihood.
as a matter of fact, the less precise the requested location is, the less accure the returned places are.
for the LocationRequest, there isn't even a PRIORITY_BALANCED, but only a PRIORITY_BALANCED_POWER_ACCURACY constant available - and the request interval will be automatically throttled when this is set, no matter what you try to set as the interval.
also see the FusedLocationProviderClient.
I'm starting to develop an app that will stay in background forever and detect when a user is staying in a certain location for a while (and then display a notification to invite the user to open the app to obtain informations about that place).
It's something very similar to what Google Maps does when you're in a restaurant and it shows you a notification to check ratings about it.
What I want is to have a minimal impact on device, so location updates should be very "passive", getting the location only when user is not moving and, if possible, recycling location data that is already got by other activities - maybe by Google Maps itself or other location apps that are running on the devices.
This is not a navigation app, so I don't need to have the live fine location but simply the approximate place with the best accuracy and minimal effort, and only if user is not moving.
LocationListener and onLocationChanged seems to be my men, but can I specify that I don't want to trigger device's sensors and only re-use location data when it's available for other scopes? My app should check these informations and then decide to do a reverse geocode if and when they are accurate enough.
Yes, LocationListener and onLocationChanged are your men, though for a passive implementation, there are a few options you can go through.
First you can check for the last known location, maybe compare it in terms of its time; i.e. getTime() and verify whether it is of use to you.
In terms of code, literally...
Google samples, android location has what is relevant for the last location part:
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
// Provides a simple way of getting a device's location and is well suited for
// applications that do not require a fine-grained location and that do not need location
// updates. Gets the best and most recent location currently available, which may be null
// in rare cases when a location is not available.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Further, you can combine it with LocationRequest object, this helps your implementation as you can call it right after trying getLastLocation() and basically have a more reliable arrangement for obtaining location.
// Create the location request
mLocationRequest = LocationRequest.create()
//priority object needs to be set, the following will definitely get results for you
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY)
//interval updates can be on the lines of 15mins or 30... acc to your requirement
.setInterval(UPDATE_INTERVAL)
.setFastestInterval(FASTEST_INTERVAL);
// Request location updates
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
i suggest give PRIORITY_NO_POWER a try, could work well in combination with getLastLocation(). These power modes have been added specifically for optimising battery/power consumption and efficiency for retrieving location.
I have some code that runs multiple times per second in my app. I'm trying to get my location in every cycle. I am using the following:
Location myLastPos = LocationServices.FusedLocationApi.getLastLocation(googleApiClient)
My app also runs in the background using a PARTIAL_WAKE_LOCK. With the screen on everything seems OK. When I turn the screen off my app still runs normally but I no longer get location updates consistently.
It appears that I get updates much less frequently (often minutes in between updates). I'm checking the timestamp of the location using:
myLastPos.getElapsedRealtimeNanos()
I also found that even when the screen is on I get some strange results. Sometimes I get a few milliseconds between updates, other times I get a few seconds. This is all very concerning. Can someone either help me use FusedLocationApi properly or suggest an alternative. All I really want is to poll the gps directly for lat/long a few times a second without google libraries getting in the way.
The getLastLocation() method just gets the last known location that the device happens to know. The "last known location" here means exactly that: It may not be up-to-date. Locations do come with a time stamp which could be used to asses if the location might still be relevant.
The device doesn't determine its location on its own, but only when some application request the location. So your app is now dependent on other applications requesting location updates.
If you need updates every few seconds, then request regular location updates yourself.
Android documentation recommends the FusedLocationProvider, but the LocationManager is also a perfectly valid option, if there's any reason to avoid the Google Play services.
The basic idea is to first request location updates:
// Using LocationManager as an example.
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Using GPS, requesting location updates as soon as available and even for
// the smallest changes. Here 'this' refers to our LocationListener
// implementation.
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
The updates are then received by a listener:
#Override
public void onLocationChanged(Location location) {
// We received a location update.
// Copy the value from the method parameter to our
// class member variable.
mLocation = location;
}
And when you no longer need the updates you should cancel the request:
mLocationManager.removeUpdates(this);
The approach is very similar for the FusedLocationProvider.
I'm using the FusedLocationApi to get updates. But in spite of setting the LocationRequest.setInterval to say 500 or even 200 millis, I only get one update per second. I'd like to get two a second if possible (to do a moving average of speed). Is there a hard limit?
In my onCreate for the Activity:
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.addApi(AppIndex.API).build();
In my 'onConnected' method:
this.locationRequest = new LocationRequest();
this.locationRequest.setInterval(200);
this.locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(this.googleApiClient, this.locationRequest, this);
I also have a separate LocationManager I only use to get status changes via addGpsStatusListener() and those seem to work OK.
Thanks
As per documentations
This interval is inexact. You may not receive updates at all (if no location sources are available), or you may receive them slower than requested.
You can try to use public LocationRequest setFastestInterval (long millis). The documentation states:
he fastest rate that that you will receive updates can be controlled with setFastestInterval(long). By default this fastest rate is 6x the interval frequency.
Also make sure you request ACCESS_FINE_LOCATION. The docs also states that:
Applications with only the coarse location permission may have their interval silently throttled
As the "LocationRequest.setInterval()" docs says
The location client will actively try to obtain location updates for your application at this interval[...]
So it's possible you cannot get the location updated in the desired frequency. That's because the nature of the location retrieving techniques (GPS, WiFi, etc.) and it's very hardware dependent (and also the Google's fused location provider algorithms), and also depends on the atmospheric conditions (in the GPS case). If you want to obtain a high accuracy location, then the device needs to obtain first some samples in order to do a high accuracy estimation.
I want to get 5 consecutive update locations in order to check if the user is moving and to make sure the GPS is calibrated. I did the following:
I added android.permission.ACCESS_FINE_LOCATION" to the manifest and in onConnected:
mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(1000); // Update location every second
mLocationRequest.setFastestInterval(1000);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
and in onLocationChanged I did the following:
locationRetries++;
switch (locationRetries) {
case 1: {
firstLatitude = location.getLatitude();
firstLongitude = location.getLongitude();
}
case 5: {
locationRetries = 0;
lastLatitude = location.getLatitude();
lastLongitude = location.getLongitude();
accuracy = Math.round(location.getAccuracy());
stopLocationUpdates();//will remove from location updates and disconnects
float[] results = new float[1];
Location.distanceBetween(firstLatitude, firstLongitude, lastLatitude, lastLongitude, results);
if (results[0] > 5)... //if moved at least 5meters, show popup that the user is moving else do your thing
}
Now I have 3 issues:
1) It seems to take much less than 5 seconds to retrieve the 5 updates even though I set both parameters to be 1000 milliseconds.
2) All the 5 locations (at least the first and last ones) are the same exact location even though I was walking fast. I thought I maybe moving too slow so
3) I closed my app, reopened it on a far location and pressed the button. Almost instantly I got the previous location. I pressed the button again and then I got the real location I was on. It's as if it didn't really asked/waited for a location from the GPS but instead took the last one which was remotely inaccurate at the time. I don't have any "get last known location" code.
I guess the bottom line would be: how can I make sure that it really asks the GPS where am I when I asked for the location and also when asking it for 5 consecutive times, to give me the real locations and not from the cache(?).
The Fused Location Provider
intelligently manages the underlying location technology and gives you the best location according to your needs.
Simple APIs: Lets you specify high-level needs like "high accuracy" or "low power", instead of having to worry about location providers.
Immediately available: Gives your apps immediate access to the best, most recent location.
Power-efficiency: Minimizes your app's use of power. Based on all incoming location requests and available sensors, fused location provider chooses the most efficient way to meet those needs.
Versatility: Meets a wide range of needs, from foreground uses that need highly accurate location to background uses that need periodic location updates with negligible power impact.
There is no guarantee that the fused location provider will ever use GPS - if it has a recent location of the accuracy you request it will return until a better location is returned (i.e., live GPS is returning accurate locations). This ensures that you'll get a better location sooner without waiting for GPS to be primed.
If you specifically need data from GPS, you need to use the LocationManager using the GPS_PROVIDER.
If you are trying to determine what the user is currently doing, you can instead use the ActivityRecognitionApi, which returns DetectedActivity such as WALKING or STILL: using that would give a faster method to understand what the user is currently doing.