• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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">&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 {
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    &#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    ...
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