Antrenman planları

Bu kılavuz, Health Connect'in 1.1.0-alpha11 sürümüyle uyumludur.

Health Connect, antrenman uygulamalarının antrenman planları yazmasına ve antrenman uygulamalarının antrenman planlarını okumasına olanak tanıyan bir planlı egzersiz veri türü sağlar. Kaydedilen egzersizler (antrenmanlar), kullanıcıların antrenman hedeflerine ulaşmasına yardımcı olmak için kişiselleştirilmiş performans analizi amacıyla tekrar okunabilir.

Özellik kullanılabilirliği

Kullanıcının cihazının Health Connect'te antrenman planlarını destekleyip desteklemediğini belirlemek için istemcide FEATURE_PLANNED_EXERCISE simgesinin kullanılabilirliğini kontrol edin:

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

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

Daha fazla bilgi için Özelliklerin kullanılabilirliğini kontrol etme başlıklı makaleyi inceleyin.

Gerekli izinler

Planlanmış egzersize erişim aşağıdaki izinlerle korunur:

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

Uygulamanıza planlı egzersiz özelliği eklemek için öncelikle PlannedExerciseSession veri türü için yazma izni isteyerek başlayın.

Planlı egzersiz yazabilmek için beyan etmeniz gereken izin aşağıda verilmiştir:

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

Planlanan egzersizi okumak için aşağıdaki izinleri istemeniz gerekir:

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

Kullanıcıdan izin isteme

İstemci örneği oluşturduktan sonra uygulamanızın kullanıcıdan izin istemesi gerekir. Kullanıcıların izinleri istedikleri zaman vermesine veya reddetmesine izin verilmelidir.

Bunun için gerekli veri türleri için bir izin grubu oluşturun. Gruptaki izinlerin önce Android manifestinizde tanımlandığından emin olun.

// 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)
)

Uygulamanıza gerekli izinlerin verilip verilmediğini görmek için getGrantedPermissions aracını kullanın. Aksi takdirde, bu izinleri istemek için createRequestPermissionResultContract simgesini kullanın. Bu işlem, Health Connect izinleri ekranını gösterir.

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

Kullanıcılar izinleri istedikleri zaman verebilir veya iptal edebilir. Bu nedenle, uygulamanızın verilen izinleri düzenli olarak kontrol etmesi ve izinlerin kaybedildiği senaryoları yönetmesi gerekir.

Eğitim planları egzersiz oturumlarına bağlanır. Bu nedenle, kullanıcının Health Connect'in bu özelliğini tam olarak kullanabilmesi için antrenman planıyla ilgili her kayıt türünün kullanılmasına izin vermesi gerekir.

Örneğin, bir antrenman planı bir dizi koşu sırasında kullanıcının kalp atış hızını ölçüyorsa egzersiz oturumunu yazmak ve sonuçları daha sonra değerlendirmek için geliştiricinin aşağıdaki izinleri bildirmesi ve kullanıcının bu izinleri vermesi gerekebilir:

  • 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

Ancak, eğitim planları oluşturan ve performansı planlara göre değerlendiren uygulama genellikle eğitim planlarını kullanan ve gerçek egzersiz verilerini yazan uygulamayla aynı değildir. Uygulamanın türüne bağlı olarak tüm okuma ve yazma izinleri gerekli olmayabilir. Örneğin, her uygulama türü için yalnızca şu izinlere ihtiyacınız olabilir:

Antrenman planı uygulaması Egzersiz uygulaması
WRITE_PLANNED_EXERCISE READ_PLANNED_EXERCISE
READ_EXERCISE WRITE_EXERCISE
READ_EXERCISE_ROUTES WRITE_EXERCISE_ROUTE
READ_HEART_RATE WRITE_HEART_RATE

Planlanmış egzersiz oturumu kaydında yer alan bilgiler

  • Oturumun başlığı.
  • Planlanmış egzersiz bloklarının listesi.
  • Oturumun başlangıç ve bitiş zamanı.
  • Egzersiz türü.
  • Etkinlikle ilgili notlar.
  • Meta veri
  • Tamamlanan egzersiz oturumu kimliği: Bu, planlanan egzersiz oturumuyla ilgili bir egzersiz oturumu tamamlandıktan sonra otomatik olarak yazılır.

Planlanmış egzersiz bloğu kaydında yer alan bilgiler

Planlanmış bir egzersiz bloğu, farklı adım gruplarının tekrarını desteklemek için bir egzersiz adımları listesi içerir (örneğin, kol kıvırma, burpee ve crunch adımlarını beş kez tekrarlayın).

Planlanmış egzersiz adımı kaydında yer alan bilgiler

Desteklenen toplamalar

Bu veri türü için desteklenen toplama işlemi yok.

Örnek kullanım

Bir kullanıcının iki gün sonra 90 dakikalık bir koşu planladığını varsayalım. Bu koşuda, hedef nabız sayısı 90-110 arasında olacak şekilde gölün etrafında üç tur atılacak.

  1. Aşağıdakileri içeren planlanmış bir egzersiz oturumu, kullanıcı tarafından bir eğitim planı uygulamasında tanımlanır:
    1. Çalışmanın planlanan başlangıç ve bitiş zamanı
    2. Egzersiz türü (koşu)
    3. Tur sayısı (tekrarlar)
    4. Nabız için performans hedefi (90 ile 110 nabız/dk. arasında)
  2. Bu bilgiler egzersiz blokları ve adımlar halinde gruplandırılır ve antrenman planı uygulaması tarafından Health Connect'e PlannedExerciseSessionRecord olarak yazılır.
  3. Kullanıcı, planlanan oturumu (koşu) gerçekleştirir.
  4. Oturumla ilgili egzersiz verileri şu şekilde kaydedilir:
    1. Oturum sırasında giyilebilir bir cihazla. Örneğin, nabız. Bu veriler, aktivitenin kayıt türü olarak Health Connect'e yazılır. Bu durumda, HeartRateRecord.
    2. Oturumdan sonra kullanıcı tarafından manuel olarak. Örneğin, gerçek koşunun başlangıcını ve bitişini belirtme. Bu veriler, Health Connect'e ExerciseSessionRecord olarak yazılır.
  5. Daha sonra, antrenman planı uygulaması, planlanan egzersiz oturumunda kullanıcı tarafından belirlenen hedeflere karşı gerçek performansı değerlendirmek için Health Connect'teki verileri okur.

Egzersiz planlama ve hedef belirleme

Kullanıcılar, gelecekteki egzersizlerini planlayabilir ve hedefler belirleyebilir. Bunu Health Connect'e planlanmış egzersiz oturumu olarak yazın.

Kullanım örneği bölümünde açıklanan örnekte, kullanıcı iki gün sonra 90 dakikalık bir koşu planlıyor. Bu koşuda, 90-110 nabız/dk. hedef nabız aralığında bir gölün etrafında üç tur koşulacak.

Buna benzer bir snippet, planlanan egzersiz oturumlarını Health Connect'e kaydeden bir uygulamanın form işleyicisinde bulunabilir. Ayrıca, eğitim sunan bir hizmetle entegrasyon gibi durumlarda alım noktasında da bulunabilir.

// 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()

Egzersiz ve etkinlik verilerini günlüğe kaydetme

İki gün sonra kullanıcı, gerçek egzersiz oturumunu kaydeder. Bunu Health Connect'e egzersiz oturumu olarak yaz.

Bu örnekte, kullanıcının oturum süresi planlanan süreyle tam olarak eşleşti.

Aşağıdaki snippet, egzersiz oturumlarını Health Connect'e kaydeden bir uygulamanın form işleyicisinde bulunabilir. Ayrıca, egzersiz oturumlarını algılayıp günlüğe kaydedebilen bir giyilebilir cihazın veri alımı ve dışa aktarma işleyicilerinde de bulunabilir.

Buradaki insertedPlannedExerciseSessionId, önceki örnekten tekrar kullanılmıştır. Gerçek bir uygulamada, kimlik, kullanıcının mevcut oturumlar listesinden planlanmış bir egzersiz oturumu seçmesiyle belirlenir.

// 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))

Ayrıca, giyilebilir cihaz koşu boyunca kullanıcının nabzını kaydeder. Aşağıdaki snippet, hedef aralıkta kayıt oluşturmak için kullanılabilir.

Gerçek bir uygulamada, bu snippet'in temel parçaları, bir kol saatinden gelen bir mesajın işleyicisinde bulunabilir. Bu işleyici, toplama işleminden sonra ölçümü Health Connect'e yazar.

// 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))

Performans hedeflerini değerlendirme

Kullanıcının egzersizinden sonraki gün, kaydedilen egzersizi alabilir, planlanan egzersiz hedeflerini kontrol edebilir ve belirlenen hedeflerin karşılanıp karşılanmadığını belirlemek için ek veri türlerini değerlendirebilirsiniz.

Buna benzer bir snippet, performans hedeflerini değerlendirmek için düzenli olarak yapılan bir işte veya bir egzersiz listesi yüklenirken ve bir uygulamada performans hedefleriyle ilgili bir bildirim gösterilirken bulunabilir.

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

Egzersiz seansları

Egzersiz seansları koşudan badmintona kadar her şeyi içerebilir.

Egzersiz oturumlarını yazma

Oturum içeren bir ekleme isteği oluşturmak için şu adımları uygulayın:

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
        )
    )
}

Egzersiz oturumunu okuma

Aşağıda, egzersiz oturumunun nasıl okunacağına dair bir örnek verilmiştir:

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

Alt tür verilerini yazma

Oturumlar, oturuma ek bilgiler ekleyerek zenginleştiren isteğe bağlı alt tür verilerinden de oluşabilir.

Örneğin, egzersiz oturumları ExerciseSegment, ExerciseLap ve ExerciseRoute sınıflarını içerebilir:

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
)