Ce guide est compatible avec la version 1.1.0-alpha11 de Santé Connect.
Santé Connect fournit un type de données exercice planifié pour permettre aux applications d'entraînement d'écrire des plans d'entraînement et aux applications d'entraînement de lire des plans d'entraînement. Les exercices enregistrés (entraînements) peuvent être relus pour une analyse personnalisée des performances afin d'aider les utilisateurs à atteindre leurs objectifs d'entraînement.
Disponibilité de la fonctionnalité
Pour déterminer si l'appareil d'un utilisateur est compatible avec les plans d'entraînement sur Santé Connect, vérifiez la disponibilité deFEATURE_PLANNED_EXERCISE
sur le client :
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Pour en savoir plus, consultez Vérifier la disponibilité des fonctionnalités.
Autorisations requises
L'accès aux exercices planifiés est protégé par les autorisations suivantes :
android.permission.health.READ_PLANNED_EXERCISE
android.permission.health.WRITE_PLANNED_EXERCISE
Pour ajouter la fonctionnalité d'exercice planifié à votre application, commencez par demander des autorisations d'écriture pour le type de données PlannedExerciseSession
.
Voici l'autorisation que vous devez déclarer pour pouvoir écrire des exercices planifiés :
<application>
<uses-permission
android:name="android.permission.health.WRITE_PLANNED_EXERCISE" />
...
</application>
Pour lire les exercices planifiés, vous devez demander les autorisations suivantes :
<application>
<uses-permission
android:name="android.permission.health.READ_PLANNED_EXERCISE" />
...
</application>
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(HeartRateRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::class),
HealthPermission.getReadPermission(PlannedExerciseSessionRecord::class),
HealthPermission.getWritePermission(PlannedExerciseSessionRecord::class),
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.
Autorisations associées
Les programmes d'entraînement sont associés à des séances d'exercice. Par conséquent, l'utilisateur doit autoriser l'utilisation de chaque type d'enregistrement lié à un programme d'entraînement pour utiliser pleinement cette fonctionnalité de Santé Connect.
Par exemple, si un programme d'entraînement mesure la fréquence cardiaque d'un utilisateur lors d'une série de courses, le développeur devra peut-être déclarer les autorisations suivantes, que l'utilisateur devra accorder, afin d'enregistrer la séance d'exercice et de lire les résultats pour une évaluation ultérieure :
android.permission.health.READ_EXERCISE
android.permission.health.READ_EXERCISE_ROUTES
android.permission.health.READ_HEART_RATE
android.permission.health.WRITE_EXERCISE
android.permission.health.WRITE_EXERCISE_ROUTE
android.permission.health.WRITE_HEART_RATE
Toutefois, l'application qui crée des plans d'entraînement et évalue les performances par rapport aux plans n'est souvent pas la même que celle qui consomme les plans d'entraînement et écrit les données d'exercice réelles. Selon le type d'application, toutes les autorisations de lecture et d'écriture ne sont pas nécessaires. Par exemple, vous n'aurez peut-être besoin que des autorisations suivantes pour chaque type d'application :
Application de programme d'entraînement | Application d'entraînement |
---|---|
WRITE_PLANNED_EXERCISE |
READ_PLANNED_EXERCISE |
READ_EXERCISE |
WRITE_EXERCISE |
READ_EXERCISE_ROUTES |
WRITE_EXERCISE_ROUTE |
READ_HEART_RATE |
WRITE_HEART_RATE |
Informations incluses dans l'enregistrement d'une séance d'exercice planifiée
- Titre de la session.
- Liste des blocs d'exercices prévus.
- Heure de début et de fin de la session.
- Type d'exercice.
- Notes concernant l'activité.
- Les métadonnées
- ID de la session d'exercice terminée : il est écrit automatiquement après la fin d'une session d'exercice liée à cette session d'exercice planifiée.
Informations incluses dans un enregistrement de bloc d'exercice planifié
Un bloc d'exercices planifié contient une liste d'étapes d'exercice pour permettre la répétition de différents groupes d'étapes (par exemple, effectuer une séquence de flexions des bras, de burpees et de crunches cinq fois de suite).
- Description du bloc.
- Liste des étapes d'exercice prévues.
- Nombre de répétitions.
Informations incluses dans un enregistrement d'étape d'exercice planifié
- Description de l'étape.
- Catégorie d'exercice.
- Type d'exercice :
- Liste des objectifs de performances.
- Objectif de lecture complète.
Agrégations acceptées
Aucune agrégation n'est acceptée pour ce type de données.
Exemples d'utilisation
Supposons qu'un utilisateur prévoie de courir pendant 90 minutes dans deux jours. Cette course comprend trois tours autour d'un lac, avec une fréquence cardiaque cible comprise entre 90 et 110 bpm.
- Une séance d'exercice planifiée avec les éléments suivants est définie par l'utilisateur dans une application de programme d'entraînement :
- Début et fin prévus de la course
- Type d'exercice (course à pied)
- Nombre de longueurs (répétitions)
- Fréquence cardiaque cible (entre 90 et 110 bpm)
- Ces informations sont regroupées en blocs d'exercices et de pas, et écrites dans Santé Connect par l'application de programme d'entraînement sous la forme d'un
PlannedExerciseSessionRecord
. - L'utilisateur effectue la séance prévue (course).
- Les données d'exercice liées à la séance sont enregistrées :
- par un accessoire connecté pendant la séance. Par exemple, la fréquence cardiaque.
Ces données sont écrites dans Santé Connect en tant que type d'enregistrement de l'activité. Dans ce cas,
HeartRateRecord
. - Manuellement par l'utilisateur après la session. Par exemple, en indiquant le début et la fin de la course réelle. Ces données sont écrites dans Santé Connect sous la forme d'un
ExerciseSessionRecord
.
- par un accessoire connecté pendant la séance. Par exemple, la fréquence cardiaque.
Ces données sont écrites dans Santé Connect en tant que type d'enregistrement de l'activité. Dans ce cas,
- Plus tard, l'application de programme d'entraînement lit les données de Santé Connect pour évaluer les performances réelles par rapport aux objectifs définis par l'utilisateur lors de la séance d'entraînement prévue.
Planifier des exercices et définir des objectifs
Un utilisateur peut planifier son exercice à l'avance et définir des objectifs. Écris cela dans Santé Connect en tant que séance d'exercice planifiée.
Dans l'exemple décrit dans Exemple d'utilisation, l'utilisateur prévoit une course de 90 minutes dans deux jours. Cette course comprend trois tours autour d'un lac, avec une fréquence cardiaque cible comprise entre 90 et 110 bpm.
Un extrait de code comme celui-ci peut être trouvé dans le gestionnaire de formulaire d'une application qui enregistre les séances d'exercice planifiées dans Santé Connect. Il peut également se trouver au point d'ingestion pour les intégrations, par exemple avec un service qui propose des formations.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getWritePermission(PlannedExerciseSessionRecord::class))) {
// The user hasn't granted the app permission to write planned exercise session data.
return
}
val plannedDuration = Duration.ofMinutes(90)
val plannedStartDate = LocalDate.now().plusDays(2)
val plannedExerciseSessionRecord = PlannedExerciseSessionRecord(
startDate = plannedStartDate,
duration = plannedDuration,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
blocks = listOf(
PlannedExerciseBlock(
repetitions = 1, steps = listOf(
PlannedExerciseStep(
exerciseType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING,
exercisePhase = PlannedExerciseStep.EXERCISE_PHASE_ACTIVE,
completionGoal = ExerciseCompletionGoal.RepetitionsGoal(repetitions = 3),
performanceTargets = listOf(
ExercisePerformanceTarget.HeartRateTarget(
minHeartRate = 90.0, maxHeartRate = 110.0
)
)
),
), description = "Three laps around the lake"
)
),
title = "Run at lake",
notes = null,
metadata = Metadata.manualEntry(
device = Device(type = Device.Companion.TYPE_PHONE)
)
)
val insertedPlannedExerciseSessions =
healthConnectClient.insertRecords(listOf(plannedExerciseSessionRecord)).recordIdsList
val insertedPlannedExerciseSessionId = insertedPlannedExerciseSessions.first()
Enregistrer des données d'exercice et d'activité
Deux jours plus tard, l'utilisateur enregistre la séance d'exercice réelle. Écris cela dans Santé Connect en tant que séance d'exercice.
Dans cet exemple, la durée de la session de l'utilisateur correspond exactement à la durée prévue.
L'extrait suivant peut être trouvé dans le gestionnaire de formulaire d'une application qui enregistre des séances d'exercice dans Santé Connect. Il peut également être utilisé dans les gestionnaires d'ingestion et d'exportation de données pour un accessoire connecté capable de détecter et d'enregistrer les séances d'exercice.
insertedPlannedExerciseSessionId
est réutilisé à partir de l'exemple précédent. Dans une application réelle, l'ID serait déterminé par l'utilisateur qui sélectionne une séance d'exercice planifiée dans une liste de séances existantes.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getWritePermission(ExerciseSessionRecord::class))) {
// The user doesn't granted the app permission to write exercise session data.
return
}
val sessionDuration = Duration.ofMinutes(90)
val sessionEndTime = Instant.now()
val sessionStartTime = sessionEndTime.minus(sessionDuration)
val exerciseSessionRecord = ExerciseSessionRecord(
startTime = sessionStartTime,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionEndTime,
endZoneOffset = ZoneOffset.UTC,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
segments = listOf(
ExerciseSegment(
startTime = sessionStartTime,
endTime = sessionEndTime,
repetitions = 3,
segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_RUNNING
)
),
title = "Run at lake",
plannedExerciseSessionId = insertedPlannedExerciseSessionId,
metadata = Metadata.manualEntry(
device = Device(type = Device.Companion.TYPE_PHONE)
)
)
val insertedExerciseSessions =
healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
Un wearable enregistre également sa fréquence cardiaque tout au long de la course. L'extrait de code suivant peut être utilisé pour générer des enregistrements dans la plage cible.
Dans une application réelle, les principaux éléments de cet extrait peuvent être trouvés dans le gestionnaire d'un message provenant d'un wearable, qui écrirait la mesure dans Santé Connect lors de la collecte.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getWritePermission(HeartRateRecord::class))) {
// The user doesn't granted the app permission to write heart rate record data.
return
}
val samples = mutableListOf<HeartRateRecord.Sample>()
var currentTime = sessionStartTime
while (currentTime.isBefore(sessionEndTime)) {
val bpm = Random.nextInt(21) + 90
val heartRateRecord = HeartRateRecord.Sample(
time = currentTime,
beatsPerMinute = bpm.toLong(),
)
samples.add(heartRateRecord)
currentTime = currentTime.plusSeconds(180)
}
val heartRateRecord = HeartRateRecord(
startTime = sessionStartTime,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionEndTime,
endZoneOffset = ZoneOffset.UTC,
samples = samples,
metadata = Metadata.autoRecorded(
device = Device(type = Device.Companion.TYPE_WATCH)
)
)
val insertedHeartRateRecords = healthConnectClient.insertRecords(listOf(heartRateRecord))
Évaluer les objectifs de performances
Le lendemain de l'entraînement de l'utilisateur, vous pouvez récupérer l'exercice enregistré, vérifier s'il existe des objectifs d'exercice planifiés et évaluer d'autres types de données pour déterminer si les objectifs définis ont été atteints.
Un extrait comme celui-ci se trouve généralement dans un job périodique permettant d'évaluer les objectifs de performances ou lors du chargement d'une liste d'exercices et de l'affichage d'une notification concernant les objectifs de performances dans une application.
// Verify the user has granted all necessary permissions for this task
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.containsAll(
listOf(
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getReadPermission(PlannedExerciseSessionRecord::class),
HealthPermission.getReadPermission(HeartRateRecord::class)
)
)
) {
// The user doesn't granted the app permission to read exercise session record data.
return
}
val searchDuration = Duration.ofDays(1)
val searchEndTime = Instant.now()
val searchStartTime = searchEndTime.minus(searchDuration)
val response = healthConnectClient.readRecords(
ReadRecordsRequest<ExerciseSessionRecord>(
timeRangeFilter = TimeRangeFilter.between(searchStartTime, searchEndTime)
)
)
for (exerciseRecord in response.records) {
val plannedExerciseRecordId = exerciseRecord.plannedExerciseSessionId
val plannedExerciseRecord =
if (plannedExerciseRecordId == null) null else healthConnectClient.readRecord(
PlannedExerciseSessionRecord::class, plannedExerciseRecordId
).record
if (plannedExerciseRecord != null) {
val aggregateRequest = AggregateRequest(
metrics = setOf(HeartRateRecord.BPM_AVG),
timeRangeFilter = TimeRangeFilter.between(
exerciseRecord.startTime, exerciseRecord.endTime
),
)
val aggregationResult = healthConnectClient.aggregate(aggregateRequest)
val maxBpm = aggregationResult[HeartRateRecord.BPM_MAX]
val minBpm = aggregationResult[HeartRateRecord.BPM_MIN]
if (maxBpm != null && minBpm != null) {
plannedExerciseRecord.blocks.forEach { block ->
block.steps.forEach { step ->
step.performanceTargets.forEach { target ->
when (target) {
is ExercisePerformanceTarget.HeartRateTarget -> {
val minTarget = target.minHeartRate
val maxTarget = target.maxHeartRate
if(
minBpm >= minTarget && maxBpm <= maxTarget
) {
// Success!
}
}
// Handle more target types
}
}
}
}
}
}
}
}
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
)