Santé Connect fournit un type de données exercice planifié pour permettre aux applications d'entraînement d'écrire des programmes d'entraînement et aux applications de remise en forme de lire ces programmes. 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.
Vérifier la disponibilité de Santé Connect
Avant de tenter d'utiliser Santé Connect, votre application doit vérifier que Santé Connect est disponible sur l'appareil de l'utilisateur. Santé Connect peut ne pas être préinstallé sur tous les appareils ou être désactivé.
Vous pouvez vérifier la disponibilité à l'aide de la méthode HealthConnectClient.getSdkStatus().
Vérifier la disponibilité de Santé Connect
fun checkHealthConnectAvailability(context: Context) { val providerPackageName = "com.google.android.apps.healthdata" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName) if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) { // Health Connect is not available. Guide the user to install/enable it. // For example, show a dialog. return // early return as there is no viable integration } if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) { // Health Connect is available but requires an update. // Optionally redirect to package installer to find a provider, for example: val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding" context.startActivity( Intent(Intent.ACTION_VIEW).apply { setPackage("com.android.vending") data = Uri.parse(uriString) putExtra("overlay", true) putExtra("callerId", context.packageName) } ) return } // Health Connect is available, obtain a HealthConnectClient instance val healthConnectClient = HealthConnectClient.getOrCreate(context) // Issue operations with healthConnectClient }
En fonction de l'état renvoyé par getSdkStatus(), vous pouvez inviter l'utilisateur à installer ou à mettre à jour Santé Connect depuis le Google Play Store si nécessaire.
Disponibilité de la fonctionnalité
Pour déterminer si l'appareil d'un utilisateur est compatible avec les programmes 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
}
Autorisations requises
L'accès à l'exercice planifié est protégé par les autorisations suivantes :
android.permission.health.READ_PLANNED_EXERCISEandroid.permission.health.WRITE_PLANNED_EXERCISE
Pour ajouter la fonctionnalité d'exercice planifié à votre application, commencez par demander des autorisations 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 un exercice planifié, 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 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.
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) )
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.
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class), HealthPermission.getReadPermission(HeartRateRecord::class), HealthPermission.getWritePermission(HeartRateRecord::class) ) val requestPermissionsLauncher = rememberLauncherForActivityResult( contract = PermissionController.createRequestPermissionResultContract() ) { grantedPermissions -> if (grantedPermissions.containsAll(permissions)) { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") } } else { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") } } }
Autorisations associées
Les programmes d'entraînement sont liés aux sessions d'exercice. Par conséquent, l'utilisateur doit autoriser l'utilisation de chaque type d'enregistrement associé à un programme d'entraînement afin d'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 pour écrire la session d'exercice et lire les résultats en vue d'une évaluation ultérieure :
android.permission.health.READ_EXERCISEandroid.permission.health.READ_EXERCISE_ROUTESandroid.permission.health.READ_HEART_RATEandroid.permission.health.WRITE_EXERCISEandroid.permission.health.WRITE_EXERCISE_ROUTEandroid.permission.health.WRITE_HEART_RATE
Toutefois, l'application qui crée des programmes d'entraînement et évalue les performances par rapport à ces programmes n'est souvent pas la même que celle qui consomme les programmes 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 de ces autorisations 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 un enregistrement de session d'exercice planifié
- Titre de la session.
- Liste des blocs d'exercices planifiés.
- Heure de début et de fin de la session.
- Type d'exercice.
- Notes concernant l'activité.
- Métadonnées.
- ID de la session d'exercice terminée : cette valeur est écrite automatiquement une fois qu'une session d'exercice liée à cette session d'exercice planifiée est terminée.
Informations incluses dans un enregistrement de bloc d'exercice planifié
Un bloc d'exercice 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 crunchs cinq fois de suite).
- Description du bloc.
- Liste des étapes d'exercice planifiées.
- Nombre de répétitions.
Informations incluses dans un enregistrement d'étape d'exercice planifiée
- Description de l'étape.
- Catégorie d'exercice.
- Type d'exercice.
- Liste des objectifs de performances.
- Objectif de réalisation.
Agrégations compatibles
Aucune agrégation n'est compatible avec ce type de données.
Exemple d'utilisation
Supposons qu'un utilisateur planifie une course de 90 minutes dans deux jours. Cette course comprendra trois tours autour d'un lac avec une fréquence cardiaque cible comprise entre 90 et 110 bpm.
- Une session 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 planifiés de la course
- Type d'exercice (course à pied)
- Nombre de tours (répétitions)
- Objectif de performances pour la fréquence cardiaque (entre 90 et 110 bpm)
- Ces informations sont regroupées en blocs et étapes d'exercice, puis écrites dans Santé Connect par l'application de programme d'entraînement en tant que
PlannedExerciseSessionRecord. - L'utilisateur effectue la session planifiée (course à pied).
- Les données d'exercice liées à la session sont enregistrées :
- Par un accessoire connecté pendant la session. Par exemple, la fréquence cardiaque.
Ces données sont écrites dans Santé Connect en tant que type d'enregistrement pour 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 en tant que
ExerciseSessionRecord.
- Par un accessoire connecté pendant la session. Par exemple, la fréquence cardiaque.
Ces données sont écrites dans Santé Connect en tant que type d'enregistrement pour 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 dans la session d'exercice planifiée.
Planifier des exercices et définir des objectifs
Un utilisateur peut planifier son exercice à l'avenir et définir des objectifs. Écrivez-le dans Santé Connect en tant que session d'exercice planifiée.
Dans l'exemple décrit dans Exemple d'utilisation, l'utilisateur planifie une course de 90 minutes dans deux jours. Cette course comprendra 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 des sessions d'exercice planifiées dans Santé Connect. Il peut également être trouvé dans le point d'ingestion des intégrations, par exemple avec un service proposant 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. Log.w("HealthConnect", "Write permission for PlannedExerciseSessionRecord not granted.") return } val plannedExerciseSessionRecord = PlannedExerciseSessionRecord( startTime = startTime, endTime = endTime, 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( device = Device(type = Device.Companion.TYPE_PHONE), ), startZoneOffset = null, endZoneOffset = null, ) try { // Attempt to insert the record val response = healthConnectClient.insertRecords(listOf(plannedExerciseSessionRecord)) // If execution reaches here, the insert succeeded. // Safely extract the ID using firstOrNull() val insertedPlannedExerciseSessionId = response.recordIdsList.firstOrNull() if (insertedPlannedExerciseSessionId != null) { Log.d("HealthConnect", "Successfully inserted planned exercise session ID: $insertedPlannedExerciseSessionId") } else { Log.w("HealthConnect", "Insertion succeeded but no record IDs were returned.") } } catch (e: Exception) { // Handle API failures, database errors, or system issues safely without crashing Log.e("HealthConnect", "Failed to insert planned exercise session record", e) }
Enregistrer les données d'exercice et d'activité
Deux jours plus tard, l'utilisateur enregistre la session d'exercice réelle. Écrivez-le dans Santé Connect en tant que session d'exercice.
Dans cet exemple, la durée de la session de l'utilisateur correspond exactement à la durée prévue.
L'extrait de code suivant peut être trouvé dans le gestionnaire de formulaire d'une application qui enregistre des sessions d'exercice dans Santé Connect. Il peut également être trouvé dans les gestionnaires d'ingestion et d'exportation de données pour un accessoire connecté capable de détecter et d'enregistrer des sessions d'exercice.
insertedPlannedExerciseSessionId est réutilisé ici à partir de l'exemple précédent. Dans une application réelle, l'ID serait déterminé par l'utilisateur qui sélectionne une session d'exercice planifiée dans une liste de sessions 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( device = Device(type = Device.Companion.TYPE_PHONE) ) ) val insertedExerciseSessions = healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
Un accessoire connecté 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 éléments principaux de cet extrait de code peuvent être trouvés dans le gestionnaire d'un message provenant d'un accessoire connecté, 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( 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 les 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 de code comme celui-ci se trouve probablement dans une tâche périodique pour é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",
metadata = Metadata.manualEntry()
),
// ... 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,
metadata = Metadata.manualEntry()
)