Back to Home

Working with geofences in Android. Update

android · geofences · android development · location · user location · lockito

Working with geofences in Android. Update

Some time ago, I was tasked with determining the change in the user's location on the map. According to the results of the experiment in the article , Google Services Geofences is perfect for these purposes, in terms of accuracy and energy efficiency.


image


How to work with Geofences is described in detail in the only Russian-language example on the use of Location APIs in an article on the hub, but 2 years have passed since then, and the information is very outdated.


Unfortunately, the author’s example on github didn’t even compile, so I decided to get it under the latest versions of libraries. To my surprise, API changes between com.google.android.gms:play-services:4.0.30and com.google.android.gms:play-services:8.4.0there were many! Actually, they will be discussed further in the article.


The code

Updated example on github (at the time of writing, the author of the original example did not accept pull request).


So what are the differences?


For starters, it is advisable to familiarize yourself with the original .


Nothing has changed in the concept itself, only responsible classes have changed.
So, instead LocationClient, we have api.GoogleApiClient, of callbacks GooglePlayServicesClientalso moved in api.GoogleApiClientinstead new LocationClient(this, this, this)appeared friendly builder:


    new GoogleApiClient.Builder(this)
        .addApi(LocationServices.API)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .build();

An important difference is that the addition and removal of geofences mGoogleApiClientis not done directly, but through LocationServices.GeofencingApi.


    GeofencingRequest build = ...
    LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, builder.build(), getPendingIntent())
            .setResultCallback(new ResultCallback() {
                @Override
                public void onResult(@NonNull Status status) {
                    if (status.isSuccess()) {
                        String msg = "Geofences added: " + status.getStatusMessage();
                        Log.e("GEO", msg);
                        Toast.makeText(GeofencingService.this, msg, Toast.LENGTH_SHORT)
                                .show();
                    }
                    GeofencingService.this.onResult(status);
                }
            });

The first parameter has also changed: instead of a list of geofences, it is transmitted GeofencingRequest, which can be obtained through a special builder:


    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(mGeofenceListsToAdd);
    GeofencingRequest build = builder.build();

One of the capabilities of the new builder is to control the behavior of geofences at the time of addition. For example, in the comments on the original article they asked about the possibility of a trigger Exit geofencefor the case when the device is outside the zone at the time of its installation. Now this can be done by passing the flag GeofencingRequest.INITIAL_TRIGGER_EXITthrough the method setInitialTrigger (int initialTrigger), the default flags are GeofencingRequest.INITIAL_TRIGGER_ENTERand GeofencingRequest.INITIAL_TRIGGER_DWELL. Flags can be combined with each other.


In addition, the last callback parameter was removed, now it LocationServices.GeofencingApi.addGeofencesreturns instead PendingResult, with which you can, by blocking the stream, wait for the result or receive a response asynchronously using the callback method setResultCallback(..). In any case, the result will be the status of the operation of adding / removing geofences. This callback replaces OnAddGeofencesResultListeneror onRemoveGeofencesByRequestIdsResultfrom the original article.


You can delete unnecessary geofences through methods:


    LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, /*PendingIntent или список id геозон*/)

Changes to ReceiveTransitionsIntentService


If earlier for processing the results of triggering of triggers on the geofence, static methods from the class LocationClientwere used that required the comer as a parameter Intent, but now it is engaged in it GeofencingEvent, which has the same methods for doing the same work. You can get it as follows:


GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

The creation of the geofences themselves remains unchanged and occurs through Geofence.Builder.


Another innovation is that after the trigger is triggered, the geofence is deleted automatically, so we do not need to remove them ourselves anymore!


Also in the example code, I added another button that puts a geofence with a trigger to exit it.


A few tips for working with geofences


  • For testing, I chose the Genymotion emulator, but when I tried to set the geofence I got an LocationServiceserror status code = 1000(GEOFENCE_NOT_AVAILABLE). The solution to this problem was found on stackoverflow


  • If you set triggers GeofencingRequest.INITIAL_TRIGGER_EXITand GeofencingRequest.INITIAL_TRIGGER_ENTERand Geofence.GEOFENCE_TRANSITION_ENTERand as the triggering condition Geofence.GEOFENCE_TRANSITION_EXIT, then only one condition from each pair will work, after which the zone will be deleted


  • You can use this library to work with Rx , then the whole process of adding / removing is reduced to code:


    GeofencingRequest.Builder builder = new GeofencingRequest.Builder()
    ...
    new ReactiveLocationProvider(context).
    locationProvider.addGeofences(getPendingGeoIntent(),builder.build())
    .subscribe(status -> {
        if (status.isSuccess()) {}
    };

  • Unfortunately, if you simply change the coordinates through the built-in card in Genymotion, then the triggers do not work. But everything works fine through lockito !

References:


Read Next