Some time ago I was assigned the task of determining the change of the user's location on the map. According to the results of the experiment in the article , Google Services Geofences is perfectly suited for this purpose, in terms of definition accuracy and energy efficiency.
How to work with Geofences is described in detail in a single Russian-language example on the use of Location APIs in an article on Habré, but 2 years have passed since then, and the information is very outdated.
The author’s example on github , unfortunately, didn’t even compile, so I decided to start it under the latest versions of libraries. To my surprise, there are many changes in the API between com.google.android.gms:play-services:4.0.30
and com.google.android.gms:play-services:8.4.0
! Actually, they will be discussed further in the article.
To begin with, it is desirable to get acquainted with the original .
In the concept itself, nothing has changed, only the responsible classes have changed.
So, instead of LocationClient
we have api.GoogleApiClient
, callbacks from GooglePlayServicesClient
also moved to api.GoogleApiClient
, instead of new LocationClient(this, this, this)
a convenient builder appeared:
new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build();
An important difference is that mGoogleApiClient
is not directly mGoogleApiClient
in adding and deleting mGoogleApiClient
, but through LocationServices.GeofencingApi
.
GeofencingRequest build = ... LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, builder.build(), getPendingIntent()) .setResultCallback(new ResultCallback<Status>() { @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 the list of geozones, the GeofencingRequest
is passed, 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 features of the new builder is control over the geofence behavior at the moment of addition. For example, in the comments to the original article asked about the possibility of triggering Exit geofence
for the case when the device is outside the zone at the time of its installation. Now you can do this by passing the GeofencingRequest.INITIAL_TRIGGER_EXIT
flag via the setInitialTrigger (int initialTrigger)
method setInitialTrigger (int initialTrigger)
, by default GeofencingRequest.INITIAL_TRIGGER_ENTER
and GeofencingRequest.INITIAL_TRIGGER_DWELL
flags. Flags can be combined with each other.
In addition, the last callback parameter was removed, now LocationServices.GeofencingApi.addGeofences
returns the PendingResult
, with which you can block a stream, wait for a result or get a response asynchronously using the callback method setResultCallback(..)
. In any case, the result will be the status of the add / delete geofence operation. This callback replaces OnAddGeofencesResultListener
or onRemoveGeofencesByRequestIdsResult
from the original article.
You can remove unnecessary geofences through the methods:
LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, /*PendingIntent id */)
If earlier, the processing of trigger triggering results on the geofence used static methods from the LocationClient
class, which required the incoming Intent
as a parameter, now GeofencingEvent
, which has methods of the same name, does the same work. You can get it as follows:
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
The creation of geofences themselves remains unchanged and takes place through Geofence.Builder
.
Another of the innovations is that after the triggering of the trigger, the geofence is automatically removed, so we do not need to remove them anymore!
Also in the example code, I added another button that puts a geofence with a trigger to exit it.
For testing, I chose the Genymotion emulator, but when I tried to install the LocationServices
geofence, I got an error status code = 1000
(GEOFENCE_NOT_AVAILABLE). The solution to this problem was found on stackoverflow
If you set GeofencingRequest.INITIAL_TRIGGER_EXIT
and GeofencingRequest.INITIAL_TRIGGER_ENTER
or Geofence.GEOFENCE_TRANSITION_ENTER
and Geofence.GEOFENCE_TRANSITION_EXIT
as the trigger condition, then only one condition will work, and Geofence.GEOFENCE_TRANSITION_EXIT
will trigger only one condition.
To work with Rx, you can use this library, then the whole process of adding / deleting comes down to the code:
GeofencingRequest.Builder builder = new GeofencingRequest.Builder() ... new ReactiveLocationProvider(context). locationProvider.addGeofences(getPendingGeoIntent(),builder.build()) .subscribe(status -> { if (status.isSuccess()) {} };
References:
Source: https://habr.com/ru/post/282129/
All Articles