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