Activity Recognition API Tutorial for Android: Getting Started

Learn to track your activities in your Android app by creating a fitness app that uses the Activity Recognition API. By Andrej Vukelic.

4 (4) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Exploring Activities

Before you continue developing Pet Buddy, check out the list of supported activities in the Transition API:

  • STILL: When device sensors don’t detect any motion, such as when the device sits still on a table.
  • WALKING: A sub-activity of ON_FOOT detected when the user walks with the device.
  • RUNNING: A sub-activity of ON_FOOT detected when the user runs with the device.
  • ON_BICYCLE: Activity detected when a user rides a bicycle.
  • IN_VEHICLE: Activity when device sensors detect moving at a higher speed, such as when the user is in a car.

The Sampling API also supports these activities:

  • ON_FOOT: The device’s sensors detect movement at a normal speed when the user is walking or running.
  • TILTING: The device’s angle relative to gravity changed.
  • UNKNOWN: When the device can’t detect any of the activities.

But there’s another difference between these two APIs besides the activities they support. While the Transition API returns a list of events, for example, WALKINGEXIT and STILLENTER, the Sampling API returns the list of all activities sorted by the most confident detection.

In the next section, you’ll learn how to engage all sensors and listen for transitions. Get your shoes and your pet ready for a walk!

Detecting the Start and End of an Activity

Your pet is excited, but tell him to wait a little longer. You’re about to start working with the Transition API.

Before you can use the Activity Recognition Client components, you have to add the dependency. Open the app build.gradle, and replace TODO 5 with:

implementation 'com.google.android.gms:play-services-location:18.0.0'

Now sync gradle. Voila, now your app can access Activity Recognition Client components.

In the Transition API, you decide which transitions you want your app to detect. For example, say you’re building a messenger app that sets your status to busy when you start driving. In this case, you only need to subscribe to the ENTER and EXIT transitions of IN_VEHICLE activities by creating an instance of ActivityTransitionRequest.

But your pet isn’t excited about car rides. So, Pet Buddy will only track:

  • STILL
  • WALKING
  • RUNNING

Now that you have access to the Activity Recognition Client components, it’s time to start working.

Open SupportedActivity.kt. Then find TODO 6 and uncomment the function in the companion object. Now, the companion object looks like this:

companion object {

 fun fromActivityType(type: Int): SupportedActivity = when (type) {
   DetectedActivity.STILL -> STILL
   DetectedActivity.WALKING -> WALKING
   DetectedActivity.RUNNING -> RUNNING
   else -> throw IllegalArgumentException("activity $type not supported")
 }
}

The enum representation of DetectedActivity contains the predefined text and image resources.

Next, you’ll create the ActivityTransitionRequest for supported activities. Open TransitionHelper.kt and replace the TODO 7 with:

private fun getActivitiesToTrack(): List<ActivityTransition> =
   // 1
   mutableListOf<ActivityTransition>()
       .apply {
         // 2
         add(ActivityTransition.Builder()
             .setActivityType(DetectedActivity.STILL)
             .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
             .build())
         add(ActivityTransition.Builder()
             .setActivityType(DetectedActivity.STILL)
             .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
             .build())
         add(ActivityTransition.Builder()
             .setActivityType(DetectedActivity.WALKING)
             .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
             .build())
         add(ActivityTransition.Builder()
             .setActivityType(DetectedActivity.WALKING)
             .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
             .build())
         add(ActivityTransition.Builder()
             .setActivityType(DetectedActivity.RUNNING)
             .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
             .build())
         add(ActivityTransition.Builder()
             .setActivityType(DetectedActivity.RUNNING)
             .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
             .build())
       }

This function:

  1. Creates a list of ActivityTransition types.
  2. Then adds all activity types you want to track with ENTER and EXIT transition types.

Remember to use the key combination Option-Return on Mac or Alt-Enter on PC to resolve any missing dependencies.

Your ActivityTransitionRequest is now ready. Next, you’ll make a request.

To request Transition updates, you need to use the ActivityRecognitionClient. In TransitionHelper.kt, find TODO 8 and replace it with:

val request = ActivityTransitionRequest(getActivitiesToTrack())
val task = ActivityRecognitionClient(this).requestActivityTransitionUpdates(request,
   TransitionsReceiver.getPendingIntent(this))

