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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 2 of this article. Click here to view the first page.

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,

  1. 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.
  2. Followed by another list that contains StepsRecord and TotalCaloriesBurnedRecord records.
  3. Then finally, you insert the created record through the HealthConnectClient instance. recordUidsList contains the uids of inserted records. When the list isn’t empty, you’re showing a success message to the user.
Note: For the sake of simplicity, keep ZoneOffset null. You can read more about ZoneOffset.

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.

An animation to demo the input of steps and calories health data

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 given AggregateMetric. 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:

  1. You create a TimeRangeFilter from the start of the day until now.
  2. You then create a ReadRecordRequest for StepsRecord. Through the HealthConnectClient 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.
  3. This is the same as Step 2, but the ReadRecordsRequest is for TotalCaloriesBurnedRecord.

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)
}
Note: While importing dependencies, there are many import options for Duration. Please choose java.time.

Here’s what’s happening:

  1. 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.
  2. You’re making an aggregate request through HealthConnectClient 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.
  3. You’re calculating averageSteps and updating the layout.
  4. 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.

An animation to demo displaying the total steps and calories health data entered today and the average this month

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: