Panduan ini kompatibel dengan Health Connect versi 1.1.0-alpha11.
Health Connect menyediakan jenis data latihan yang direncanakan untuk memungkinkan aplikasi pelatihan menulis rencana pelatihan dan memungkinkan aplikasi latihan membaca rencana pelatihan. Latihan (olahraga) yang direkam dapat dibaca kembali untuk analisis performa yang dipersonalisasi guna membantu pengguna mencapai target pelatihan mereka.
Ketersediaan fitur
Untuk menentukan apakah perangkat pengguna mendukung rencana latihan di Health Connect, periksa ketersediaanFEATURE_PLANNED_EXERCISE
di klien:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Lihat Memeriksa ketersediaan fitur untuk mempelajari lebih lanjut.
Izin yang diperlukan
Akses ke latihan fisik yang direncanakan dilindungi oleh izin berikut:
android.permission.health.READ_PLANNED_EXERCISE
android.permission.health.WRITE_PLANNED_EXERCISE
Untuk menambahkan kemampuan latihan fisik terencana ke aplikasi Anda, mulailah dengan meminta
izin tulis untuk jenis data PlannedExerciseSession
.
Berikut adalah izin yang harus Anda deklarasikan agar dapat menulis latihan yang direncanakan:
<application>
<uses-permission
android:name="android.permission.health.WRITE_PLANNED_EXERCISE" />
...
</application>
Untuk membaca latihan fisik yang direncanakan, Anda perlu meminta izin berikut:
<application>
<uses-permission
android:name="android.permission.health.READ_PLANNED_EXERCISE" />
...
</application>
Meminta izin dari pengguna
Setelah membuat instance klien, aplikasi Anda perlu meminta izin dari pengguna. Pengguna harus diizinkan untuk memberikan atau menolak izin setiap saat.
Untuk melakukannya, buat kumpulan izin untuk jenis data yang diperlukan. Pastikan izin dalam kumpulan dinyatakan dalam manifes Android Anda terlebih dahulu.
// 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)
)
Gunakan getGrantedPermissions
untuk mengetahui apakah aplikasi Anda sudah
mendapatkan izin yang diperlukan. Jika belum, gunakan
createRequestPermissionResultContract
untuk meminta
izin tersebut. Tindakan ini akan menampilkan layar izin 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)
}
}
Karena pengguna dapat memberikan atau mencabut izin kapan saja, aplikasi Anda harus memeriksa izin yang diberikan secara berkala dan menangani skenario saat izin tersebut hilang.
Izin terkait
Rencana latihan dikaitkan dengan sesi latihan. Oleh karena itu, pengguna harus memberikan izin untuk menggunakan setiap jenis data yang terkait dengan rencana latihan agar dapat memanfaatkan fitur Health Connect ini sepenuhnya.
Misalnya, jika rencana latihan mengukur detak jantung pengguna selama serangkaian lari, izin berikut mungkin perlu dideklarasikan oleh developer dan diberikan oleh pengguna untuk menulis sesi latihan dan membaca hasilnya untuk evaluasi selanjutnya:
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
Namun, sering kali aplikasi yang membuat rencana pelatihan dan mengevaluasi performa terhadap rencana tidak sama dengan aplikasi yang menggunakan rencana pelatihan dan menulis data latihan fisik yang sebenarnya. Bergantung pada jenis aplikasi, tidak semua izin baca dan tulis diperlukan. Misalnya, Anda mungkin hanya memerlukan izin ini untuk setiap jenis aplikasi:
Aplikasi rencana pelatihan | Aplikasi latihan fisik |
---|---|
WRITE_PLANNED_EXERCISE |
READ_PLANNED_EXERCISE |
READ_EXERCISE |
WRITE_EXERCISE |
READ_EXERCISE_ROUTES |
WRITE_EXERCISE_ROUTE |
READ_HEART_RATE |
WRITE_HEART_RATE |
Informasi yang disertakan dalam catatan sesi latihan fisik yang direncanakan
- Judul sesi.
- Daftar blok latihan yang direncanakan.
- Waktu mulai dan berakhir sesi.
- Jenis latihan.
- Catatan untuk aktivitas.
- Metadata.
- ID sesi latihan yang telah selesai — ID ini ditulis secara otomatis setelah sesi latihan yang terkait dengan sesi latihan yang direncanakan ini selesai.
Informasi yang disertakan dalam catatan blok latihan yang direncanakan
Blok latihan yang direncanakan berisi daftar langkah latihan, untuk mendukung pengulangan berbagai kelompok langkah (misalnya, lakukan urutan latihan angkat beban lengan, burpee, dan crunch lima kali berturut-turut).
- Deskripsi blok.
- Daftar langkah latihan yang direncanakan.
- Jumlah pengulangan.
Informasi yang disertakan dalam catatan langkah latihan yang direncanakan
- Deskripsi langkah.
- Kategori latihan.
- Jenis latihan.
- Daftar sasaran performa.
- Sasaran penyelesaian.
Agregasi yang didukung
Tidak ada agregasi yang didukung untuk jenis data ini.
Contoh penggunaan
Misalnya, pengguna merencanakan lari selama 90 menit dua hari dari sekarang. Latihan ini akan menampilkan tiga putaran di sekitar danau dengan target detak jantung antara 90 dan 110 bpm.
- Sesi latihan yang direncanakan dengan hal berikut ditentukan oleh pengguna di
aplikasi rencana pelatihan:
- Awal dan akhir lari yang direncanakan
- Jenis latihan (berlari)
- Jumlah putaran (pengulangan)
- Target performa untuk detak jantung (antara 90 dan 110 bpm)
- Informasi ini dikelompokkan ke dalam blok latihan dan langkah serta ditulis
ke Health Connect oleh aplikasi rencana latihan sebagai
PlannedExerciseSessionRecord
. - Pengguna melakukan sesi terencana (berjalan).
- Data latihan terkait sesi dicatat dengan:
- Oleh perangkat wearable selama sesi. Misalnya, detak jantung.
Data ini ditulis ke Health Connect sebagai jenis catatan untuk
aktivitas. Dalam hal ini,
HeartRateRecord
. - Secara manual oleh pengguna setelah sesi. Misalnya, menunjukkan
awal dan akhir proses sebenarnya. Data ini ditulis ke Health
Connect sebagai
ExerciseSessionRecord
.
- Oleh perangkat wearable selama sesi. Misalnya, detak jantung.
Data ini ditulis ke Health Connect sebagai jenis catatan untuk
aktivitas. Dalam hal ini,
- Di lain waktu, aplikasi rencana pelatihan membaca data dari Health Connect untuk mengevaluasi performa sebenarnya terhadap target yang ditetapkan oleh pengguna dalam sesi latihan yang direncanakan.
Merencanakan latihan dan menetapkan target
Pengguna dapat merencanakan latihan di masa mendatang dan menetapkan target. Tulis ini ke Health Connect sebagai sesi latihan yang direncanakan.
Dalam contoh yang dijelaskan di Penggunaan contoh, pengguna merencanakan lari selama 90 menit dua hari dari sekarang. Lari ini akan menampilkan tiga putaran di sekitar danau dengan target detak jantung antara 90 dan 110 bpm.
Cuplikan seperti ini dapat ditemukan di pengendali formulir untuk aplikasi yang mencatat sesi latihan yang direncanakan ke Health Connect. ID ini juga dapat ditemukan di titik penyerapan untuk integrasi, misalnya dengan layanan yang menawarkan pelatihan.
// 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()
Mencatat data latihan dan aktivitas
Dua hari kemudian, pengguna mencatat sesi latihan yang sebenarnya. Tulis ini ke Health Connect sebagai sesi latihan.
Dalam contoh ini, durasi sesi pengguna sama persis dengan durasi yang direncanakan.
Cuplikan berikut mungkin ditemukan di pengendali formulir untuk aplikasi yang mencatat sesi latihan ke Health Connect. ID ini juga dapat ditemukan di handler penyerapan dan ekspor data untuk perangkat wearable yang dapat mendeteksi dan mencatat sesi latihan.
insertedPlannedExerciseSessionId
di sini digunakan kembali dari contoh sebelumnya. Di
aplikasi sebenarnya, ID akan ditentukan oleh pengguna yang memilih sesi latihan yang direncanakan
dari daftar sesi yang ada.
// 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))
Perangkat wearable juga mencatat detak jantungnya selama berlari. Cuplikan berikut dapat digunakan untuk membuat rekaman dalam rentang target.
Dalam aplikasi sebenarnya, bagian utama cuplikan ini dapat ditemukan di handler untuk pesan dari perangkat wearable, yang akan menulis pengukuran ke Health Connect setelah pengumpulan.
// 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))
Mengevaluasi target performa
Sehari setelah latihan fisik pengguna, Anda dapat mengambil latihan fisik yang dicatat, memeriksa apakah ada target latihan fisik yang direncanakan, dan mengevaluasi jenis data tambahan untuk menentukan apakah target yang ditetapkan tercapai.
Cuplikan seperti ini kemungkinan akan ditemukan dalam tugas berkala untuk mengevaluasi target performa atau saat memuat daftar latihan dan menampilkan notifikasi tentang target performa di aplikasi.
// 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
}
}
}
}
}
}
}
}
Sesi olahraga
Sesi olahraga dapat mencakup apa saja, dari berlari hingga bulu tangkis.
Menulis sesi olahraga
Berikut ini cara mem-build permintaan penyisipan yang menyertakan sesi:
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
)
)
}
Membaca sesi olahraga
Berikut ini contoh cara membaca sesi olahraga:
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
}
}
Menulis data subjenis
Sesi juga dapat terdiri dari data subjenis opsional yang memperkaya sesi dengan informasi tambahan.
Misalnya, sesi olahraga dapat menyertakan class ExerciseSegment
, ExerciseLap
,
dan 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
)