Plany treningowe

Ten przewodnik jest zgodny z Health Connect w wersji 1.1.0-alpha11.

Health Connect udostępnia typ danych planowany trening, który umożliwia aplikacjom treningowym zapisywanie planów treningowych i aplikacjom do ćwiczeń odczytywanie planów treningowych. Zapisane treningi można odczytać w celu spersonalizowanej analizy wyników, aby pomóc użytkownikom w osiąganiu celów treningowych.

Dostępność funkcji

Aby sprawdzić, czy urządzenie użytkownika obsługuje plany szkoleniowe w Health Connect, sprawdź dostępność FEATURE_PLANNED_EXERCISE na kliencie:

if (healthConnectClient
     .features
     .getFeatureStatus(
       HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
     ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {

  // Feature is available
} else {
  // Feature isn't available
}

Więcej informacji znajdziesz w sekcji Sprawdzanie dostępności funkcji.

Wymagane uprawnienia

Dostęp do planów treningowych jest chroniony przez te uprawnienia:

  • android.permission.health.READ_PLANNED_EXERCISE
  • android.permission.health.WRITE_PLANNED_EXERCISE

Zadeklaruj te uprawnienia w Konsoli Play dla swojej aplikacji, a także w pliku manifestu aplikacji:

<application>
  <uses-permission
android:name="android.permission.health.READ_PLANNED_EXERCISE" />
  <uses-permission
android:name="android.permission.health.WRITE_PLANNED_EXERCISE" />
...
</application>

Użytkownik jest odpowiedzialny za zadeklarowanie wszystkich odpowiednich uprawnień, których zamierza używać na urządzeniach i w aplikacjach. Przed użyciem musisz też sprawdzić, czy użytkownik przyznał Ci odpowiednie uprawnienia.

Plany treningowe są powiązane z sesjami ćwiczeń. Aby w pełni korzystać z tej funkcji Health Connect, użytkownik musi zezwolić na używanie każdego typu rekordu związanego z planem szkoleniowym.

Jeśli na przykład plan treningowy mierzy tętno użytkownika podczas serii biegów, deweloper może być zobowiązany do zadeklarowania tych uprawnień i przyznania ich przez użytkownika, aby zapisać sesję ćwiczeń i odczytać wyniki w celu późniejszej oceny:

  • android.permission.health.READ_EXERCISE
  • android.permission.health.READ_EXERCISE_ROUTE
  • 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 ich skuteczność, nie jest tą samą aplikacją, która korzysta z planów treningowych i zapisuje dane dotyczące rzeczywistych ćwiczeń. W zależności od typu aplikacji nie wszystkie uprawnienia odczytu i zapisu będą potrzebne. Na przykład w przypadku każdego typu aplikacji mogą być potrzebne tylko te uprawnienia:

Aplikacja z planem treningowym Aplikacja do treningu
WRITE_PLANNED_EXERCISE READ_PLANNED_EXERCISE
READ_EXERCISE WRITE_EXERCISE
READ_EXERCISE_ROUTE 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.
  • Uwagi dotyczące aktywności.
  • Metadane
  • Identyfikator ukończonej sesji ćwiczeń – jest zapisywany automatycznie po zakończeniu sesji ćwiczeń powiązanej z tą zaplanowaną sesją ćwiczeń.

Informacje zawarte w rejestrze zaplanowanego bloku ćwiczeń

Zaplanowany blok ćwiczeń zawiera listę ćwiczeń, które można powtarzać w różnych grupach (np. 5 razy z rzędu wykonać sekwencję ćwiczeń na bicepsy, burpee i skłony tułowi).

Informacje zawarte w rekordzie zaplanowanego kroku ćwiczenia

Obsługiwane agregacje

W przypadku tego typu danych nie ma obsługiwanych agregacji.

Przykład użycia

Załóżmy, że użytkownik planuje 90-minutowy bieg za 2 dni. W ramach tego biegu wykonasz 3 okrążenia wokół jeziora z docelowym tętnem w zakresie 90–110 uderzeń na minutę.

  1. W aplikacji z planem treningowym użytkownik definiuje zaplanowaną sesję ćwiczeń:
    1. planowany początek i koniec biegu,
    2. Typ ćwiczenia (bieg)
    3. Liczba okrążeń (powtórzeń)
    4. Docelowy wynik tętna (między 90 a 110 uderzeń na minutę)
  2. Informacje te są grupowane w bloki ćwiczeń i kroki, a następnie zapisywane w Health Connect przez aplikację z planem treningowym jako PlannedExerciseSessionRecord.
  3. Użytkownik wykonuje zaplanowaną sesję (bieg).
  4. Dane dotyczące ćwiczeń związane z sesją są rejestrowane w ten sposób:
    1. 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.
    2. Ręcznie przez użytkownika po zakończeniu sesji. Na przykład wskazujące początek i koniec rzeczywistego wykonania. Te dane są zapisywane w Health Connect jako ExerciseSessionRecord.
  5. Później aplikacja z planem treningowym odczytuje dane z Health Connect, aby ocenić rzeczywiste wyniki w porównaniu z docelami określonymi przez użytkownika w planowanej sesji ćwiczeń.

Planowanie ćwiczeń i ustalanie celów

Użytkownik może zaplanować ćwiczenia na przyszłość i ustawić cele. Zapisz to w Health Connect jako planowaną sesję ćwiczeń.

W przykładzie opisanym w sekcji Przykładowe zastosowanie użytkownik planuje 90-minutowy bieg za 2 dni. W ramach tego treningu wykonasz 3 okrążenia wokół jeziora z docelowym tętnem w zakresie 90–110 uderzeń na minutę.

Taki fragment kodu możesz znaleźć w obiekcie obsługującym formularz w aplikacji, która rejestruje zaplanowane sesje ćwiczeń w Health Connect. Może się ona też znajdować w najlepszym miejscu na potrzeby integracji, np. z usługą, która oferuje szkolenia.

// Ensure 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 = ExerciseSessionRecord.EXERCISE_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
)
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 informacje w Health Connect jako sesję ćwiczeń.

W tym przykładzie czas trwania sesji użytkownika dokładnie odpowiadał zaplanowanemu czasowi.

Ten fragment kodu może znajdować się w obiekcie obsługującym formularz w aplikacji, która rejestruje sesje ćwiczeń w Health Connect. Może się ona też znajdować w elementach obsługi importowania i eksportowania danych w przypadku urządzeń noszonych, które są w stanie wykrywać i rejestrować sesje ćwiczeń.

Wartość insertedPlannedExerciseSessionId jest tu ponownie użyta z poprzedniego przykładu. W prawdziwej aplikacji identyfikator jest określany przez użytkownika, który wybiera zaplanowany trening z listy istniejących treningów.

// Ensure 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,
)
val insertedExerciseSessions =
    healthConnectClient.insertRecords(listOf(exerciseSessionRecord))

Urządzenie noszone rejestruje również tętno podczas biegu. Do generowania rekordów w zakresie docelowym można użyć tego fragmentu kodu.

W rzeczywistej aplikacji główne elementy tego fragmentu kodu można znaleźć w obiekcie obsługującym dla wiadomości z urządzenia do noszenia, który po zebraniu danych zapisze je w Health Connect.

// Ensure 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,
)
val insertedHeartRateRecords = healthConnectClient.insertRecords(listOf(heartRateRecord))

Ocena celów skuteczności

Dzień po treningu użytkownika możesz pobrać zarejestrowany trening, sprawdzić zaplanowane cele treningowe i przeanalizować dodatkowe typy danych, aby określić, czy cele zostały osiągnięte.

Taki fragment kodu znajdziesz prawdopodobnie w okresowym zadaniu służącym do oceny celów skuteczności lub podczas wczytywania listy ćwiczeń i wyświetlania powiadomienia o celach skuteczności w aplikacji.

// Ensure 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
                            }
                        }
                    }
                }
            }
        }
    }
}