Making an application context-aware is one of the best ways to offer useful services to your users. There are a few ways to do this, including building applications that use geofences and other location services. This article focuses on using the Google Play Services Activity Recognition API to determine if the user is running, walking, in a vehicle, biking, or remaining still.
Knowing what activity the user is performing can allow you to cater the application experience, for example, by asking if the user is starting to exercise so you can keep track of it with Google Fit, or preventing notifications from being sent when the user is driving. The source files for this tutorial can be found on GitHub.
1. Project Setup
The first thing you need to do is create a new Android application. For this sample application, I set the minimum SDK to 14 and created a default empty Activity
. Once Android Studio has created the base application, open the build.gradle file, and import Play Services (version 8.4 is the latest at the time of this writing) under the dependencies
node.
compile 'com.google.android.gms:play-services:8.4.0'
Next, create a new class, name it ActivityRecognizedService
, and have it extend IntentService
. When Google Play Services returns the user's activity, it will be sent to this IntentService
. This will allow you to perform your application logic in the background as the user goes about their day.
public class ActivityRecognizedService extends IntentService { public ActivityRecognizedService() { super("ActivityRecognizedService"); } public ActivityRecognizedService(String name) { super(name); } @Override protected void onHandleIntent(Intent intent) { } }
To finish setting up, open AndroidManifest.xml. You need to declare ActivityRecognizedService
and include the com.google.android.gms.permission.ACTIVITY_RECOGNITION
permission for your application.
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tutsplus.activityrecognition"><uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" /><application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><service android:name=".ActivityRecognizedService" /></application></manifest>
Now that you have the base structure of your application completed, you can move to the next step of connecting to Google Play Services and requesting activity data.
2. Requesting Activity Recognition
In order to use Google Play Services, you first need to connect to them. Start by opening MainActivity.java and implement the ConnectionCallbacks
and OnConnectionFailedListener
interfaces. You also need to create a member variable of type GoogleApiClient
to keep a reference to the API client.
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { public GoogleApiClient mApiClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onConnected(@Nullable Bundle bundle) { } @Override public void onConnectionSuspended(int i) { } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { } }
After implementing the required interfaces for the GoogleApiClient
, you can initialize the client and connect to Google Play Services in onCreate()
by requesting the ActivityRecognition.API
and associating your listeners with the GoogleApiClient
instance.
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mApiClient = new GoogleApiClient.Builder(this) .addApi(ActivityRecognition.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); mApiClient.connect(); }
Once the GoogleApiClient
instance has connected, onConnected()
is called. When this happens, you need to create a PendingIntent
that goes to the IntentService
you created earlier, and pass it to the ActivityRecognitionApi
. You also need to set an interval for how often the API should check the user's activity. For this sample application, we use a value of 3000
, or three seconds, though in an actual application you may want to check less frequently to conserve power.
@Override public void onConnected(@Nullable Bundle bundle) { Intent intent = new Intent( this, ActivityRecognizedService.class ); PendingIntent pendingIntent = PendingIntent.getService( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT ); ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates( mApiClient, 3000, pendingIntent ); }
At this point, your application should attempt to recognize the user's activity every three seconds and send that data to ActivityRecognizedService
.
3. Handling Activity Recognition
In the onHandleIntent()
method of ActivityRecognizedService
, the first thing you do is validate that the received Intent
contains activity recognition data. If it does, then you can extract the ActivityRecognitionResult
from the Intent
to see what activities your user might be performing. You can retrieve a list of the possible activities by calling getProbableActivities()
on the ActivityRecognitionResult
object.
@Override protected void onHandleIntent(Intent intent) { if(ActivityRecognitionResult.hasResult(intent)) { ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent); handleDetectedActivities( result.getProbableActivities() ); } }
For this sample application, you simply log each activity that has been detected and display how confident Google Play Services is that the user is performing that activity by calling getConfidence()
on a DetectedActivity
instance. If a confidence is 75 or higher, then it's safe to assume that the user is performing that activity. To demonstrate this, you also display a notification when your app detects that the user is walking with a high confidence.
private void handleDetectedActivities(List<DetectedActivity> probableActivities) { for( DetectedActivity activity : probableActivities ) { switch( activity.getType() ) { case DetectedActivity.IN_VEHICLE: { Log.e( "ActivityRecogition", "In Vehicle: " + activity.getConfidence() ); break; } case DetectedActivity.ON_BICYCLE: { Log.e( "ActivityRecogition", "On Bicycle: " + activity.getConfidence() ); break; } case DetectedActivity.ON_FOOT: { Log.e( "ActivityRecogition", "On Foot: " + activity.getConfidence() ); break; } case DetectedActivity.RUNNING: { Log.e( "ActivityRecogition", "Running: " + activity.getConfidence() ); break; } case DetectedActivity.STILL: { Log.e( "ActivityRecogition", "Still: " + activity.getConfidence() ); break; } case DetectedActivity.TILTING: { Log.e( "ActivityRecogition", "Tilting: " + activity.getConfidence() ); break; } case DetectedActivity.WALKING: { Log.e( "ActivityRecogition", "Walking: " + activity.getConfidence() ); if( activity.getConfidence() >= 75 ) { NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentText( "Are you walking?" ); builder.setSmallIcon( R.mipmap.ic_launcher ); builder.setContentTitle( getString( R.string.app_name ) ); NotificationManagerCompat.from(this).notify(0, builder.build()); } break; } case DetectedActivity.UNKNOWN: { Log.e( "ActivityRecogition", "Unknown: " + activity.getConfidence() ); break; } } } }
If you run this application, go for a run, and then plug your device into your computer, you should see a log similar to the following in your development console.
E/ActivityRecogition: On Foot: 92 E/ActivityRecogition: Running: 87 E/ActivityRecogition: On Bicycle: 8 E/ActivityRecogition: Walking: 5
If you go for a walk, you should receive a notification asking if you are on a walk. If you have an exercise application, this would be the perfect opportunity to provide your user with an action that would allow them to start keeping track of their exercise.
Conclusion
Google Play Services makes it very easy to determine the user's current activity. This can be incredibly valuable for making your applications context-aware and useful for your users, which is great for everyone involved. With just a few lines of code, you too can make your applications smarter.