Health Connect Android API
Learn how to use the Health Connect Android API to create an app that reads and writes health data and manages health permissions. By Zahid Rasheed.
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
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
Health Connect Android API
15 mins
- Getting Started
- Setting up an Emulator to Test the Health Connect API
- Downloading and Exploring the Starter Project
- Introducing Health Connect
- Privacy Controls
- Storage
- Data Types
- Health Connect APK
- Handling Dependencies
- Handling Permissions and Privacy Policy
- Writing Data
- Inserting Records
- Build and Run
- Reading Data
- Reading Data through a ReadRecordsRequest
- Reading Data through an AggregateRequest
- Build and Run
- Where to Go From Here?
Writing Data
Inserting Records
Add the following code after onPermissionAvailable
.
private fun insertData(client: HealthConnectClient, steps: Long, caloriesBurned: Double) {
// 1
val startTime = ZonedDateTime.now().minusSeconds(1).toInstant()
val endTime = ZonedDateTime.now().toInstant()
// 2
val records = listOf(
StepsRecord(
count = steps,
startTime = startTime,
endTime = endTime,
startZoneOffset = null,
endZoneOffset = null,
),
TotalCaloriesBurnedRecord(
energy = Energy.calories(caloriesBurned),
startTime = startTime,
endTime = endTime,
startZoneOffset = null,
endZoneOffset = null,
)
)
// 3
lifecycleScope.launch {
val insertRecords = client.insertRecords(records)
if (insertRecords.recordUidsList.isNotEmpty()) {
runOnUiThread{
Toast.makeText(
this@MainActivity,
"Records inserted successfully",
Toast.LENGTH_SHORT
).show()
}
}
}
}
With this update,
- You’re creating a time range with a start and end. You record the data in a small interval. This way you can insert the data multiple times in a day.
- Followed by another list that contains
StepsRecord
andTotalCaloriesBurnedRecord
records. - Then finally, you insert the created record through the
HealthConnectClient
instance.recordUidsList
contains theuid
s of inserted records. When the list isn’t empty, you’re showing a success message to the user.
Now, at the end of onCreate
in the MainActivity.kt, add the following code:
val stepsEditText = findViewById<EditText>(R.id.stepsEditText)
val caloriesEditText = findViewById<EditText>(R.id.caloriesEditText)
findViewById<Button>(R.id.submit).setOnClickListener {
val steps = stepsEditText.text.toString().toLong()
val calories = caloriesEditText.text.toString().toDouble()
val client = HealthConnectClient.getOrCreate(this)
insertData(client, steps, calories)
// clear input fields after insertion and close the keyboard
stepsEditText.text.clear()
caloriesEditText.text.clear()
caloriesEditText.onEditorAction(EditorInfo.IME_ACTION_DONE)
}
In the code above, when a user taps Button, you read input values and save them with insertData()
. You then clear input fields and close the keyboard.
Build and Run
That’s all you need to do to write data through the Health Connect API. Run the project, input values and tap the button.
Reading Data
You can read data in two ways using HealthConnectClient.
-
ReadRecordsRequest
: Read records determined by time range and other filters. You’ll use this method to read the daily steps count and calories intake. -
AggregateRequest
: Read aggregations for a givenAggregateMetric
. You’ll use this method to read monthly step counts and caloric intakes.
Reading Data through a ReadRecordsRequest
In MainActivity.kt, add the following after insertData()
:
private suspend fun readDailyRecords(client: HealthConnectClient) {
// 1
val today = ZonedDateTime.now()
val startOfDay = today.truncatedTo(ChronoUnit.DAYS)
val timeRangeFilter = TimeRangeFilter.between(
startOfDay.toLocalDateTime(),
today.toLocalDateTime()
)
// 2
val stepsRecordRequest = ReadRecordsRequest(StepsRecord::class, timeRangeFilter)
val numberOfStepsToday = client.readRecords(stepsRecordRequest)
.records
.sumOf { it.count }
val stepsTextView = findViewById<TextView>(R.id.stepsTodayValue)
stepsTextView.text = numberOfStepsToday.toString()
// 3
val caloriesRecordRequest = ReadRecordsRequest(
TotalCaloriesBurnedRecord::class,
timeRangeFilter
)
val caloriesBurnedToday = client.readRecords(caloriesRecordRequest)
.records
.sumOf { it.energy.inCalories }
val caloriesTextView = findViewById<TextView>(R.id.caloriesTodayValue)
caloriesTextView.text = caloriesBurnedToday.toString()
}
And now the breakdown:
- You create a
TimeRangeFilter
from the start of the day until now. - You then create a
ReadRecordRequest
forStepsRecord
. Through theHealthConnectClient
instance, you read records and get the sum. You get the sum because there can be many records for steps taken today. Finally, you display the daily steps count. - This is the same as Step 2, but the
ReadRecordsRequest
is forTotalCaloriesBurnedRecord
.
Reading Data through an AggregateRequest
Add the following method at the bottom of MainActivity:
private suspend fun readAggregatedData(client: HealthConnectClient) {
// 1
val today = ZonedDateTime.now()
val startOfDayOfThisMonth = today.withDayOfMonth(1)
.truncatedTo(ChronoUnit.DAYS)
val elapsedDaysInMonth = Duration.between(startOfDayOfThisMonth, today)
.toDays() + 1
val timeRangeFilter = TimeRangeFilter.between(
startOfDayOfThisMonth.toInstant(),
today.toInstant()
)
// 2
val data = client.aggregate(
AggregateRequest(
metrics = setOf(
StepsRecord.COUNT_TOTAL,
TotalCaloriesBurnedRecord.ENERGY_TOTAL
),
timeRangeFilter = timeRangeFilter,
)
)
// 3
val steps = data[StepsRecord.COUNT_TOTAL] ?: 0
val averageSteps = steps / elapsedDaysInMonth
val stepsAverageTextView = findViewById<TextView>(R.id.stepsAverageValue)
stepsAverageTextView.text = averageSteps.toString()
// 4
val caloriesBurned = data[TotalCaloriesBurnedRecord.ENERGY_TOTAL]
?.inCalories ?: 0.0
val averageCaloriesBurned = caloriesBurned / elapsedDaysInMonth
val caloriesAverageTextView = findViewById<TextView>(
R.id.caloriesAverageValue
)
caloriesAverageTextView.text = getString(R.string.format_calories_average)
.format(averageCaloriesBurned)
}
java.time
.Here’s what’s happening:
- You’re creating a
TimeRangeFilter
from the start of the month until now. Also, you’re calculating the days elapsed in the month so you can calculate the average. - You’re making an
aggregate
request throughHealthConnectClient
with a set of measurements to get the total steps and calories within a specific time range. The benefit of aggregated data is it includes basic aggregations or aggregating data into buckets. - You’re calculating
averageSteps
and updating the layout. - You’re calculating
averageCaloriesBurned
and updating the layout. For a better UI, you’re rounding the calorie values up to two decimals.
Add the following in the strings.xml file.
<string name="format_calories_average">%.2f</string>
You’re almost there. You now have functions that read data, but you need to connect a few final dots.
Add another method in the MainActivity.kt:
private suspend fun readData(client: HealthConnectClient) {
readDailyRecords(client)
readAggregatedData(client)
}
This helper function commands both functions you created above to read data.
Now, inside onPermissionAvailable
, replace // todo: read data
with the following.
readData(client)
By doing so, you can read the data and update the layout as soon as you have permissions available.
Finally, add the following at the end of the lifecycleScope.launch {
block, inside insertData
:
readData(client)
This will ensure you show updated data after a user inserts new data.
Build and Run
You’re done! Build and run the app to see the result. You can now write data and also see your existing data.
Where to Go From Here?
Congratulations! You’ve learned about Health Connect by making a simple app. Now you have one less excuse to get fitter and healthier. Use the Download Materials button at the top or bottom of this article to download the final project.
You can take a few steps to improve your app:
- The app can also track the exact start and end time and write data through sessions.
- You created this sample app with a simple UI. To make it a really beneficial app for a user, follow the Health Connect UX developer guidance.
- Because users can use the Health Connect app to revoke permissions at any time, it’s important to have proper exception handling.
- To keep your app compliant with Android policy, take a look at the Health Connect Policy Requirement FAQs.