task.run {
 addOnSuccessListener {
   Log.d("TransitionUpdate", getString(R.string.transition_update_request_success))
 }
 addOnFailureListener {
   Log.d("TransitionUpdate", getString(R.string.transition_update_request_failed))
 }
}

Then, add the next import:

import com.google.android.gms.location.ActivityRecognitionClient

To start the transition updates, you’ll need ActivityTransitionRequest and PendingIntent.
In this case, PendingIntent points to TransitionReceiver, the implementation of BroadcastReceiver.

To save the user’s battery, you need to stop tracking the updates. In the same class, find TODO 9 and replace it with:

val task = ActivityRecognitionClient(this).removeActivityTransitionUpdates(
   TransitionsReceiver.getPendingIntent(this))

task.run {
 addOnSuccessListener {
   Log.d("TransitionUpdate", getString(R.string.transition_update_remove_success))
 }
 addOnFailureListener {
   Log.d("TransitionUpdate", getString(R.string.transition_update_remove_failed))
 }
}

This code stops listening for transition updates.

Build and run. Lift the logcat and tap Start. You’ll see the next log:

com.raywenderlich.android.petbuddy D/TransitionUpdate: Transition updates requested

Now, tap Stop. Listening stops, and you’ll see the next log:

com.raywenderlich.android.petbuddy D/TransitionUpdate: Transition updates removed

Now Pet Buddy is ready to receive updates on your activities. Next, you’ll extract those updates and show them in the MainActivity.kt

Keeping Track of Activity Transitions

First, open TransitionsReceiver.kt and replace TODO 10 with:

    // 1
    if (ActivityTransitionResult.hasResult(intent)) {
      // 2
      val result = ActivityTransitionResult.extractResult(intent)
      result?.let { handleTransitionEvents(it.transitionEvents) }
    }

There will be an error in the code that you’ll clear up in the next step. Now, replace TODO 11 with:

private fun handleTransitionEvents(transitionEvents: List<ActivityTransitionEvent>) {
    transitionEvents
        // 3
        .filter { it.transitionType == ActivityTransition.ACTIVITY_TRANSITION_ENTER }
        // 4
        .forEach { action?.invoke(SupportedActivity.fromActivityType(it.activityType)) }
  }

Use the quick keys mentioned above to resolve any missing imports or dependencies.

Here’s a code breakdown:

  1. ActivityRecognitionClient provides ActivityTransitionResult. Result verifies that the intent is of the right type.
  2. It also exposes a function to extract ActivityTransitionResult from the intent.
  3. In handleTransitionsEvents, you filter only ACTIVITY_TRANSITION_ENTER events.
  4. Then you invoke the action with the activity enter transition mapped to SupportedActivity.

You’re close to showing the transition updates on the main screen.

In MainActivity.kt, find TODO 12 and replace it with:

registerReceiver(transitionBroadcastReceiver, IntentFilter(TRANSITIONS_RECEIVER_ACTION))

Then replace TODO 13 with:

unregisterReceiver(transitionBroadcastReceiver)

Now MainActivity.kt is ready to receive transitions. Find TODO 14 and replace it with:

activityImage.setImageDrawable(ContextCompat.getDrawable(this, supportedActivity.activityImage))
activityTitle.text = getString(supportedActivity.activityText)

Your app is now ready to use! Is your pet still ready for a walk?

Build and run. Take your pet for a walk. Start tracking the transition updates and you’ll see:

Transition from standing still to walking

Now you can track activity transition changes. Before starting the Sampling API implementation, you’ll learn more about confidence grades.

Note: If you’re not getting any results, be patient. Try shaking your phone or go outside and walk for a few minutes to wake up the sensors.

Confidence Grades

As you read before, the Transition API always does calculations under the hood. Once the system is confident you’re doing some activity, you’ll get a message.

The Sampling API is a little different. Here, you get a list of probable activities with confidence grades. Confidence grades for all the probable activities range from zero to 100.

You need to set the level of confidence that will suit you. For example, you could consider the activity as confirmed if the confidence grade is above 75. But there are some cases where many activities could have high confidence.

In that case, sensors can’t distinguish between two close activities. For example, if you run slowly enough, WALKING and RUNNING will both have high confidence. But, if you’re driving a car, there’s no chance that WALKING will be a probable activity.

In the next section, you’ll learn how to implement the Sampling API and use it while your app is in the background.