Home | History | Annotate | Download | only in location
      1 page.title=Recognizing the User's Current Activity
      2 
      3 trainingnavtop=true
      4 @jd:body
      5 
      6 <div id="tb-wrapper">
      7 <div id="tb">
      8 <h2>This lesson teaches you to</h2>
      9 <ol>
     10     <li><a href="#RequestUpdates">Request Activity Recognition Updates</a></li>
     11     <li><a href="#HandleUpdates">Handle Activity Updates</a>
     12     <li><a href="#RemoveUpdates">Stop Activity Recognition Updates</a>
     13 </ol>
     14 <h2>You should also read</h2>
     15 <ul>
     16     <li>
     17         <a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
     18     </li>
     19     <li>
     20         <a href="receive-location-updates.html">Receiving Location Updates</a>
     21     </li>
     22 </ul>
     23 <h2>Try it out</h2>
     24 
     25 <div class="download-box">
     26   <a href="http://developer.android.com/shareables/training/ActivityRecognition.zip" class="button">Download the sample</a>
     27   <p class="filename">ActivityRecognition.zip</p>
     28 </div>
     29 
     30 </div>
     31 </div>
     32 
     33 <p>
     34     This lesson shows you how to request activity recognition updates from Location Services.
     35     Activity recognition tries to detect the user's current physical activity, such as walking,
     36     driving, or standing still. Requests for updates go through an activity recognition client,
     37     which, while different from the location client used by location or geofencing, follows a
     38     similar pattern. Based on the update interval you choose, Location Services sends out
     39     activity information containing one or more possible activities and the confidence level for
     40     each one.
     41 </p>
     42 <h2 id="RequestUpdates">Request Activity Recognition Updates</h2>
     43 <p>
     44     Requesting activity recognition updates from Location Services is similar to requesting
     45     periodic location updates. You send the request through a client, and Location Services sends
     46     updates back to your app by means of a {@link android.app.PendingIntent}. However, you need to
     47     request a special permission before you request activity updates, and you use a different type
     48     of client to make requests. The following sections show how to request the permission,
     49     connect the client, and request updates.
     50 </p>
     51 <h3>Request permission to receive updates</h3>
     52 <p>
     53     An app that wants to get activity recognition updates must have the permission
     54     {@code com.google.android.gms.permission.ACTIVITY_RECOGNITION}. To request this permission for
     55     your app, add the following XML element to your manifest 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
     61     android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/&gt;
     62 </pre>
     63 <p>
     64     Activity recognition does not require the permissions
     65     {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
     66     {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}.
     67 </p>
     68 <!-- Check for Google Play services -->
     69 <h3>Check for Google Play Services</h3>
     70 <p>
     71     Location Services is part of the Google Play services APK. Since it's hard to anticipate the
     72     state of the user's device, you should always check that the APK is installed before you attempt
     73     to connect to Location Services. To check that the APK is installed, call
     74 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
     75     which returns one of the
     76     integer result codes listed in the API reference documentation. If you encounter an error,
     77     call
     78 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
     79     to retrieve localized dialog that prompts users to take the correct action, then display
     80     the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
     81     user to correct the problem, in which case Google Play services may send a result back to your
     82     activity. To handle this result, override the method
     83     {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
     84 
     85 </p>
     86 <p class="note">
     87     <strong>Note:</strong> To make your app compatible with
     88     platform version 1.6 and later, the activity that displays the
     89     {@link android.support.v4.app.DialogFragment} must subclass
     90     {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
     91     {@link android.support.v4.app.FragmentActivity} also allows you to call
     92     {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
     93     getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
     94 </p>
     95 <p>
     96     Since you usually need to check for Google Play services in more than one place in your code,
     97     define a method that encapsulates the check, then call the method before each connection
     98     attempt. The following snippet contains all of the code required to check for Google
     99     Play services:
    100 </p>
    101 <pre>
    102 public class MainActivity extends FragmentActivity {
    103     ...
    104     // Global constants
    105     /*
    106      * Define a request code to send to Google Play services
    107      * This code is returned in Activity.onActivityResult
    108      */
    109     private final static int
    110             CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    111     ...
    112     // Define a DialogFragment that displays the error dialog
    113     public static class ErrorDialogFragment extends DialogFragment {
    114         // Global field to contain the error dialog
    115         private Dialog mDialog;
    116         // Default constructor. Sets the dialog field to null
    117         public ErrorDialogFragment() {
    118             super();
    119             mDialog = null;
    120         }
    121         // Set the dialog to display
    122         public void setDialog(Dialog dialog) {
    123             mDialog = dialog;
    124         }
    125         // Return a Dialog to the DialogFragment.
    126         &#64;Override
    127         public Dialog onCreateDialog(Bundle savedInstanceState) {
    128             return mDialog;
    129         }
    130     }
    131     ...
    132     /*
    133      * Handle results returned to the FragmentActivity
    134      * by Google Play services
    135      */
    136     &#64;Override
    137     protected void onActivityResult(
    138             int requestCode, int resultCode, Intent data) {
    139         // Decide what to do based on the original request code
    140         switch (requestCode) {
    141             ...
    142             case CONNECTION_FAILURE_RESOLUTION_REQUEST :
    143             /*
    144              * If the result code is Activity.RESULT_OK, try
    145              * to connect again
    146              */
    147                 switch (resultCode) {
    148                     case Activity.RESULT_OK :
    149                     /*
    150                      * Try the request again
    151                      */
    152                     ...
    153                     break;
    154                 }
    155             ...
    156         }
    157         ...
    158     }
    159     ...
    160     private boolean servicesConnected() {
    161         // Check that Google Play services is available
    162         int resultCode =
    163                 GooglePlayServicesUtil.
    164                         isGooglePlayServicesAvailable(this);
    165         // If Google Play services is available
    166         if (ConnectionResult.SUCCESS == resultCode) {
    167             // In debug mode, log the status
    168             Log.d("Activity Recognition",
    169                     "Google Play services is available.");
    170             // Continue
    171             return true;
    172         // Google Play services was not available for some reason
    173         } else {
    174             // Get the error dialog from Google Play services
    175             Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    176                     resultCode,
    177                     this,
    178                     CONNECTION_FAILURE_RESOLUTION_REQUEST);
    179 
    180             // If Google Play services can provide an error dialog
    181             if (errorDialog != null) {
    182                 // Create a new DialogFragment for the error dialog
    183                 ErrorDialogFragment errorFragment =
    184                         new ErrorDialogFragment();
    185                 // Set the dialog in the DialogFragment
    186                 errorFragment.setDialog(errorDialog);
    187                 // Show the error dialog in the DialogFragment
    188                 errorFragment.show(
    189                         getSupportFragmentManager(),
    190                         "Activity Recognition");
    191             }
    192             return false;
    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 <h3>Send the activity update request</h3>
    203 <p>
    204     Send the update request from an {@link android.app.Activity} or
    205     {@link android.support.v4.app.Fragment} that implements the callback methods required by
    206     Location Services. Making the request is an asynchronous process that starts when you request
    207     a connection to an activity recognition client. When the client is connected, Location Services
    208     invokes your implementation of
    209 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>.
    210     In this method, you can send the update request to Location Services; this request is
    211     synchronous. Once you've made the request, you can disconnect the client.
    212 </p>
    213 <p>
    214     This process is described in the following snippets.
    215 </p>
    216 <h4 id="DefineActivity">Define the Activity or Fragment</h4>
    217 <p>
    218     Define an {@link android.support.v4.app.FragmentActivity} or
    219     {@link android.support.v4.app.Fragment} that implements the following interfaces:
    220 </p>
    221 <dl>
    222     <dt>
    223 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
    224     </dt>
    225     <dd>
    226         Specifies methods that Location Services calls when the client is connected or
    227         disconnected.
    228     </dd>
    229     <dt>
    230 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
    231     </dt>
    232     <dd>
    233         Specifies a method that Location Services calls if an error occurs while attempting to
    234         connect the client.
    235     </dd>
    236 </dl>
    237 <p>
    238     For example:
    239 </p>
    240 <pre>
    241 public class MainActivity extends FragmentActivity implements
    242         ConnectionCallbacks, OnConnectionFailedListener {
    243     ...
    244 }
    245 </pre>
    246 <p>
    247     Next, define global variables and constants.  Define constants for the update interval,
    248     add a variable for the activity recognition client, and another for the
    249     {@link android.app.PendingIntent} that Location Services uses to send updates to your app:
    250 </p>
    251 <pre>
    252 public class MainActivity extends FragmentActivity implements
    253         ConnectionCallbacks, OnConnectionFailedListener {
    254     ...
    255     // Constants that define the activity detection interval
    256     public static final int MILLISECONDS_PER_SECOND = 1000;
    257     public static final int DETECTION_INTERVAL_SECONDS = 20;
    258     public static final int DETECTION_INTERVAL_MILLISECONDS =
    259             MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
    260     ...
    261     /*
    262      * Store the PendingIntent used to send activity recognition events
    263      * back to the app
    264      */
    265     private PendingIntent mActivityRecognitionPendingIntent;
    266     // Store the current activity recognition client
    267     private ActivityRecognitionClient mActivityRecognitionClient;
    268     ...
    269 }
    270 </pre>
    271 <p>
    272     In {@link android.app.Activity#onCreate onCreate()}, instantiate the activity recognition
    273     client and the {@link android.app.PendingIntent}:
    274 </p>
    275 <pre>
    276 public class MainActivity extends FragmentActivity implements
    277         ConnectionCallbacks, OnConnectionFailedListener {
    278     ...
    279     &#64;Override
    280     onCreate(Bundle savedInstanceState) {
    281         ...
    282         /*
    283          * Instantiate a new activity recognition client. Since the
    284          * parent Activity implements the connection listener and
    285          * connection failure listener, the constructor uses "this"
    286          * to specify the values of those parameters.
    287          */
    288         mActivityRecognitionClient =
    289                 new ActivityRecognitionClient(mContext, this, this);
    290         /*
    291          * Create the PendingIntent that Location Services uses
    292          * to send activity recognition updates back to this app.
    293          */
    294         Intent intent = new Intent(
    295                 mContext, ActivityRecognitionIntentService.class);
    296         /*
    297          * Return a PendingIntent that starts the IntentService.
    298          */
    299         mActivityRecognitionPendingIntent =
    300                 PendingIntent.getService(mContext, 0, intent,
    301                 PendingIntent.FLAG_UPDATE_CURRENT);
    302         ...
    303     }
    304     ...
    305 }
    306 </pre>
    307 <h4>Start the request process</h4>
    308 <p>
    309     Define a method that requests activity recognition updates. In the method, request a
    310     connection to Location Services. You can call this method from anywhere in your activity; its
    311     purpose is to start the chain of method calls for requesting updates.
    312 </p>
    313 <p>
    314     To guard against race conditions that might arise if your app tries to start another request
    315     before the first one finishes, define a boolean flag that tracks the state of the current
    316     request. Set the flag to {@code true} when you start a request, and then set it to
    317     {@code false} when the request completes.
    318 </p>
    319 <p>
    320     The following snippet shows how to start a request for updates:
    321 </p>
    322 <pre>
    323 public class MainActivity extends FragmentActivity implements
    324         ConnectionCallbacks, OnConnectionFailedListener {
    325     ...
    326     // Global constants
    327     ...
    328     // Flag that indicates if a request is underway.
    329     private boolean mInProgress;
    330     ...
    331     &#64;Override
    332     onCreate(Bundle savedInstanceState) {
    333         ...
    334         // Start with the request flag set to false
    335         mInProgress = false;
    336         ...
    337     }
    338     ...
    339     /**
    340      * Request activity recognition updates based on the current
    341      * detection interval.
    342      *
    343      */
    344      public void startUpdates() {
    345         // Check for Google Play services
    346 
    347         if (!servicesConnected()) {
    348             return;
    349         }
    350         // If a request is not already underway
    351         if (!mInProgress) {
    352             // Indicate that a request is in progress
    353             mInProgress = true;
    354             // Request a connection to Location Services
    355             mActivityRecognitionClient.connect();
    356         //
    357         } else {
    358             /*
    359              * A request is already underway. You can handle
    360              * this situation by disconnecting the client,
    361              * re-setting the flag, and then re-trying the
    362              * request.
    363              */
    364         }
    365     }
    366     ...
    367 }
    368 </pre>
    369 <p>
    370     Implement
    371 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>.
    372     In this method, request activity recognition updates from Location Services. When Location
    373     Services finishes connecting to the client and calls
    374 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>,
    375     the update request is called immediately:
    376 </p>
    377 <pre>
    378 public class MainActivity extends FragmentActivity implements
    379         ConnectionCallbacks, OnConnectionFailedListener {
    380     ...
    381     /*
    382      * Called by Location Services once the location client is connected.
    383      *
    384      * Continue by requesting activity updates.
    385      */
    386     &#64;Override
    387     public void onConnected(Bundle dataBundle) {
    388         /*
    389          * Request activity recognition updates using the preset
    390          * detection interval and PendingIntent. This call is
    391          * synchronous.
    392          */
    393         mActivityRecognitionClient.requestActivityUpdates(
    394                 DETECTION_INTERVAL_MILLISECONDS,
    395                 mActivityRecognitionPendingIntent);
    396         /*
    397          * Since the preceding call is synchronous, turn off the
    398          * in progress flag and disconnect the client
    399          */
    400         mInProgress = false;
    401         mActivityRecognitionClient.disconnect();
    402     }
    403     ...
    404 }
    405 </pre>
    406 <h3>Handle disconnections</h3>
    407 <p>
    408     In some cases, Location Services may disconnect from the activity recognition client before
    409     you call
    410 <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#disconnect()">disconnect()</a></code>.
    411     To handle this situation, implement <code>
    412 <a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>.
    413     In this method, set the request flag to indicate that a request is not in progress, and
    414     delete the client:
    415 </p>
    416 <pre>
    417 public class MainActivity extends FragmentActivity implements
    418         ConnectionCallbacks, OnConnectionFailedListener {
    419     ...
    420     /*
    421      * Called by Location Services once the activity recognition
    422      * client is disconnected.
    423      */
    424     &#64;Override
    425     public void onDisconnected() {
    426         // Turn off the request flag
    427         mInProgress = false;
    428         // Delete the client
    429         mActivityRecognitionClient = null;
    430     }
    431     ...
    432 }
    433 </pre>
    434 <!-- Handle connection errors -->
    435 <h3>Handle connection errors</h3>
    436 <p>
    437     Besides handling the normal callbacks from Location Services, you have to provide a callback
    438     method that Location Services calls if a connection error occurs. This callback method
    439     can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to
    440     handle the check for Google Play services. It can also re-use the override you defined
    441     for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that
    442     receives any Google Play services results that occur when the user interacts with the
    443     error dialog. The following snippet shows you a sample implementation of the callback method:
    444 </p>
    445 <pre>
    446 public class MainActivity extends FragmentActivity implements
    447         ConnectionCallbacks, OnConnectionFailedListener {
    448     ...
    449     // Implementation of OnConnectionFailedListener.onConnectionFailed
    450     &#64;Override
    451     public void onConnectionFailed(ConnectionResult connectionResult) {
    452         // Turn off the request flag
    453         mInProgress = false;
    454         /*
    455          * If the error has a resolution, start a Google Play services
    456          * activity to resolve it.
    457          */
    458         if (connectionResult.hasResolution()) {
    459             try {
    460                 connectionResult.startResolutionForResult(
    461                         this,
    462                         CONNECTION_FAILURE_RESOLUTION_REQUEST);
    463             } catch (SendIntentException e) {
    464                 // Log the error
    465                 e.printStackTrace();
    466             }
    467         // If no resolution is available, display an error dialog
    468         } else {
    469             // Get the error code
    470             int errorCode = connectionResult.getErrorCode();
    471             // Get the error dialog from Google Play services
    472             Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    473                     errorCode,
    474                     this,
    475                     CONNECTION_FAILURE_RESOLUTION_REQUEST);
    476             // If Google Play services can provide an error dialog
    477             if (errorDialog != null) {
    478                 // Create a new DialogFragment for the error dialog
    479                 ErrorDialogFragment errorFragment =
    480                         new ErrorDialogFragment();
    481                 // Set the dialog in the DialogFragment
    482                 errorFragment.setDialog(errorDialog);
    483                 // Show the error dialog in the DialogFragment
    484                 errorFragment.show(
    485                         getSupportFragmentManager(),
    486                         "Activity Recognition");
    487             }
    488         }
    489         ...
    490     }
    491     ...
    492 }
    493 </pre>
    494 <!-- Create Intent Service -->
    495 <h2 id="HandleUpdates">Handle Activity Updates</h2>
    496 <p>
    497     To handle the {@link android.content.Intent} that Location Services sends for each update
    498     interval, define an {@link android.app.IntentService} and its required method
    499     {@link android.app.IntentService#onHandleIntent onHandleIntent()}. Location Services
    500     sends out activity recognition updates as {@link android.content.Intent} objects, using the
    501     the {@link android.app.PendingIntent} you provided when you called
    502 <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#requestActivityUpdates(long, android.app.PendingIntent)">requestActivityUpdates()</a></code>.
    503     Since you provided an explicit intent for the {@link android.app.PendingIntent}, the only
    504     component that receives the intent is the {@link android.app.IntentService} you're defining.
    505 </p>
    506 <p>
    507     The following snippets demonstrate how to examine the data in an activity recognition
    508     update.
    509 </p>
    510 <h3>Define an IntentService</h3>
    511 <p>
    512     Start by defining the class and the required method
    513     {@link android.app.IntentService#onHandleIntent onHandleIntent()}:
    514 </p>
    515 <pre>
    516 /**
    517  * Service that receives ActivityRecognition updates. It receives
    518  * updates in the background, even if the main Activity is not visible.
    519  */
    520 public class ActivityRecognitionIntentService extends IntentService {
    521     ...
    522     /**
    523      * Called when a new activity detection update is available.
    524      */
    525     &#64;Override
    526     protected void onHandleIntent(Intent intent) {
    527         ...
    528     }
    529     ...
    530 }
    531 </pre>
    532 <p>
    533     Next, examine the data in the intent. From the update, you can get a list of possible activities
    534     and the probability of each one. The following snippet shows how to get the most probable
    535     activity, the confidence level for the activity (the probability that this is the actual
    536     activity), and its type:
    537 </p>
    538 <pre>
    539 public class ActivityRecognitionIntentService extends IntentService {
    540     ...
    541     &#64;Override
    542     protected void onHandleIntent(Intent intent) {
    543         ...
    544         // If the incoming intent contains an update
    545         if (ActivityRecognitionResult.hasResult(intent)) {
    546             // Get the update
    547             ActivityRecognitionResult result =
    548                     ActivityRecognitionResult.extractResult(intent);
    549             // Get the most probable activity
    550             DetectedActivity mostProbableActivity =
    551                     result.getMostProbableActivity();
    552             /*
    553              * Get the probability that this activity is the
    554              * the user's actual activity
    555              */
    556             int confidence = mostProbableActivity.getConfidence();
    557             /*
    558              * Get an integer describing the type of activity
    559              */
    560             int activityType = mostProbableActivity.getType();
    561             String activityName = getNameFromType(activityType);
    562             /*
    563              * At this point, you have retrieved all the information
    564              * for the current update. You can display this
    565              * information to the user in a notification, or
    566              * send it to an Activity or Service in a broadcast
    567              * Intent.
    568              */
    569             ...
    570         } else {
    571             /*
    572              * This implementation ignores intents that don't contain
    573              * an activity update. If you wish, you can report them as
    574              * errors.
    575              */
    576         }
    577         ...
    578     }
    579     ...
    580 }
    581 </pre>
    582 <p>
    583     The method {@code getNameFromType()} converts activity types into descriptive
    584     strings. In a production app, you should retrieve the strings from resources instead of
    585     using fixed values:
    586 </p>
    587 <pre>
    588 public class ActivityRecognitionIntentService extends IntentService {
    589     ...
    590     /**
    591      * Map detected activity types to strings
    592      *&#64;param activityType The detected activity type
    593      *&#64;return A user-readable name for the type
    594      */
    595     private String getNameFromType(int activityType) {
    596         switch(activityType) {
    597             case DetectedActivity.IN_VEHICLE:
    598                 return "in_vehicle";
    599             case DetectedActivity.ON_BICYCLE:
    600                 return "on_bicycle";
    601             case DetectedActivity.ON_FOOT:
    602                 return "on_foot";
    603             case DetectedActivity.STILL:
    604                 return "still";
    605             case DetectedActivity.UNKNOWN:
    606                 return "unknown";
    607             case DetectedActivity.TILTING:
    608                 return "tilting";
    609         }
    610         return "unknown";
    611     }
    612     ...
    613 }
    614 </pre>
    615 <!-- Define IntentService -->
    616 <h3>Specify the IntentService in the manifest</h3>
    617 <p>
    618     To identify the {@link android.app.IntentService} to the system, add a
    619     <code><a href="{@docRoot}guide/topics/manifest/service-element.html">&lt;service&gt;</a></code>
    620     element to the app manifest. For example:
    621 </p>
    622 <pre>
    623 &lt;service
    624     android:name="com.example.android.location.ActivityRecognitionIntentService"
    625     android:label="&#64;string/app_name"
    626     android:exported="false"&gt;
    627 &lt;/service&gt;
    628 </pre>
    629 <p>
    630     Notice that you don't have to specify intent filters for the service, because it only receives
    631     explicit intents. How the incoming activity update intents are created is described in the
    632     section <a id="DefineActivity">Define the Activity or Fragment</a>.
    633 </p>
    634 <h2 id="RemoveUpdates">Stop Activity Recognition Updates</h2>
    635 <p>
    636     To stop activity recognition updates, use the same pattern you used to request updates,
    637     but call <code>
    638 <a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>
    639     instead of <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#requestActivityUpdates(long, android.app.PendingIntent)">requestActivityUpdates()</a></code>.
    640 </p>
    641 <p>
    642 <p>
    643     Since removing updates uses some of the methods you use to add updates, start by defining
    644     request types for the two operations:
    645 </p>
    646 <pre>
    647 public class MainActivity extends FragmentActivity implements
    648         ConnectionCallbacks, OnConnectionFailedListener {
    649     ...
    650     public enum REQUEST_TYPE {START, STOP}
    651     private REQUEST_TYPE mRequestType;
    652     ...
    653 }
    654 </pre>
    655 <p>
    656     Modify the code that starts activity recognition so that it uses the {@code START}
    657     request type:
    658 </p>
    659 <pre>
    660 public class MainActivity extends FragmentActivity implements
    661         ConnectionCallbacks, OnConnectionFailedListener {
    662     ...
    663     public void startUpdates() {
    664         // Set the request type to START
    665         mRequestType = REQUEST_TYPE.START;
    666         /*
    667          * Test for Google Play services after setting the request type.
    668          * If Google Play services isn't present, the proper request type
    669          * can be restarted.
    670          */
    671         if (!servicesConnected()) {
    672             return;
    673         }
    674         ...
    675     }
    676     ...
    677     public void onConnected(Bundle dataBundle) {
    678         switch (mRequestType) {
    679             case START :
    680                 /*
    681                  * Request activity recognition updates using the
    682                  * preset detection interval and PendingIntent.
    683                  * This call is synchronous.
    684                  */
    685                 mActivityRecognitionClient.requestActivityUpdates(
    686                         DETECTION_INTERVAL_MILLISECONDS,
    687                         mActivityRecognitionPendingIntent);
    688                 break;
    689                 ...
    690                 /*
    691                  * An enum was added to the definition of REQUEST_TYPE,
    692                  * but it doesn't match a known case. Throw an exception.
    693                  */
    694                 default :
    695                 throw new Exception("Unknown request type in onConnected().");
    696                 break;
    697         }
    698         ...
    699     }
    700     ...
    701 }
    702 </pre>
    703 <h3>Start the process</h3>
    704 <p>
    705     Define a method that requests a stop to activity recognition updates. In the method,
    706     set the request type and then request a connection to Location Services. You can call this
    707     method from anywhere in your activity; its purpose is to start the chain of method calls that
    708     stop activity updates:
    709 </p>
    710 <pre>
    711 public class MainActivity extends FragmentActivity implements
    712         ConnectionCallbacks, OnConnectionFailedListener {
    713     ...
    714     /**
    715      * Turn off activity recognition updates
    716      *
    717      */
    718     public void stopUpdates() {
    719         // Set the request type to STOP
    720         mRequestType = REQUEST_TYPE.STOP;
    721         /*
    722          * Test for Google Play services after setting the request type.
    723          * If Google Play services isn't present, the request can be
    724          * restarted.
    725          */
    726         if (!servicesConnected()) {
    727             return;
    728         }
    729         // If a request is not already underway
    730         if (!mInProgress) {
    731             // Indicate that a request is in progress
    732             mInProgress = true;
    733             // Request a connection to Location Services
    734             mActivityRecognitionClient.connect();
    735         //
    736         } else {
    737             /*
    738              * A request is already underway. You can handle
    739              * this situation by disconnecting the client,
    740              * re-setting the flag, and then re-trying the
    741              * request.
    742              */
    743         }
    744         ...
    745     }
    746     ...
    747 }
    748 </pre>
    749 <p>
    750     In
    751 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>,
    752     if the request type is STOP, call
    753 <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>.
    754     Pass the {@link android.app.PendingIntent} you used to start updates as the parameter to
    755 <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>:
    756 </p>
    757 <pre>
    758 public class MainActivity extends FragmentActivity implements
    759         ConnectionCallbacks, OnConnectionFailedListener {
    760     ...
    761     public void onConnected(Bundle dataBundle) {
    762         switch (mRequestType) {
    763             ...
    764             case STOP :
    765             mActivityRecognitionClient.removeActivityUpdates(
    766                     mActivityRecognitionPendingIntent);
    767             break;
    768             ...
    769         }
    770         ...
    771     }
    772     ...
    773 }
    774 </pre>
    775 <p>
    776     You do not have to modify your implementation of
    777 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>
    778     or
    779 <code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed()</a></code>,
    780     because these methods do not depend on the request type.
    781 </p>
    782 <p>
    783     You now have the basic structure of an app that implements activity recognition. You can combine
    784     activity recognition with other location-aware features, such as periodic location updates or
    785     geofencing, which are described in other lessons in this class.
    786 </p>
    787