blob: d50064b00d6eb2af21b513c3bce549ed02559bfa [file] [log] [blame]
Joe Malin3ddba7e2013-03-04 10:33:30 -08001page.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>
Joe Malin3ddba7e2013-03-04 10:33:30 -080034 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
Joe Malin72fdbc82013-07-26 17:26:52 -070039 each one. This lesson shows you how to request activity recognition updates from Location
40 Services.
Joe Malin3ddba7e2013-03-04 10:33:30 -080041</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>
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 &#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 {
Joe Malin3ddba7e2013-03-04 10:33:30 -0800174 // Get the error dialog from Google Play services
175 Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
Joe Malinef445f12013-06-20 16:22:55 -0700176 resultCode,
Joe Malin3ddba7e2013-03-04 10:33:30 -0800177 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 &#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>
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 &#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>
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 &#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>
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 &#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>
446public 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 */
520public 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>
539public 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>
588public 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>
647public class MainActivity extends FragmentActivity implements
648 ConnectionCallbacks, OnConnectionFailedListener {
649 ...
Joe Malinef445f12013-06-20 16:22:55 -0700650 public enum REQUEST_TYPE {START, STOP}
Joe Malin3ddba7e2013-03-04 10:33:30 -0800651 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
Joe Malinef445f12013-06-20 16:22:55 -0700665 mRequestType = REQUEST_TYPE.START;
Joe Malin3ddba7e2013-03-04 10:33:30 -0800666 /*
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,
Joe Malinef445f12013-06-20 16:22:55 -0700687 mActivityRecognitionPendingIntent);
Joe Malin3ddba7e2013-03-04 10:33:30 -0800688 break;
Joe Malinef445f12013-06-20 16:22:55 -0700689 ...
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;
Joe Malin3ddba7e2013-03-04 10:33:30 -0800697 }
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
Joe Malinef445f12013-06-20 16:22:55 -0700720 mRequestType = REQUEST_TYPE.STOP;
Joe Malin3ddba7e2013-03-04 10:33:30 -0800721 /*
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;
Joe Malinef445f12013-06-20 16:22:55 -0700768 ...
Joe Malin3ddba7e2013-03-04 10:33:30 -0800769 }
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>