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 istemcideFEATURE_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
}
Gerekli izinler
Planlanan egzersizlere erişim aşağıdaki izinlerle korunur:
android.permission.health.READ_PLANNED_EXERCISEandroid.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) )
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.") } } }
İlgili izinler
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_EXERCISEandroid.permission.health.READ_EXERCISE_ROUTESandroid.permission.health.READ_HEART_RATEandroid.permission.health.WRITE_EXERCISEandroid.permission.health.WRITE_EXERCISE_ROUTEandroid.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).
- Blok açıklaması.
- Planlı egzersiz adımlarının listesi.
- Tekrar sayısı.
Planlı egzersiz adımı kaydında yer alan bilgiler
- Adımın açıklaması.
- Egzersiz kategorisi.
- Egzersiz türü.
- Performans hedefleri listesi.
- Tamamlama hedefi.
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.
- Kullanıcı, antrenman planı uygulamasında aşağıdaki bilgileri içeren planlı bir egzersiz oturumu tanımlar:
- Çalıştırmanın planlanan başlangıç ve bitiş zamanı
- Egzersiz türü (koşu)
- Tur sayısı (tekrar sayısı)
- Nabız için performans hedefi (90 ile 110 nabız/dk arasında)
- Bu bilgiler, egzersiz blokları ve adımları halinde gruplandırılır ve antrenman planı uygulaması tarafından Health Connect'e
PlannedExerciseSessionRecordolarak yazılır. - Kullanıcı, planlanan oturumu (koşu) gerçekleştirir.
- Oturumla ilgili egzersiz verileri şu şekilde kaydedilir:
- 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. - 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
ExerciseSessionRecordolarak yazılır.
- 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
- 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()
)