summaryrefslogtreecommitdiffstats
path: root/docs/html/training/location/geofencing.jd
diff options
context:
space:
mode:
Diffstat (limited to 'docs/html/training/location/geofencing.jd')
-rw-r--r--docs/html/training/location/geofencing.jd1407
1 files changed, 1407 insertions, 0 deletions
diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd
new file mode 100644
index 0000000..748b6ec
--- /dev/null
+++ b/docs/html/training/location/geofencing.jd
@@ -0,0 +1,1407 @@
+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">Request Geofence Monitoring</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>
+
+<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>
+
+</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
+ 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.
+</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.
+</p>
+<!--
+ Send geofences to Location Services
+ -->
+<h2 id="RequestGeofences">Request 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:
+</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:
+</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>
+<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>
+<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;
+
+ /**
+ * @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) {
+
+ // 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;
+ }
+ }
+ ...
+}
+</pre>
+<h3>Create Geofence objects</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:
+</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());
+ }
+ ...
+}
+</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>
+<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
+ how 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);
+ ...
+ }
+ }
+ ...
+}
+</pre>
+<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:
+</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");
+ }
+ }
+ }
+ ...
+}
+</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
+</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
+ 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);
+
+ String[] triggerIds = new String[geofenceList.size()];
+
+ 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
+ } else {
+ Log.e("ReceiveTransitionsIntentService",
+ "Geofence transition error: " +
+ Integer.toString()transitionType));
+ }
+ }
+ ...
+}
+</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>
+<!--
+ 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>
+<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:
+</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();
+ }
+ ...
+}
+</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.
+</p>