Ce guide est compatible avec la version 1.1.0-alpha12 de Santé Connect.
Les parcours sportifs permettent aux utilisateurs de suivre un itinéraire GPS pour les activités physiques associées et de partager des cartes de leurs entraînements avec d'autres applications.
Ce guide explique comment demander des autorisations à l'utilisateur et comment les applications sont autorisées à écrire des données de parcours dans le cadre d'une session sportive.
Les fonctionnalités de lecture et d'écriture des parcours sportifs incluent :
- Les applications créent une autorisation d'écriture pour les parcours sportifs.
- L'insertion se produit en écrivant une session sportive dont le champ est un parcours.
- Lecture :
- Pour le propriétaire de la session, les données sont accessibles par le biais d'une lecture de session.
- À partir d'une application tierce, une boîte de dialogue permet à l'utilisateur d'accorder une lecture unique d'un parcours.
Si l'utilisateur ne dispose pas d'autorisations d'écriture et que le parcours n'est pas défini, celui-ci n'est pas mis à jour.
Si votre application dispose d'une autorisation d'écriture sur le parcours et tente de mettre à jour une session en transmettant un objet de session sans le parcours, le parcours existant est supprimé.
Disponibilité de la fonctionnalité
Il n'existe aucun indicateur de disponibilité des fonctionnalités pour ce type de données.
Autorisations requises
L'accès à l'itinéraire d'exercice est protégé par les autorisations suivantes :
android.permission.health.READ_EXERCISE_ROUTE
android.permission.health.WRITE_EXERCISE_ROUTE
Pour ajouter la fonctionnalité de parcours sportifs à votre application, commencez par demander des autorisations d'écriture pour le type de données ExerciseSession
.
Voici l'autorisation que vous devez déclarer pour pouvoir écrire un parcours sportif :
<application>
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
...
</application>
Pour lire un parcours sportif, vous devez demander les autorisations suivantes :
<application>
<uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTE" />
...
</application>
Vous devez également déclarer une autorisation, car chaque parcours est associé à une session sportive (une session = un entraînement).
Pour demander des autorisations, utilisez la méthode PermissionController.createRequestPermissionResultContract()
lorsque vous connectez votre application pour la première fois à Santé Connect. Vous pouvez demander plusieurs autorisations :
- Lire les données de santé, y compris les données de routage :
HealthPermission.getReadPermission(ExerciseSessionRecord::class)
- Écrire des données de santé, y compris des données de routage :
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
- Écrire des données de parcours sportif :
HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE
Demander des autorisations à l'utilisateur
Après avoir créé une instance de client, votre application doit demander des autorisations à l'utilisateur. Les utilisateurs doivent être autorisés à accorder ou à refuser des autorisations à tout moment.
Pour ce faire, créez un ensemble d'autorisations pour les types de données requis. Assurez-vous d'abord que les autorisations de l'ensemble sont déclarées dans votre fichier manifeste Android.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
)
Utilisez getGrantedPermissions
pour voir si votre application dispose déjà des autorisations requises accordées. Si ce n'est pas le cas, utilisez createRequestPermissionResultContract
pour demander ces autorisations. L'écran des autorisations de Santé Connect s'affiche.
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
if (granted.containsAll(PERMISSIONS)) {
// Permissions successfully granted
} else {
// Lack of required permissions
}
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
val granted = healthConnectClient.permissionController.getGrantedPermissions()
if (granted.containsAll(PERMISSIONS)) {
// Permissions already granted; proceed with inserting or reading data
} else {
requestPermissions.launch(PERMISSIONS)
}
}
Étant donné que les utilisateurs peuvent accorder ou révoquer des autorisations à tout moment, votre application doit vérifier régulièrement les autorisations accordées et être capable de gérer les cas de perte d'autorisations.
Informations incluses dans un enregistrement de séance d'exercice
Chaque enregistrement de séance d'exercice contient les informations suivantes :
- Le type d'exercice, par exemple le vélo.
- Le parcours de l'exercice, qui contient des informations telles que la latitude, la longitude et l'altitude.
Agrégations acceptées
Aucune agrégation n'est acceptée pour ce type de données.
Exemples d'utilisation
Les extraits de code suivants montrent comment lire et écrire un parcours sportif.
Lire le parcours sportif
Votre application ne peut pas lire les données d'itinéraire d'exercice créées par d'autres applications lorsqu'elle s'exécute en arrière-plan.
Lorsque votre application s'exécute en arrière-plan et tente de lire un itinéraire d'exercice créé par une autre application, Santé Connect renvoie une réponse ExerciseRouteResult.ConsentRequired
, même si votre application dispose d'un accès Toujours autoriser aux données d'itinéraire d'exercice.
Pour cette raison, nous vous recommandons vivement de demander des itinéraires lors d'une interaction délibérée de l'utilisateur avec votre application, lorsqu'il est activement engagé avec l'UI de votre application.
Pour en savoir plus sur les lectures en arrière-plan, consultez Exemple de lecture en arrière-plan.
L'extrait de code suivant montre comment lire une session dans Santé Connect et y demander un parcours :
suspend fun readExerciseSessionAndRoute() {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofHours(1))
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()
val recordId = exerciseRecord.metadata.id
// 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.
}
}
Écrire un parcours sportif
Le code suivant montre comment enregistrer une session comprenant un parcours sportif :
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.now()
val sessionDuration = Duration.ofMinutes(20)
val sessionEndTime = sessionStartTime.plus(sessionDuration)
val exerciseRoute =
if (grantedPermissions.contains(PERMISSION_WRITE_EXERCISE_ROUTE)) ExerciseRoute(
listOf(
ExerciseRoute.Location(
// Location times must be on or after the session start time
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(
// Location times must be before the session end time
time = sessionEndTime.minusSeconds(1),
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 = sessionStartTime,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionEndTime,
endZoneOffset = ZoneOffset.UTC,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
title = "Morning Bike Ride",
exerciseRoute = exerciseRoute,
metadata = Metadata.manualEntry(
device = Device(type = Device.TYPE_PHONE)
),
)
val response = healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
}
Sessions d'exercice
Les sessions d'exercice peuvent inclure toutes sortes d'activités, de la course à pied au badminton.
Écrire des sessions d'exercice
Voici comment créer une requête d'insertion incluant une session :
suspend fun writeExerciseSession(healthConnectClient: HealthConnectClient) {
healthConnectClient.insertRecords(
listOf(
ExerciseSessionRecord(
startTime = START_TIME,
startZoneOffset = START_ZONE_OFFSET,
endTime = END_TIME,
endZoneOffset = END_ZONE_OFFSET,
exerciseType = ExerciseSessionRecord.ExerciseType.RUNNING,
title = "My Run"
),
// ... other records
)
)
}
Lire une session d'exercice
Voici un exemple de lecture d'une session d'exercice :
suspend fun readExerciseSessions(
healthConnectClient: HealthConnectClient,
startTime: Instant,
endTime: Instant
) {
val response =
healthConnectClient.readRecords(
ReadRecordsRequest(
ExerciseSessionRecord::class,
timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
)
for (exerciseRecord in response.records) {
// Process each exercise record
// Optionally pull in with other data sources of the same time range.
val distanceRecord =
healthConnectClient
.readRecords(
ReadRecordsRequest(
DistanceRecord::class,
timeRangeFilter =
TimeRangeFilter.between(
exerciseRecord.startTime,
exerciseRecord.endTime
)
)
)
.records
}
}
Écrire des données de sous-type
Les sessions peuvent également être constituées de données de sous-type facultatives, qui enrichiront la session avec des informations supplémentaires.
Par exemple, les sessions d'exercice peuvent inclure les classes ExerciseSegment
, ExerciseLap
et ExerciseRoute
:
val segments = listOf(
ExerciseSegment(
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
segmentType = ActivitySegmentType.BENCH_PRESS,
repetitions = 373
)
)
val laps = listOf(
ExerciseLap(
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
length = 0.meters
)
)
ExerciseSessionRecord(
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS,
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
segments = segments,
laps = laps,
route = route
)
Supprimer une session d'exercice
Il existe deux façons de supprimer une session d'exercice :
- Par période de temps
- Par UID
Voici comment supprimer les données de sous-type en fonction d'une période de temps :
suspend fun deleteExerciseSessionByTimeRange(
healthConnectClient: HealthConnectClient,
exerciseRecord: ExerciseSessionRecord,
) {
val timeRangeFilter = TimeRangeFilter.between(exerciseRecord.startTime, exerciseRecord.endTime)
healthConnectClient.deleteRecords(ExerciseSessionRecord::class, timeRangeFilter)
// delete the associated distance record
healthConnectClient.deleteRecords(DistanceRecord::class, timeRangeFilter)
}
Vous pouvez également supprimer les données de sous-type par UID. Toutefois, cela ne supprimera que la session d'exercice, pas les données associées :
suspend fun deleteExerciseSessionByUid(
healthConnectClient: HealthConnectClient,
exerciseRecord: ExerciseSessionRecord,
) {
healthConnectClient.deleteRecords(
ExerciseSessionRecord::class,
recordIdsList = listOf(exerciseRecord.metadata.id),
clientRecordIdsList = emptyList()
)
}