I am using the built in geofence APIs (play services) and have been having mixed results. It looks like after setting a geofence, the notifications for entering/exiting are very inconsistent even when GPS is on, with an up-to-date location (LocationClient connected, running in the background.) I started monitoring (polling) location changes and distances in a debug text field, and saw that, even when based on the location registered by the device and the location of the geofence I am technically inside/outside of the geofence, notifications are sometimes triggered and sometimes not. Any way to make this more predictable? I am almost tempted to abandon this api and implement my own (battery draining) geofences based on polling of device location.
A couple of suggestions:
Make sure you're using a BroadcastReceiver and not a Service to receive the transitions, otherwise you will not/might not get it if your app gets killed/turned off. As discussed here:
Android Geofence eventually stop getting transition intents
Make sure you recreate your geofences after the device is rebooted, preferably using a boot-broadcast receiver. As discussed here:
Do Geofences remain active in android after a device reboot
One other common misconception, and which stumped me since it's different than IOS is that you'll always get a trigger immediately for a newly created geofence, if the device discovers that you're inside the geofence when creating it. I have solved this myself using a "grace period" for newly created geofences, which i explained in this thread:
addProximityAlert doesn't work as expected
Finally one important thing: Having your LocationClient connected in your app or not should not matter at all if you follow the points above. My process for adding or removing Geofences in my code is basically:
Create and connect locationclient.
In connect-callback, do adding/removing of geofence(s)
In geofence-result callback, disconnect locationclient.
Every time this happens, my location client is only connected a few seconds in total. The operating system will still produce geofence alerts and call my BroadcastReceiver whenever they happen.
If you do these things I bet your experience will improve.
Hope this helps!
I tried all the suggestions from the accepted response but the accurate wasn't better for my. If you are here looking more ideas you should read the following.
I was able manage the problem doing my own geofences algorithm based on android location api plus JTS Location Suite
To be more accurate. All you need is to create a jts polygon thats represent mathematically your fence.
static GeometryFactory GF = new GeometryFactory();
public Polygon getPolygon() {
ArrayList<Coordinate> coors = new ArrayList<>();
for (Point p : getPolygonPoints()) {
coors.add(new Coordinate(p.lat, p.lon));
}
// close the polygon is needs
final Coordinate first = coors.get(0);
final Coordinate last = coors.get(coors.size()-1);
if (!first.equals2D(last)) {
coors.add(coors.get(0));
}
// create polygon
return GF.createPolygon(coors.toArray(new Coordinate[] {}));
}
Then you can check whether if a current GPS point is inside or not of given polygon.
final Point point = GF.createPoint(new Coordinate(lat, lon));
if (point.within(getPolygon())) {
// hit
} else {
// miss
}
That's it all. Then you can create a java.util.Timer to periodically iterate a list of polygons (geo fences) to know which was hit or which was behind.
Note: Add the following in your module build.gradle to add JTS library in your project:
implementation(group: 'org.locationtech.jts.io', name: 'jts-io-common', version: '1.18.1') {
exclude(group: 'org.hamcrest')
}
Related
after researching the Android Location FusedLocationProviderClient I am very confused about what you should and should not do!
The use case is: The App holds different locations with latitude and longitude. While the user is walking around the app (with the phone in the pocket, screen off) needs to track if he is close to one of the locations and plays a short beep when closer than a defined range.
Would the Geofence option the best solution? These locations will also change during runtime and periodically - they might also be more than 100. Any suggestion?
I am also thinking about a background service to handle all this when the device moves. This would be more flexible and allows for the reload of the locations as well. Any thoughts?
Thanks!!
You don't actually need to build a background service to monitor if a user is close to a location. There is already a geofence provided by Google. It uses fusedlocation provider internally.
Please refer this
You can combine this with a FusedLocationProviderClient and subscribe to location updates. This way you'll get location updates as well.
In my application i want to receive location updates of different distances from the set point.
In the main activity i have several objects, and each object needs to get location event on some distance from its set point.
I hope i'm clear.
I tried the function requestLocationUpdates with its listener, but in case of several sequential requests, it fires the event only from the closest distance request.
mLocationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER, 0, 1000, locationListener);
Is there any way to do what i want ?
I do not fully understand what you are asking but it sounds like the new Awareness API might help. https://developers.google.com/awareness/
It allows you to combine location with other pieces of information and only get call backs when they all occur.
My understanding is that you need to receive updates when the device is at different specified distances from a certain point (lat-lng). If so, you may create LocationFences with radius set to your distances and register using Awareness Fence API. When you receive the fence callbacks, you can call the getLocation() snapshot to get the actual location and use that to update your app.
I hope I've interpreted your question correctly.
I am in the process of creating a custom Phonegap plugin for Android that monitors location both when the app is in the foreground and when it is backgrounded. Google documentation on using the FusedLocationProviderAPI is remarkably clear. The process I have worked out thus far is as follows
Ensure that the API is available
GoogleApiAvailability api = GoogleApiAvailability.getInstance();
int code = api.isGooglePlayServicesAvailable(ctxt);
return (code == ConnectionResult.SUCCESS);
Define a LocationListener with assigned callbacks to handle the results retured by the requestLocationUpdates method.
Create a LocationRequest
Here is where things become slightly unclear
setInterval - the interval at which the app wants location updates
setFastestInterval - the interval at which it will consume updates if available.
setSmallestDistance & setPriorty - clear enough
setNumUpdates - how this works is not clear to me. Reading between the lines I am assuming that if I use setInterval(60000) and setNumUpdates(1000) the system will keep sending back location updates for the next 6000 minutes or until such time as the app is backgrounded/shutdown or I/the user cancels location updates.
But then this begs the question - what does the app need to do to be a good citizen. I am assuming that would have to be something like this
Record the PendingResult being returned by the requestLocationUpdates call.
Detect when the onPause event occurs
call PendingResultt.cancel() prior to letting the app go to the background
I'd be much obliged if someone could comment on the correctness of this workflow.
A related issue - The documentation for PendingResult states
It is the responsibility of the caller or callback receiver to release any resources associated with the returned result.
It is not clear to me what resources they are talking about here. The LocationListener.onLocationChanged event returns a Location object which I assume will be garbage collected when it goes out of scope. Presumably the PendingResult being returned by requestLocationUpdates should be canceled and then set to null when the app goes to the background. Is there anything else one needs to do by way of releasing resources?
A few hours later
I created two versions of my test app
App 1:Sets up the LocationRequest with setNumUpdates(10000). Pops up toasts on location change in the form App 1:Location is...
App 2:Sets up the LocationRequest with setNumUpdates(1). Pops up toasts on location change in the form App 2`:Location is...
I had the two apps running simultaneously and simulated position changes with the help of a really neat little app called FakeGPS. Both App1 and App2 provided me with an update when I did my first fake location change. However, all subsequent location changes were reported only by App 1.
By inference then setNumUpdates provides a mechanism for polling for updates periodically. What is slightly confusing is that the updates continue even after the app is backgrounded - though I assume that this is largely because it is at the mercy of the OS which will kill it when it deems fit.
However, all of the above is based on empirical testing. I find surprisingly little on the setNumUpdates setting.
To your question, Is update continue even if app is in background:
Ans: What ever be the case setNumUpdates is 1 or x, when your app is in background and is still registered to update, you will get the updates, unless the OS has killed your app, for memory.
The only difference that setNumUpdates does is, as you said correctly, if its set to 1, it will give only one update, unless you has reregistered again.
Link has sufficient definition for setNumUpdates
https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.html#public-methods
I read in the documentation that I can setup geofencing so I know when the user entered/left the geofenced area.
However, I was wondering, can't I get the same behaviour by just taking my current location (ona regular location update) and check the distance with the center of the intended geofence. If it is less than a certain radius then I am within that area.
Wouldn't that have the same effect (infact way less code than geofence)?
Thank you
I believe the advantage of a geofence as opposed to what you describe, is that geofences are objects that come with a lot of extra support in Google Play Services. Using geofences allows multiple different Activities or even Applications to get notifications/callbacks from a single (or set of) geofence objects created elsewhere. These callbacks will happen automatically even if you are in another application because Google Play Services can monitor location in the background without you writing additional code in your Activity, and they can expire on their own. Doing this would be more difficult if you were to roll your own distance calculations as you would have to set it up your own Service framework.
From this answer by CommonsWare:
Geofences are designed to work even without your application running.
However, if your goal is only to check whether you are a certain distance from a geographical point (without caring about what happens when the user goes to check their email or puts their phone back in their pocket), then what you describe might be easier.
greetings
I have a ICE Cream sandwitch phone with google play service.
Installed the Geofencedetection sample app from the following
http://developer.android.com/training/location/geofencing.html
I also installed a Mock Location application
Using this mock location i have set my location as new delhi (lat 28.66 long 77.21).
Now i registered the following geofences using the sample app
Geofence1
lat 11.94898
long 79.48475
radius 1000 meters
Geofence2
lat 77.00000
long 77.00000
radius 1000 meters
Now i again opened the mock location and set the
location as (within geofence 1) my home.
lat 11.94898
long 79.48475
I have checked the mock location with the Maps application.
It displays correctly. I enabled the Allow Mock Locations
in Developer options
But the sample Geofence application is not triggering any
notification. I checked the code. As per the code it should send
notification when the user enters the geofence.
I could not understand why it is not sending notification.
Will this work only on real network?
Please let me know if i am doing anything wrong?
Best Regards
jothi
I had a similar experience using the sample MockLocation app from
http://developer.android.com/training/location/location-testing.html
For example if my geofence is set at 5,100 and I use the mocklocation app to inject a single mock location at 5,100, the geofence will not trigger.
However, if you repeatedly inject multiple mock locations at 5,100 the geofence will trigger. In my case, injecting mock locations at 5,100 three times consecutively caused my geofence to trigger. To be clear, when I say inject I mean doing:
mLocationClient.setMockLocation(yourMockLocation);
Everything below is pure speculation on my part so take it with a grain of salt:
My speculation is there is some filtering algorithm in the LocationClient backend whose purpose is to filter out spurious sensor values and it might be detecting that huge change in lat/long as a possible spurious value. Another possibility is the geofence simply doesnt trigger until it receives multiple readings that indicate you are within its vicinity.
Please check notification of app when app in foreground because i am also not getting any update for geofence in background. But in foreground, It is working fine.
I have stuck with this problem for sometime. Then eventually it turns out the google mock location tutorial (http://developer.android.com/training/location/location-testing.html) failed to mention you will need to set below two calls with the mock location you created:
/*
setElapsedRealtimeNanos() was added in API 17
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
newLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
newLocation.setTime(System.currentTimeMillis());
Without these two calls my mock location won't trigger my geofence. After calling these my geofence is triggered without issues.
I also had a similar experience with MockLocation app, just use a large number of seconds on "send interval" field, like 7 seconds, and the geofence will trigger.
After spending a ton of time on this I finally figured out that the secret was to use the provider name "network" when creating the mock location instance.
Location location = new Location("network");