a0e3eb7ba2
Change-Id: Ibc9e2561cd93e73eab87a78f4aabe64ae9c8a0b3
307 lines
14 KiB
Plaintext
307 lines
14 KiB
Plaintext
page.title=Creating and Monitoring Geofences
|
|
|
|
trainingnavtop=true
|
|
@jd:body
|
|
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#RequestGeofences">Set up for Geofence Monitoring</a></li>
|
|
<li><a href="#CreateAdd">Create and Add Geofences</a></li>
|
|
<li><a href="#HandleGeofenceTransitions">Handle Geofence Transitions</a></li>
|
|
<li><a href="#StopGeofenceMonitoring">Stop Geofence Monitoring</a></li>
|
|
|
|
</ol>
|
|
|
|
<h2>You should also read</h2>
|
|
<ul>
|
|
<li>
|
|
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<h2>Try it out</h2>
|
|
|
|
<ul>
|
|
<li>
|
|
<a href="https://github.com/googlesamples/android-play-location/tree/master/Geofencing"
|
|
class="external-link">Geofencing</a>
|
|
</li>
|
|
</ul>
|
|
|
|
</div>
|
|
</div>
|
|
<p>
|
|
Geofencing combines awareness of the user's current location with awareness of the user's
|
|
proximity to locations that may be of interest. To mark a
|
|
location of interest, you specify its latitude and longitude. To adjust the proximity for the
|
|
location, you add a radius. The latitude, longitude, and radius define a geofence, creating a
|
|
circular area, or fence, around the location of interest.
|
|
</p>
|
|
<p>
|
|
You can have multiple active geofences, with a limit of 100 per device user. For each geofence,
|
|
you can ask Location Services to send you entrance and exit events, or you can specify a
|
|
duration within the geofence area to wait, or <em>dwell</em>, before triggering an event. You
|
|
can limit the duration of any geofence by specifying an expiration duration in milliseconds.
|
|
After the geofence expires, Location Services automatically removes it.
|
|
</p>
|
|
|
|
<img src="{@docRoot}images/training/geofence@2x.png"
|
|
srcset="{@docRoot}images/training/geofence.png 1x, {@docRoot}images/training/geofence@2x.png 2x" alt=""
|
|
width="400" height="400"/>
|
|
<p>
|
|
This lesson shows you how to add and remove geofences, and then listen for geofence transitions
|
|
using an {@link android.app.IntentService}.</p>
|
|
|
|
<h2 id="RequestGeofences">Set up for Geofence Monitoring</h2>
|
|
<p>
|
|
The first step in requesting geofence monitoring is to request the necessary permission.
|
|
To use geofencing, your app must request
|
|
{@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. To request this
|
|
permission, add the following element as a child element of the
|
|
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>
|
|
element in your app manifest:
|
|
</p>
|
|
<pre>
|
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
|
</pre>
|
|
|
|
<p>
|
|
If you want to use an {@link android.app.IntentService} to listen for geofence transitions,
|
|
add an element specifying the service name. This element must be
|
|
a child of the <code><a href="{@docRoot}guide/topics/manifest/application-element.html">
|
|
<application></a></code> element:
|
|
</p>
|
|
|
|
<pre>
|
|
<application
|
|
android:allowBackup="true">
|
|
...
|
|
<service android:name=".GeofenceTransitionsIntentService"/>
|
|
<application/>
|
|
</pre>
|
|
|
|
<p>To access the location APIs, you need to create an instance of the
|
|
Google Play services API client. To learn how to connect your client, see
|
|
<a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect
|
|
to Google Play Services</a>.</p>
|
|
|
|
<h2 id="CreateAdd">Create and Add Geofences</h2>
|
|
|
|
<p>Your app needs to create and add geofences using the location API's builder class for
|
|
creating Geofence objects, and the convenience class for adding them. Also, to handle the
|
|
intents sent from Location Services when geofence transitions occur, you can define a
|
|
{@link android.app.PendingIntent} as shown in this section.
|
|
</p>
|
|
|
|
<h3>Create geofence objects</h3>
|
|
|
|
<p>
|
|
First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.
|
|
html">Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
|
|
transition types for the geofence. For example, to populate a list object named
|
|
{@code mGeofenceList}:
|
|
</p>
|
|
|
|
<pre>
|
|
mGeofenceList.add(new Geofence.Builder()
|
|
// Set the request ID of the geofence. This is a string to identify this
|
|
// geofence.
|
|
.setRequestId(entry.getKey())
|
|
|
|
.setCircularRegion(
|
|
entry.getValue().latitude,
|
|
entry.getValue().longitude,
|
|
Constants.GEOFENCE_RADIUS_IN_METERS
|
|
)
|
|
.setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
|
|
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
|
|
Geofence.GEOFENCE_TRANSITION_EXIT)
|
|
.build());
|
|
</pre>
|
|
|
|
<p>This example pulls data from a constants file. In actual practice, apps might
|
|
dynamically create geofences based on the user's location.</p>
|
|
|
|
<h3>Specify geofences and initial triggers</h3>
|
|
|
|
<p>
|
|
The following snippet uses the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html">
|
|
GeofencingRequest</a></code> class
|
|
and its nested <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.Builder.html">
|
|
GeofencingRequestBuilder</a></code> class to
|
|
specify the geofences to monitor and to set how related geofence events are triggered:
|
|
</p>
|
|
<pre>
|
|
private GeofencingRequest getGeofencingRequest() {
|
|
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
|
|
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
|
|
builder.addGeofences(mGeofenceList);
|
|
return builder.build();
|
|
}
|
|
</pre>
|
|
|
|
<p>
|
|
This example shows the use of two geofence triggers. The <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
|
|
GEOFENCE_TRANSITION_ENTER</a></code>
|
|
transition triggers when a device enters a geofence, and the <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">
|
|
GEOFENCE_TRANSITION_EXIT</a></code>
|
|
transition triggers when a device exits a geofence. Specifying
|
|
<code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_ENTER">
|
|
INITIAL_TRIGGER_ENTER</a></code> tells Location services that
|
|
<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
|
|
GEOFENCE_TRANSITION_ENTER</a></code>
|
|
should be triggered if the the device is already inside the geofence.</p>
|
|
</p>
|
|
|
|
<p>In many cases, it may be preferable to use instead <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_DWELL">
|
|
INITIAL_TRIGGER_DWELL</a></code>,
|
|
which triggers events only when the user stops for a defined duration within a geofence.
|
|
This approach can help reduce "alert spam" resulting from large numbers notifications when a
|
|
device briefly enters and exits geofences. Another strategy for getting best results from your
|
|
geofences is to set a minimum radius of 100 meters. This helps account for the location accuracy
|
|
of typical WiFi networks, and also helps reduce device power consumption.
|
|
</p>
|
|
|
|
<h3>Define an Intent for geofence transitions</h3>
|
|
<p>
|
|
The {@link android.content.Intent} sent from Location Services can trigger various actions in
|
|
your app, but you should <i>not</i> have it start an activity or fragment, because components
|
|
should only become visible in response to a user action. In many cases, an
|
|
{@link android.app.IntentService} is a good way to handle the intent. An
|
|
{@link android.app.IntentService} can post a notification, do long-running background work,
|
|
send intents to other services, or send a broadcast intent. The following snippet shows how
|
|
to define a {@link android.app.PendingIntent} that starts an {@link android.app.IntentService}:
|
|
</p>
|
|
<pre>
|
|
public class MainActivity extends FragmentActivity {
|
|
...
|
|
private PendingIntent getGeofencePendingIntent() {
|
|
// Reuse the PendingIntent if we already have it.
|
|
if (mGeofencePendingIntent != null) {
|
|
return mGeofencePendingIntent;
|
|
}
|
|
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
|
|
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
|
|
// calling addGeofences() and removeGeofences().
|
|
return PendingIntent.getService(this, 0, intent, PendingIntent.
|
|
FLAG_UPDATE_CURRENT);
|
|
}
|
|
</pre>
|
|
|
|
<h3>Add geofences</h3>
|
|
|
|
<p>
|
|
To add geofences, use the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingApi.html#addGeofences(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.GeofencingRequest, android.app.PendingIntent)">{@code GeoencingApi.addGeofences()}</a></code> method.
|
|
Provide the Google API client, the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest">
|
|
GeofencingRequest</a></code> object, and the {@link android.app.PendingIntent}.
|
|
The following snippet, which processes the results in <code><a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html#onResult(R)">
|
|
onResult()</a></code>, assumes that the main activity implements <code><a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html">
|
|
ResultCallback</a></code>:
|
|
</p>
|
|
<pre>
|
|
public class MainActivity extends FragmentActivity {
|
|
...
|
|
LocationServices.GeofencingApi.addGeofences(
|
|
mGoogleApiClient,
|
|
getGeofencingRequest(),
|
|
getGeofencePendingIntent()
|
|
).setResultCallback(this);
|
|
</pre>
|
|
|
|
|
|
<h2 id="HandleGeofenceTransitions">Handle Geofence Transitions</h2>
|
|
<p>
|
|
When Location Services detects that the user has entered or exited a geofence, it
|
|
sends out the {@link android.content.Intent} contained in the {@link android.app.PendingIntent}
|
|
you included in the request to add geofences. This {@link android.content.Intent} is received
|
|
by a service like <code>GeofenceTransitionsIntentService</code>,
|
|
which obtains the geofencing event from the intent, determines the type of Geofence transition(s),
|
|
and determines which of the defined geofences was triggered. It then sends a notification as
|
|
the output.
|
|
</p>
|
|
<p>
|
|
The following snippet shows how to define an {@link android.app.IntentService} that posts a
|
|
notification when a geofence transition occurs. When the user clicks the notification, the
|
|
app's main activity appears:
|
|
</p>
|
|
<pre>
|
|
public class GeofenceTransitionsIntentService extends IntentService {
|
|
...
|
|
protected void onHandleIntent(Intent intent) {
|
|
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
|
|
if (geofencingEvent.hasError()) {
|
|
String errorMessage = GeofenceErrorMessages.getErrorString(this,
|
|
geofencingEvent.getErrorCode());
|
|
Log.e(TAG, errorMessage);
|
|
return;
|
|
}
|
|
|
|
// Get the transition type.
|
|
int geofenceTransition = geofencingEvent.getGeofenceTransition();
|
|
|
|
// Test that the reported transition was of interest.
|
|
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
|
|
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
|
|
|
|
// Get the geofences that were triggered. A single event can trigger
|
|
// multiple geofences.
|
|
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
|
|
|
|
// Get the transition details as a String.
|
|
String geofenceTransitionDetails = getGeofenceTransitionDetails(
|
|
this,
|
|
geofenceTransition,
|
|
triggeringGeofences
|
|
);
|
|
|
|
// Send notification and log the transition details.
|
|
sendNotification(geofenceTransitionDetails);
|
|
Log.i(TAG, geofenceTransitionDetails);
|
|
} else {
|
|
// Log the error.
|
|
Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
|
|
geofenceTransition));
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>After detecting the transition event via the {@link android.app.PendingIntent},
|
|
this {@link android.app.IntentService} gets the geofence transition type and tests whether
|
|
it is one of the events the app uses to trigger notifications -- either
|
|
<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">GEOFENCE_TRANSITION_ENTER</a></code>
|
|
or <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">GEOFENCE_TRANSITION_EXIT</a></code>
|
|
in this case. The service then sends a notification and logs the transition details.</p>
|
|
<!--
|
|
Remove Geofences
|
|
-->
|
|
<h2 id="StopGeofenceMonitoring">Stop Geofence Monitoring</h2>
|
|
|
|
<p>Stopping geofence monitoring when it is no longer needed or desired can help save battery
|
|
power and CPU cycles on the device. You can stop geofence monitoring
|
|
in the main activity used to add and remove geofences; removing a geofence stops it
|
|
immediately. The API provides methods to
|
|
remove geofences either by request IDs, or by removing geofences associated with a given
|
|
{@link android.app.PendingIntent}.
|
|
</p>
|
|
<p>
|
|
The following snippet removes geofences by {@link android.app.PendingIntent}, stopping all
|
|
further notification when the device enters or exits previously added geofences:
|
|
</p>
|
|
<pre>
|
|
LocationServices.GeofencingApi.removeGeofences(
|
|
mGoogleApiClient,
|
|
// This is the same pending intent that was used in addGeofences().
|
|
getGeofencePendingIntent()
|
|
).setResultCallback(this); // Result processed in onResult().
|
|
}
|
|
</pre>
|
|
|
|
<p>
|
|
You can combine geofencing with other location-aware features, such as periodic location updates.
|
|
For more information, see the other lessons in this class.
|
|
</p>
|