Ten przewodnik jest zgodny z wersją Health Connect 1.1.0-alpha11.
Health Connect udostępnia typ danych zaplanowane ćwiczenia, aby umożliwić aplikacjom do treningu zapisywanie planów treningowych, a aplikacjom do ćwiczeń odczytywanie tych planów. Zarejestrowane ćwiczenia (treningi) można odczytywać w celu spersonalizowanej analizy wydajności, aby pomagać użytkownikom w osiąganiu celów treningowych.
Sprawdzanie dostępności Health Connect
Zanim spróbujesz użyć Health Connect, sprawdź, czy jest on dostępny na urządzeniu użytkownika. Aplikacja Health Connect może nie być wstępnie zainstalowana na wszystkich urządzeniach lub może być wyłączona.
Dostępność możesz sprawdzić za pomocą HealthConnectClient.getSdkStatus()
metody.
Jak sprawdzić dostępność Health 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 }
W zależności od stanu zwróconego przez getSdkStatus()
możesz w razie potrzeby poprosić użytkownika o zainstalowanie lub zaktualizowanie Health Connect ze Sklepu Google Play.
Dostępność funkcji
Aby sprawdzić, czy urządzenie użytkownika obsługuje plany treningowe w Health Connect, sprawdź dostępnośćFEATURE_PLANNED_EXERCISE
w aplikacji:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Wymagane uprawnienia
Dostęp do zaplanowanych ćwiczeń jest chroniony przez te uprawnienia:
android.permission.health.READ_PLANNED_EXERCISE
android.permission.health.WRITE_PLANNED_EXERCISE
Aby dodać do aplikacji możliwość planowania ćwiczeń, zacznij od poproszenia o uprawnienia do zapisu w przypadku typu danych PlannedExerciseSession
.
Aby móc zapisywać zaplanowane ćwiczenia, musisz zadeklarować to uprawnienie:
<application>
<uses-permission
android:name="android.permission.health.WRITE_PLANNED_EXERCISE" />
...
</application>
Aby odczytać zaplanowane ćwiczenie, musisz poprosić o te uprawnienia:
<application>
<uses-permission
android:name="android.permission.health.READ_PLANNED_EXERCISE" />
...
</application>
Prośba użytkownika o uprawnienia
Po utworzeniu instancji klienta aplikacja musi poprosić użytkownika o uprawnienia. Użytkownicy muszą mieć możliwość przyznania lub odmowy przyznania uprawnień w dowolnym momencie.
Aby to zrobić, utwórz zestaw uprawnień dla wymaganych typów danych. Sprawdź, czy uprawnienia w zestawie są najpierw zadeklarowane w pliku manifestu Androida.
// 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)
)
Użyj getGrantedPermissions
, aby sprawdzić, czy Twoja aplikacja ma już przyznane wymagane uprawnienia. Jeśli nie, użyj createRequestPermissionResultContract
, aby poprosić o te uprawnienia. Wyświetli się ekran uprawnień Health Connect.
// 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)
}
}
Użytkownicy mogą w dowolnym momencie przyznawać i wycofywać uprawnienia, dlatego aplikacja musi okresowo sprawdzać, czy uprawnienia zostały przyznane, i obsługiwać sytuacje, w których uprawnienia zostaną utracone.
Powiązane uprawnienia
Plany treningowe są powiązane z sesjami ćwiczeń. Dlatego użytkownik musi zezwolić na używanie każdego typu rekordu związanego z planem treningowym, aby w pełni korzystać z tej funkcji Health Connect.
Jeśli na przykład plan treningowy mierzy tętno użytkownika podczas serii biegów, deweloper może musieć zadeklarować te uprawnienia, a użytkownik może musieć je przyznać, aby zapisać sesję ćwiczeń i odczytać wyniki do późniejszej oceny:
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
Często jednak aplikacja, która tworzy plany treningowe i ocenia skuteczność na ich podstawie, nie jest tą samą aplikacją, która wykorzystuje plany treningowe i zapisuje rzeczywiste dane o ćwiczeniach. W zależności od typu aplikacji nie wszystkie uprawnienia do odczytu i zapisu będą potrzebne. Na przykład w przypadku poszczególnych typów aplikacji możesz potrzebować tylko tych uprawnień:
Aplikacja z planem treningowym | Aplikacja do ćwiczeń |
---|---|
WRITE_PLANNED_EXERCISE |
READ_PLANNED_EXERCISE |
READ_EXERCISE |
WRITE_EXERCISE |
READ_EXERCISE_ROUTES |
WRITE_EXERCISE_ROUTE |
READ_HEART_RATE |
WRITE_HEART_RATE |
Informacje zawarte w rekordzie zaplanowanej sesji ćwiczeń
- Tytuł sesji.
- Lista zaplanowanych bloków ćwiczeń.
- czas rozpoczęcia i zakończenia sesji;
- Typ ćwiczenia.
- Notatki dotyczące aktywności.
- Metadane
- Identyfikator ukończonej sesji ćwiczeń – jest zapisywany automatycznie po ukończeniu sesji ćwiczeń powiązanej z tą zaplanowaną sesją.
Informacje zawarte w rekordzie zaplanowanego bloku ćwiczeń
Zaplanowany blok ćwiczeń zawiera listę kroków ćwiczeń, aby umożliwić powtarzanie różnych grup kroków (np. wykonanie sekwencji ugięć ramion, burpees i brzuszków 5 razy z rzędu).
- Opis bloku.
- Lista zaplanowanych kroków ćwiczenia.
- Liczba powtórzeń.
Informacje zawarte w rekordzie zaplanowanego kroku ćwiczenia
- Opis kroku.
- Kategoria ćwiczenia.
- Typ ćwiczenia
- Lista celów skuteczności.
- Cel ukończenia
Obsługiwane agregacje
W przypadku tego typu danych nie ma obsługiwanych agregacji.
Przykład użycia
Załóżmy, że użytkownik zaplanował 90-minutowy bieg za 2 dni. Ten bieg będzie obejmować 3 okrążenia wokół jeziora z docelowym tętnem od 90 do 110 uderzeń na minutę.
- Zaplanowana sesja ćwiczeń z tymi parametrami jest definiowana przez użytkownika w aplikacji do planowania treningów:
- Planowany początek i koniec przejazdu
- Rodzaj ćwiczenia (bieganie)
- Liczba okrążeń (powtórzeń)
- Docelowe tętno (90–110 uderz./min)
- Te informacje są grupowane w bloki ćwiczeń i kroków, a następnie zapisywane w Health Connect przez aplikację planu treningowego jako
PlannedExerciseSessionRecord
. - Użytkownik przeprowadza zaplanowaną sesję (bieganie).
- Dane ćwiczeń związane z sesją są rejestrowane w jeden z tych sposobów:
- przez urządzenie do noszenia podczas sesji; Na przykład tętno.
Te dane są zapisywane w Health Connect jako typ rekordu aktywności. W tym przypadku
HeartRateRecord
. - ręcznie przez użytkownika po zakończeniu sesji; Na przykład wskazanie początku i końca rzeczywistego biegu. Te dane są zapisywane w Health Connect jako
ExerciseSessionRecord
.
- przez urządzenie do noszenia podczas sesji; Na przykład tętno.
Te dane są zapisywane w Health Connect jako typ rekordu aktywności. W tym przypadku
- Później aplikacja z planem treningowym odczytuje dane z Health Connect, aby ocenić rzeczywiste wyniki w porównaniu z celami wyznaczonymi przez użytkownika w zaplanowanej sesji ćwiczeń.
Planowanie ćwiczeń i wyznaczanie celów
Użytkownik może zaplanować ćwiczenia na przyszłość i ustawić cele. Zapisz to w Health Connect jako zaplanowaną sesję ćwiczeń.
W przykładzie opisanym w sekcji Przykłady użycia użytkownik planuje 90-minutowy bieg za 2 dni. Ten bieg będzie obejmować 3 okrążenia wokół jeziora z docelowym tętnem od 90 do 110 uderzeń na minutę.
Fragment kodu podobny do tego może znajdować się w programie obsługi formularzy aplikacji, która rejestruje zaplanowane sesje ćwiczeń w Health Connect. Może się też znajdować w punkcie odbioru danych na potrzeby integracji, np. z usługą oferującą szkolenia.
// 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()
Zapisywanie danych o ćwiczeniach i aktywności
Dwa dni później użytkownik rejestruje rzeczywistą sesję ćwiczeń. Zapisz te dane w Health Connect jako sesję ćwiczeń.
W tym przykładzie czas trwania sesji użytkownika dokładnie odpowiadał zaplanowanemu czasowi trwania.
Poniższy fragment kodu może znajdować się w programie obsługi formularzy aplikacji, która rejestruje sesje ćwiczeń w Health Connect. Może się też znajdować w programach do pozyskiwania i eksportowania danych na urządzeniu do noszenia, które wykrywa i rejestruje sesje ćwiczeń.
insertedPlannedExerciseSessionId
jest ponownie używane z poprzedniego przykładu. W prawdziwej aplikacji identyfikator byłby określany przez użytkownika, który wybiera zaplanowaną sesję ćwiczeń z listy istniejących sesji.
// 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))
Urządzenie do noszenia rejestruje też tętno podczas biegu. Poniższy fragment kodu może służyć do generowania rekordów w zakresie docelowym.
W prawdziwej aplikacji główne elementy tego fragmentu kodu mogą znajdować się w procedurze obsługi wiadomości z urządzenia do noszenia, która po zebraniu danych zapisuje pomiary w Health Connect.
// 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))
Ocena celów skuteczności
Następnego dnia po treningu użytkownika możesz pobrać zarejestrowane ćwiczenie, sprawdzić, czy są jakieś zaplanowane cele ćwiczeń, i ocenić dodatkowe typy danych, aby określić, czy ustawione cele zostały osiągnięte.
Taki fragment kodu można znaleźć w zadaniu okresowym, które służy do oceny celów skuteczności, lub podczas wczytywania listy ćwiczeń i wyświetlania w aplikacji powiadomienia o celach skuteczności.
// 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
}
}
}
}
}
}
}
}
Sesje ćwiczeń
Sesje ćwiczeń mogą obejmować wszystko, od biegania po badmintona.
Zapisywanie sesji ćwiczeń
Oto jak utworzyć żądanie wstawienia, które zawiera sesję:
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
)
)
}
Odczytywanie sesji ćwiczeń
Oto przykład odczytywania sesji ćwiczeń:
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
}
}
Zapisywanie danych podtypu
Sesje mogą też zawierać opcjonalne dane podtypu, które wzbogacają sesję o dodatkowe informacje.
Na przykład sesje ćwiczeń mogą obejmować zajęcia ExerciseSegment
, ExerciseLap
i 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
)