1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
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>
|