Exercise routes allow users to track a GPS route for associated exercise activities and share maps of their workouts with other apps.
This guide outlines how apps receive permission to write route data as part of an exercise session.
Here's a brief summary of the read and write functionality for exercise routes:
- Apps create a new write permission for exercise routes.
- Insertion happens by writing an exercise session with a route as its field.
- Reading:
- For the session owner, data is accessed using a session read.
- From a third-party app, through a dialog that allows the user to grant a one-time read of a route.
Permissions
Exercise routes have their own runtime write permission
(android.permission.health.WRITE_EXERCISE_ROUTE
).
To add exercise route capability to your app, start by requesting write permissions for a specific data type.
Android 14 permission request
Android 13 permission request
You also have to declare an exercise permission, as each route is associated with an exercise session (one session = one workout).
Here's the permission you need to declare to be able to write exercise routes:
<application>
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE" />
...
</application>
To read exercise routes, you need to request the following permissions:
<application>
<uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
<uses-permission
android:name="android.permission.health.READ_EXERCISE" />
...
</application>
To request permissions, use the
PermissionController.createRequestPermissionResultContract()
method when you
first connect your app to Health Connect. Several permissions that you might
want to request are:
- Read health data, including route data:
HealthPermission.getReadPermission(ExerciseSessionRecord::class)
- Write health data, including route data:
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
- Write exercise route data:
HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE
Read and write route data
Apps insert a route by writing a session with a route as a field.
If the user doesn't have write permissions and the route is not set, the route doesn't update.
If your app has a route write permission and tries to update a session by passing in a session object without a route, the existing route is deleted.
Whenever your app needs to read route data provided by a third-party app, a dialog appears asking the user to allow the read operation.
Request a route from a session
Here's how to read a session in Health Connect and request a route from that session:
suspend fun readExerciseSessionAndRoute() {
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getReadPermission(ExerciseSessionRecord::class))) {
// The user doesn't allow the app to read exercise session data.
return
}
val readResponse =
healthConnectClient.readRecords(
ReadRecordsRequest(
ExerciseSessionRecord::class,
TimeRangeFilter.between(startTime, endTime)
)
)
val exerciseRecord = readResponse.records.first()
// See https://developer.android.com/training/basics/intents/result#launch
// for appropriately handling ActivityResultContract.
val requestExerciseRouteLauncher = fragment.registerForActivityResul
(ExerciseRouteRequestContract()) { exerciseRoute: ExerciseRoute? ->
if (exerciseRoute != null) {
displayExerciseRoute(exerciseRoute)
} else {
// Consent was denied
}
}
val exerciseSessionRecord =
healthConnectClient.readRecord(ExerciseSessionRecord::class, recordId).record
when (val exerciseRouteResult = exerciseSessionRecord.exerciseRouteResult) {
is ExerciseRouteResult.Data ->
displayExerciseRoute(exerciseRouteResult.exerciseRoute)
is ExerciseRouteResult.ConsentRequired ->
requestExerciseRouteLauncher.launch(recordId)
is ExerciseRouteResult.NoData -> Unit // No exercise route to show
else -> Unit
}
}
fun displayExerciseRoute(route: ExerciseRoute?) {
val locations = route.route.orEmpty()
for (location in locations) {
// Handle location.
}
}
Write a route from a session
The following code demonstrates how to record a session that includes an exercise route:
suspend fun InsertExerciseRoute(healthConnectClient: HealthConnectClient) {
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
getWritePermission(ExerciseSessionRecord::class))) {
// The user doesn't allow the app to write exercise session data.
return
}
val sessionStartTime = Instant.parse("2023-01-01T10:00:00.00Z")
val sessionDuration = Duration.ofMinutes(20)
val exerciseRoute =
if (getPermissions.contains(PERMISSION_EXERCISE_ROUTE_WRITE) {
ExerciseRoute(
listOf(
ExerciseRoute.Location(
time = sessionStartTime
latitude = 6.5483
longitude = 0.5488
horizontalAccuracy = Length.meters(2.0)
verticalAccuracy = Length.meters(2.0),
altitude = Length.meters(9.0)
),
ExerciseRoute.Location(
time = sessionStartTime.plus(sessionDuration)
latitude = 6.4578
longitude = 0.6577
horizontalAccuracy = Length.meters(2.0)
verticalAccuracy = Length.meters(2.0),
altitude = Length.meters(9.2)
)
)
)
} else {
// The user doesn't allow the app to write exercise route data.
null
}
val exerciseSessionRecord =
ExerciseSessionRecord(
startTime = /* starting time in milliseconds */,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionStartTime.plus(sessionDuration),
endZoneOffset = ZoneOffset.UTC,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
title = "Morning Bike Ride",
exerciseRoute = exerciseRoute
)
healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
}