I am working on a Geofencing application. The JobIntentService subclass that handles the GeofenceTransitions never receives the intent. I am receiving location updates at one minute interval then creating a new geofence list then adding the geofences based on a user's current location.
Here's my AndroidManifest.xml
........
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<service
android:name=".GeofenceTransitionsService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
<receiver
android:name=".GeofenceBroadcastReceiver"
android:enabled="true"
android:exported="true" />
.............
My GeofenceBroadcastReceiver.java
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
/**
* Receives incoming intents.
*
* #param context the application context.
* #param intent sent by Location Services. This Intent is provided to Location
* Services (inside a PendingIntent) when addGeofences() is called.
*/
#Override
public void onReceive(Context context, Intent intent) {
// Enqueues a JobIntentService passing the context and intent as parameters
GeofenceTransitionsService.enqueueWork(context, intent);
}
}
My GeofenceTransitionsService.java that handles the triggered geofences
public class GeofenceTransitionsService extends JobIntentService {
..........
/**
* Convenience method for enqueuing work in to this service
* Enqueue new work to be dispatched to onHandleWork
*/
public static void enqueueWork(Context context, Intent intent) {
Log.d(TAG, "Received intent: " + intent);
enqueueWork(context, GeofenceTransitionsService.class, JOB_ID, intent);
}
#Override
protected void onHandleWork(Intent intent){
// We have received work to do. The system or framework is already
// holding a wake lock for us at this point, so we can just go.
Log.d(TAG, "Received intent: " + intent);
}
}
Here's part of my code in PointOfInterestMapFragment.java that creates a geofencing request, creates the pending intent and adds the geofences
/* Use the GeofencingRequest class and its nested GeofencingRequestBuilder
* class to specify the geofences to monitor and to set how related geofence events are
* triggered
*/
private GeofencingRequest getGeofencingRequest(){
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
//tell Location services that GEOFENCE_TRANSITION_DWELL should be triggered if the
//device is already inside the geofence
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
builder.addGeofences(mGeofenceList);
return builder.build();
}//end method getGeofencingRequest
/*Pending intent that starts the IntentService*/
private PendingIntent getGeofencePendingIntent(){
Log.d(TAG, "getPendingIntent()");
//Reuse the pending intent if we already have it
if(mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(getActivity(), GeofenceBroadcastReceiver.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
mGeofencePendingIntent = PendingIntent.getBroadcast(getActivity()
, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}//end method PendingIntent
/*Add geofences*/
#SuppressWarnings("MissingPermission")
private void addGeofence(){
if(checkPermissions()){
mGeofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Geofence added");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Failed to add geofence: " + e.getMessage());
}
})
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
//drawGeofence();
}
});
}else{
requestPermissions();
}
}//end method addGeofence
Here's the part of code in PointOfInterestMapFragment.java where I am receiving the location updates, populating the GeofenceList then adding geofences
/**
* Creates a callback for receiving location events.
*/
private void createLocationCallback() {
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
mCurrentLocation = locationResult.getLastLocation();
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
//populateGeofenceList to reflect the new current location bounds
populateGeofenceList();
addGeofence();
}
};
}
When the app executes, the I get the message in log cat from the line of code Log.d(TAG, "getPendingIntent()"); in getGeofencePendingIntent() but never get the message supposed to be displayed in onHandleWork() method
I had a similar 'problem'. The code is fine. In my case, I thought the code was not working because I had not understood properly how a geofence works. I thought to add the geofences, in your case a call to addGeofence() is the trigger, so I was waiting to see the notifications at that particular point in time. However a call to the method only adds the geofences for monitoring, then an intent is only delivered to the service when any of the filters are satisified (Geofence.GEOFENCE_TRANSITION_DWELL, Geofence.GEOFENCE_TRANSITION_EXIT OR Geofence.GEOFENCE_TRANSITION_ENTER) on an added geofence. You can read more from the documentation here
So, you might receive a Geofence added message in your log cat, but that's what it literally means, the geofences have been added not triggered. Wait for some time after the geofence have been added and if any of the filters are satisfied for a geofence that was added, then the intent is sent. So the solution that worked for me was to wait and I received the intent and notifications after some period of time.
If waiting does not work, you might want to extend the GEOFENCE_RADIUS, say to 3000 metres the check to see whether there is any change. Also, set the expiration duration to a higher value or to Geofence.NEVER_EXPIRE
This is how I add my geofences:
public void setGeofenceRequest(Location location) {
if (geofences == null) {
geofences = new ArrayList<Geofence>();
}
geofences.add(new Geofence.Builder()
.setRequestId("3")
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
.setCircularRegion(
location.getLatitude(), location.getLongitude(), PSLocationService.getInstance(context).kPSGeofencingDistanceMedium)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build());
Intent intent = new Intent(context, ReceiveTransitionsBroadcastReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (geofences.size() > 0) {
LocationServices.GeofencingApi.addGeofences(mLocationClient, geofences, pi);
Log.i("", "geof autopilot2 will set geofence for autopilot-3");
}
}
And this is my BroadcastReceiver. Where I should receive them:
public class ReceiveTransitionsBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
Log.i("","autopilot valid geof on receive transisionts broadcast receiver");
PSMotionService.getInstance(ctx).buildGoogleApiClient();
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
int transitionType = geofencingEvent.getGeofenceTransition();
Location geofenceCenter = PSApplicationClass.getInstance().pref.getGeoCenter(ctx);
if(geofencingEvent.getTriggeringLocation() != null) {
if (geofenceCenter != null) {
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString() + " / GEOFENCE CENTER: " + geofenceCenter.getLatitude() + ", " + geofenceCenter.getLongitude(), "D", Constants.TRACKER);
} else
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString(), "D", Constants.TRACKER);
}else Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver ERROR => TRIGGERING LOCATION NULL", "D", Constants.TRACKER);
if(transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
List<Geofence> triggerList = geofencingEvent.getTriggeringGeofences();
for (Geofence geofence : triggerList) {
Log.i("", "geof is s receive transition broadcast receiver " + transitionType + " GPS zone " + geofence.getRequestId());
if(geofence.getRequestId().contentEquals("3")) {
Log.i("", "geof autopilot2 ENTERED GEOFENCE will start pilot with first location");
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver check to see if should start pilot", "T", Constants.TRACKER);
PSLocationService.getInstance(ctx).fastGPS = -1;
PSLocationService.getInstance(ctx).RequestLocationUpdates();
if(PSTrip.getActiveTrip() != null) {
PSLocationService.getInstance(ctx).removeAutoPilotGeofence();
}else PSMotionService.getInstance(ctx).checkinTime = System.currentTimeMillis() / 1000;
}
}
}
}
}
Now usually it works, but not always. I would say that only about 75% of the time it should work, the geofence events are actually called. I feel like the more time since I've set the geofence, the less likely it will be to be called.
Why is this happening? Is the triggering event also being dismissed, when the app is cleaned by the garbage collector?
How can I make it so that my geofence is always being called, when the case?
EDIT:
This is my defaultConfig:
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
ndk {
moduleName "ndkVidyoSample"
}
}
I changed from a Broadcast Receiver to a IntentService:
public class PSGeofenceTransitionsIntentService extends IntentService {
private static ActivityManager manager;
private static PSGeofenceTransitionsIntentService instance;
private GeofencingClient mGeofencingClient;
Context context;
private PendingIntent mGeofencePendingIntent;
public static boolean isMyServiceRunning(Class<?> serviceClass) {
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
public static PSGeofenceTransitionsIntentService getInstance(Context context) {
if (instance == null) {
// Create the instance
instance = new PSGeofenceTransitionsIntentService(context);
}
if (!isMyServiceRunning(PSGeofenceTransitionsIntentService.class)) {
Intent bindIntent = new Intent(context, PSGeofenceTransitionsIntentService.class);
context.startService(bindIntent);
}
// Return the instance
return instance;
}
public PSGeofenceTransitionsIntentService() {
super("GeofenceTransitionsIntentService");
}
public PSGeofenceTransitionsIntentService(Context context) {
super("GeofenceTransitionsIntentService");
mGeofencingClient = LocationServices.getGeofencingClient(context);
manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
instance = this;
this.context = context;
}
protected void onHandleIntent(Intent intent) {
Log.i("", "autopilot valid geof on receive transisionts broadcast receiver");
PSMotionService.getInstance(context).buildGoogleApiClient();
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
int transitionType = geofencingEvent.getGeofenceTransition();
Location geofenceCenter = PSApplicationClass.getInstance().pref.getGeoCenter(context);
if (geofencingEvent.getTriggeringLocation() != null) {
if (geofenceCenter != null) {
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString() + " / GEOFENCE CENTER: " + geofenceCenter.getLatitude() + ", " + geofenceCenter.getLongitude(), "D", Constants.TRACKER);
} else
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString(), "D", Constants.TRACKER);
} else
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver ERROR => TRIGGERING LOCATION NULL", "D", Constants.TRACKER);
if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
List<Geofence> triggerList = geofencingEvent.getTriggeringGeofences();
for (Geofence geofence : triggerList) {
Log.i("", "geof is s receive transition broadcast receiver " + transitionType + " GPS zone " + geofence.getRequestId());
if (geofence.getRequestId().contentEquals("3")) {
Log.i("", "geof autopilot2 ENTERED GEOFENCE will start pilot with first location");
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver check to see if should start pilot", "T", Constants.TRACKER);
PSLocationService.getInstance(context).isLocationRequestsOn = -1;
PSLocationService.getInstance(context).RequestLocationUpdates();
if (PSTrip.getActiveTrip() != null) {
removeAutoPilotGeofence();
} else
PSMotionService.getInstance(context).checkinTime = System.currentTimeMillis() / 1000;
}
}
}
}
public void removeAutoPilotGeofence() {
try {
Log.i("", "autopilot remove autopilot geofence");
List<String> list = new ArrayList<String>();
list.add("3");
if(mGeofencingClient == null)
mGeofencingClient = LocationServices.getGeofencingClient(context);
mGeofencingClient.removeGeofences(list).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Utils.appendLog("GEOFENCE removeAutoPilotGeofence Success removing geofences!", "I", Constants.TRACKER);
Log.i("", "GEOFENCE removeAutoPilotGeofence Success removing geofences!");
PSApplicationClass.getInstance().pref.setGeoCenterString(context, "-1");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Utils.appendLog("GEOFENCE removeAutoPilotGeofence FAILURE removing geofences!" + e.getMessage(), "I", Constants.TRACKER);
Log.i("", "GEOFENCE removeAutoPilotGeofence FAILURE removing geofences!" + e.getMessage());
}
});
Utils.appendLog("GEOFENCE: Disabling geofence done removeAutoPilotGeofence", "E", Constants.TRACKER);
} catch (final Exception e) {
if (e.getMessage().contains("GoogleApiClient") && e.getMessage().contains("not connected")) {
PSLocationService.getInstance(context).startLocationClient();
Handler han = new Handler();
han.postDelayed(new Runnable() {
#Override
public void run() {
Utils.appendLog("autopilot2 error will try again", "E", Constants.TRACKER);
removeAutoPilotGeofence();
}
}, 1000);
}
Log.i("", "autopilot2 error replaceFragment autopilot geofence:" + e.getMessage());
Utils.appendLog("autopilot2 error replaceFragment autopilot geofence:" + e.getMessage(), "E", Constants.TRACKER);
}
}
public void setGeofenceRequest(final Location location) {
ArrayList geofences = new ArrayList<>();
geofences.add(new Geofence.Builder()
.setRequestId("3")
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
.setCircularRegion(
location.getLatitude(), location.getLongitude(), PSLocationService.kPSGeofencingDistanceMedium)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build());
//ADDING GEOFENCES
if (geofences.size() > 0) {
if(mGeofencingClient == null)
mGeofencingClient = LocationServices.getGeofencingClient(context);
mGeofencingClient.addGeofences(getGeofencingRequest(location, geofences), getGeofencePendingIntent()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
RealmLocation realmLocation = new RealmLocation(location.getLatitude(), location.getLongitude(), location.getTime() / 1000, null, true);
realmLocation.setAccuracy(location.getAccuracy());
realmLocation.setSpeed(location.getSpeed());
PSApplicationClass.getInstance().pref.setGeoCenter(realmLocation, context);
Utils.appendLog("GEOFENCE setGeofenceRequest Success adding geofences!" + location.getLatitude() + " / " + location.getLongitude(), "I", Constants.TRACKER);
Log.i("", "GEOFENCE setGeofenceRequest Success adding geofences! " + location.getLatitude() + " / " + location.getLongitude());
PSLocationService.getInstance(context).stopLocationClient();
PSMotionService.getInstance(context).buildGoogleApiClient();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Utils.appendLog("GEOFENCE setGeofenceRequest FAILURE adding geofences!" + e.getMessage(), "I", Constants.TRACKER);
Log.i("", "GEOFENCE setGeofenceRequest FAILURE adding geofences!" + e.getMessage());
}
});
Log.i("", "geof autopilot2 will set geofence for autopilot-3");
}
}
/**
* Gets a PendingIntent to send with the request to add or remove Geofences. Location Services
* issues the Intent inside this PendingIntent whenever a geofence transition occurs for the
* current list of geofences.
*
* #return A PendingIntent for the IntentService that handles geofence transitions.
*/
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(context, PSGeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Builds and returns a GeofencingRequest. Specifies the list of geofences to be monitored.
* Also specifies how the geofence notifications are initially triggered.
*/
private GeofencingRequest getGeofencingRequest(Location location, ArrayList<Geofence> geofences) {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
// The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a
// GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device
// is already inside that geofence.
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_EXIT);
// Add the geofences to be monitored by geofencing service.
builder.addGeofences(geofences);
// Return a GeofencingRequest.
return builder.build();
}
}
I have in it also the code to remove and add the geofences, and the listener always goes into onSuccess regarding adding them.
For starters, I would not put this code inside a BroadcastReceiver.
Besides being bad practice, the component might be shutdown before the code has finished executing.
Please consider starting a Service from your Receiver, if you need to run code that might take some time.
Otherwise for a short execution time, you may use an IntentService.
By looking at your code, I'm aware of two reasons your Geofences are not working as expected:
1) The nature of Geofences
Geofences API retrieves your location mostly from WiFi / Cellular Data, which is often unavailable.
I tried to use Geofences once, and I found them very inaccurate. I switched to LocationManager making it use pure GPS location and it met my expectations.
Please see this answer, which advises to
Poll the GPS hardware on an interval without doing anything with the result and you'll start getting more accurate geofences.
I have never tried Google's FusedLocation API, but I have heard people saying it worked very well for them.
If you use LocationManager, you will have to implement your 'Geofencing logic' yourself; you can easily do it with Location.distanceTo(Location).
Example:
final float distanceFromCenter = currentLocation.distanceTo(this.destination);
if (distanceFromCenter <= YOUR_RADIUS_IN_METERS) {
// you are inside your geofence
}
2) CPU is not active
The fact that the Geofences are active, does not necessarily mean that your phone is awake and computing location checks.
To fix that, you can start a ForegroundService from your BroacastReceiver. The Service should hold a partial WakeLock as well.
This guarantees that:
The OS does not kill the service (or better: less chance to be killed...)
The user is aware of the service and can dismiss it if necessary
The CPU is running. Therefore you can be sure that the code that retrieves the location is running (please remember to to release the WakeLock when the service stops).
Please note that Android may still kill your service if necessary.
You can find plenty of examples on the web on how to start a ForegroundService from a BroadcastReceiver, how to hold a WakeLock and so on...
Also, check out to the new Android O API, that brought some minor changes to the ForegroundService and other components.
PS: I have developed and application that uses all the components mentioned above (except for the FusedLocation) and I was extremely satisfied.
EDIT: Answering OP's questions
Okey, let's try to make some order here, otherwise future readers may easily get confused. I'll start by answering what written in the original question and the 'bounty banner', then the OP edits, and finally the questions the OP placed in the comments.
1) Original question
Is the triggering event also being dismissed, when the app is cleaned by the garbage collector?
Most probably yes. See this answer where OP implemented a service that runs in a separate process, in order to make geofence be triggered even when the app is killed.
I need to understand what causes the geofences not to get called, if enough time has passed
Plenty of reasons. See my original answer.
I saw an implementation of the geofence logic with an Service instead of a broadcast receiver, will that work better?
A Receiver and a Service are two different things. Please read Android's documentation. You can start a Service from a BroadcastReceiver, which is usually the preferred way to 'receive' PendingIntents and do something with them.
2) Edits
Please note that I did not tell you to replace the BroadcastReceiver with a Service, but that it might be a good idea to start a Service from your Receiver and handle all your logic there.
Making your IntentService a Singleton class is not necessary as (from IntentService documentation)
All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
Do not store Context into a Singleton class or some static references. I'm impressed Android Studio did not warn you.
3) Comments
I need this to work 24/7 hence I cannot use the location all the time, cause of obvious battery issues.
Please read Android Oreo Background Execution Limits. This might be an issue for you.
Also now that I changed to a intentService, is that enough to ensure it should stay awake?
No, as I said, you probably need a partial WakeLock in order to turn on the CPU.
Do I need to initiate it another way, in order to keep it in the foreground?
Yes. In order to start a Foreground Service, you need to call startForeground(int, Notification)
Please note: IntentServices lifespan is limited to the end of the onHandleIntent() function. They are not supposed to live for more than a few seconds, typically. Use the Service class if you want to start a Foreground.
Moreover, as said in the original answer, a new Foreground API is available and preferred for Android Oreo.
Not a question, just a notice: I need to use here Geofencing. (Geofencing will start if necessary the gps
Ok perfect. See what works best for you.
I have an app which requests activity updates via the ActivityRecognition API. When I load it on a device with Android For Work, the work version of the app never gets activity updates, but the personal version works fine.
I'm checking the result from requestActivityUpdates() as shown below, and I get success, but never actually get any activity intents.
Intent intent = new Intent(context, BreakTimeMonitor.class);
intent.setAction(ACTION_DETECT_ACTIVITY);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Register for activity updates
PendingResult<Status> pendingResult = ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(
googleApiClient,
DETECTION_INTERVAL,
pendingIntent);
pendingResult.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (!status.isSuccess()) {
Log.e(TAG, "Error requesting activity updates: " + status.getStatusCode() + "," + status.getStatusMessage());
} else {
Log.d(TAG, "Request for activity updates successful: " + status.getStatusCode() + "," + status.getStatusMessage());
}
}
});
EDIT: The manifest does have an entry for the BreakTimeMonitor service, so that's not the issue. I've also tried adding a different user account to see if that worked, and it did - so it's just the work account that doesn't work.
I'm using the relatively new version of the Geofence API in Google's Play Services. The issue (apart from outdated documentation) being that the received Intents (after a geofence exit) don't seem to contain any data.
I add a category myself and this category is successfully retrieved.
I added a serializeable extra too which is perfectly retrieved from the intent.
The above two arguments prove that the intent I am receiving is in fact the one I scheduled for a geofence transition.
What doesn't work.
The GeofenceEvent seems to return defaults for all methods. So I get;
false for hasError()
and -1 for getErrorCode()
getGeofenceTransition() returns -1
the list returned by getTriggeredGeofences() is empty
This seems to contradict the API documentation...
getGeofenceTransition();
-1 if the intent specified in fromIntent(Intent) is not generated for a transition alert; Otherwise returns the GEOFENCE_TRANSITION_ flags value defined in Geofence.
with getErrorCode();
the error code specified in GeofenceStatusCodes or -1 if hasError() returns false.
and hasError();
true if an error triggered the intent specified in fromIntent(Intent), otherwise false
How I add the Geofence
// create fence
Geofence fence = new Geofence.Builder()
.setRequestId(an_id)
.setCircularRegion(lat, long, radius)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
.setExpirationDuration(240 * 60 * 1000)
.build();
// create pendingintent
Intent launch = new Intent(this, same_class.class);
launch.putExtra(extra_object_key, extra_object);
launch.addCategory(category_name);
PendingIntent intent = PendingIntent.getService(
getApplicationContext(),
0, launch,
PendingIntent.FLAG_UPDATE_CURRENT
);
// create add fence request
GeofencingRequest request = new GeofencingRequest.Builder()
.addGeofence(fence)
.build();
// add fence request
PendingResult<Status> result = LocationServices.GeofencingApi.addGeofences(googleApiClient,
request, intent);
// we'll do this synchronous
Status status = result.await();
if (status.getStatusCode() != GeofenceStatusCodes.SUCCESS)
{
Log.e(SERVICE_NAME, "Failed to add a geofence; " + status.getStatusMessage());
return false;
}
How I receive the intent
protected void onHandleIntent(Intent intent)
{
if (intent.hasCategory(category_name))
{
GeofencingEvent event = GeofencingEvent.fromIntent(intent);
if (event.hasError())
{
Log.e(SERVICE_NAME, "Failed geofence intent, code: " + event.getErrorCode());
}
else
{
// checked 'event' here with debugger and prints for the stuff I wrote above
switch (event.getGeofenceTransition())
{
case Geofence.GEOFENCE_TRANSITION_EXIT:
// NEVER REACHED
type extra_object = (type)intent.getSerializableExtra(extra_object_key);
onGeofenceExit(extra_object);
break;
default:
// ALWAYS END UP HERE
throw new AssertionError("Geofence events we didn't register for.");
}
}
}
else
{
throw new AssertionError("Received unexpected intent.");
}
}
Connecting to play services
public void onCreate()
{
super.onCreate();
googleApiAvailable = false;
GoogleApiClient.Builder gApiBuilder = new GoogleApiClient.Builder(getApplicationContext());
gApiBuilder.addApi(LocationServices.API);
gApiBuilder.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks()
{
#Override
public void onConnected(Bundle bundle)
{
googleApiAvailable = true;
}
#Override
public void onConnectionSuspended(int i)
{
googleApiAvailable = false;
}
});
gApiBuilder.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener()
{
#Override
public void onConnectionFailed(ConnectionResult connectionResult)
{
googleApiAvailable = false;
}
});
googleApiClient = gApiBuilder.build();
}
Where do you connect to googleApiClient? There should be
googleApiClient.connect();
and wait for onConnected or
googleApiClient.blockingConnect();
in you code but I can't see it. Post whole code.
I use Geofences in my app, everything works fine except the removal of triggered geofences.
I red the guide from the official documentation of Android but they don't explain how to remove a geofence inside of the IntentService.
Here is the code of the event handler of the service:
#Override
protected void onHandleIntent(Intent intent)
{
Log.e("GeofenceIntentService", "Location handled");
if (LocationClient.hasError(intent))
{
int errorCode = LocationClient.getErrorCode(intent);
Log.e("GeofenceIntentService", "Location Services error: " + Integer.toString(errorCode));
}
else
{
int transitionType = LocationClient.getGeofenceTransition(intent);
if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER)
{
List <Geofence> triggerList = LocationClient.getTriggeringGeofences(intent);
String[] triggerIds = new String[triggerList.size()];
for (int i = 0; i < triggerIds.length; i++)
{
// Store the Id of each geofence
triggerIds[i] = triggerList.get(i).getRequestId();
Picture p = PicturesManager.getById(triggerIds[i], getApplicationContext());
/* ... do a lot of work here ... */
}
}
else
Log.e("ReceiveTransitionsIntentService", "Geofence transition error: " + Integer.toString(transitionType));
}
}
How can I delete the geofence after he got triggered ?
You can do something like this:
LocationServices.GeofencingApi.removeGeofences(
mGoogleApiClient,
// This is the same pending intent that was used in addGeofences().
getGeofencePendingIntent()
).setResultCallback(this); // Result processed in onResult().
And your getGeofencePendingIntent() method can look like this:
/**
* Gets a PendingIntent to send with the request to add or remove Geofences. Location Services
* issues the Intent inside this PendingIntent whenever a geofence transition occurs for the
* current list of geofences.
*
* #return A PendingIntent for the IntentService that handles geofence transitions.
*/
private PendingIntent getGeofencePendingIntent() {
Log.d(TAG, "getGeofencePendingIntent()");
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(mContext, GeofenceIntentServiceStub.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
mGeofencePendingIntent = PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}
You would proceed as you did when adding Geofences (create a LocationClient and wait for it to connect). Once it is connected, in the onConnected callback method, you would call removeGeofences on the LocationClient instance instead and pass it a list of request IDs you want to remove and an instance of OnRemoveGeofencesResultListener as a callback handler.
Of course, you must use the same request IDs you used when creating the GeoFence with GeoFence.Builder's setRequestId.
#Override
public void onConnected(Bundle arg0) {
locationClient.removeGeofences(requestIDsList,
new OnRemoveGeofencesResultListener() {
...
});