1

I have exhausted everything and really appreciate some help.

What I want to do is exactly what Geofences are for: to get a notification when I enter, dwell or exit a location.

So far, the performance has been horrible, to say the least.

I have scoured SO for couple of weeks now. Here's a summary of what I have tried:

  1. Use of a broadcast receiver (instead of Service as given in the official example, not that it should change anything)
  2. Use ENTER | DEWLL | EXIT transitions, to get a hit on any transition
  3. Increase radius to 250m
  4. Periodically poll GPS for location using RequestLocationUpdates

So far, the geofences triggers are a hit and miss. Most of the time they are not triggered at all and sometimes I triggered when I have drove past the location 5 minutes ago.

The only thing that seemed to help was the periodic GPS polling, but even that, the pending intents are received at very irregular intervals.

Do you have anything else I can try? Please let me know. Thanks!!!

Here are some snippets of my code:

GPS Polling

public static void CreateLocationUpdateRequest(Context context)
{
    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setInterval(10000);
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setMaxWaitTime(10000);
    locationRequest.setExpirationDuration(1000000);

    Intent locReqIntent = new Intent("LOCATION_UPDATE");
    PendingIntent locUpdatePendingIntent = PendingIntent.getBroadcast(context, 0, locReqIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    FusedLocationProviderClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
        Task<Void> locationTask = fusedLocationProviderClient.requestLocationUpdates(locationRequest, locUpdatePendingIntent);

        locationTask.addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                Log.i("CreateGeofencingRequest", "SUCCESS");
            }
        });

        locationTask.addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.i("CreateGeofencingRequest", "FAILED");
            }
        });
    }
}

Creating Geofence request

geofenceTransitionType = Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_DWELL | Geofence.GEOFENCE_TRANSITION_EXIT;


        Geofence geofence = new Geofence.Builder()
                                .setRequestId(String.valueOf(geofencingReq.getId()))
                                .setCircularRegion(Double.parseDouble(location.getLatitude()), Double.parseDouble(location.getLongitude()), 250)
                                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                                .setTransitionTypes(geofenceTransitionType)
                                .setLoiteringDelay(2000)
                                .build();

        GeofencingRequest geofencingRequest = new GeofencingRequest.Builder()
                                                  .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
                                                  .addGeofence(geofence)
                                                  .build();

        Intent geofencingIntent = new Intent(context, GeofenceBroadcastReceiver.class);
        geofencingIntent.putExtra("ID", geofencingReq.getId());
        geofencingIntent.setAction("GEOFENCE_TRIGGER_BROADCAST");


        PendingIntent geofencingPendingIntent = PendingIntent.getBroadcast(context, geofencingReq.getId(), geofencingIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        LocationManager locManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        boolean providerEnabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

        if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            Task<Void> geofenceTask = mGeofencingClient.addGeofences(geofencingRequest, geofencingPendingIntent);

            geofenceTask.addOnSuccessListener(new OnSuccessListener<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    Log.i("CreateGeofencingRequest", "SUCCESS");
                }
            });

            geofenceTask.addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Log.i("CreateGeofencingRequest", "FAIL");
                }
            });
        }
    }
madu
  • 5,232
  • 14
  • 56
  • 96
  • 1
    How are you testing this? Geofence is a hit/miss when you test on emulator. Try testing on physical device with mock locations app like `Lockito` – NinjaCoder Nov 01 '17 at 23:44
  • Thanks. I am testing on a real device using "GeoFix" app. But now I realize that Android maybe taking more than GPS location to geofence triggers. It looks like it considers the travel route, etc. So actually trying this out on the road is the best way. – madu Nov 02 '17 at 00:54
  • [Capybara](https://stackoverflow.com/users/12388549/capybara) has the same issue. Due to his low rep he can't comment here. His comment: `i'm with the exactly same problem, you could answer how you did to resolve this? The problem was just the emulator and fake gps?` – Tschallacka Jul 14 '20 at 15:01

1 Answers1

2

I know the time has gone away but I was able to manage the same problem getting more control over fences algorithm. My solution was to implement my own geofence system based on android location api plus JTS Location Suite

To be more accurate. All you need is to create a jts polygon thats represent your fence mathematically.

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 if 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.

// lat & lon values  are given from geoloc api
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')
}
Nico
  • 858
  • 10
  • 20