Merge "Deleting pages so redirects work on DevSite. b/28670825" into mnc-mr-docs

am: 965f4bf1ef

* commit '965f4bf1ef2e8958ec9a53bab00be919a49bc8c2':
  Deleting pages so redirects work on DevSite. b/28670825

Change-Id: If2a4f83aadd1994c86c34a9443cf50af96b633d6
This commit is contained in:
Amanda Kassay
2016-05-11 17:44:06 +00:00
committed by android-build-merger
2 changed files with 0 additions and 1157 deletions

View File

@@ -1,786 +0,0 @@
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">&lt;manifest&gt;</a></code>
element:
</p>
<pre>
&lt;uses-permission
android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/&gt;
</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.
&#64;Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
&#64;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 {
...
&#64;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;
...
&#64;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.
*/
&#64;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.
*/
&#64;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
&#64;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.
*/
&#64;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 {
...
&#64;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
*&#64;param activityType The detected activity type
*&#64;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">&lt;service&gt;</a></code>
element to the app manifest. For example:
</p>
<pre>
&lt;service
android:name="com.example.android.location.ActivityRecognitionIntentService"
android:label="&#64;string/app_name"
android:exported="false"&gt;
&lt;/service&gt;
</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>

View File

@@ -1,371 +0,0 @@
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>