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.
Related
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
I'm trying to use the Google GeofencingClient (replacement of GeofencingApi) on Android. After setting up and adding the geofence, I do get a callback from the success listener and also receive the initial geofence entered/exited event in my IntentService. However, I do not receive any subsequent geofence events as I move in and out of the area. I have my radius set to 200 meters. Here's the code I'm using to add the geofence:
Geofence.Builder geoBuilder = new Geofence.Builder();
geoBuilder.setRequestId(Constants.GEOFENCE_ID);
geoBuilder.setNotificationResponsiveness(5 * 60 * 1000);
geoBuilder.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT);
geoBuilder.setCircularRegion(latitude, longitude, 200f);
geoBuilder.setExpirationDuration(Geofence.NEVER_EXPIRE);
GeofencingRequest.Builder reqBuilder = new GeofencingRequest.Builder();
reqBuilder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_EXIT);
reqBuilder.addGeofence(geoBuilder.build());
PendingIntent pi = PendingIntent.getService(context, 0,
new Intent(context, GService.class), PendingIntent.FLAG_UPDATE_CURRENT);
GeofencingClient client = LocationServices.getGeofencingClient(context);
Task task = client.addGeofences(reqBuilder.build(), pi);
task.addOnSuccessListener(new OnSuccessListener() {
#Override
public void onSuccess(Object o) {
Toast.makeText(context, "SUCCESS", Toast.LENGTH_SHORT).show();
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(context, "FAIL", Toast.LENGTH_SHORT).show();
}
});
In my IntentService, I log an entry when geofencingEvent.hasError() returns true. I have however not seen any error log entries, so no geofencing event has been posted to the service at all after the initial event.
Any help would be appreciated!
I am having the opposite issue, after switching from LocationServices.GeofencingApi.addGeofences to geofencingClient.addGeofences I no longer get the initial "enter" intent fired when I put the geofence just around my current location.
Before (did print "success" and fire the intent):
LocationServices.GeofencingApi.addGeofences(
googleApiClient,
request,
createGeofencePendingIntent()
).setResultCallback(status -> {
Log.i(TAG, "onResult: " + status);
if (status.isSuccess()) {
mapsActivity.message("Adding geo-fence... Success");
drawGeofence(position, radius);
} else {
mapsActivity.message("Adding geo-fence... FAILED!");
}
});
After (does print "success" but does not fire initial "Enter"):
geofencingClient.addGeofences(request, createGeofencePendingIntent())
.addOnSuccessListener((Void)->{
mapsActivity.message("Adding geo-fence... Success");
drawGeofence(position, radius);
}).addOnFailureListener((Void)->{
// TODO: inform about fail
mapsActivity.message("Adding geo-fence... FAILED!");
})
;
Update: After writing this, it just started working (no code change). The only difference is that in the meanwhile the app "Google" updated to version 7.18.50.21.arm64
I am making calling app using twilio sdk i have integrated all stuff and it is in wokring condition. But the problem is when my android app remains in the background for long time then app didn't receive incoming call request , pending intent not invoked by Twilio Here is the code:
if (!Twilio.isInitialized()) {
/*
* Needed for setting/abandoning audio focus during call
*/
Twilio.initialize(context, new Twilio.InitListener() {
/*
* Now that the SDK is initialized we can register using a Capability Token.
* A Capability Token is a JSON Web Token (JWT) that specifies how an associated Device
* can interact with Twilio services.
*/
#Override
public void onInitialized() {
isInitialized = true;
Twilio.setLogLevel(Log.VERBOSE);
/*
* Retrieve the Capability Token from your own web server
*/
retrieveCapabilityToken();
}
#Override
public void onError(Exception e) {
isInitialized = false;
Logging.e(TAG, e.toString());
notifyOnStopListening(null,-1,context.getString(R.string.error_message_not_connecting));
}
});
} else {
retrieveCapabilityToken();
}
}
private void retrieveCapabilityToken() {
// in the success of api response
if (clientDevice == null) {
clientDevice = Twilio.createDevice(capabilityToken,deviceListener);
}
if(clientDevice != null) {
Intent intent = new Intent(context, ReceiveCallActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
clientDevice.setIncomingIntent(pendingIntent);
isIntentAdded = true;
Logging.e(TAG,"in addPendingIntent intent added");
}
}
Any suggestion , help would be appreciated.
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() {
...
});
I have started developing on Android with last Location services feature : Geofences !! Is there any known problem with mock location provider ? Following example here (https://developer.android.com/training/location/geofencing.html) my intent service never fired even if the current location is inside geofence. I'm using FakeGPS android app as mock location provider and if I simulate a route I see the location changes on Google Maps app, so the mock location provider is working well. Any ideas ?
Thanks.
Paolo.
I tried forever to get this to work. What a pain Google! Since it says geofences can easily be tested using mocks.
The magic trick is to use the provider name "network" in the Location passed to setMockLocation.
Location location = new Location("network");
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setTime(new Date().getTime());
location.setAccuracy(3.0f);
location.setElapsedRealtimeNanos(System.nanoTime());
LocationServices.FusedLocationApi.setMockLocation(_googleApiClient, location);
Actually Intent service used in the mentioned example works good if your app is in foreground but when the app is in background, this IntentService is never called.So we need to use Broadcast-Receiver instead of Intent service.
I found this blog helpful in getting solution.
http://davehiren.blogspot.in/2015/01/android-geofence-stop-getting.html
Geofences use FusedLocationProviderApi so to mock them you have to use FusedLocationProviderApi.setMockLocation
Make sure to enable mock locations on your phone. Select Settings->Developer Options->"Allow mock locations".
LocationServices.FusedLocationApi.setMockMode(googleApiClient, true)
needs to be used before setting Mock Location.
You can use broadcast receiver instead of activity like this
public class GeofenceReceiver extends BroadcastReceiver
implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
ResultCallback<Status>{
GoogleApiClient mGoogleApiClient;
PendingIntent mGeofencePendingIntent ;
Context mContext;
#Override
public void onReceive(Context context, Intent intent) {
mContext = context;
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addOnConnectionFailedListener(this)
.addConnectionCallbacks(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
try {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
// The GeofenceRequest object.
getGeofencingRequest(),
getGeofencePendingIntent()
).setResultCallback(this); // Result processed in onResult().
} catch (SecurityException securityException) {
Log.i(getClass().getSimpleName(),securityException.getMessage());
}
}
// Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
/**
* Runs when the result of calling addGeofences() and removeGeofences() becomes available.
* Either method can complete successfully or with an error.
*
* Since this activity implements the {#link ResultCallback} interface, we are required to
* define this method.
*
* #param status The Status returned through a PendingIntent when addGeofences() or
* removeGeofences() get called.
*/
#Override
public void onResult(#NonNull Status status) {
if (status.isSuccess()) {
Log.i(getClass().getSimpleName(),"Success");
} else {
// Get the status code for the error and log it using a user-friendly message.
Log.i(getClass().getSimpleName(),getErrorString(status.getStatusCode()));
}
}
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL);
builder.addGeofences(getGeofecne());
return builder.build();
}
private List<Geofence> getGeofecne(){
List<Geofence> mGeofenceList = new ArrayList<>();
//add one object
mGeofenceList.add(new Geofence.Builder()
// Set the request ID of the geofence. This is a string to identify this
// geofence.
.setRequestId("key")
// Set the circular region of this geofence.
.setCircularRegion(
25.768466, //lat
47.567625, //long
50) // radios
// Set the expiration duration of the geofence. This geofence gets automatically
// removed after this period of time.
//1000 millis * 60 sec * 5 min
.setExpirationDuration(1000 * 60 * 5)
// Set the transition types of interest. Alerts are only generated for these
// transition. We track entry and exit transitions in this sample.
.setTransitionTypes(
Geofence.GEOFENCE_TRANSITION_DWELL)
//it's must to set time in millis with dwell transition
.setLoiteringDelay(3000)
// Create the geofence.
.build());
return mGeofenceList;
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(mContext, GeofenceTransitionsIntentService.class);
return PendingIntent.getService(mContext, 0, intent, PendingIntent.
FLAG_UPDATE_CURRENT);
}
}
check out my repo, there is a full example of using geofence
https://github.com/3zcs/Geofence