+++ /dev/null
-page.title=Recognizing the User's Current Activity
-
-trainingnavtop=true
-@jd:body
-
-<div id="tb-wrapper">
-<div id="tb">
-<h2>This lesson teaches you to</h2>
-<ol>
- <li><a href="#RequestUpdates">Request Activity Recognition Updates</a></li>
- <li><a href="#HandleUpdates">Handle Activity Updates</a>
- <li><a href="#RemoveUpdates">Stop Activity Recognition Updates</a>
-</ol>
-<h2>You should also read</h2>
-<ul>
- <li>
- <a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
- </li>
- <li>
- <a href="receive-location-updates.html">Receiving Location Updates</a>
- </li>
-</ul>
-<h2>Try it out</h2>
-
-<div class="download-box">
- <a href="http://developer.android.com/shareables/training/ActivityRecognition.zip" class="button">Download the sample</a>
- <p class="filename">ActivityRecognition.zip</p>
-</div>
-
-</div>
-</div>
-
-<p>
- Activity recognition tries to detect the user's current physical activity, such as walking,
- driving, or standing still. Requests for updates go through an activity recognition client,
- which, while different from the location client used by location or geofencing, follows a
- similar pattern. Based on the update interval you choose, Location Services sends out
- activity information containing one or more possible activities and the confidence level for
- each one. This lesson shows you how to request activity recognition updates from Location
- Services.
-</p>
-<h2 id="RequestUpdates">Request Activity Recognition Updates</h2>
-<p>
- Requesting activity recognition updates from Location Services is similar to requesting
- periodic location updates. You send the request through a client, and Location Services sends
- updates back to your app by means of a {@link android.app.PendingIntent}. However, you need to
- request a special permission before you request activity updates, and you use a different type
- of client to make requests. The following sections show how to request the permission,
- connect the client, and request updates.
-</p>
-<h3>Request permission to receive updates</h3>
-<p>
- An app that wants to get activity recognition updates must have the permission
- {@code com.google.android.gms.permission.ACTIVITY_RECOGNITION}. To request this permission for
- your app, add the following XML element to your manifest as a child element of the
-<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>
- element:
-</p>
-<pre>
-<uses-permission
- android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
-</pre>
-<p>
- Activity recognition does not require the permissions
- {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
- {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}.
-</p>
-<!-- Check for Google Play services -->
-<h3>Check for Google Play Services</h3>
-<p>
- Location Services is part of the Google Play services APK. Since it's hard to anticipate the
- state of the user's device, you should always check that the APK is installed before you attempt
- to connect to Location Services. To check that the APK is installed, call
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
- which returns one of the
- integer result codes listed in the API reference documentation. If you encounter an error,
- call
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
- to retrieve localized dialog that prompts users to take the correct action, then display
- the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
- user to correct the problem, in which case Google Play services may send a result back to your
- activity. To handle this result, override the method
- {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
-
-</p>
-<p class="note">
- <strong>Note:</strong> To make your app compatible with
- platform version 1.6 and later, the activity that displays the
- {@link android.support.v4.app.DialogFragment} must subclass
- {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
- {@link android.support.v4.app.FragmentActivity} also allows you to call
- {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
- getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
-</p>
-<p>
- Since you usually need to check for Google Play services in more than one place in your code,
- define a method that encapsulates the check, then call the method before each connection
- attempt. The following snippet contains all of the code required to check for Google
- Play services:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity {
- ...
- // Global constants
- /*
- * Define a request code to send to Google Play services
- * This code is returned in Activity.onActivityResult
- */
- private final static int
- CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
- ...
- // Define a DialogFragment that displays the error dialog
- public static class ErrorDialogFragment extends DialogFragment {
- // Global field to contain the error dialog
- private Dialog mDialog;
- // Default constructor. Sets the dialog field to null
- public ErrorDialogFragment() {
- super();
- mDialog = null;
- }
- // Set the dialog to display
- public void setDialog(Dialog dialog) {
- mDialog = dialog;
- }
- // Return a Dialog to the DialogFragment.
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return mDialog;
- }
- }
- ...
- /*
- * Handle results returned to the FragmentActivity
- * by Google Play services
- */
- @Override
- protected void onActivityResult(
- int requestCode, int resultCode, Intent data) {
- // Decide what to do based on the original request code
- switch (requestCode) {
- ...
- case CONNECTION_FAILURE_RESOLUTION_REQUEST :
- /*
- * If the result code is Activity.RESULT_OK, try
- * to connect again
- */
- switch (resultCode) {
- case Activity.RESULT_OK :
- /*
- * Try the request again
- */
- ...
- break;
- }
- ...
- }
- ...
- }
- ...
- private boolean servicesConnected() {
- // Check that Google Play services is available
- int resultCode =
- GooglePlayServicesUtil.
- isGooglePlayServicesAvailable(this);
- // If Google Play services is available
- if (ConnectionResult.SUCCESS == resultCode) {
- // In debug mode, log the status
- Log.d("Activity Recognition",
- "Google Play services is available.");
- // Continue
- return true;
- // Google Play services was not available for some reason
- } else {
- // Get the error dialog from Google Play services
- Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
- resultCode,
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
-
- // If Google Play services can provide an error dialog
- if (errorDialog != null) {
- // Create a new DialogFragment for the error dialog
- ErrorDialogFragment errorFragment =
- new ErrorDialogFragment();
- // Set the dialog in the DialogFragment
- errorFragment.setDialog(errorDialog);
- // Show the error dialog in the DialogFragment
- errorFragment.show(
- getSupportFragmentManager(),
- "Activity Recognition");
- }
- return false;
- }
- }
- ...
-}
-</pre>
-<p>
- Snippets in the following sections call this method to verify that Google Play services is
- available.
-</p>
-<h3>Send the activity update request</h3>
-<p>
- Send the update request from an {@link android.app.Activity} or
- {@link android.support.v4.app.Fragment} that implements the callback methods required by
- Location Services. Making the request is an asynchronous process that starts when you request
- a connection to an activity recognition client. When the client is connected, Location Services
- invokes your implementation of
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>.
- In this method, you can send the update request to Location Services; this request is
- synchronous. Once you've made the request, you can disconnect the client.
-</p>
-<p>
- This process is described in the following snippets.
-</p>
-<h4 id="DefineActivity">Define the Activity or Fragment</h4>
-<p>
- Define an {@link android.support.v4.app.FragmentActivity} or
- {@link android.support.v4.app.Fragment} that implements the following interfaces:
-</p>
-<dl>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
- </dt>
- <dd>
- Specifies methods that Location Services calls when the client is connected or
- disconnected.
- </dd>
- <dt>
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
- </dt>
- <dd>
- Specifies a method that Location Services calls if an error occurs while attempting to
- connect the client.
- </dd>
-</dl>
-<p>
- For example:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
-}
-</pre>
-<p>
- Next, define global variables and constants. Define constants for the update interval,
- add a variable for the activity recognition client, and another for the
- {@link android.app.PendingIntent} that Location Services uses to send updates to your app:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- // Constants that define the activity detection interval
- public static final int MILLISECONDS_PER_SECOND = 1000;
- public static final int DETECTION_INTERVAL_SECONDS = 20;
- public static final int DETECTION_INTERVAL_MILLISECONDS =
- MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
- ...
- /*
- * Store the PendingIntent used to send activity recognition events
- * back to the app
- */
- private PendingIntent mActivityRecognitionPendingIntent;
- // Store the current activity recognition client
- private ActivityRecognitionClient mActivityRecognitionClient;
- ...
-}
-</pre>
-<p>
- In {@link android.app.Activity#onCreate onCreate()}, instantiate the activity recognition
- client and the {@link android.app.PendingIntent}:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- @Override
- onCreate(Bundle savedInstanceState) {
- ...
- /*
- * Instantiate a new activity recognition client. Since the
- * parent Activity implements the connection listener and
- * connection failure listener, the constructor uses "this"
- * to specify the values of those parameters.
- */
- mActivityRecognitionClient =
- new ActivityRecognitionClient(mContext, this, this);
- /*
- * Create the PendingIntent that Location Services uses
- * to send activity recognition updates back to this app.
- */
- Intent intent = new Intent(
- mContext, ActivityRecognitionIntentService.class);
- /*
- * Return a PendingIntent that starts the IntentService.
- */
- mActivityRecognitionPendingIntent =
- PendingIntent.getService(mContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- ...
- }
- ...
-}
-</pre>
-<h4>Start the request process</h4>
-<p>
- Define a method that requests activity recognition updates. In the method, request a
- connection to Location Services. You can call this method from anywhere in your activity; its
- purpose is to start the chain of method calls for requesting updates.
-</p>
-<p>
- To guard against race conditions that might arise if your app tries to start another request
- before the first one finishes, define a boolean flag that tracks the state of the current
- request. Set the flag to {@code true} when you start a request, and then set it to
- {@code false} when the request completes.
-</p>
-<p>
- The following snippet shows how to start a request for updates:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- // Global constants
- ...
- // Flag that indicates if a request is underway.
- private boolean mInProgress;
- ...
- @Override
- onCreate(Bundle savedInstanceState) {
- ...
- // Start with the request flag set to false
- mInProgress = false;
- ...
- }
- ...
- /**
- * Request activity recognition updates based on the current
- * detection interval.
- *
- */
- public void startUpdates() {
- // Check for Google Play services
-
- if (!servicesConnected()) {
- return;
- }
- // If a request is not already underway
- if (!mInProgress) {
- // Indicate that a request is in progress
- mInProgress = true;
- // Request a connection to Location Services
- mActivityRecognitionClient.connect();
- //
- } else {
- /*
- * A request is already underway. You can handle
- * this situation by disconnecting the client,
- * re-setting the flag, and then re-trying the
- * request.
- */
- }
- }
- ...
-}
-</pre>
-<p>
- Implement
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>.
- In this method, request activity recognition updates from Location Services. When Location
- Services finishes connecting to the client and calls
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>,
- the update request is called immediately:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- /*
- * Called by Location Services once the location client is connected.
- *
- * Continue by requesting activity updates.
- */
- @Override
- public void onConnected(Bundle dataBundle) {
- /*
- * Request activity recognition updates using the preset
- * detection interval and PendingIntent. This call is
- * synchronous.
- */
- mActivityRecognitionClient.requestActivityUpdates(
- DETECTION_INTERVAL_MILLISECONDS,
- mActivityRecognitionPendingIntent);
- /*
- * Since the preceding call is synchronous, turn off the
- * in progress flag and disconnect the client
- */
- mInProgress = false;
- mActivityRecognitionClient.disconnect();
- }
- ...
-}
-</pre>
-<h3>Handle disconnections</h3>
-<p>
- In some cases, Location Services may disconnect from the activity recognition client before
- you call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#disconnect()">disconnect()</a></code>.
- To handle this situation, implement <code>
-<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>.
- In this method, set the request flag to indicate that a request is not in progress, and
- delete the client:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- /*
- * Called by Location Services once the activity recognition
- * client is disconnected.
- */
- @Override
- public void onDisconnected() {
- // Turn off the request flag
- mInProgress = false;
- // Delete the client
- mActivityRecognitionClient = null;
- }
- ...
-}
-</pre>
-<!-- Handle connection errors -->
-<h3>Handle connection errors</h3>
-<p>
- Besides handling the normal callbacks from Location Services, you have to provide a callback
- method that Location Services calls if a connection error occurs. This callback method
- can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to
- handle the check for Google Play services. It can also re-use the override you defined
- for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that
- receives any Google Play services results that occur when the user interacts with the
- error dialog. The following snippet shows you a sample implementation of the callback method:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- // Implementation of OnConnectionFailedListener.onConnectionFailed
- @Override
- public void onConnectionFailed(ConnectionResult connectionResult) {
- // Turn off the request flag
- mInProgress = false;
- /*
- * If the error has a resolution, start a Google Play services
- * activity to resolve it.
- */
- if (connectionResult.hasResolution()) {
- try {
- connectionResult.startResolutionForResult(
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
- } catch (SendIntentException e) {
- // Log the error
- e.printStackTrace();
- }
- // If no resolution is available, display an error dialog
- } else {
- // Get the error code
- int errorCode = connectionResult.getErrorCode();
- // Get the error dialog from Google Play services
- Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
- errorCode,
- this,
- CONNECTION_FAILURE_RESOLUTION_REQUEST);
- // If Google Play services can provide an error dialog
- if (errorDialog != null) {
- // Create a new DialogFragment for the error dialog
- ErrorDialogFragment errorFragment =
- new ErrorDialogFragment();
- // Set the dialog in the DialogFragment
- errorFragment.setDialog(errorDialog);
- // Show the error dialog in the DialogFragment
- errorFragment.show(
- getSupportFragmentManager(),
- "Activity Recognition");
- }
- }
- ...
- }
- ...
-}
-</pre>
-<!-- Create Intent Service -->
-<h2 id="HandleUpdates">Handle Activity Updates</h2>
-<p>
- To handle the {@link android.content.Intent} that Location Services sends for each update
- interval, define an {@link android.app.IntentService} and its required method
- {@link android.app.IntentService#onHandleIntent onHandleIntent()}. Location Services
- sends out activity recognition updates as {@link android.content.Intent} objects, using the
- the {@link android.app.PendingIntent} you provided when you called
-<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#requestActivityUpdates(long, android.app.PendingIntent)">requestActivityUpdates()</a></code>.
- Since you provided an explicit intent for the {@link android.app.PendingIntent}, the only
- component that receives the intent is the {@link android.app.IntentService} you're defining.
-</p>
-<p>
- The following snippets demonstrate how to examine the data in an activity recognition
- update.
-</p>
-<h3>Define an IntentService</h3>
-<p>
- Start by defining the class and the required method
- {@link android.app.IntentService#onHandleIntent onHandleIntent()}:
-</p>
-<pre>
-/**
- * Service that receives ActivityRecognition updates. It receives
- * updates in the background, even if the main Activity is not visible.
- */
-public class ActivityRecognitionIntentService extends IntentService {
- ...
- /**
- * Called when a new activity detection update is available.
- */
- @Override
- protected void onHandleIntent(Intent intent) {
- ...
- }
- ...
-}
-</pre>
-<p>
- Next, examine the data in the intent. From the update, you can get a list of possible activities
- and the probability of each one. The following snippet shows how to get the most probable
- activity, the confidence level for the activity (the probability that this is the actual
- activity), and its type:
-</p>
-<pre>
-public class ActivityRecognitionIntentService extends IntentService {
- ...
- @Override
- protected void onHandleIntent(Intent intent) {
- ...
- // If the incoming intent contains an update
- if (ActivityRecognitionResult.hasResult(intent)) {
- // Get the update
- ActivityRecognitionResult result =
- ActivityRecognitionResult.extractResult(intent);
- // Get the most probable activity
- DetectedActivity mostProbableActivity =
- result.getMostProbableActivity();
- /*
- * Get the probability that this activity is the
- * the user's actual activity
- */
- int confidence = mostProbableActivity.getConfidence();
- /*
- * Get an integer describing the type of activity
- */
- int activityType = mostProbableActivity.getType();
- String activityName = getNameFromType(activityType);
- /*
- * At this point, you have retrieved all the information
- * for the current update. You can display this
- * information to the user in a notification, or
- * send it to an Activity or Service in a broadcast
- * Intent.
- */
- ...
- } else {
- /*
- * This implementation ignores intents that don't contain
- * an activity update. If you wish, you can report them as
- * errors.
- */
- }
- ...
- }
- ...
-}
-</pre>
-<p>
- The method {@code getNameFromType()} converts activity types into descriptive
- strings. In a production app, you should retrieve the strings from resources instead of
- using fixed values:
-</p>
-<pre>
-public class ActivityRecognitionIntentService extends IntentService {
- ...
- /**
- * Map detected activity types to strings
- *@param activityType The detected activity type
- *@return A user-readable name for the type
- */
- private String getNameFromType(int activityType) {
- switch(activityType) {
- case DetectedActivity.IN_VEHICLE:
- return "in_vehicle";
- case DetectedActivity.ON_BICYCLE:
- return "on_bicycle";
- case DetectedActivity.ON_FOOT:
- return "on_foot";
- case DetectedActivity.STILL:
- return "still";
- case DetectedActivity.UNKNOWN:
- return "unknown";
- case DetectedActivity.TILTING:
- return "tilting";
- }
- return "unknown";
- }
- ...
-}
-</pre>
-<!-- Define IntentService -->
-<h3>Specify the IntentService in the manifest</h3>
-<p>
- To identify the {@link android.app.IntentService} to the system, add a
- <code><a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code>
- element to the app manifest. For example:
-</p>
-<pre>
-<service
- android:name="com.example.android.location.ActivityRecognitionIntentService"
- android:label="@string/app_name"
- android:exported="false">
-</service>
-</pre>
-<p>
- Notice that you don't have to specify intent filters for the service, because it only receives
- explicit intents. How the incoming activity update intents are created is described in the
- section <a id="DefineActivity">Define the Activity or Fragment</a>.
-</p>
-<h2 id="RemoveUpdates">Stop Activity Recognition Updates</h2>
-<p>
- To stop activity recognition updates, use the same pattern you used to request updates,
- but call <code>
-<a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>
- instead of <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#requestActivityUpdates(long, android.app.PendingIntent)">requestActivityUpdates()</a></code>.
-</p>
-<p>
-<p>
- Since removing updates uses some of the methods you use to add updates, start by defining
- request types for the two operations:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- public enum REQUEST_TYPE {START, STOP}
- private REQUEST_TYPE mRequestType;
- ...
-}
-</pre>
-<p>
- Modify the code that starts activity recognition so that it uses the {@code START}
- request type:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- public void startUpdates() {
- // Set the request type to START
- mRequestType = REQUEST_TYPE.START;
- /*
- * Test for Google Play services after setting the request type.
- * If Google Play services isn't present, the proper request type
- * can be restarted.
- */
- if (!servicesConnected()) {
- return;
- }
- ...
- }
- ...
- public void onConnected(Bundle dataBundle) {
- switch (mRequestType) {
- case START :
- /*
- * Request activity recognition updates using the
- * preset detection interval and PendingIntent.
- * This call is synchronous.
- */
- mActivityRecognitionClient.requestActivityUpdates(
- DETECTION_INTERVAL_MILLISECONDS,
- mActivityRecognitionPendingIntent);
- break;
- ...
- /*
- * An enum was added to the definition of REQUEST_TYPE,
- * but it doesn't match a known case. Throw an exception.
- */
- default :
- throw new Exception("Unknown request type in onConnected().");
- break;
- }
- ...
- }
- ...
-}
-</pre>
-<h3>Start the process</h3>
-<p>
- Define a method that requests a stop to activity recognition updates. In the method,
- set the request type and then request a connection to Location Services. You can call this
- method from anywhere in your activity; its purpose is to start the chain of method calls that
- stop activity updates:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- /**
- * Turn off activity recognition updates
- *
- */
- public void stopUpdates() {
- // Set the request type to STOP
- mRequestType = REQUEST_TYPE.STOP;
- /*
- * Test for Google Play services after setting the request type.
- * If Google Play services isn't present, the request can be
- * restarted.
- */
- if (!servicesConnected()) {
- return;
- }
- // If a request is not already underway
- if (!mInProgress) {
- // Indicate that a request is in progress
- mInProgress = true;
- // Request a connection to Location Services
- mActivityRecognitionClient.connect();
- //
- } else {
- /*
- * A request is already underway. You can handle
- * this situation by disconnecting the client,
- * re-setting the flag, and then re-trying the
- * request.
- */
- }
- ...
- }
- ...
-}
-</pre>
-<p>
- In
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>,
- if the request type is STOP, call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>.
- Pass the {@link android.app.PendingIntent} you used to start updates as the parameter to
-<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>:
-</p>
-<pre>
-public class MainActivity extends FragmentActivity implements
- ConnectionCallbacks, OnConnectionFailedListener {
- ...
- public void onConnected(Bundle dataBundle) {
- switch (mRequestType) {
- ...
- case STOP :
- mActivityRecognitionClient.removeActivityUpdates(
- mActivityRecognitionPendingIntent);
- break;
- ...
- }
- ...
- }
- ...
-}
-</pre>
-<p>
- You do not have to modify your implementation of
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>
- or
-<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed()</a></code>,
- because these methods do not depend on the request type.
-</p>
-<p>
- You now have the basic structure of an app that implements activity recognition. You can combine
- activity recognition with other location-aware features, such as periodic location updates or
- geofencing, which are described in other lessons in this class.
-</p>
+++ /dev/null
-page.title=Testing Using Mock Locations
-
-trainingnavtop=true
-@jd:body
-
-<div id="tb-wrapper">
-<div id="tb">
-
-<!-- table of contents -->
-<h2>This lesson teaches you to</h2>
-<ol>
- <li><a href="#TurnOnMockMode">Turn On Mock Mode</a></li>
- <li><a href="#SendMockLocations">Send Mock Locations</a></li>
- <li><a href="#RunProvider">Run the Mock Location Provider App</a></li>
- <li><a href="#TestingTips">Testing Tips</a>
-</ol>
-
-<h2>You should also read</h2>
-<ul>
- <li><a href="receive-location-updates.html">Receiving Location Updates</a></li>
- <li><a href="geofencing.html">Creating and Monitoring Geofences</a></li>
- <li><a href="{@docRoot}guide/components/services.html">Services</a></li>
- <li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a>
-</ul>
-
-<h2>Example Test App</h2>
-
-<div class="download-box">
- <a href="http://developer.android.com/shareables/training/LocationProvider.zip" class="button"
- >Download the sample</a>
- <p class="filename">LocationProvider.zip</p>
-</div>
-
-</div>
-</div>
-<p>
- To test a location-aware app that uses Location Services, you don't need to move your device
- from place to place to generate location data. Instead, you can put Location Services into mock
- mode. In this mode, you can send mock {@link android.location.Location} objects to
- Location Services, which then sends them to location clients. In mock mode, Location Services
- also uses mock {@link android.location.Location} objects to trigger geofences.
-</p>
-<p>
- Using mock locations has several advantages:
-</p>
-<ul>
- <li>
- Mock locations allow you to create specific mock data, instead of trying to approximate
- data by moving an actual device.
- </li>
- <li>
- Since mock locations come from Location Services, they test every part of your
- location-handling code. In addition, since you can send the mock data from outside your
- production app, you don't have to disable or remove test code before you publish.
- </li>
- <li>
- Since you don't have to generate test locations by moving a device, you can test an app
- using the emulator.
- </li>
-</ul>
-<p>
- The best way to use mock locations is to send them from a separate mock location provider app.
- This lesson includes a provider app that you can download and use to test your own software.
- Modify the provider app as necessary to suit your own needs. Some ideas for providing test data
- to the app are listed in the section <a href="#TestData">Managing test data</a>.
-</p>
-<p>
- The remainder of this lesson shows you how to turn on mock mode and use a location client to
- send mock locations to Location Services.
-</p>
-<p class="note">
- <strong>Note:</strong> Mock locations have no effect on the activity recognition algorithm used
- by Location Services. To learn more about activity recognition, see the lesson
- <a href="activity-recognition.html">Recognizing the User's Current Activity</a>.
-</p>
-<!--
- Create a Test App
- -->
-<h2 id="TurnOnMockMode">Turn On Mock Mode</h2>
-<p>
- To send mock locations to Location Services in mock mode, a test app must request the permission
- android.Manifest.permission#ACCESS_MOCK_LOCATION. In addition, you must enable mock
- locations on the test device using the option <b>Enable mock locations</b>. To learn how to
- enable mock locations on the device, see
- <a href="{@docRoot}tools/device.html#setting-up">Setting up a Device for Development</a>.
-</p>
-<p>
- To turn on mock mode in Location Services, start by connecting a location client to Location
- Services, as described in the lesson
- <a href="retrieve-current.html">Retrieving the Current Location</a>.
- Next, call the method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#setMockMode(boolean)">LocationClient.setMockMode(true)</a></code>.
- Once you call this method, Location Services turns off its internal location providers and only
- sends out the mock locations you provide it. The following snippet shows you how to call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#setMockMode(boolean)">LocationClient.setMockMode(true)</a></code>:
-</p>
-<pre>
- // Define a LocationClient object
- public LocationClient mLocationClient;
- ...
- // Connect to Location Services
- mLocationClient.connect();
- ...
- // When the location client is connected, set mock mode
- mLocationClinet.setMockMode(true);
-</pre>
-<p>
- Once you have connected the location client to Location Services, you must keep it connected
- until you finish sending out mock locations. Once you call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#disconnect()">LocationClient.disconnect()</a></code>,
- Location Services returns to using its internal location providers. To turn off mock mode while
- the location client is connected, call
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#setMockMode(boolean)">LocationClient.setMockMode(false)</a></code>.
-</p>
-<h2 id="SendMockLocations">Send Mock Locations</h2>
-<p>
- Once you have set mock mode, you can create mock {@link android.location.Location} objects and
- send them to Location Services. In turn, Location Services sends these mock
- {@link android.location.Location} objects to connected location clients. Location Services also
- uses the mock {@link android.location.Location} objects to control geofence triggering.
-</p>
-<p>
- To create a new mock {@link android.location.Location}, create a new
- {@link android.location.Location} object using your test data. Always set the provider
- value to {@code flp}, which is the code that Location Services puts into the
- {@link android.location.Location} objects it sends out. The following snippet shows you how
- to create a new mock {@link android.location.Location}:
-</p>
-<pre>
- private static final String PROVIDER = "flp";
- private static final double LAT = 37.377166;
- private static final double LNG = -122.086966;
- private static final float ACCURACY = 3.0f;
- ...
- /*
- * From input arguments, create a single Location with provider set to
- * "flp"
- */
- public Location createLocation(double lat, double lng, float accuracy) {
- // Create a new Location
- Location newLocation = new Location(PROVIDER);
- newLocation.setLatitude(lat);
- newLocation.setLongitude(lng);
- newLocation.setAccuracy(accuracy);
- return newLocation;
- }
- ...
- // Example of creating a new Location from test data
- Location testLocation = createLocation(LAT, LNG, ACCURACY);
-</pre>
-<p>
- In mock mode, to send a mock location to Location Services call the method
-<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#setMockLocation(android.location.Location)">LocationClient.setMockLocation()</a></code>.
- For example:
-</p>
-<pre>
- mLocationClient.setMockLocation(testLocation);
-</pre>
-<p>
- Location Services sets this mock location as the current location, and this location is sent
- out as the next location update. If this new mock location moves across a geofence boundary,
- Location Services triggers the geofence.
-</p>
-<!--
- Run the Mock Location Provider
- -->
-<h2 id="RunProvider">Run the Mock Location Provider App</h2>
-<p>
- This section contains a brief overview of the mock location provider sample app
- (available for download above) and gives you directions for testing an app using the sample app.
-</p>
-<h3>Overview</h3>
-<p>
- The mock location provider app included with this lesson sends mock
- {@link android.location.Location} objects to Location Services from a background thread running
- in a started {@link android.app.Service}. By using a started service, the provider app is able
- to keep running even if the app's main {@link android.app.Activity} is destroyed because of
- a configuration change or other system event. By using a background thread, the service is able
- to perform a long-running test without blocking the UI thread.
-</p>
-<p>
- The {@link android.app.Activity} that starts when you run the provider app allows you to
- send test parameters to the {@link android.app.Service} and control the type of test you want.
- You have the following options:
-</p>
-<dl>
- <dt>
- Pause before test
- </dt>
- <dd>
- The number of seconds to wait before the provider app starts sending test data to Location
- Services. This interval allows you to switch from the provider app to the app under test
- before the testing actually starts.
- </dd>
- <dt>
- Send interval
- </dt>
- <dd>
- The number of seconds that the provider app waits before it sends another mock location to
- Location Services. See the section <a href="#TestingTips">Testing Tips</a> to learn more
- about setting the send interval.
- </dd>
- <dt>
- Run once
- </dt>
- <dd>
- Switch from normal mode to mock mode, run through the test data once, switch back to
- normal mode, and then kill the {@link android.app.Service}.
- </dd>
- <dt>
- Run continuously
- </dt>
- <dd>
- Switch from normal mode to mock mode, then run through the test data indefinitely. The
- background thread and the started {@link android.app.Service} continue to run, even if the
- main {@link android.app.Activity} is destroyed.
- </dd>
- <dt>
- Stop test
- </dt>
- <dd>
- If a continuous test is in progress, stop it; otherwise, return a warning message. The
- started {@link android.app.Service} switches from mock mode to normal mode and then
- stops itself. This also stops the background thread.
- </dd>
-</dl>
-<p>
- Besides the options, the provider app has two status displays:
-</p>
-<dl>
- <dt>
- App status
- </dt>
- <dd>
- Displays messages related to the lifecycle of the provider app.
- </dd>
- <dt>
- Connection status
- </dt>
- <dd>
- Displays messages related to the state of the location client connection.
- </dd>
-</dl>
-<p>
- While the started {@link android.app.Service} is running, it also posts notifications with the
- testing status. These notifications allow you to see status updates even if the app is not in
- the foreground. When you click on a notification, the main {@link android.app.Activity} of the
- provider app returns to the foreground.
-</p>
-<h3>Test using the mock location provider app</h3>
-<p>
- To test mock location data coming from the mock location provider app:
-</p>
-<ol>
- <li>
- Install the mock location provider app on a device that has Google Play services installed.
- Location Services is part of Google Play services.
- </li>
- <li>
- On the device, enable mock locations. To learn how to do this, see the topic
- <a href="{@docRoot}tools/device.html#setting-up">Setting up a Device for Development</a>.
- </li>
- <li>
- Start the provider app from the Launcher, then choose the options you want from the main
- screen.
- </li>
- <li>
- Unless you've removed the pause interval feature, the mock location provider app
- pauses for a few seconds, and then starts sending mock location data to Location
- Services.
- </li>
- <li>
- Run the app you want to test. While the mock location provider app is running, the app
- you're testing receives mock locations instead of real locations.
- </li>
- <li>
- If the provider app is in the midst of a continuous test, you can switch back to real
- locations by clicking <b>Stop test</b>. This forces the started {@link android.app.Service}
- to turn off mock mode and then stop itself. When the service stops itself, the background
- thread is also destroyed.
- </li>
-
-</ol>
-<h2 id="TestingTips">Testing Tips</h2>
-<p>
- The following sections contain tips for creating mock location data and using the data with a
- mock location provider app.
-</p>
-<h3>Choosing a send interval</h3>
-<p>
- Each location provider that contributes to the fused location sent out by Location Services has
- its own minimum update cycle. For example, the GPS provider can't send a new location more often
- than once per second, and the Wi-Fi provider can't send a new location more often than once
- every five seconds. These cycle times are handled automatically for real locations, but you
- should account for them when you send mock locations. For example, you shouldn't send a new mock
- location more than once per second. If you're testing indoor locations, which rely heavily on
- the Wi-Fi provider, then you should consider using a send interval of five seconds.
-</p>
-<h3>Simulating speed</h3>
-<p>
- To simulate the speed of an actual device, shorten or lengthen the distance between two
- successive locations. For example, changing the location by 88 feet every second simulates
- car travel, because this change works out to 60 miles an hour. In comparison, changing the
- location by 1.5 feet every second simulates brisk walking, because this change works out to
- 3 miles per hour.
-</p>
-<h3>Calculating location data</h3>
-<p>
- By searching the web, you can find a variety of small programs that calculate a new set of
- latitude and longitude coordinates from a starting location and a distance, as well as
- references to formulas for calculating the distance between two points based on their latitude
- and longitude. In addition, the {@link android.location.Location} class offers two methods for
- calculating the distance between points:
-</p>
-<dl>
- <dt>
- {@link android.location.Location#distanceBetween distanceBetween()}
- </dt>
- <dd>
- A static method that calculates the distance between two points specified by latitude and
- longitude.
- </dd>
- <dt>
- {@link android.location.Location#distanceTo distanceTo()}
- </dt>
- <dd>
- For a given {@link android.location.Location}, returns the distance to another
- {@link android.location.Location}.
- </dd>
-</dl>
-<h3>Geofence testing</h3>
-<p>
- When you test an app that uses geofence detection, use test data that reflects different modes
- of travel, including walking, cycling, driving, and traveling by train. For a slow mode of
- travel, make small changes in position between points. Conversely, for a fast mode of travel,
- make a large change in position between points.
-</p>
-<h3 id="TestData">Managing test data</h3>
-<p>
- The mock location provider app included with this lesson contains test latitude, longitude,
- and accuracy values in the form of constants. You may want to consider other ways of organizing
- data as well:
-</p>
-<dl>
- <dt>
- XML
- </dt>
- <dd>
- Store location data in XML files that are including in the provider app. By separating the
- data from the code, you facilitate changes to the data.
- </dd>
- <dt>
- Server download
- </dt>
- <dd>
- Store location data on a server and then have the provider app download it. Since the data
- is completely separate from the app, you can change the data without having to rebuild the
- app. You can also change the data on the server and have the changes reflected immediately
- in the mock locations you're testing.
- </dd>
- <dt>
- Recorded data
- </dt>
- <dd>
- Instead of making up test data, write a utility app that records location data as you move
- the device. Use the recorded data as your test data, or use the data to guide you in
- developing test data. For example, record locations as you walk with a device, and then
- create mock locations that have an appropriate change in latitude and longitude over
- time.
- </dd>
-</dl>