summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorEric Gilmore <egilmore@google.com>2015-03-09 17:01:32 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-03-09 17:01:32 +0000
commit89d0c0fbaec976b8b06c81a2950aed3a3c5e5b1b (patch)
tree17a81ddea147d7a3c586adc58bce940a61f75cfa /docs
parentf507759eea9aaf5eb9aeaef9d93ab4914c1c5b25 (diff)
parentbcea1fcd452d17c91b51853e8a690e6c52f2f77a (diff)
downloadframeworks_base-89d0c0fbaec976b8b06c81a2950aed3a3c5e5b1b.zip
frameworks_base-89d0c0fbaec976b8b06c81a2950aed3a3c5e5b1b.tar.gz
frameworks_base-89d0c0fbaec976b8b06c81a2950aed3a3c5e5b1b.tar.bz2
am bcea1fcd: am f1eab6f5: am 39d47c7c: am a2d342e3: am beed9332: am 0bceaacf: Merge "Reworking geofencing training to new API and sample." into lmp-docs
* commit 'bcea1fcd452d17c91b51853e8a690e6c52f2f77a': Reworking geofencing training to new API and sample.
Diffstat (limited to 'docs')
-rw-r--r--docs/html/images/training/geofence.pngbin0 -> 141763 bytes
-rw-r--r--docs/html/images/training/geofence@2x.pngbin0 -> 275832 bytes
-rw-r--r--docs/html/training/location/geofencing.jd1499
-rw-r--r--docs/html/training/location/index.jd6
-rw-r--r--docs/html/training/training_toc.cs4
5 files changed, 209 insertions, 1300 deletions
diff --git a/docs/html/images/training/geofence.png b/docs/html/images/training/geofence.png
new file mode 100644
index 0000000..2d5d3aa
--- /dev/null
+++ b/docs/html/images/training/geofence.png
Binary files differ
diff --git a/docs/html/images/training/geofence@2x.png b/docs/html/images/training/geofence@2x.png
new file mode 100644
index 0000000..2f83105
--- /dev/null
+++ b/docs/html/images/training/geofence@2x.png
Binary files differ
diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd
index 748b6ec..59fc4c6 100644
--- a/docs/html/training/location/geofencing.jd
+++ b/docs/html/training/location/geofencing.jd
@@ -9,9 +9,11 @@ trainingnavtop=true
<h2>This lesson teaches you to</h2>
<ol>
- <li><a href="#RequestGeofences">Request Geofence Monitoring</a></li>
+ <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>
@@ -23,577 +25,148 @@ trainingnavtop=true
<h2>Try it out</h2>
-<div class="download-box">
- <a href="http://developer.android.com/shareables/training/GeofenceDetection.zip" class="button">Download the sample</a>
- <p class="filename">GeofenceDetection.zip</p>
-</div>
+ <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 nearby
- features, defined as the user's proximity to locations that may be of interest. To mark a
+ 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.
- You can have multiple active geofences at one time.
+ 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>
- Location Services treats a geofences as an area rather than as a points and proximity. This
- allows it to detect when the user enters or exits a geofence. For each geofence, you can ask
- Location Services to send you entrance events or exit events or both. You can also limit the
- duration of a geofence by specifying an expiration duration in milliseconds. After the geofence
- expires, Location Services automatically removes it.
+ 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>
-<!--
- Send geofences to Location Services
- -->
-<h2 id="RequestGeofences">Request Geofence Monitoring</h2>
+
+<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">&lt;manifest&gt;</a></code>
- element:
+ element in your app manifest:
</p>
<pre>
&lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
</pre>
-<!-- Check for Google Play services -->
-<h3>Check for Google Play Services</h3>
-<p>
- Location Services is part of the Google Play services APK. Since it's hard to anticipate the
- state of the user's device, you should always check that the APK is installed before you attempt
- to connect to Location Services. To check that the APK is installed, call
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
- which returns one of the
- integer result codes listed in the API reference documentation. If you encounter an error,
- call
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
- to retrieve localized dialog that prompts users to take the correct action, then display
- the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
- user to correct the problem, in which case Google Play services may send a result back to your
- activity. To handle this result, override the method
- {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
-</p>
-<p class="note">
- <strong>Note:</strong> To make your app compatible with
- platform version 1.6 and later, the activity that displays the
- {@link android.support.v4.app.DialogFragment} must subclass
- {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
- {@link android.support.v4.app.FragmentActivity} also allows you to call
- {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
- getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
-</p>
<p>
- Since you usually need to check for Google Play services in more than one place in your code,
- define a method that encapsulates the check, then call the method before each connection
- attempt. The following snippet contains all of the code required to check for Google
- Play services:
+ 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">
+ &lt;application&gt;</a></code> element:
</p>
-<pre>
-public class MainActivity extends FragmentActivity {
- ...
- // Global constants
- /*
- * Define a request code to send to Google Play services
- * This code is returned in Activity.onActivityResult
- */
- private final static int
- CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
- ...
- // Define a DialogFragment that displays the error dialog
- public static class ErrorDialogFragment extends DialogFragment {
- // Global field to contain the error dialog
- private Dialog mDialog;
- ...
- // Default constructor. Sets the dialog field to null
- public ErrorDialogFragment() {
- super();
- mDialog = null;
- }
- ...
- // Set the dialog to display
- public void setDialog(Dialog dialog) {
- mDialog = dialog;
- }
- ...
- // Return a Dialog to the DialogFragment.
- &#64;Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return mDialog;
- }
- ...
- }
- ...
- /*
- * Handle results returned to the FragmentActivity
- * by Google Play services
- */
- &#64;Override
- protected void onActivityResult(
- int requestCode, int resultCode, Intent data) {
- // Decide what to do based on the original request code
- switch (requestCode) {
- ...
- case CONNECTION_FAILURE_RESOLUTION_REQUEST :
- /*
- * If the result code is Activity.RESULT_OK, try
- * to connect again
- */
- switch (resultCode) {
- ...
- case Activity.RESULT_OK :
- /*
- * Try the request again
- */
- ...
- break;
- }
- ...
- }
- ...
- }
- ...
- private boolean servicesConnected() {
- // Check that Google Play services is available
- int resultCode =
- GooglePlayServicesUtil.
- isGooglePlayServicesAvailable(this);
- // If Google Play services is available
- if (ConnectionResult.SUCCESS == resultCode) {
- // In debug mode, log the status
- Log.d("Geofence Detection",
- "Google Play services is available.");
- // Continue
- return true;
- // Google Play services was not available for some reason
- } else {
- // Get the error code
- int errorCode = connectionResult.getErrorCode();
- // Get the error dialog from Google Play services
- Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
- errorCode,
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
- // If Google Play services can provide an error dialog
- if (errorDialog != null) {
- // Create a new DialogFragment for the error dialog
- ErrorDialogFragment errorFragment =
- new ErrorDialogFragment();
- // Set the dialog in the DialogFragment
- errorFragment.setDialog(errorDialog);
- // Show the error dialog in the DialogFragment
- errorFragment.show(
- getSupportFragmentManager(),
- "Geofence Detection");
- }
- }
- }
- ...
-}
+<pre>
+&lt;application
+ android:allowBackup=&quot;true&quot;&gt;
+ ...
+ &lt;service android:name=".GeofenceTransitionsIntentService"/&gt;
+&lt;application/&gt;
</pre>
-<p>
- Snippets in the following sections call this method to verify that Google Play services is
- available.
-</p>
-<p>
- To use geofencing, start by defining the geofences you want to monitor. Although you usually
- store geofence data in a local database or download it from the network, you need to send
- a geofence to Location Services as an instance of
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>,
- which you create with
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">Geofence.Builder</a></code>.
- Each
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
- object contains the following information:
-</p>
-<dl>
- <dt>Latitude, longitude, and radius</dt>
- <dd>
- Define a circular area for the geofence. Use the latitude and longitude to mark a location
- of interest, and then use the radius to adjust how close the user needs to approach the
- location before the geofence is detected. The larger the radius, the more likely the
- user will trigger a geofence transition alert by approaching the geofence. For example,
- providing a large radius for a geofencing app that turns on lights in the user's house as
- the user returns home might cause the lights to go on even if the user is simply passing by.
- </dd>
- <dt>Expiration time</dt>
- <dd>
- How long the geofence should remain active. Once the expiration time is reached, Location
- Services deletes the geofence. Most of the time, you should specify an expiration time, but
- you may want to keep permanent geofences for the user's home or place of work.
- </dd>
- <dt>Transition type</dt>
- <dd>
- Location Services can detect when the user steps within the radius of the geofence ("entry")
- and when the user steps outside the radius of the geofence ("exit"), or both.
- </dd>
- <dt>Geofence ID</dt>
- <dd>
- A string that is stored with the geofence. You should make this unique, so that you can
- use it to remove a geofence from Location Services tracking.
- </dd>
-</dl>
-<h3>Define geofence storage</h3>
-<p>
- A geofencing app needs to read and write geofence data to persistent storage. You shouldn't use
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
- objects to do this; instead, use storage techniques such as databases that can store groups of
- related data.
+
+<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>
- As an example of storing geofence data, the following snippet defines two classes that use
- the app's {@link android.content.SharedPreferences} instance for persistent storage. The class
- {@code SimpleGeofence}, analogous to a database record, stores the
- data for a single
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
- object in a "flattened" form. The class {@code SimpleGeofenceStore}, analogous to a database,
- reads and writes {@code SimpleGeofence} data to the
- {@link android.content.SharedPreferences} instance.
-</p>
-<pre>
-public class MainActivity extends FragmentActivity {
- ...
- /**
- * A single Geofence object, defined by its center and radius.
- */
- public class SimpleGeofence {
- // Instance variables
- private final String mId;
- private final double mLatitude;
- private final double mLongitude;
- private final float mRadius;
- private long mExpirationDuration;
- private int mTransitionType;
+ 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>
- /**
- * @param geofenceId The Geofence's request ID
- * @param latitude Latitude of the Geofence's center.
- * @param longitude Longitude of the Geofence's center.
- * @param radius Radius of the geofence circle.
- * @param expiration Geofence expiration duration
- * @param transition Type of Geofence transition.
- */
- public SimpleGeofence(
- String geofenceId,
- double latitude,
- double longitude,
- float radius,
- long expiration,
- int transition) {
- // Set the instance fields from the constructor
- this.mId = geofenceId;
- this.mLatitude = latitude;
- this.mLongitude = longitude;
- this.mRadius = radius;
- this.mExpirationDuration = expiration;
- this.mTransitionType = transition;
- }
- // Instance field getters
- public String getId() {
- return mId;
- }
- public double getLatitude() {
- return mLatitude;
- }
- public double getLongitude() {
- return mLongitude;
- }
- public float getRadius() {
- return mRadius;
- }
- public long getExpirationDuration() {
- return mExpirationDuration;
- }
- public int getTransitionType() {
- return mTransitionType;
- }
- /**
- * Creates a Location Services Geofence object from a
- * SimpleGeofence.
- *
- * @return A Geofence object
- */
- public Geofence toGeofence() {
- // Build a new Geofence object
- return new Geofence.Builder()
- .setRequestId(getId())
- .setTransitionTypes(mTransitionType)
- .setCircularRegion(
- getLatitude(), getLongitude(), getRadius())
- .setExpirationDuration(mExpirationDuration)
- .build();
- }
- }
- ...
- /**
- * Storage for geofence values, implemented in SharedPreferences.
- */
- public class SimpleGeofenceStore {
- // Keys for flattened geofences stored in SharedPreferences
- public static final String KEY_LATITUDE =
- "com.example.android.geofence.KEY_LATITUDE";
- public static final String KEY_LONGITUDE =
- "com.example.android.geofence.KEY_LONGITUDE";
- public static final String KEY_RADIUS =
- "com.example.android.geofence.KEY_RADIUS";
- public static final String KEY_EXPIRATION_DURATION =
- "com.example.android.geofence.KEY_EXPIRATION_DURATION";
- public static final String KEY_TRANSITION_TYPE =
- "com.example.android.geofence.KEY_TRANSITION_TYPE";
- // The prefix for flattened geofence keys
- public static final String KEY_PREFIX =
- "com.example.android.geofence.KEY";
- /*
- * Invalid values, used to test geofence storage when
- * retrieving geofences
- */
- public static final long INVALID_LONG_VALUE = -999l;
- public static final float INVALID_FLOAT_VALUE = -999.0f;
- public static final int INVALID_INT_VALUE = -999;
- // The SharedPreferences object in which geofences are stored
- private final SharedPreferences mPrefs;
- // The name of the SharedPreferences
- private static final String SHARED_PREFERENCES =
- "SharedPreferences";
- // Create the SharedPreferences storage with private access only
- public SimpleGeofenceStore(Context context) {
- mPrefs =
- context.getSharedPreferences(
- SHARED_PREFERENCES,
- Context.MODE_PRIVATE);
- }
- /**
- * Returns a stored geofence by its id, or returns {@code null}
- * if it's not found.
- *
- * @param id The ID of a stored geofence
- * @return A geofence defined by its center and radius. See
- */
- public SimpleGeofence getGeofence(String id) {
- /*
- * Get the latitude for the geofence identified by id, or
- * INVALID_FLOAT_VALUE if it doesn't exist
- */
- double lat = mPrefs.getFloat(
- getGeofenceFieldKey(id, KEY_LATITUDE),
- INVALID_FLOAT_VALUE);
- /*
- * Get the longitude for the geofence identified by id, or
- * INVALID_FLOAT_VALUE if it doesn't exist
- */
- double lng = mPrefs.getFloat(
- getGeofenceFieldKey(id, KEY_LONGITUDE),
- INVALID_FLOAT_VALUE);
- /*
- * Get the radius for the geofence identified by id, or
- * INVALID_FLOAT_VALUE if it doesn't exist
- */
- float radius = mPrefs.getFloat(
- getGeofenceFieldKey(id, KEY_RADIUS),
- INVALID_FLOAT_VALUE);
- /*
- * Get the expiration duration for the geofence identified
- * by id, or INVALID_LONG_VALUE if it doesn't exist
- */
- long expirationDuration = mPrefs.getLong(
- getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
- INVALID_LONG_VALUE);
- /*
- * Get the transition type for the geofence identified by
- * id, or INVALID_INT_VALUE if it doesn't exist
- */
- int transitionType = mPrefs.getInt(
- getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
- INVALID_INT_VALUE);
- // If none of the values is incorrect, return the object
- if (
- lat != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
- lng != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
- radius != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
- expirationDuration !=
- GeofenceUtils.INVALID_LONG_VALUE &amp;&amp;
- transitionType != GeofenceUtils.INVALID_INT_VALUE) {
+<pre>
+mGeofenceList.add(new Geofence.Builder()
+ // Set the request ID of the geofence. This is a string to identify this
+ // geofence.
+ .setRequestId(entry.getKey())
- // Return a true Geofence object
- return new SimpleGeofence(
- id, lat, lng, radius, expirationDuration,
- transitionType);
- // Otherwise, return null.
- } else {
- return null;
- }
- }
- /**
- * Save a geofence.
- * @param geofence The SimpleGeofence containing the
- * values you want to save in SharedPreferences
- */
- public void setGeofence(String id, SimpleGeofence geofence) {
- /*
- * Get a SharedPreferences editor instance. Among other
- * things, SharedPreferences ensures that updates are atomic
- * and non-concurrent
- */
- Editor editor = mPrefs.edit();
- // Write the Geofence values to SharedPreferences
- editor.putFloat(
- getGeofenceFieldKey(id, KEY_LATITUDE),
- (float) geofence.getLatitude());
- editor.putFloat(
- getGeofenceFieldKey(id, KEY_LONGITUDE),
- (float) geofence.getLongitude());
- editor.putFloat(
- getGeofenceFieldKey(id, KEY_RADIUS),
- geofence.getRadius());
- editor.putLong(
- getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
- geofence.getExpirationDuration());
- editor.putInt(
- getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
- geofence.getTransitionType());
- // Commit the changes
- editor.commit();
- }
- public void clearGeofence(String id) {
- /*
- * Remove a flattened geofence object from storage by
- * removing all of its keys
- */
- Editor editor = mPrefs.edit();
- editor.remove(getGeofenceFieldKey(id, KEY_LATITUDE));
- editor.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));
- editor.remove(getGeofenceFieldKey(id, KEY_RADIUS));
- editor.remove(getGeofenceFieldKey(id,
- KEY_EXPIRATION_DURATION));
- editor.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));
- editor.commit();
- }
- /**
- * Given a Geofence object's ID and the name of a field
- * (for example, KEY_LATITUDE), return the key name of the
- * object's values in SharedPreferences.
- *
- * @param id The ID of a Geofence object
- * @param fieldName The field represented by the key
- * @return The full key name of a value in SharedPreferences
- */
- private String getGeofenceFieldKey(String id,
- String fieldName) {
- return KEY_PREFIX + "_" + id + "_" + fieldName;
- }
- }
- ...
-}
+ .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>
-<h3>Create Geofence objects</h3>
+
+<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 SimpleGeofence} and {@code SimpleGeofenceStore} classes
- gets geofence data from the UI, stores it in {@code SimpleGeofence} objects, stores these
- objects in a {@code SimpleGeofenceStore} object, and then creates
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
- objects:
+ 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>
-public class MainActivity extends FragmentActivity {
- ...
- /*
- * Use to set an expiration time for a geofence. After this amount
- * of time Location Services will stop tracking the geofence.
- */
- private static final long SECONDS_PER_HOUR = 60;
- private static final long MILLISECONDS_PER_SECOND = 1000;
- private static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;
- private static final long GEOFENCE_EXPIRATION_TIME =
- GEOFENCE_EXPIRATION_IN_HOURS *
- SECONDS_PER_HOUR *
- MILLISECONDS_PER_SECOND;
- ...
- /*
- * Handles to UI views containing geofence data
- */
- // Handle to geofence 1 latitude in the UI
- private EditText mLatitude1;
- // Handle to geofence 1 longitude in the UI
- private EditText mLongitude1;
- // Handle to geofence 1 radius in the UI
- private EditText mRadius1;
- // Handle to geofence 2 latitude in the UI
- private EditText mLatitude2;
- // Handle to geofence 2 longitude in the UI
- private EditText mLongitude2;
- // Handle to geofence 2 radius in the UI
- private EditText mRadius2;
- /*
- * Internal geofence objects for geofence 1 and 2
- */
- private SimpleGeofence mUIGeofence1;
- private SimpleGeofence mUIGeofence2;
- ...
- // Internal List of Geofence objects
- List&lt;Geofence&gt; mGeofenceList;
- // Persistent storage for geofences
- private SimpleGeofenceStore mGeofenceStorage;
- ...
- &#64;Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ...
- // Instantiate a new geofence storage area
- mGeofenceStorage = new SimpleGeofenceStore(this);
-
- // Instantiate the current List of geofences
- mCurrentGeofences = new ArrayList&lt;Geofence&gt;();
- }
- ...
- /**
- * Get the geofence parameters for each geofence from the UI
- * and add them to a List.
- */
- public void createGeofences() {
- /*
- * Create an internal object to store the data. Set its
- * ID to "1". This is a "flattened" object that contains
- * a set of strings
- */
- mUIGeofence1 = new SimpleGeofence(
- "1",
- Double.valueOf(mLatitude1.getText().toString()),
- Double.valueOf(mLongitude1.getText().toString()),
- Float.valueOf(mRadius1.getText().toString()),
- GEOFENCE_EXPIRATION_TIME,
- // This geofence records only entry transitions
- Geofence.GEOFENCE_TRANSITION_ENTER);
- // Store this flat version
- mGeofenceStorage.setGeofence("1", mUIGeofence1);
- // Create another internal object. Set its ID to "2"
- mUIGeofence2 = new SimpleGeofence(
- "2",
- Double.valueOf(mLatitude2.getText().toString()),
- Double.valueOf(mLongitude2.getText().toString()),
- Float.valueOf(mRadius2.getText().toString()),
- GEOFENCE_EXPIRATION_TIME,
- // This geofence records both entry and exit transitions
- Geofence.GEOFENCE_TRANSITION_ENTER |
- Geofence.GEOFENCE_TRANSITION_EXIT);
- // Store this flat version
- mGeofenceStorage.setGeofence(2, mUIGeofence2);
- mGeofenceList.add(mUIGeofence1.toGeofence());
- mGeofenceList.add(mUIGeofence2.toGeofence());
- }
- ...
+private GeofencingRequest getGeofencingRequest() {
+ GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
+ builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
+ builder.addGeofences(mGeofenceList);
+ return builder.build();
}
</pre>
+
<p>
- In addition to the {@link java.util.List} of
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
- objects you want to monitor, you need to provide Location Services with the
- {@link android.content.Intent} that it sends to your app when it detects geofence
- transitions.
-<h4>Define a Intent for geofence transitions</h4>
+ 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
@@ -601,807 +174,133 @@ public class MainActivity extends FragmentActivity {
{@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
- how to define a {@link android.app.PendingIntent} that starts an
- {@link android.app.IntentService}:
+ to define a {@link android.app.PendingIntent} that starts an {@link android.app.IntentService}:
</p>
<pre>
public class MainActivity extends FragmentActivity {
...
- /*
- * Create a PendingIntent that triggers an IntentService in your
- * app when a geofence transition occurs.
- */
- private PendingIntent getTransitionPendingIntent() {
- // Create an explicit Intent
- Intent intent = new Intent(this,
- ReceiveTransitionsIntentService.class);
- /*
- * Return the PendingIntent
- */
- return PendingIntent.getService(
- this,
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
- ...
-}
-</pre>
-<p>
- Now you have all the code you need to send a request to monitor geofences to Location
- Services.
-</p>
-<!-- Send the monitoring request -->
-<h3 id="requestmonitoring">Send the monitoring request</h3>
-<p>
- Sending the monitoring request requires two asynchronous operations. The first operation gets a
- location client for the request, and the second makes the request using the client. In both
- cases, Location Services invokes a callback method when it finishes the operation. The best way
- to handle these operations is to chain together the method calls. The following snippets
- demonstrate how to set up an activity, define the methods, and call them in the proper order.
-</p>
-<p>
- First, modify the activity's class definition to implement the necessary callback interfaces.
- Add the following interfaces:
-</p>
-<dl>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
- </dt>
- <dd>
- Specifies methods that Location Services calls when a location client is connected or
- disconnected.
- </dd>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
- </dt>
- <dd>
- Specifies a method that Location Services calls if an error occurs while attempting to
- connect the location client.
- </dd>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html">OnAddGeofencesResultListener</a></code>
- </dt>
- <dd>
- Specifies a method that Location Services calls once it has added the geofences.
- </dd>
-</dl>
-<p>
- For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
-}
-</pre>
-<h4>Start the request process</h4>
-<p>
- Next, define a method that starts the request process by connecting to Location Services.
- Mark this as a request to add a geofence by setting a global variable. This allows you to
- use the callback
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>
- to add geofences and to remove them, as described in succeeding sections.
-</p>
-<p>
-<p>
- To guard against race conditions that might arise if your app tries to start another request
- before the first one finishes, define a boolean flag that tracks the state of the current
- request:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- // Holds the location client
- private LocationClient mLocationClient;
- // Stores the PendingIntent used to request geofence monitoring
- private PendingIntent mGeofenceRequestIntent;
- // Defines the allowable request types.
- public enum REQUEST_TYPE = {ADD}
- private REQUEST_TYPE mRequestType;
- // Flag that indicates if a request is underway.
- private boolean mInProgress;
- ...
- &#64;Override
- protected void onCreate(Bundle savedInstanceState) {
- ...
- // Start with the request flag set to false
- mInProgress = false;
- ...
- }
- ...
- /**
- * Start a request for geofence monitoring by calling
- * LocationClient.connect().
- */
- public void addGeofences() {
- // Start a request to add geofences
- mRequestType = ADD;
- /*
- * Test for Google Play services after setting the request type.
- * If Google Play services isn't present, the proper request
- * can be restarted.
- */
- if (!servicesConnected()) {
- return;
- }
- /*
- * Create a new location client object. Since the current
- * activity class implements ConnectionCallbacks and
- * OnConnectionFailedListener, pass the current activity object
- * as the listener for both parameters
- */
- mLocationClient = new LocationClient(this, this, this)
- // If a request is not already underway
- if (!mInProgress) {
- // Indicate that a request is underway
- mInProgress = true;
- // Request a connection from the client to Location Services
- mLocationClient.connect();
- } else {
- /*
- * A request is already underway. You can handle
- * this situation by disconnecting the client,
- * re-setting the flag, and then re-trying the
- * request.
- */
- }
- }
- ...
-}
-</pre>
-<h4>Send a request to add the geofences</h4>
-<p>
- In your implementation of
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>,
- call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#addGeofences(java.util.List<com.google.android.gms.location.Geofence>, android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener)">LocationClient.addGeofences()</a></code>.
- Notice that if the connection fails,
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>
- isn't called, and the request stops.
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /*
- * Provide the implementation of ConnectionCallbacks.onConnected()
- * Once the connection is available, send a request to add the
- * Geofences
- */
- &#64;Override
- private void onConnected(Bundle dataBundle) {
- ...
- switch (mRequestType) {
- case ADD :
- // Get the PendingIntent for the request
- mTransitionPendingIntent =
- getTransitionPendingIntent();
- // Send a request to add the current geofences
- mLocationClient.addGeofences(
- mCurrentGeofences, pendingIntent, this);
- ...
+ 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>
- Notice that
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#addGeofences(java.util.List<com.google.android.gms.location.Geofence>, android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener)">addGeofences()</a></code>
- returns immediately, but the status of the request is indeterminate until Location Services
- calls
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>
- Once this method is called, you can determine if the request was successful or not.
-</p>
-<h4>Check the result returned by Location Services</h4>
-<p>
- When Location Services invokes your implementation of the callback method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>,
- indicating that the request is complete, examine the incoming status code. If the request
- was successful, the geofences you requested are active. If the request was unsuccessful,
- the geofences aren't active, and you need to re-try the request or report an error. For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /*
- * Provide the implementation of
- * OnAddGeofencesResultListener.onAddGeofencesResult.
- * Handle the result of adding the geofences
- *
- */
- &#64;Override
- public void onAddGeofencesResult(
- int statusCode, String[] geofenceRequestIds) {
- // If adding the geofences was successful
- if (LocationStatusCodes.SUCCESS == statusCode) {
- /*
- * Handle successful addition of geofences here.
- * You can send out a broadcast intent or update the UI.
- * geofences into the Intent's extended data.
- */
- } else {
- // If adding the geofences failed
- /*
- * Report errors here.
- * You can log the error using Log.e() or update
- * the UI.
- */
- }
- // Turn off the in progress flag and disconnect the client
- mInProgress = false;
- mLocationClient.disconnect();
- }
- ...
-}
-</pre>
-<!-- Handle disconnections -->
-<h3>Handle disconnections</h3>
-<p>
- In some cases, Location Services may disconnect from the activity recognition client before
- you call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#disconnect()">disconnect()</a></code>.
- To handle this situation, implement <code>
-<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>.
- In this method, set the request flag to indicate that a request is not in progress, and
- delete the client:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /*
- * Implement ConnectionCallbacks.onDisconnected()
- * Called by Location Services once the location client is
- * disconnected.
- */
- &#64;Override
- public void onDisconnected() {
- // Turn off the request flag
- mInProgress = false;
- // Destroy the current location client
- mLocationClient = null;
- }
- ...
-}
-</pre>
-<!-- Handle connection errors -->
-<h3>Handle connection errors</h3>
-<p>
- Besides handling the normal callbacks from Location Services, you have to provide a callback
- method that Location Services calls if a connection error occurs. This callback method
- can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to
- handle the check for Google Play services. It can also re-use the override you defined
- for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that
- receives any Google Play services results that occur when the user interacts with the
- error dialog. The following snippet shows you a sample implementation of the callback method:
+ 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 implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- // Implementation of OnConnectionFailedListener.onConnectionFailed
- &#64;Override
- public void onConnectionFailed(ConnectionResult connectionResult) {
- // Turn off the request flag
- mInProgress = false;
- /*
- * If the error has a resolution, start a Google Play services
- * activity to resolve it.
- */
- if (connectionResult.hasResolution()) {
- try {
- connectionResult.startResolutionForResult(
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
- } catch (SendIntentException e) {
- // Log the error
- e.printStackTrace();
- }
- // If no resolution is available, display an error dialog
- } else {
- // Get the error code
- int errorCode = connectionResult.getErrorCode();
- // Get the error dialog from Google Play services
- Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
- errorCode,
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
- // If Google Play services can provide an error dialog
- if (errorDialog != null) {
- // Create a new DialogFragment for the error dialog
- ErrorDialogFragment errorFragment =
- new ErrorDialogFragment();
- // Set the dialog in the DialogFragment
- errorFragment.setDialog(errorDialog);
- // Show the error dialog in the DialogFragment
- errorFragment.show(
- getSupportFragmentManager(),
- "Geofence Detection");
- }
- }
- }
+public class MainActivity extends FragmentActivity {
...
-}
+ LocationServices.GeofencingApi.addGeofences(
+ mGoogleApiClient,
+ getGeofencingRequest(),
+ getGeofencePendingIntent()
+ ).setResultCallback(this);
</pre>
-<!--
- Handle Geofence Transitions
- -->
+
+
<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
+ 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>
-<h3>Define an IntentService</h3>
<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 ReceiveTransitionsIntentService extends IntentService {
- ...
- /**
- * Sets an identifier for the service
- */
- public ReceiveTransitionsIntentService() {
- super("ReceiveTransitionsIntentService");
- }
- /**
- * Handles incoming intents
- *&#64;param intent The Intent sent by Location Services. This
- * Intent is provided
- * to Location Services (inside a PendingIntent) when you call
- * addGeofences()
- */
- &#64;Override
+public class GeofenceTransitionsIntentService extends IntentService {
+ ...
protected void onHandleIntent(Intent intent) {
- // First check for errors
- if (LocationClient.hasError(intent)) {
- // Get the error code with a static method
- int errorCode = LocationClient.getErrorCode(intent);
- // Log the error
- Log.e("ReceiveTransitionsIntentService",
- "Location Services error: " +
- Integer.toString(errorCode));
- /*
- * You can also send the error code to an Activity or
- * Fragment with a broadcast Intent
- */
- /*
- * If there's no error, get the transition type and the IDs
- * of the geofence or geofences that triggered the transition
- */
- } else {
- // Get the type of transition (entry or exit)
- int transitionType =
- LocationClient.getGeofenceTransition(intent);
- // Test that a valid transition was reported
- if (
- (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER)
- ||
- (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT)
- ) {
- List &lt;Geofence&gt; triggerList =
- getTriggeringGeofences(intent);
+ GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
+ if (geofencingEvent.hasError()) {
+ String errorMessage = GeofenceErrorMessages.getErrorString(this,
+ geofencingEvent.getErrorCode());
+ Log.e(TAG, errorMessage);
+ return;
+ }
- String[] triggerIds = new String[geofenceList.size()];
+ // 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
+ );
- for (int i = 0; i &lt; triggerIds.length; i++) {
- // Store the Id of each geofence
- triggerIds[i] = triggerList.get(i).getRequestId();
- }
- /*
- * At this point, you can store the IDs for further use
- * display them, or display the details associated with
- * them.
- */
- }
- // An invalid transition was reported
+ // Send notification and log the transition details.
+ sendNotification(geofenceTransitionDetails);
+ Log.i(TAG, geofenceTransitionDetails);
} else {
- Log.e("ReceiveTransitionsIntentService",
- "Geofence transition error: " +
- Integer.toString()transitionType));
+ // Log the error.
+ Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
+ geofenceTransition));
}
}
- ...
-}
-</pre>
-<!-- Specify the IntentService in the manifest -->
-<h3>Specify the IntentService in the manifest</h3>
-<p>
- To identify the {@link android.app.IntentService} to the system, add a
- <code><a href="{@docRoot}guide/topics/manifest/service-element.html">&lt;service&gt;</a></code>
- element to the app manifest. For example:
-</p>
-<pre>
-&lt;service
- android:name="com.example.android.location.ReceiveTransitionsIntentService"
- android:label="&#64;string/app_name"
- android:exported="false"&gt;
-&lt;/service&gt;
</pre>
-<p>
- Notice that you don't have to specify intent filters for the service, because it only receives
- explicit intents. How the incoming geofence transition intents are created is described in the
- section <a href="#requestmonitoring">Send the monitoring request</a>.
-</p>
+
+<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>
- To stop geofence monitoring, you remove the geofences themselves. You can remove a specific
- set of geofences or all the geofences associated with a {@link android.app.PendingIntent}. The
- procedure is similar to adding geofences. The first operation gets a location
- client for the removal request, and the second makes the request using the client.
-</p>
-<p>
- The callback methods that Location Services invokes when it has finished removing geofences
- are defined in the interface
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html">LocationClient.OnRemoveGeofencesResultListener</a></code>. Declare
- this interface as part of your class definition, and then add definitions for its two methods:
-</p>
-<dl>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>
- </dt>
- <dd>
- Callback invoked when Location Services finishes a request to remove all geofences made
- by the method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener)</a></code>.
- </dd>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult(List&lt;String&gt;, LocationClient.OnRemoveGeofencesResultListener)</a></code>
- </dt>
- <dd>
- Callback invoked when Location Services finished a request to remove a set of geofences,
- specified by their geofence IDs, by the method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(java.util.List<java.lang.String>, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(List&lt;String&gt;, LocationClient.OnRemoveGeofencesResultListener)</a></code>.
- </dd>
-</dl>
-<p>
- Examples of implementing these methods are shown in the next snippets.
-</p>
-<h3>Remove all geofences</h3>
-<p>
- Since removing geofences uses some of the methods you use to add geofences, start by defining
- another request type:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- // Enum type for controlling the type of removal requested
- public enum REQUEST_TYPE = {ADD, REMOVE_INTENT}
- ...
-}
-</pre>
-<p>
- Start the removal request by getting a connection to Location Services. If the connection fails,
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code> isn't called,
- and the request stops. The following snippet shows how to start the request:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /**
- * Start a request to remove geofences by calling
- * LocationClient.connect()
- */
- public void removeGeofences(PendingIntent requestIntent) {
- // Record the type of removal request
- mRequestType = REMOVE_INTENT;
- /*
- * Test for Google Play services after setting the request type.
- * If Google Play services isn't present, the request can be
- * restarted.
- */
- if (!servicesConnected()) {
- return;
- }
- // Store the PendingIntent
- mGeofenceRequestIntent = requestIntent;
- /*
- * Create a new location client object. Since the current
- * activity class implements ConnectionCallbacks and
- * OnConnectionFailedListener, pass the current activity object
- * as the listener for both parameters
- */
- mLocationClient = new LocationClient(this, this, this);
- // If a request is not already underway
- if (!mInProgress) {
- // Indicate that a request is underway
- mInProgress = true;
- // Request a connection from the client to Location Services
- mLocationClient.connect();
- } else {
- /*
- * A request is already underway. You can handle
- * this situation by disconnecting the client,
- * re-setting the flag, and then re-trying the
- * request.
- */
- }
- }
- ...
-}
-</pre>
-<p>
- When Location Services invokes the callback method indicating that the connection is open,
- make the request to remove all geofences. Disconnect the client after making the request.
- For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /**
- * Once the connection is available, send a request to remove the
- * Geofences. The method signature used depends on which type of
- * remove request was originally received.
- */
- private void onConnected(Bundle dataBundle) {
- /*
- * Choose what to do based on the request type set in
- * removeGeofences
- */
- switch (mRequestType) {
- ...
- case REMOVE_INTENT :
- mLocationClient.removeGeofences(
- mGeofenceRequestIntent, this);
- break;
- ...
- }
- }
- ...
-}
-</pre>
-<p>
- Although the call to
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener)</a></code> Services calls
- returns immediately, the result of the removal request is indeterminate until Location Services
- calls
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>.
- The following snippet shows how to define this method:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /**
- * When the request to remove geofences by PendingIntent returns,
- * handle the result.
- *
- *&#64;param statusCode the code returned by Location Services
- *&#64;param requestIntent The Intent used to request the removal.
- */
- &#64;Override
- public void onRemoveGeofencesByPendingIntentResult(int statusCode,
- PendingIntent requestIntent) {
- // If removing the geofences was successful
- if (statusCode == LocationStatusCodes.SUCCESS) {
- /*
- * Handle successful removal of geofences here.
- * You can send out a broadcast intent or update the UI.
- * geofences into the Intent's extended data.
- */
- } else {
- // If adding the geocodes failed
- /*
- * Report errors here.
- * You can log the error using Log.e() or update
- * the UI.
- */
- }
- /*
- * Disconnect the location client regardless of the
- * request status, and indicate that a request is no
- * longer in progress
- */
- mInProgress = false;
- mLocationClient.disconnect();
- }
- ...
-}
-</pre>
-<h3>Remove individual geofences</h3>
-<p>
- The procedure for removing an individual geofence or set of geofences is similar to the
- removal of all geofences. To specify the geofences you want remove, add their geofence ID
- values to a {@link java.util.List} of String objects. Pass this {@link java.util.List} to a
- different definition of {@code removeGeofences} with the appropriate signature. This method
- then starts the removal process.
-</p>
-<p>
- Start by adding a request type for removing geofences by a list, and also add a global variable
- for storing the list of geofences:
-</p>
-<pre>
- ...
- // Enum type for controlling the type of removal requested
- public enum REQUEST_TYPE = {ADD, REMOVE_INTENT, REMOVE_LIST}
- // Store the list of geofence Ids to remove
- String&lt;List&gt; mGeofencesToRemove;
-</pre>
-<p>
- Next, define a list of geofences you want to remove. For example, this snippet removes the
-<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
- defined by the geofence ID "1":
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- List&lt;String&gt; listOfGeofences =
- Collections.singletonList("1");
- removeGeofences(listOfGeofences);
- ...
-}
-</pre>
-<p>
- The following snippet defines the {@code removeGeofences()} method:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /**
- * Start a request to remove monitoring by
- * calling LocationClient.connect()
- *
- */
- public void removeGeofences(List&lt;String&gt; geofenceIds) {
- // If Google Play services is unavailable, exit
- // Record the type of removal request
- mRequestType = REMOVE_LIST;
- /*
- * Test for Google Play services after setting the request type.
- * If Google Play services isn't present, the request can be
- * restarted.
- */
- if (!servicesConnected()) {
- return;
- }
- // Store the list of geofences to remove
- mGeofencesToRemove = geofenceIds;
- /*
- * Create a new location client object. Since the current
- * activity class implements ConnectionCallbacks and
- * OnConnectionFailedListener, pass the current activity object
- * as the listener for both parameters
- */
- mLocationClient = new LocationClient(this, this, this);
- // If a request is not already underway
- if (!mInProgress) {
- // Indicate that a request is underway
- mInProgress = true;
- // Request a connection from the client to Location Services
- mLocationClient.connect();
- } else {
- /*
- * A request is already underway. You can handle
- * this situation by disconnecting the client,
- * re-setting the flag, and then re-trying the
- * request.
- */
- }
- }
- ...
-}
-</pre>
-<p>
- When Location Services invokes the callback method indicating that the connection is open,
- make the request to remove the list of geofences. Disconnect the client after making the request.
- For example:
+
+<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>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- private void onConnected(Bundle dataBundle) {
- ...
- switch (mRequestType) {
- ...
- // If removeGeofencesById was called
- case REMOVE_LIST :
- mLocationClient.removeGeofences(
- mGeofencesToRemove, this);
- break;
- ...
- }
- ...
- }
- ...
-}
-</pre>
<p>
- Define an implementation of
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult()</a></code>.
- Location Services invokes this callback method to indicate that the request to remove a list of
- geofences is complete. In this method, examine the incoming status code and take the
- appropriate action:
+ 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>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks,
- OnConnectionFailedListener,
- OnAddGeofencesResultListener {
- ...
- /**
- * When the request to remove geofences by IDs returns, handle the
- * result.
- *
- * &#64;param statusCode The code returned by Location Services
- * &#64;param geofenceRequestIds The IDs removed
- */
- &#64;Override
- public void onRemoveGeofencesByRequestIdsResult(
- int statusCode, String[] geofenceRequestIds) {
- // If removing the geocodes was successful
- if (LocationStatusCodes.SUCCESS == statusCode) {
- /*
- * Handle successful removal of geofences here.
- * You can send out a broadcast intent or update the UI.
- * geofences into the Intent's extended data.
- */
- } else {
- // If removing the geofences failed
- /*
- * Report errors here.
- * You can log the error using Log.e() or update
- * the UI.
- */
- }
- // Indicate that a request is no longer in progress
- mInProgress = false;
- // Disconnect the location client
- mLocationClient.disconnect();
- }
- ...
+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
- or activity recognition, which are described in other lessons in this class.
-</p>
-<p>
- The next lesson,
- <a href="activity-recognition.html">Recognizing the User's Current Activity</a>, shows you how
- to request and receive activity updates. At regular intervals, Location Services can send you
- information about the user's current physical activity. Based on this information, you can
- change your app's behavior; for example, you can switch to a longer update interval if you
- detect that the user is walking instead of driving.
+ 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>
diff --git a/docs/html/training/location/index.jd b/docs/html/training/location/index.jd
index 35e177f..c4dec99 100644
--- a/docs/html/training/location/index.jd
+++ b/docs/html/training/location/index.jd
@@ -81,4 +81,10 @@ startpage=true
Learn how to convert a location's latitude and longitude into an address
(reverse geocoding).
</dd>
+ <dt>
+ <b><a href="geofencing.html">Creating and Monitoring Geofences</a></b>
+ </dt> <dd>
+ Learn how to define one or more geographic areas as locations of interest,
+ called geofences, and detect when the user is close to or inside a geofence.
+ </dd>
</dl>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 89e72f1..11ae1a6 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -736,6 +736,10 @@ include the action bar on devices running Android 2.1 or higher."
Displaying a Location Address
</a>
</li>
+ <li><a href="<?cs var:toroot ?>training/location/geofencing.html">
+ Creating and Monitoring Geofences
+ </a>
+ </li>
</ul>
</li>
</ul>