72fdbc88bb
Change-Id: I855e1c3dd0db65be7dc970437af2d52ccf836cb7
787 lines
30 KiB
Plaintext
787 lines
30 KiB
Plaintext
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>
|