• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Creating and Monitoring Geofences
2
3trainingnavtop=true
4@jd:body
5
6
7<div id="tb-wrapper">
8<div id="tb">
9
10<h2>This lesson teaches you to</h2>
11<ol>
12    <li><a href="#RequestGeofences">Request Geofence Monitoring</a></li>
13    <li><a href="#HandleGeofenceTransitions">Handle Geofence Transitions</a></li>
14    <li><a href="#StopGeofenceMonitoring">Stop Geofence Monitoring</a></li>
15</ol>
16
17<h2>You should also read</h2>
18<ul>
19    <li>
20        <a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
21    </li>
22</ul>
23
24<h2>Try it out</h2>
25
26<div class="download-box">
27  <a href="http://developer.android.com/shareables/training/GeofenceDetection.zip" class="button">Download the sample</a>
28  <p class="filename">GeofenceDetection.zip</p>
29</div>
30
31</div>
32</div>
33<p>
34    Geofencing combines awareness of the user's current location with awareness of nearby
35    features, defined as the user's proximity to locations that may be of interest. To mark a
36    location of interest, you specify its latitude and longitude. To adjust the proximity for the
37    location, you add a radius. The latitude, longitude, and radius define a geofence.
38    You can have multiple active geofences at one time.
39</p>
40<p>
41    Location Services treats a geofences as an area rather than as a points and proximity. This
42    allows it to detect when the user enters or exits a geofence. For each geofence, you can ask
43    Location Services to send you entrance events or exit events or both. You can also limit the
44    duration of a geofence by specifying an expiration duration in milliseconds. After the geofence
45    expires, Location Services automatically removes it.
46</p>
47<!--
48    Send geofences to Location Services
49 -->
50<h2 id="RequestGeofences">Request Geofence Monitoring</h2>
51<p>
52    The first step in requesting geofence monitoring is to request the necessary permission.
53    To use geofencing, your app must request
54    {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. To request this
55    permission, add the following element as a child element of the
56<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>
57    element:
58</p>
59<pre>
60&lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
61</pre>
62<!-- Check for Google Play services -->
63<h3>Check for Google Play Services</h3>
64<p>
65    Location Services is part of the Google Play services APK. Since it's hard to anticipate the
66    state of the user's device, you should always check that the APK is installed before you attempt
67    to connect to Location Services. To check that the APK is installed, call
68<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
69    which returns one of the
70    integer result codes listed in the API reference documentation. If you encounter an error,
71    call
72<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
73    to retrieve localized dialog that prompts users to take the correct action, then display
74    the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
75    user to correct the problem, in which case Google Play services may send a result back to your
76    activity. To handle this result, override the method
77    {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
78
79</p>
80<p class="note">
81    <strong>Note:</strong> To make your app compatible with
82    platform version 1.6 and later, the activity that displays the
83    {@link android.support.v4.app.DialogFragment} must subclass
84    {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
85    {@link android.support.v4.app.FragmentActivity} also allows you to call
86    {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
87    getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
88</p>
89<p>
90    Since you usually need to check for Google Play services in more than one place in your code,
91    define a method that encapsulates the check, then call the method before each connection
92    attempt. The following snippet contains all of the code required to check for Google
93    Play services:
94</p>
95<pre>
96public class MainActivity extends FragmentActivity {
97    ...
98    // Global constants
99    /*
100     * Define a request code to send to Google Play services
101     * This code is returned in Activity.onActivityResult
102     */
103    private final static int
104            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
105    ...
106    // Define a DialogFragment that displays the error dialog
107    public static class ErrorDialogFragment extends DialogFragment {
108        // Global field to contain the error dialog
109        private Dialog mDialog;
110        ...
111        // Default constructor. Sets the dialog field to null
112        public ErrorDialogFragment() {
113            super();
114            mDialog = null;
115        }
116        ...
117        // Set the dialog to display
118        public void setDialog(Dialog dialog) {
119            mDialog = dialog;
120        }
121        ...
122        // Return a Dialog to the DialogFragment.
123        &#64;Override
124        public Dialog onCreateDialog(Bundle savedInstanceState) {
125            return mDialog;
126        }
127        ...
128    }
129    ...
130    /*
131     * Handle results returned to the FragmentActivity
132     * by Google Play services
133     */
134     &#64;Override
135    protected void onActivityResult(
136            int requestCode, int resultCode, Intent data) {
137        // Decide what to do based on the original request code
138        switch (requestCode) {
139            ...
140            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
141            /*
142             * If the result code is Activity.RESULT_OK, try
143             * to connect again
144             */
145                switch (resultCode) {
146                    ...
147                    case Activity.RESULT_OK :
148                    /*
149                     * Try the request again
150                     */
151                    ...
152                    break;
153                }
154            ...
155        }
156        ...
157    }
158    ...
159    private boolean servicesConnected() {
160        // Check that Google Play services is available
161        int resultCode =
162                GooglePlayServicesUtil.
163                        isGooglePlayServicesAvailable(this);
164        // If Google Play services is available
165        if (ConnectionResult.SUCCESS == resultCode) {
166            // In debug mode, log the status
167            Log.d("Geofence Detection",
168                    "Google Play services is available.");
169            // Continue
170            return true;
171        // Google Play services was not available for some reason
172        } else {
173            // Get the error code
174            int errorCode = connectionResult.getErrorCode();
175            // Get the error dialog from Google Play services
176            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
177                    errorCode,
178                    this,
179                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
180
181            // If Google Play services can provide an error dialog
182            if (errorDialog != null) {
183                // Create a new DialogFragment for the error dialog
184                ErrorDialogFragment errorFragment =
185                        new ErrorDialogFragment();
186                // Set the dialog in the DialogFragment
187                errorFragment.setDialog(errorDialog);
188                // Show the error dialog in the DialogFragment
189                errorFragment.show(
190                        getSupportFragmentManager(),
191                        "Geofence Detection");
192            }
193        }
194    }
195    ...
196}
197</pre>
198<p>
199    Snippets in the following sections call this method to verify that Google Play services is
200    available.
201</p>
202<p>
203    To use geofencing, start by defining the geofences you want to monitor. Although you usually
204    store geofence data in a local database or download it from the network, you need to send
205    a geofence to Location Services as an instance of
206<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>,
207    which you create with
208<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">Geofence.Builder</a></code>.
209    Each
210<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
211    object contains the following information:
212</p>
213<dl>
214    <dt>Latitude, longitude, and radius</dt>
215    <dd>
216        Define a circular area for the geofence. Use the latitude and longitude to mark a location
217        of interest, and then use the radius to adjust how close the user needs to approach the
218        location before the geofence is detected. The larger the radius, the more likely the
219        user will trigger a geofence transition alert by approaching the geofence. For example,
220        providing a large radius for a geofencing app that turns on lights in the user's house as
221        the user returns home might cause the lights to go on even if the user is simply passing by.
222    </dd>
223    <dt>Expiration time</dt>
224    <dd>
225        How long the geofence should remain active. Once the expiration time is reached, Location
226        Services deletes the geofence. Most of the time, you should specify an expiration time, but
227        you may want to keep permanent geofences for the user's home or place of work.
228    </dd>
229    <dt>Transition type</dt>
230    <dd>
231        Location Services can detect when the user steps within the radius of the geofence ("entry")
232        and when the user steps outside the radius of the geofence ("exit"), or both.
233    </dd>
234    <dt>Geofence ID</dt>
235    <dd>
236        A string that is stored with the geofence. You should make this unique, so that you can
237        use it to remove a geofence from Location Services tracking.
238    </dd>
239</dl>
240<h3>Define geofence storage</h3>
241<p>
242    A geofencing app needs to read and write geofence data to persistent storage. You shouldn't use
243<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
244    objects to do this; instead, use storage techniques such as databases that can store groups of
245    related data.
246</p>
247<p>
248    As an example of storing geofence data, the following snippet defines two classes that use
249    the app's {@link android.content.SharedPreferences} instance for persistent storage. The class
250    {@code SimpleGeofence}, analogous to a database record, stores the
251    data for a single
252<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
253    object in a "flattened" form. The class {@code SimpleGeofenceStore}, analogous to a database,
254    reads and writes {@code SimpleGeofence} data to the
255    {@link android.content.SharedPreferences} instance.
256</p>
257<pre>
258public class MainActivity extends FragmentActivity {
259    ...
260    /**
261     * A single Geofence object, defined by its center and radius.
262     */
263    public class SimpleGeofence {
264            // Instance variables
265            private final String mId;
266            private final double mLatitude;
267            private final double mLongitude;
268            private final float mRadius;
269            private long mExpirationDuration;
270            private int mTransitionType;
271
272        /**
273         * @param geofenceId The Geofence's request ID
274         * @param latitude Latitude of the Geofence's center.
275         * @param longitude Longitude of the Geofence's center.
276         * @param radius Radius of the geofence circle.
277         * @param expiration Geofence expiration duration
278         * @param transition Type of Geofence transition.
279         */
280        public SimpleGeofence(
281                String geofenceId,
282                double latitude,
283                double longitude,
284                float radius,
285                long expiration,
286                int transition) {
287            // Set the instance fields from the constructor
288            this.mId = geofenceId;
289            this.mLatitude = latitude;
290            this.mLongitude = longitude;
291            this.mRadius = radius;
292            this.mExpirationDuration = expiration;
293            this.mTransitionType = transition;
294        }
295        // Instance field getters
296        public String getId() {
297            return mId;
298        }
299        public double getLatitude() {
300            return mLatitude;
301        }
302        public double getLongitude() {
303            return mLongitude;
304        }
305        public float getRadius() {
306            return mRadius;
307        }
308        public long getExpirationDuration() {
309            return mExpirationDuration;
310        }
311        public int getTransitionType() {
312            return mTransitionType;
313        }
314        /**
315         * Creates a Location Services Geofence object from a
316         * SimpleGeofence.
317         *
318         * @return A Geofence object
319         */
320        public Geofence toGeofence() {
321            // Build a new Geofence object
322            return new Geofence.Builder()
323                    .setRequestId(getId())
324                    .setTransitionTypes(mTransitionType)
325                    .setCircularRegion(
326                            getLatitude(), getLongitude(), getRadius())
327                    .setExpirationDuration(mExpirationDuration)
328                    .build();
329        }
330    }
331    ...
332    /**
333     * Storage for geofence values, implemented in SharedPreferences.
334     */
335    public class SimpleGeofenceStore {
336        // Keys for flattened geofences stored in SharedPreferences
337        public static final String KEY_LATITUDE =
338                "com.example.android.geofence.KEY_LATITUDE";
339        public static final String KEY_LONGITUDE =
340                "com.example.android.geofence.KEY_LONGITUDE";
341        public static final String KEY_RADIUS =
342                "com.example.android.geofence.KEY_RADIUS";
343        public static final String KEY_EXPIRATION_DURATION =
344                "com.example.android.geofence.KEY_EXPIRATION_DURATION";
345        public static final String KEY_TRANSITION_TYPE =
346                "com.example.android.geofence.KEY_TRANSITION_TYPE";
347        // The prefix for flattened geofence keys
348        public static final String KEY_PREFIX =
349                "com.example.android.geofence.KEY";
350        /*
351         * Invalid values, used to test geofence storage when
352         * retrieving geofences
353         */
354        public static final long INVALID_LONG_VALUE = -999l;
355        public static final float INVALID_FLOAT_VALUE = -999.0f;
356        public static final int INVALID_INT_VALUE = -999;
357        // The SharedPreferences object in which geofences are stored
358        private final SharedPreferences mPrefs;
359        // The name of the SharedPreferences
360        private static final String SHARED_PREFERENCES =
361                "SharedPreferences";
362        // Create the SharedPreferences storage with private access only
363        public SimpleGeofenceStore(Context context) {
364            mPrefs =
365                    context.getSharedPreferences(
366                            SHARED_PREFERENCES,
367                            Context.MODE_PRIVATE);
368        }
369        /**
370         * Returns a stored geofence by its id, or returns {@code null}
371         * if it's not found.
372         *
373         * @param id The ID of a stored geofence
374         * @return A geofence defined by its center and radius. See
375         */
376        public SimpleGeofence getGeofence(String id) {
377            /*
378             * Get the latitude for the geofence identified by id, or
379             * INVALID_FLOAT_VALUE if it doesn't exist
380             */
381            double lat = mPrefs.getFloat(
382                    getGeofenceFieldKey(id, KEY_LATITUDE),
383                    INVALID_FLOAT_VALUE);
384            /*
385             * Get the longitude for the geofence identified by id, or
386             * INVALID_FLOAT_VALUE if it doesn't exist
387             */
388            double lng = mPrefs.getFloat(
389                    getGeofenceFieldKey(id, KEY_LONGITUDE),
390                    INVALID_FLOAT_VALUE);
391            /*
392             * Get the radius for the geofence identified by id, or
393             * INVALID_FLOAT_VALUE if it doesn't exist
394             */
395            float radius = mPrefs.getFloat(
396                    getGeofenceFieldKey(id, KEY_RADIUS),
397                    INVALID_FLOAT_VALUE);
398            /*
399             * Get the expiration duration for the geofence identified
400             * by id, or INVALID_LONG_VALUE if it doesn't exist
401             */
402            long expirationDuration = mPrefs.getLong(
403                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
404                    INVALID_LONG_VALUE);
405            /*
406             * Get the transition type for the geofence identified by
407             * id, or INVALID_INT_VALUE if it doesn't exist
408             */
409            int transitionType = mPrefs.getInt(
410                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
411                    INVALID_INT_VALUE);
412            // If none of the values is incorrect, return the object
413            if (
414                lat != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
415                lng != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
416                radius != GeofenceUtils.INVALID_FLOAT_VALUE &amp;&amp;
417                expirationDuration !=
418                        GeofenceUtils.INVALID_LONG_VALUE &amp;&amp;
419                transitionType != GeofenceUtils.INVALID_INT_VALUE) {
420
421                // Return a true Geofence object
422                return new SimpleGeofence(
423                        id, lat, lng, radius, expirationDuration,
424                        transitionType);
425            // Otherwise, return null.
426            } else {
427                return null;
428            }
429        }
430        /**
431         * Save a geofence.
432         * @param geofence The SimpleGeofence containing the
433         * values you want to save in SharedPreferences
434         */
435        public void setGeofence(String id, SimpleGeofence geofence) {
436            /*
437             * Get a SharedPreferences editor instance. Among other
438             * things, SharedPreferences ensures that updates are atomic
439             * and non-concurrent
440             */
441            Editor editor = mPrefs.edit();
442            // Write the Geofence values to SharedPreferences
443            editor.putFloat(
444                    getGeofenceFieldKey(id, KEY_LATITUDE),
445                    (float) geofence.getLatitude());
446            editor.putFloat(
447                    getGeofenceFieldKey(id, KEY_LONGITUDE),
448                    (float) geofence.getLongitude());
449            editor.putFloat(
450                    getGeofenceFieldKey(id, KEY_RADIUS),
451                    geofence.getRadius());
452            editor.putLong(
453                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
454                    geofence.getExpirationDuration());
455            editor.putInt(
456                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
457                    geofence.getTransitionType());
458            // Commit the changes
459            editor.commit();
460        }
461        public void clearGeofence(String id) {
462            /*
463             * Remove a flattened geofence object from storage by
464             * removing all of its keys
465             */
466            Editor editor = mPrefs.edit();
467            editor.remove(getGeofenceFieldKey(id, KEY_LATITUDE));
468            editor.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));
469            editor.remove(getGeofenceFieldKey(id, KEY_RADIUS));
470            editor.remove(getGeofenceFieldKey(id,
471                    KEY_EXPIRATION_DURATION));
472            editor.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));
473            editor.commit();
474        }
475        /**
476         * Given a Geofence object's ID and the name of a field
477         * (for example, KEY_LATITUDE), return the key name of the
478         * object's values in SharedPreferences.
479         *
480         * @param id The ID of a Geofence object
481         * @param fieldName The field represented by the key
482         * @return The full key name of a value in SharedPreferences
483         */
484        private String getGeofenceFieldKey(String id,
485                String fieldName) {
486            return KEY_PREFIX + "_" + id + "_" + fieldName;
487        }
488    }
489    ...
490}
491</pre>
492<h3>Create Geofence objects</h3>
493<p>
494    The following snippet uses the {@code SimpleGeofence} and {@code SimpleGeofenceStore} classes
495    gets geofence data from the UI, stores it in {@code SimpleGeofence} objects, stores these
496    objects in a {@code SimpleGeofenceStore} object, and then creates
497<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
498    objects:
499</p>
500<pre>
501public class MainActivity extends FragmentActivity {
502    ...
503    /*
504     * Use to set an expiration time for a geofence. After this amount
505     * of time Location Services will stop tracking the geofence.
506     */
507    private static final long SECONDS_PER_HOUR = 60;
508    private static final long MILLISECONDS_PER_SECOND = 1000;
509    private static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;
510    private static final long GEOFENCE_EXPIRATION_TIME =
511            GEOFENCE_EXPIRATION_IN_HOURS *
512            SECONDS_PER_HOUR *
513            MILLISECONDS_PER_SECOND;
514    ...
515    /*
516     * Handles to UI views containing geofence data
517     */
518    // Handle to geofence 1 latitude in the UI
519    private EditText mLatitude1;
520    // Handle to geofence 1 longitude in the UI
521    private EditText mLongitude1;
522    // Handle to geofence 1 radius in the UI
523    private EditText mRadius1;
524    // Handle to geofence 2 latitude in the UI
525    private EditText mLatitude2;
526    // Handle to geofence 2 longitude in the UI
527    private EditText mLongitude2;
528    // Handle to geofence 2 radius in the UI
529    private EditText mRadius2;
530    /*
531     * Internal geofence objects for geofence 1 and 2
532     */
533    private SimpleGeofence mUIGeofence1;
534    private SimpleGeofence mUIGeofence2;
535    ...
536    // Internal List of Geofence objects
537    List&lt;Geofence&gt; mGeofenceList;
538    // Persistent storage for geofences
539    private SimpleGeofenceStore mGeofenceStorage;
540    ...
541    &#64;Override
542    protected void onCreate(Bundle savedInstanceState) {
543        super.onCreate(savedInstanceState);
544        ...
545        // Instantiate a new geofence storage area
546        mGeofenceStorage = new SimpleGeofenceStore(this);
547
548        // Instantiate the current List of geofences
549        mCurrentGeofences = new ArrayList&lt;Geofence&gt;();
550    }
551    ...
552    /**
553     * Get the geofence parameters for each geofence from the UI
554     * and add them to a List.
555     */
556    public void createGeofences() {
557        /*
558         * Create an internal object to store the data. Set its
559         * ID to "1". This is a "flattened" object that contains
560         * a set of strings
561         */
562        mUIGeofence1 = new SimpleGeofence(
563                "1",
564                Double.valueOf(mLatitude1.getText().toString()),
565                Double.valueOf(mLongitude1.getText().toString()),
566                Float.valueOf(mRadius1.getText().toString()),
567                GEOFENCE_EXPIRATION_TIME,
568                // This geofence records only entry transitions
569                Geofence.GEOFENCE_TRANSITION_ENTER);
570        // Store this flat version
571        mGeofenceStorage.setGeofence("1", mUIGeofence1);
572        // Create another internal object. Set its ID to "2"
573        mUIGeofence2 = new SimpleGeofence(
574                "2",
575                Double.valueOf(mLatitude2.getText().toString()),
576                Double.valueOf(mLongitude2.getText().toString()),
577                Float.valueOf(mRadius2.getText().toString()),
578                GEOFENCE_EXPIRATION_TIME,
579                // This geofence records both entry and exit transitions
580                Geofence.GEOFENCE_TRANSITION_ENTER |
581                Geofence.GEOFENCE_TRANSITION_EXIT);
582        // Store this flat version
583        mGeofenceStorage.setGeofence(2, mUIGeofence2);
584        mGeofenceList.add(mUIGeofence1.toGeofence());
585        mGeofenceList.add(mUIGeofence2.toGeofence());
586    }
587    ...
588}
589</pre>
590<p>
591    In addition to the {@link java.util.List} of
592<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
593    objects you want to monitor, you need to provide Location Services with the
594    {@link android.content.Intent} that it sends to your app when it detects geofence
595    transitions.
596<h4>Define a Intent for geofence transitions</h4>
597<p>
598    The {@link android.content.Intent} sent from Location Services can trigger various actions in
599    your app, but you should <i>not</i> have it start an activity or fragment, because components
600    should only become visible in response to a user action. In many cases, an
601    {@link android.app.IntentService} is a good way to handle the intent. An
602    {@link android.app.IntentService} can post a notification, do long-running background work,
603    send intents to other services, or send a broadcast intent. The following snippet shows how
604    how to define a {@link android.app.PendingIntent} that starts an
605    {@link android.app.IntentService}:
606</p>
607<pre>
608public class MainActivity extends FragmentActivity {
609    ...
610    /*
611     * Create a PendingIntent that triggers an IntentService in your
612     * app when a geofence transition occurs.
613     */
614    private PendingIntent getTransitionPendingIntent() {
615        // Create an explicit Intent
616        Intent intent = new Intent(this,
617                ReceiveTransitionsIntentService.class);
618        /*
619         * Return the PendingIntent
620         */
621        return PendingIntent.getService(
622                this,
623                0,
624                intent,
625                PendingIntent.FLAG_UPDATE_CURRENT);
626    }
627    ...
628}
629</pre>
630<p>
631    Now you have all the code you need to send a request to monitor geofences to Location
632    Services.
633</p>
634<!-- Send the monitoring request -->
635<h3 id="requestmonitoring">Send the monitoring request</h3>
636<p>
637    Sending the monitoring request requires two asynchronous operations. The first operation gets a
638    location client for the request, and the second makes the request using the client. In both
639    cases, Location Services invokes a callback method when it finishes the operation. The best way
640    to handle these operations is to chain together the method calls. The following snippets
641    demonstrate how to set up an activity, define the methods, and call them in the proper order.
642</p>
643<p>
644     First, modify the activity's class definition to implement the necessary callback interfaces.
645     Add the following interfaces:
646</p>
647<dl>
648    <dt>
649<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
650    </dt>
651    <dd>
652        Specifies methods that Location Services calls when a location client is connected or
653        disconnected.
654    </dd>
655    <dt>
656<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
657    </dt>
658    <dd>
659        Specifies a method that Location Services calls if an error occurs while attempting to
660        connect the location client.
661    </dd>
662    <dt>
663<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html">OnAddGeofencesResultListener</a></code>
664    </dt>
665    <dd>
666        Specifies a method that Location Services calls once it has added the geofences.
667    </dd>
668</dl>
669<p>
670    For example:
671</p>
672<pre>
673public class MainActivity extends FragmentActivity implements
674        ConnectionCallbacks,
675        OnConnectionFailedListener,
676        OnAddGeofencesResultListener {
677    ...
678}
679</pre>
680<h4>Start the request process</h4>
681<p>
682    Next, define a method that starts the request process by connecting to Location Services.
683    Mark this as a request to add a geofence by setting a global variable. This allows you to
684    use the callback
685<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>
686    to add geofences and to remove them, as described in succeeding sections.
687</p>
688<p>
689<p>
690    To guard against race conditions that might arise if your app tries to start another request
691    before the first one finishes, define a boolean flag that tracks the state of the current
692    request:
693</p>
694<pre>
695public class MainActivity extends FragmentActivity implements
696        ConnectionCallbacks,
697        OnConnectionFailedListener,
698        OnAddGeofencesResultListener {
699    ...
700    // Holds the location client
701    private LocationClient mLocationClient;
702    // Stores the PendingIntent used to request geofence monitoring
703    private PendingIntent mGeofenceRequestIntent;
704    // Defines the allowable request types.
705    public enum REQUEST_TYPE = {ADD}
706    private REQUEST_TYPE mRequestType;
707    // Flag that indicates if a request is underway.
708    private boolean mInProgress;
709    ...
710    &#64;Override
711    protected void onCreate(Bundle savedInstanceState) {
712        ...
713        // Start with the request flag set to false
714        mInProgress = false;
715        ...
716    }
717    ...
718    /**
719     * Start a request for geofence monitoring by calling
720     * LocationClient.connect().
721     */
722    public void addGeofences() {
723        // Start a request to add geofences
724        mRequestType = ADD;
725        /*
726         * Test for Google Play services after setting the request type.
727         * If Google Play services isn't present, the proper request
728         * can be restarted.
729         */
730        if (!servicesConnected()) {
731            return;
732        }
733        /*
734         * Create a new location client object. Since the current
735         * activity class implements ConnectionCallbacks and
736         * OnConnectionFailedListener, pass the current activity object
737         * as the listener for both parameters
738         */
739        mLocationClient = new LocationClient(this, this, this)
740        // If a request is not already underway
741        if (!mInProgress) {
742            // Indicate that a request is underway
743            mInProgress = true;
744            // Request a connection from the client to Location Services
745            mLocationClient.connect();
746        } else {
747            /*
748             * A request is already underway. You can handle
749             * this situation by disconnecting the client,
750             * re-setting the flag, and then re-trying the
751             * request.
752             */
753        }
754    }
755    ...
756}
757</pre>
758<h4>Send a request to add the geofences</h4>
759<p>
760    In your implementation of
761<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>,
762    call
763<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>.
764    Notice that if the connection fails,
765<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>
766    isn't called, and the request stops.
767</p>
768<pre>
769public class MainActivity extends FragmentActivity implements
770        ConnectionCallbacks,
771        OnConnectionFailedListener,
772        OnAddGeofencesResultListener {
773    ...
774    /*
775     * Provide the implementation of ConnectionCallbacks.onConnected()
776     * Once the connection is available, send a request to add the
777     * Geofences
778     */
779    &#64;Override
780    private void onConnected(Bundle dataBundle) {
781        ...
782        switch (mRequestType) {
783            case ADD :
784                // Get the PendingIntent for the request
785                mTransitionPendingIntent =
786                        getTransitionPendingIntent();
787                // Send a request to add the current geofences
788                mLocationClient.addGeofences(
789                        mCurrentGeofences, pendingIntent, this);
790            ...
791        }
792    }
793    ...
794}
795</pre>
796<p>
797    Notice that
798<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>
799    returns immediately, but the status of the request is indeterminate until Location Services
800    calls
801<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>
802    Once this method is called, you can determine if the request was successful or not.
803</p>
804<h4>Check the result returned by Location Services</h4>
805<p>
806    When Location Services invokes your implementation of the callback method
807<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>,
808    indicating that the request is complete, examine the incoming status code. If the request
809    was successful, the geofences you requested are active. If the request was unsuccessful,
810    the geofences aren't active, and you need to re-try the request or report an error. For example:
811</p>
812<pre>
813public class MainActivity extends FragmentActivity implements
814        ConnectionCallbacks,
815        OnConnectionFailedListener,
816        OnAddGeofencesResultListener {
817        ...
818    /*
819     * Provide the implementation of
820     * OnAddGeofencesResultListener.onAddGeofencesResult.
821     * Handle the result of adding the geofences
822     *
823     */
824    &#64;Override
825    public void onAddGeofencesResult(
826            int statusCode, String[] geofenceRequestIds) {
827        // If adding the geofences was successful
828        if (LocationStatusCodes.SUCCESS == statusCode) {
829            /*
830             * Handle successful addition of geofences here.
831             * You can send out a broadcast intent or update the UI.
832             * geofences into the Intent's extended data.
833             */
834        } else {
835        // If adding the geofences failed
836            /*
837             * Report errors here.
838             * You can log the error using Log.e() or update
839             * the UI.
840             */
841        }
842        // Turn off the in progress flag and disconnect the client
843        mInProgress = false;
844        mLocationClient.disconnect();
845    }
846    ...
847}
848</pre>
849<!-- Handle disconnections -->
850<h3>Handle disconnections</h3>
851<p>
852    In some cases, Location Services may disconnect from the activity recognition client before
853    you call
854<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#disconnect()">disconnect()</a></code>.
855    To handle this situation, implement <code>
856<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>.
857    In this method, set the request flag to indicate that a request is not in progress, and
858    delete the client:
859</p>
860<pre>
861public class MainActivity extends FragmentActivity implements
862        ConnectionCallbacks,
863        OnConnectionFailedListener,
864        OnAddGeofencesResultListener {
865    ...
866    /*
867     * Implement ConnectionCallbacks.onDisconnected()
868     * Called by Location Services once the location client is
869     * disconnected.
870     */
871    &#64;Override
872    public void onDisconnected() {
873        // Turn off the request flag
874        mInProgress = false;
875        // Destroy the current location client
876        mLocationClient = null;
877    }
878    ...
879}
880</pre>
881<!-- Handle connection errors -->
882<h3>Handle connection errors</h3>
883<p>
884    Besides handling the normal callbacks from Location Services, you have to provide a callback
885    method that Location Services calls if a connection error occurs. This callback method
886    can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to
887    handle the check for Google Play services. It can also re-use the override you defined
888    for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that
889    receives any Google Play services results that occur when the user interacts with the
890    error dialog. The following snippet shows you a sample implementation of the callback method:
891</p>
892<pre>
893public class MainActivity extends FragmentActivity implements
894        ConnectionCallbacks,
895        OnConnectionFailedListener,
896        OnAddGeofencesResultListener {
897    ...
898    // Implementation of OnConnectionFailedListener.onConnectionFailed
899    &#64;Override
900    public void onConnectionFailed(ConnectionResult connectionResult) {
901        // Turn off the request flag
902        mInProgress = false;
903        /*
904         * If the error has a resolution, start a Google Play services
905         * activity to resolve it.
906         */
907        if (connectionResult.hasResolution()) {
908            try {
909                connectionResult.startResolutionForResult(
910                        this,
911                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
912            } catch (SendIntentException e) {
913                // Log the error
914                e.printStackTrace();
915            }
916        // If no resolution is available, display an error dialog
917        } else {
918            // Get the error code
919            int errorCode = connectionResult.getErrorCode();
920            // Get the error dialog from Google Play services
921            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
922                    errorCode,
923                    this,
924                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
925            // If Google Play services can provide an error dialog
926            if (errorDialog != null) {
927                // Create a new DialogFragment for the error dialog
928                ErrorDialogFragment errorFragment =
929                        new ErrorDialogFragment();
930                // Set the dialog in the DialogFragment
931                errorFragment.setDialog(errorDialog);
932                // Show the error dialog in the DialogFragment
933                errorFragment.show(
934                        getSupportFragmentManager(),
935                        "Geofence Detection");
936            }
937        }
938    }
939    ...
940}
941</pre>
942<!--
943    Handle Geofence Transitions
944 -->
945<h2 id="HandleGeofenceTransitions">Handle Geofence Transitions</h2>
946<p>
947    When Location Services detects that the user has entered or exited a geofence, it
948    sends out the {@link android.content.Intent} contained in the {@link android.app.PendingIntent}
949    you included in the request to add geofences. This {@link android.content.Intent} is
950</p>
951<h3>Define an IntentService</h3>
952<p>
953    The following snippet shows how to define an {@link android.app.IntentService} that posts a
954    notification when a geofence transition occurs. When the user clicks the notification, the
955    app's main activity appears:
956</p>
957<pre>
958public class ReceiveTransitionsIntentService extends IntentService {
959    ...
960    /**
961     * Sets an identifier for the service
962     */
963    public ReceiveTransitionsIntentService() {
964        super("ReceiveTransitionsIntentService");
965    }
966    /**
967     * Handles incoming intents
968     *&#64;param intent The Intent sent by Location Services. This
969     * Intent is provided
970     * to Location Services (inside a PendingIntent) when you call
971     * addGeofences()
972     */
973    &#64;Override
974    protected void onHandleIntent(Intent intent) {
975        // First check for errors
976        if (LocationClient.hasError(intent)) {
977            // Get the error code with a static method
978            int errorCode = LocationClient.getErrorCode(intent);
979            // Log the error
980            Log.e("ReceiveTransitionsIntentService",
981                    "Location Services error: " +
982                    Integer.toString(errorCode));
983            /*
984             * You can also send the error code to an Activity or
985             * Fragment with a broadcast Intent
986             */
987        /*
988         * If there's no error, get the transition type and the IDs
989         * of the geofence or geofences that triggered the transition
990         */
991        } else {
992            // Get the type of transition (entry or exit)
993            int transitionType =
994                    LocationClient.getGeofenceTransition(intent);
995            // Test that a valid transition was reported
996            if (
997                (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER)
998                 ||
999                (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT)
1000               ) {
1001                List &lt;Geofence&gt; triggerList =
1002                        getTriggeringGeofences(intent);
1003
1004                String[] triggerIds = new String[geofenceList.size()];
1005
1006                for (int i = 0; i &lt; triggerIds.length; i++) {
1007                    // Store the Id of each geofence
1008                    triggerIds[i] = triggerList.get(i).getRequestId();
1009                }
1010                /*
1011                 * At this point, you can store the IDs for further use
1012                 * display them, or display the details associated with
1013                 * them.
1014                 */
1015            }
1016        // An invalid transition was reported
1017        } else {
1018            Log.e("ReceiveTransitionsIntentService",
1019                    "Geofence transition error: " +
1020                    Integer.toString()transitionType));
1021        }
1022    }
1023    ...
1024}
1025</pre>
1026<!-- Specify the IntentService in the manifest -->
1027<h3>Specify the IntentService in the manifest</h3>
1028<p>
1029    To identify the {@link android.app.IntentService} to the system, add a
1030    <code><a href="{@docRoot}guide/topics/manifest/service-element.html">&lt;service&gt;</a></code>
1031    element to the app manifest. For example:
1032</p>
1033<pre>
1034&lt;service
1035    android:name="com.example.android.location.ReceiveTransitionsIntentService"
1036    android:label="&#64;string/app_name"
1037    android:exported="false"&gt;
1038&lt;/service&gt;
1039</pre>
1040<p>
1041    Notice that you don't have to specify intent filters for the service, because it only receives
1042    explicit intents. How the incoming geofence transition intents are created is described in the
1043    section <a href="#requestmonitoring">Send the monitoring request</a>.
1044</p>
1045<!--
1046    Remove Geofences
1047 -->
1048<h2 id="StopGeofenceMonitoring">Stop Geofence Monitoring</h2>
1049<p>
1050    To stop geofence monitoring, you remove the geofences themselves. You can remove a specific
1051    set of geofences or all the geofences associated with a {@link android.app.PendingIntent}. The
1052    procedure is similar to adding geofences. The first operation gets a location
1053    client for the removal request, and the second makes the request using the client.
1054</p>
1055<p>
1056    The callback methods that Location Services invokes when it has finished removing geofences
1057    are defined in the interface
1058<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html">LocationClient.OnRemoveGeofencesResultListener</a></code>. Declare
1059    this interface as part of your class definition, and then add definitions for its two methods:
1060</p>
1061<dl>
1062    <dt>
1063<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>
1064    </dt>
1065    <dd>
1066        Callback invoked when Location Services finishes a request to remove all geofences made
1067        by the method
1068<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>.
1069    </dd>
1070    <dt>
1071<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>
1072    </dt>
1073    <dd>
1074        Callback invoked when Location Services finished a request to remove a set of geofences,
1075        specified by their geofence IDs, by the method
1076<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>.
1077    </dd>
1078</dl>
1079<p>
1080    Examples of implementing these methods are shown in the next snippets.
1081</p>
1082<h3>Remove all geofences</h3>
1083<p>
1084    Since removing geofences uses some of the methods you use to add geofences, start by defining
1085    another request type:
1086</p>
1087<pre>
1088public class MainActivity extends FragmentActivity implements
1089        ConnectionCallbacks,
1090        OnConnectionFailedListener,
1091        OnAddGeofencesResultListener {
1092    ...
1093    // Enum type for controlling the type of removal requested
1094    public enum REQUEST_TYPE = {ADD, REMOVE_INTENT}
1095    ...
1096}
1097</pre>
1098<p>
1099    Start the removal request by getting a connection to Location Services. If the connection fails,
1100<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code> isn't called,
1101    and the request stops. The following snippet shows how to start the request:
1102</p>
1103<pre>
1104public class MainActivity extends FragmentActivity implements
1105        ConnectionCallbacks,
1106        OnConnectionFailedListener,
1107        OnAddGeofencesResultListener {
1108    ...
1109    /**
1110     * Start a request to remove geofences by calling
1111     * LocationClient.connect()
1112     */
1113    public void removeGeofences(PendingIntent requestIntent) {
1114        // Record the type of removal request
1115        mRequestType = REMOVE_INTENT;
1116        /*
1117         * Test for Google Play services after setting the request type.
1118         * If Google Play services isn't present, the request can be
1119         * restarted.
1120         */
1121        if (!servicesConnected()) {
1122            return;
1123        }
1124        // Store the PendingIntent
1125        mGeofenceRequestIntent = requestIntent;
1126        /*
1127         * Create a new location client object. Since the current
1128         * activity class implements ConnectionCallbacks and
1129         * OnConnectionFailedListener, pass the current activity object
1130         * as the listener for both parameters
1131         */
1132        mLocationClient = new LocationClient(this, this, this);
1133        // If a request is not already underway
1134        if (!mInProgress) {
1135            // Indicate that a request is underway
1136            mInProgress = true;
1137            // Request a connection from the client to Location Services
1138            mLocationClient.connect();
1139        } else {
1140            /*
1141             * A request is already underway. You can handle
1142             * this situation by disconnecting the client,
1143             * re-setting the flag, and then re-trying the
1144             * request.
1145             */
1146        }
1147    }
1148    ...
1149}
1150</pre>
1151<p>
1152   When Location Services invokes the callback method indicating that the connection is open,
1153   make the request to remove all geofences. Disconnect the client after making the request.
1154   For example:
1155</p>
1156<pre>
1157public class MainActivity extends FragmentActivity implements
1158        ConnectionCallbacks,
1159        OnConnectionFailedListener,
1160        OnAddGeofencesResultListener {
1161    ...
1162    /**
1163     * Once the connection is available, send a request to remove the
1164     * Geofences. The method signature used depends on which type of
1165     * remove request was originally received.
1166     */
1167    private void onConnected(Bundle dataBundle) {
1168        /*
1169         * Choose what to do based on the request type set in
1170         * removeGeofences
1171         */
1172        switch (mRequestType) {
1173            ...
1174            case REMOVE_INTENT :
1175                mLocationClient.removeGeofences(
1176                        mGeofenceRequestIntent, this);
1177                break;
1178            ...
1179        }
1180    }
1181    ...
1182}
1183</pre>
1184<p>
1185    Although the call to
1186<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
1187    returns immediately, the result of the removal request is indeterminate until Location Services
1188    calls
1189<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>.
1190    The following snippet shows how to define this method:
1191</p>
1192<pre>
1193public class MainActivity extends FragmentActivity implements
1194        ConnectionCallbacks,
1195        OnConnectionFailedListener,
1196        OnAddGeofencesResultListener {
1197    ...
1198    /**
1199     * When the request to remove geofences by PendingIntent returns,
1200     * handle the result.
1201     *
1202     *&#64;param statusCode the code returned by Location Services
1203     *&#64;param requestIntent The Intent used to request the removal.
1204     */
1205    &#64;Override
1206    public void onRemoveGeofencesByPendingIntentResult(int statusCode,
1207            PendingIntent requestIntent) {
1208        // If removing the geofences was successful
1209        if (statusCode == LocationStatusCodes.SUCCESS) {
1210            /*
1211             * Handle successful removal of geofences here.
1212             * You can send out a broadcast intent or update the UI.
1213             * geofences into the Intent's extended data.
1214             */
1215        } else {
1216        // If adding the geocodes failed
1217            /*
1218             * Report errors here.
1219             * You can log the error using Log.e() or update
1220             * the UI.
1221             */
1222        }
1223        /*
1224         * Disconnect the location client regardless of the
1225         * request status, and indicate that a request is no
1226         * longer in progress
1227         */
1228        mInProgress = false;
1229        mLocationClient.disconnect();
1230    }
1231    ...
1232}
1233</pre>
1234<h3>Remove individual geofences</h3>
1235<p>
1236    The procedure for removing an individual geofence or set of geofences is similar to the
1237    removal of all geofences. To specify the geofences you want remove, add their geofence ID
1238    values to a {@link java.util.List} of String objects. Pass this {@link java.util.List} to a
1239    different definition of {@code removeGeofences} with the appropriate signature. This method
1240    then starts the removal process.
1241</p>
1242<p>
1243    Start by adding a request type for removing geofences by a list, and also add a global variable
1244    for storing the list of geofences:
1245</p>
1246<pre>
1247    ...
1248    // Enum type for controlling the type of removal requested
1249    public enum REQUEST_TYPE = {ADD, REMOVE_INTENT, REMOVE_LIST}
1250    // Store the list of geofence Ids to remove
1251    String&lt;List&gt; mGeofencesToRemove;
1252</pre>
1253<p>
1254    Next, define a list of geofences you want to remove. For example, this snippet removes the
1255<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>
1256    defined by the geofence ID "1":
1257</p>
1258<pre>
1259public class MainActivity extends FragmentActivity implements
1260        ConnectionCallbacks,
1261        OnConnectionFailedListener,
1262        OnAddGeofencesResultListener {
1263    ...
1264        List&lt;String&gt; listOfGeofences =
1265                Collections.singletonList("1");
1266        removeGeofences(listOfGeofences);
1267    ...
1268}
1269</pre>
1270<p>
1271    The following snippet defines the {@code removeGeofences()} method:
1272</p>
1273<pre>
1274public class MainActivity extends FragmentActivity implements
1275        ConnectionCallbacks,
1276        OnConnectionFailedListener,
1277        OnAddGeofencesResultListener {
1278    ...
1279    /**
1280     * Start a request to remove monitoring by
1281     * calling LocationClient.connect()
1282     *
1283     */
1284    public void removeGeofences(List&lt;String&gt; geofenceIds) {
1285        // If Google Play services is unavailable, exit
1286        // Record the type of removal request
1287        mRequestType = REMOVE_LIST;
1288        /*
1289         * Test for Google Play services after setting the request type.
1290         * If Google Play services isn't present, the request can be
1291         * restarted.
1292         */
1293        if (!servicesConnected()) {
1294            return;
1295        }
1296        // Store the list of geofences to remove
1297        mGeofencesToRemove = geofenceIds;
1298        /*
1299         * Create a new location client object. Since the current
1300         * activity class implements ConnectionCallbacks and
1301         * OnConnectionFailedListener, pass the current activity object
1302         * as the listener for both parameters
1303         */
1304        mLocationClient = new LocationClient(this, this, this);
1305        // If a request is not already underway
1306        if (!mInProgress) {
1307            // Indicate that a request is underway
1308            mInProgress = true;
1309            // Request a connection from the client to Location Services
1310            mLocationClient.connect();
1311        } else {
1312            /*
1313             * A request is already underway. You can handle
1314             * this situation by disconnecting the client,
1315             * re-setting the flag, and then re-trying the
1316             * request.
1317             */
1318        }
1319    }
1320    ...
1321}
1322</pre>
1323<p>
1324   When Location Services invokes the callback method indicating that the connection is open,
1325   make the request to remove the list of geofences. Disconnect the client after making the request.
1326   For example:
1327</p>
1328<pre>
1329public class MainActivity extends FragmentActivity implements
1330        ConnectionCallbacks,
1331        OnConnectionFailedListener,
1332        OnAddGeofencesResultListener {
1333    ...
1334    private void onConnected(Bundle dataBundle) {
1335        ...
1336        switch (mRequestType) {
1337        ...
1338        // If removeGeofencesById was called
1339            case REMOVE_LIST :
1340                mLocationClient.removeGeofences(
1341                        mGeofencesToRemove, this);
1342                break;
1343        ...
1344        }
1345        ...
1346    }
1347    ...
1348}
1349</pre>
1350<p>
1351    Define an implementation of
1352<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult()</a></code>.
1353    Location Services invokes this callback method to indicate that the request to remove a list of
1354    geofences is complete. In this method, examine the incoming status code and take the
1355    appropriate action:
1356</p>
1357<pre>
1358public class MainActivity extends FragmentActivity implements
1359        ConnectionCallbacks,
1360        OnConnectionFailedListener,
1361        OnAddGeofencesResultListener {
1362    ...
1363    /**
1364     * When the request to remove geofences by IDs returns, handle the
1365     * result.
1366     *
1367     * &#64;param statusCode The code returned by Location Services
1368     * &#64;param geofenceRequestIds The IDs removed
1369     */
1370    &#64;Override
1371    public void onRemoveGeofencesByRequestIdsResult(
1372            int statusCode, String[] geofenceRequestIds) {
1373        // If removing the geocodes was successful
1374        if (LocationStatusCodes.SUCCESS == statusCode) {
1375            /*
1376             * Handle successful removal of geofences here.
1377             * You can send out a broadcast intent or update the UI.
1378             * geofences into the Intent's extended data.
1379             */
1380        } else {
1381        // If removing the geofences failed
1382            /*
1383             * Report errors here.
1384             * You can log the error using Log.e() or update
1385             * the UI.
1386             */
1387        }
1388        // Indicate that a request is no longer in progress
1389        mInProgress = false;
1390        // Disconnect the location client
1391        mLocationClient.disconnect();
1392    }
1393    ...
1394}
1395</pre>
1396<p>
1397    You can combine geofencing with other location-aware features, such as periodic location updates
1398    or activity recognition, which are described in other lessons in this class.
1399</p>
1400<p>
1401    The next lesson,
1402    <a href="activity-recognition.html">Recognizing the User's Current Activity</a>, shows you how
1403    to request and receive activity updates. At regular intervals, Location Services can send you
1404    information about the user's current physical activity. Based on this information, you can
1405    change your app's behavior; for example, you can switch to a longer update interval if you
1406    detect that the user is walking instead of driving.
1407</p>
1408