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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Activity Recognition API Tutorial for Android: Getting Started
20 mins
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 ofON_FOOT
detected when the user walks with the device. -
RUNNING
: A sub-activity ofON_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, WALKING
– EXIT
and STILL
– ENTER
, 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:
- Creates a list of
ActivityTransition
types. - Then adds all activity types you want to track with
ENTER
andEXIT
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:
-
ActivityRecognitionClient
providesActivityTransitionResult
.Result
verifies that the intent is of the right type. - It also exposes a function to extract
ActivityTransitionResult
from the intent. - In
handleTransitionsEvents
, you filter onlyACTIVITY_TRANSITION_ENTER
events. - 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:
Now you can track activity transition changes. Before starting the Sampling API implementation, you’ll learn more about confidence grades.
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.