Antrenman planları

Bu kılavuz, Health Connect 1.1.0-alpha12 veya sonraki sürümlerle uyumludur.

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

Health Connect'in kullanılabilirlik durumunu kontrol etme

Uygulamanız, Health Connect'i kullanmaya çalışmadan önce Health Connect'in kullanıcının cihazında kullanılabilir olduğunu doğrulamalıdır. Health Connect, tüm cihazlara önceden yüklenmemiş veya devre dışı bırakılmış olabilir. HealthConnectClient.getSdkStatus() yöntemini kullanarak kullanılabilirlik durumunu kontrol edebilirsiniz.

Health Connect'in kullanılabilirliğini kontrol etme

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata"
    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
}

getSdkStatus() tarafından döndürülen duruma bağlı olarak, kullanıcıyı gerekirse Google Play Store'dan Health Connect'i yüklemeye veya güncellemeye yönlendirebilirsiniz.

Özelliğin kullanılabilirliği

Bir kullanıcının cihazının Health Connect'teki antrenman planlarını destekleyip desteklemediğini belirlemek için istemcide FEATURE_PLANNED_EXERCISE özelliğinin 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 edinmek için Özellik kullanılabilirliğini kontrol etme bölümünü inceleyin.

Gerekli izinler

Planlanan egzersizlere 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 izin isteyin.

Planlı egzersiz yazabilmek için beyan etmeniz gereken izin şudur:

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

Planlanan egzersizleri 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

Uygulamanız, istemci örneği oluşturduktan sonra kullanıcıdan izin istemelidir. Kullanıcıların diledikleri zaman izin vermelerine veya izinleri reddetmelerine olanak tanınmalıdır. Bunun için gerekli veri türlerine yönelik bir izin grubu oluşturun. Öncelikle, kümedeki izinlerin Android manifest dosyanızda beyan edildiğinden emin olun.

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ızın gerekli izinlere sahip olup olmadığını görmek için getGrantedPermissions yöntemini kullanın. Aksi takdirde bu izinleri istemek için createRequestPermissionResultContract yöntemini kullanın. Health Connect izinleri ekranı açılır.
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.") }
    }
}
Kullanıcılar istedikleri zaman izin verebildikleri veya izinleri iptal edebildikleri için uygulamanızın, izinleri kullanmadan önce her seferinde kontrol etmesi ve iznin kaybedildiği senaryoları yönetmesi gerekir.

Antrenman planları egzersiz seanslarına bağlıdır. Bu nedenle, Health Connect'in bu özelliğinden tam olarak yararlanmak için kullanıcının, antrenman planıyla ilgili her kayıt türünün kullanımına izin vermesi gerekir.

Örneğin, bir antrenman planı bir dizi koşu sırasında kullanıcının nabzını ölçüyorsa egzersiz oturumunu yazmak ve sonuçları daha sonra değerlendirmek üzere okumak için geliştiricinin aşağıdaki izinleri beyan etmesi 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 antrenman planları oluşturan ve performansı planlara göre değerlendiren uygulama, genellikle antrenman 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 gerekmeyebilir. Örneğin, her uygulama türü için yalnızca şu izinlere ihtiyacınız olabilir:

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

Planlı egzersiz seansı kaydında yer alan bilgiler

  • Oturumun başlığı.
  • Planlanan 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.

Planlı egzersiz bloğu kaydında yer alan bilgiler

Planlı egzersiz bloğu, farklı adım gruplarının tekrarlanmasını desteklemek için egzersiz adımlarının listesini içerir (ör. arka arkaya beş kez kol bükme, burpee ve mekik yapma).

Planlı egzersiz adımı kaydında yer alan bilgiler

Desteklenen toplamalar

Bu veri türü için desteklenen toplama 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, gölün etrafında üç tur atılacak ve hedef nabız 90 ile 110 nabız/dk arasında olacak.

  1. Kullanıcı, antrenman planı uygulamasında aşağıdaki bilgileri içeren planlı bir egzersiz oturumu tanımlar:
    1. Çalıştırmanın planlanan başlangıç ve bitiş zamanı
    2. Egzersiz türü (koşu)
    3. Tur sayısı (tekrar sayısı)
    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 cihaz tarafından. Örneğin, nabız. Bu veriler, etkinliğin 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 belirtmek için. 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 göre gerçek performansı değerlendirmek için Health Connect'teki verileri okur.

Egzersizleri planlama ve hedef belirleme

Kullanıcı, gelecekteki egzersizlerini planlayabilir ve hedefler belirleyebilir. Bu bilgiyi Health Connect'e planlı egzersiz oturumu olarak yazın.

Örnek kullanım bölümünde açıklanan örnekte, kullanıcı iki gün sonra 90 dakikalık bir koşu planlıyor. Bu koşuda bir gölün etrafında üç tur atacağız ve hedef nabzımız 90 ile 110 bpm arasında olacak.

Planlanan egzersiz oturumlarını Health Connect'e kaydeden bir uygulamanın form işleyicisinde buna benzer bir snippet bulunabilir. Ayrıca, entegrasyonlar için giriş noktasında da bulunabilir. Örneğin, eğitim sunan bir hizmetle entegrasyon yapılıyorsa.

// 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.activelyRecorded(
        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)
}

Egzersiz ve aktivite verilerini kaydetme

Kullanıcı iki gün sonra gerçek egzersiz seansını kaydeder. Bu verileri Health Connect'e egzersiz seansı olarak yazın.

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 kaydedebilen giyilebilir cihazların veri kullanımı ve dışa aktarma işleyicilerinde de bulunabilir.

Buradaki insertedPlannedExerciseSessionId, önceki örnekten yeniden 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.activelyRecorded(
        device = Device(type = Device.Companion.TYPE_PHONE)
    )
)
val insertedExerciseSessions =
    healthConnectClient.insertRecords(listOf(exerciseSessionRecord))

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

Gerçek bir uygulamada, bu snippet'in birincil parçaları, giyilebilir cihazdan gelen bir mesajın işleyicisinde bulunabilir. Bu işleyici, ölçümü toplandığında 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.activelyRecorded(
        device = Device(type = Device.Companion.TYPE_WATCH)
    )
)
val insertedHeartRateRecords = healthConnectClient.insertRecords(listOf(heartRateRecord))

Performans hedeflerini değerlendirme

Kullanıcının antrenmanından sonraki gün, kaydedilen egzersizi alabilir, planlanan egzersiz hedeflerini kontrol edebilir ve belirlenen hedeflere ulaşılıp ulaşılmadığını belirlemek için ek veri türlerini değerlendirebilirsiniz.

Buna benzer bir snippet, performans hedeflerini değerlendirmek için düzenli olarak çalıştırılan bir işte veya bir egzersiz listesi yüklenip uygulamadaki performans hedefleriyle ilgili 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 seanslarını yazma

Oturum içeren bir ekleme isteği şu şekilde oluşturulur:

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

Egzersiz seansını okuma

Egzersiz seansını okumayla ilgili bir örnek:

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, oturumu ek bilgilerle zenginleştiren isteğe bağlı alt tür verilerinden de oluşabilir.

Örneğin, egzersiz seansları 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,
  metadata = Metadata.manualEntry()
)