Home | History | Annotate | Download | only in location
      1 page.title=Creating and Monitoring Geofences
      2 
      3 trainingnavtop=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>
     96 public 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>
    258 public 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>
    501 public 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>
    608 public 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>
    673 public 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>
    695 public 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>
    769 public 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>
    813 public 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>
    861 public 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>
    893 public 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>
    958 public 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>
   1088 public 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>
   1104 public 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>
   1157 public 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>
   1193 public 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>
   1259 public 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>
   1274 public 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>
   1329 public 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>
   1358 public 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