Diese Anleitung ist mit Health Connect-Version 1.1.0-alpha11 kompatibel.
Health Connect bietet den Datentyp Geplantes Training, damit Trainings-Apps Trainingspläne schreiben und Workout-Apps Trainingspläne lesen können. Aufgezeichnete Trainings können für personalisierte Leistungsanalysen zurückgelesen werden, um Nutzern beim Erreichen ihrer Trainingsziele zu helfen.
Verfügbarkeit der Funktion
Wenn du herausfinden möchtest, ob das Gerät eines Nutzers Trainingspläne in Health Connect unterstützt, prüfe die Verfügbarkeit vonFEATURE_PLANNED_EXERCISE
auf dem Client:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Erforderliche Berechtigungen
Der Zugriff auf geplante Trainings wird durch die folgenden Berechtigungen geschützt:
android.permission.health.READ_PLANNED_EXERCISE
android.permission.health.WRITE_PLANNED_EXERCISE
Wenn du deiner App die Funktion für geplante Trainings hinzufügen möchtest, musst du zuerst Schreibberechtigungen für den Datentyp PlannedExerciseSession
anfordern.
Hier ist die Berechtigung, die du deklarieren musst, um geplante Trainings schreiben zu können:
<application>
<uses-permission
android:name="android.permission.health.WRITE_PLANNED_EXERCISE" />
...
</application>
Zum Lesen geplanter Trainings benötigen Sie die folgenden Berechtigungen:
<application>
<uses-permission
android:name="android.permission.health.READ_PLANNED_EXERCISE" />
...
</application>
Berechtigungen vom Nutzer anfordern
Nachdem Sie eine Client-Instanz erstellt haben, muss Ihre App Berechtigungen vom Nutzer anfordern. Nutzer müssen jederzeit die Möglichkeit haben, Berechtigungen zu erteilen oder zu verweigern.
Erstellen Sie dazu eine Reihe von Berechtigungen für die erforderlichen Datentypen. Achten Sie darauf, dass die Berechtigungen im Set zuerst in Ihrem Android-Manifest deklariert werden.
// 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)
)
Mit getGrantedPermissions
können Sie prüfen, ob Ihrer App bereits die erforderlichen Berechtigungen erteilt wurden. Falls nicht, verwenden Sie createRequestPermissionResultContract
, um diese Berechtigungen anzufordern. Dadurch wird der Bildschirm mit den Health Connect-Berechtigungen angezeigt.
// 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)
}
}
Da Nutzer Berechtigungen jederzeit erteilen oder widerrufen können, muss Ihre App regelmäßig prüfen, welche Berechtigungen erteilt wurden, und Szenarien berücksichtigen, in denen Berechtigungen verloren gehen.
Zugehörige Berechtigungen
Trainingspläne sind mit Trainingseinheiten verknüpft. Daher muss der Nutzer die Berechtigung zur Verwendung jedes Datensatztyps im Zusammenhang mit einem Trainingsplan erteilen, um diese Health Connect-Funktion vollständig nutzen zu können.
Wenn in einem Trainingsplan beispielsweise die Herzfrequenz eines Nutzers während einer Reihe von Läufen gemessen wird, müssen die folgenden Berechtigungen vom Entwickler deklariert und vom Nutzer gewährt werden, damit die Trainingseinheit geschrieben und die Ergebnisse für die spätere Auswertung gelesen werden können:
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
Oft ist die App, die Trainingspläne erstellt und die Leistung anhand von Plänen bewertet, jedoch nicht dieselbe wie die App, die Trainingspläne verwendet und tatsächliche Trainingsdaten schreibt. Je nach App-Typ sind nicht alle Lese- und Schreibberechtigungen erforderlich. Beispiel: Möglicherweise benötigen Sie für die einzelnen App-Typen nur die folgenden Berechtigungen:
App für Trainingspläne | Trainings-App |
---|---|
WRITE_PLANNED_EXERCISE |
READ_PLANNED_EXERCISE |
READ_EXERCISE |
WRITE_EXERCISE |
READ_EXERCISE_ROUTES |
WRITE_EXERCISE_ROUTE |
READ_HEART_RATE |
WRITE_HEART_RATE |
Informationen in einem Eintrag für eine geplante Trainingseinheit
- Titel der Sitzung.
- Eine Liste der geplanten Trainingsblöcke.
- Start- und Endzeit der Sitzung.
- Trainingstyp
- Notizen zur Aktivität.
- Metadaten
- ID der abgeschlossenen Trainingseinheit: Diese wird automatisch geschrieben, nachdem eine Trainingseinheit abgeschlossen wurde, die sich auf diese geplante Trainingseinheit bezieht.
Informationen in einem geplanten Trainingsblock
Ein geplanter Trainingsblock enthält eine Liste von Trainingsschritten, um die Wiederholung verschiedener Gruppen von Schritten zu unterstützen (z. B. eine Abfolge von Armbeugen, Burpees und Crunches fünfmal hintereinander).
- Beschreibung des Blocks.
- Eine Liste der geplanten Trainingsschritte.
- Anzahl der Wiederholungen.
Informationen in einem geplanten Trainingsschritt-Eintrag
- Beschreibung des Schritts.
- Trainingskategorie:
- Trainingstyp:
- Eine Liste mit Leistungszielen.
- Zielvorhaben für Abschlüsse:
Unterstützte Aggregationen
Für diesen Datentyp sind keine unterstützten Aggregationen vorhanden.
Beispiel für die Verwendung
Angenommen, ein Nutzer plant einen 90-minütigen Lauf in zwei Tagen. Dieser Lauf besteht aus drei Runden um einen See mit einer Zielherzfrequenz zwischen 90 und 110 bpm.
- Eine geplante Trainingseinheit mit den folgenden Angaben wird vom Nutzer in einer Trainingsplan-App definiert:
- Geplanter Start und geplantes Ende des Laufs
- Die Art des Trainings (Laufen)
- Anzahl der Runden (Wiederholungen)
- Leistungsziel für die Herzfrequenz (zwischen 90 und 110 bpm)
- Diese Informationen werden in Trainingsblöcke und Schritte gruppiert und von der Trainingsplan-App als
PlannedExerciseSessionRecord
in Health Connect geschrieben. - Der Nutzer führt die geplante Trainingseinheit (Laufen) durch.
- Trainingsdaten im Zusammenhang mit der Sitzung werden entweder so aufgezeichnet:
- Von einem Wearable während der Trainingseinheit. Zum Beispiel die Herzfrequenz.
Diese Daten werden als Datensatztyp für die Aktivität in Health Connect gespeichert. In diesem Fall ist das
HeartRateRecord
. - Manuell vom Nutzer nach der Sitzung. Geben Sie beispielsweise den Beginn und das Ende des tatsächlichen Laufs an. Diese Daten werden als
ExerciseSessionRecord
in Health Connect gespeichert.
- Von einem Wearable während der Trainingseinheit. Zum Beispiel die Herzfrequenz.
Diese Daten werden als Datensatztyp für die Aktivität in Health Connect gespeichert. In diesem Fall ist das
- Später liest die Trainingsplan-App Daten aus Health Connect, um die tatsächliche Leistung mit den vom Nutzer in der geplanten Trainingseinheit festgelegten Zielen zu vergleichen.
Trainings planen und Ziele festlegen
Nutzer können ihr Training in der Zukunft planen und Ziele festlegen. Schreibe dies als geplantes Training in Health Connect.
Im Beispiel unter Beispiel für die Verwendung plant der Nutzer einen 90-minütigen Lauf in zwei Tagen. Dieser Lauf besteht aus drei Runden um einen See mit einer Zielherzfrequenz zwischen 90 und 110 Schlägen pro Minute.
Ein solcher Ausschnitt kann im Formular-Handler für eine App zu finden sein, die geplante Trainingseinheiten in Health Connect protokolliert. Sie könnte auch am Aufnahmepunkt für Integrationen zu finden sein, z. B. bei einem Dienst, der Training anbietet.
// 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()
Trainings- und Aktivitätsdaten aufzeichnen
Zwei Tage später protokolliert der Nutzer das tatsächliche Training. Schreibe dies als Trainingssitzung in Health Connect.
In diesem Beispiel entspricht die Sitzungsdauer des Nutzers genau der geplanten Dauer.
Das folgende Snippet kann im Formular-Handler für eine App gefunden werden, die Trainingseinheiten in Health Connect protokolliert. Sie kann auch in Handlern für die Aufnahme und den Export von Daten für ein Wearable gefunden werden, das Trainingseinheiten erkennen und protokollieren kann.
insertedPlannedExerciseSessionId
wird hier aus dem vorherigen Beispiel wiederverwendet. In einer echten App würde die ID dadurch bestimmt, dass der Nutzer eine geplante Trainingseinheit aus einer Liste vorhandener Einheiten auswählt.
// 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))
Ein Wearable zeichnet auch die Herzfrequenz während des gesamten Laufs auf. Mit dem folgenden Snippet können Datensätze im Zielbereich generiert werden.
In einer echten App sind die primären Teile dieses Snippets möglicherweise im Handler für eine Nachricht von einem Wearable zu finden, der die Messung nach der Erfassung in Health Connect schreibt.
// 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))
Leistungsziele auswerten
Am Tag nach dem Training des Nutzers können Sie das protokollierte Training abrufen, nach geplanten Trainingszielen suchen und zusätzliche Datentypen auswerten, um festzustellen, ob die festgelegten Ziele erreicht wurden.
Ein solcher Code-Snippet würde wahrscheinlich in einem periodischen Job zum Auswerten von Leistungszielen oder beim Laden einer Liste von Übungen und Anzeigen einer Benachrichtigung zu Leistungszielen in einer App verwendet.
// 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
}
}
}
}
}
}
}
}
Trainingseinheiten
Zu den Trainingseinheiten können alle Aktivitäten gehören, vom Laufen bis zum Badminton.
Trainingseinheiten schreiben
So erstellen Sie eine Einfügungsanfrage, die eine Sitzung enthält:
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
)
)
}
Trainingssitzung lesen
Hier ein Beispiel dafür, wie eine Trainingseinheit gelesen wird:
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
}
}
Untertypdaten schreiben
Sitzungen können auch optionale Untertypdaten enthalten, die die Sitzung mit zusätzlichen Informationen anreichern.
Trainings können beispielsweise die Klassen ExerciseSegment
, ExerciseLap
und ExerciseRoute
enthalten:
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
)