1page.title=Recognizing the User's Current Activity 2 3trainingnavtop=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 Activity recognition tries to detect the user's current physical activity, such as walking, 35 driving, or standing still. Requests for updates go through an activity recognition client, 36 which, while different from the location client used by location or geofencing, follows a 37 similar pattern. Based on the update interval you choose, Location Services sends out 38 activity information containing one or more possible activities and the confidence level for 39 each one. This lesson shows you how to request activity recognition updates from Location 40 Services. 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"><manifest></a></code> 57 element: 58</p> 59<pre> 60<uses-permission 61 android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/> 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> 102public 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 @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 @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> 241public 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> 252public 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> 276public class MainActivity extends FragmentActivity implements 277 ConnectionCallbacks, OnConnectionFailedListener { 278 ... 279 @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> 323public 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 @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> 378public 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 @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> 417public 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 @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> 446public class MainActivity extends FragmentActivity implements 447 ConnectionCallbacks, OnConnectionFailedListener { 448 ... 449 // Implementation of OnConnectionFailedListener.onConnectionFailed 450 @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 */ 520public class ActivityRecognitionIntentService extends IntentService { 521 ... 522 /** 523 * Called when a new activity detection update is available. 524 */ 525 @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> 539public class ActivityRecognitionIntentService extends IntentService { 540 ... 541 @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> 588public class ActivityRecognitionIntentService extends IntentService { 589 ... 590 /** 591 * Map detected activity types to strings 592 *@param activityType The detected activity type 593 *@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"><service></a></code> 620 element to the app manifest. For example: 621</p> 622<pre> 623<service 624 android:name="com.example.android.location.ActivityRecognitionIntentService" 625 android:label="@string/app_name" 626 android:exported="false"> 627</service> 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> 647public 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> 660public 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> 711public 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> 758public 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