แผนการฝึก

คู่มือนี้ใช้ได้กับ Health Connect เวอร์ชัน 1.1.0-alpha11

Health Connect มีประเภทข้อมูลการออกกำลังกายที่วางแผนไว้เพื่อให้แอปฝึกซ้อม เขียนแผนการฝึกซ้อม และช่วยให้แอปออกกำลังกายอ่านแผนการฝึกซ้อมได้ ระบบจะอ่านการออกกำลังกายที่บันทึกไว้ (การออกกำลังกาย) เพื่อวิเคราะห์ประสิทธิภาพที่ปรับเปลี่ยนในแบบของคุณ เพื่อช่วยให้ผู้ใช้บรรลุเป้าหมายการฝึกซ้อม

ความพร้อมใช้งานของฟีเจอร์

หากต้องการดูว่าอุปกรณ์ของผู้ใช้รองรับแผนการฝึกใน Health Connect หรือไม่ ให้ตรวจสอบความพร้อมใช้งานของ FEATURE_PLANNED_EXERCISE ในไคลเอ็นต์ ดังนี้

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

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

ดูข้อมูลเพิ่มเติมได้ที่ตรวจสอบความพร้อมใช้งานของฟีเจอร์

สิทธิ์ที่จำเป็น

การเข้าถึงการออกกำลังกายที่วางแผนไว้จะได้รับการปกป้องโดยสิทธิ์ต่อไปนี้

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

หากต้องการเพิ่มความสามารถในการออกกำลังกายที่วางแผนไว้ลงในแอป ให้เริ่มต้นด้วยการขอสิทธิ์เขียนสำหรับประเภทข้อมูล PlannedExerciseSession

นี่คือสิทธิ์ที่คุณต้องประกาศเพื่อให้เขียน การออกกำลังกายที่วางแผนไว้ได้

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

หากต้องการอ่านการออกกำลังกายที่วางแผนไว้ คุณต้องขอสิทธิ์ต่อไปนี้

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

ขอสิทธิ์จากผู้ใช้

หลังจากสร้างอินสแตนซ์ไคลเอ็นต์แล้ว แอปของคุณต้องขอสิทธิ์จากผู้ใช้ ผู้ใช้ต้องได้รับอนุญาตให้ให้หรือปฏิเสธสิทธิ์ได้ทุกเมื่อ

โดยให้สร้างชุดสิทธิ์สำหรับประเภทข้อมูลที่จำเป็น ตรวจสอบว่าได้ประกาศสิทธิ์ในชุดไว้ใน Android manifest ก่อน

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

ใช้ getGrantedPermissions เพื่อดูว่าแอปของคุณได้รับสิทธิ์ที่จำเป็นแล้วหรือไม่ หากไม่มี ให้ใช้ createRequestPermissionResultContract เพื่อขอสิทธิ์เหล่านั้น ซึ่งจะแสดงหน้าจอสิทธิ์ของ 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)
  }
}

เนื่องจากผู้ใช้สามารถให้หรือเพิกถอนสิทธิ์ได้ทุกเมื่อ แอปของคุณจึงต้อง ตรวจสอบสิทธิ์ที่ได้รับเป็นระยะๆ และจัดการสถานการณ์ที่ สิทธิ์สูญหาย

แผนการฝึกจะลิงก์กับเซสชันการออกกำลังกาย ดังนั้น ผู้ใช้ต้องให้สิทธิ์ในการใช้บันทึกแต่ละประเภทที่เกี่ยวข้องกับแผนการฝึกเพื่อใช้ฟีเจอร์นี้ของ Health Connect ได้อย่างเต็มที่

ตัวอย่างเช่น หากแผนการฝึกวัดอัตราการเต้นของหัวใจของผู้ใช้ระหว่างการวิ่งหลายครั้ง นักพัฒนาแอปอาจต้องประกาศสิทธิ์ต่อไปนี้และผู้ใช้ต้องให้สิทธิ์เพื่อเขียนเซสชันการออกกำลังกายและอ่าน ผลลัพธ์เพื่อการประเมินในภายหลัง

  • 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

อย่างไรก็ตาม บ่อยครั้งที่แอปที่สร้างแผนการฝึกและประเมินประสิทธิภาพ เทียบกับแผนไม่ใช่แอปเดียวกับที่ใช้แผนการฝึกและเขียน ข้อมูลการออกกำลังกายจริง สิทธิ์การอ่านและการเขียน บางอย่างอาจไม่จำเป็น ทั้งนี้ขึ้นอยู่กับประเภทแอป ตัวอย่างเช่น คุณอาจต้องมีสิทธิ์ต่อไปนี้ สำหรับแอปแต่ละประเภท

แอปแผนการฝึก แอปออกกำลังกาย
WRITE_PLANNED_EXERCISE READ_PLANNED_EXERCISE
READ_EXERCISE WRITE_EXERCISE
READ_EXERCISE_ROUTES WRITE_EXERCISE_ROUTE
READ_HEART_RATE WRITE_HEART_RATE

ข้อมูลที่รวมอยู่ในบันทึกเซสชันการออกกำลังกายที่วางแผนไว้

  • ชื่อเซสชัน
  • รายการช่วงเวลาออกกำลังกายที่วางแผนไว้
  • เวลาเริ่มต้นและสิ้นสุดของเซสชัน
  • ประเภทการออกกำลังกาย
  • หมายเหตุสำหรับกิจกรรม
  • ข้อมูลเมตา
  • รหัสเซสชันการออกกำลังกายที่เสร็จสมบูรณ์ - ระบบจะเขียนรหัสนี้โดยอัตโนมัติหลังจากเซสชันการออกกำลังกายที่เกี่ยวข้องกับเซสชันการออกกำลังกายที่วางแผนไว้เสร็จสมบูรณ์

ข้อมูลที่รวมอยู่ในบันทึกช่วงเวลาออกกำลังกายที่วางแผนไว้

บล็อกการออกกำลังกายที่วางแผนไว้จะมีรายการขั้นตอนการออกกำลังกายเพื่อรองรับ การทำซ้ำขั้นตอนกลุ่มต่างๆ (เช่น ทำท่าดัมเบลยกแขน เบอร์พี และครันช์ 5 ครั้งติดต่อกัน)

ข้อมูลที่รวมอยู่ในบันทึกขั้นตอนการออกกำลังกายที่วางแผนไว้

การรวมที่รองรับ

ไม่มีการรวบรวมที่รองรับสำหรับข้อมูลประเภทนี้

ตัวอย่างการใช้งาน

สมมติว่าผู้ใช้วางแผนที่จะวิ่ง 90 นาทีในอีก 2 วันข้างหน้า การวิ่งนี้จะประกอบด้วย วิ่งรอบทะเลสาบ 3 รอบโดยมีอัตราการเต้นของหัวใจเป้าหมายระหว่าง 90 ถึง 110 ครั้งต่อนาที

  1. เซสชันการออกกำลังกายที่วางแผนไว้ซึ่งมีองค์ประกอบต่อไปนี้จะได้รับการกำหนดโดยผู้ใช้ใน แอปแผนการฝึก
    1. การเริ่มต้นและสิ้นสุดการเรียกใช้ที่วางแผนไว้
    2. ประเภทการออกกำลังกาย (วิ่ง)
    3. จำนวนรอบ (การทำซ้ำ)
    4. เป้าหมายประสิทธิภาพสำหรับอัตราการเต้นของหัวใจ (ระหว่าง 90 ถึง 110 bpm)
  2. ข้อมูลนี้จะจัดกลุ่มเป็นบล็อกการออกกำลังกายและขั้นตอนต่างๆ แล้วเขียนลงใน Health Connect โดยแอปแผนการฝึกเป็นPlannedExerciseSessionRecord
  3. ผู้ใช้ทำเซสชันที่วางแผนไว้ (วิ่ง)
  4. ระบบจะบันทึกข้อมูลการออกกำลังกายที่เกี่ยวข้องกับเซสชันในลักษณะใดลักษณะหนึ่งต่อไปนี้
    1. โดยอุปกรณ์ที่สวมใส่ได้ระหว่างเซสชัน เช่น อัตราการเต้นของหัวใจ ระบบจะเขียนข้อมูลนี้ลงใน Health Connect เป็นประเภทบันทึกสำหรับ กิจกรรม ในกรณีนี้คือ HeartRateRecord
    2. ผู้ใช้ดำเนินการด้วยตนเองหลังจากเซสชัน เช่น การระบุจุดเริ่มต้นและจุดสิ้นสุดของการวิ่งจริง ระบบจะเขียนข้อมูลนี้ลงใน Health Connect เป็น ExerciseSessionRecord
  5. ในภายหลัง แอปแผนการฝึกจะอ่านข้อมูลจาก Health Connect เพื่อ ประเมินประสิทธิภาพจริงเทียบกับเป้าหมายที่ผู้ใช้ตั้งไว้ใน เซสชันการออกกำลังกายที่วางแผนไว้

วางแผนการออกกำลังกายและตั้งเป้าหมาย

ผู้ใช้อาจวางแผนการออกกำลังกายในอนาคตและตั้งเป้าหมาย เขียนข้อมูลนี้ไปยัง Health Connect เป็นเซสชันการออกกำลังกายที่วางแผนไว้

ในตัวอย่างที่อธิบายไว้ในตัวอย่างการใช้งาน ผู้ใช้วางแผนการวิ่ง 90 นาทีในอีก 2 วันข้างหน้า การวิ่งนี้จะประกอบด้วยการวิ่งรอบทะเลสาบ 3 รอบ โดยมีอัตราการเต้นของหัวใจเป้าหมายระหว่าง 90 ถึง 110 ครั้งต่อนาที

คุณอาจพบข้อมูลโค้ดลักษณะนี้ในตัวแฮนเดิลอร์แบบฟอร์มสำหรับแอปที่บันทึก เซสชันการออกกำลังกายที่วางแผนไว้ไปยัง Health Connect นอกจากนี้ ยังอาจพบใน จุดรับข้อมูลสำหรับการผสานรวม เช่น กับบริการที่เสนอการฝึกอบรม

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

บันทึกข้อมูลการออกกำลังกายและกิจกรรม

2 วันต่อมา ผู้ใช้บันทึกเซสชันการออกกำลังกายจริง เขียนข้อมูลนี้ลงใน Health Connect เป็นเซสชันการออกกำลังกาย

ในตัวอย่างนี้ ระยะเวลาเซสชันของผู้ใช้ตรงกับระยะเวลาที่วางแผนไว้ ทุกประการ

คุณอาจเห็นข้อมูลโค้ดต่อไปนี้ในตัวแฮนเดิลแบบฟอร์มสำหรับแอปที่บันทึก เซสชันการออกกำลังกายไปยัง Health Connect นอกจากนี้ ยังอาจพบในตัวแฮนเดิลการนำเข้าและส่งออกข้อมูลสำหรับอุปกรณ์สวมใส่ที่ตรวจจับและบันทึกเซสชันการออกกำลังกายได้

insertedPlannedExerciseSessionId ที่นี่จะนำมาจากตัวอย่างก่อนหน้า ในแอปจริง ระบบจะกำหนดรหัสโดยผู้ใช้เลือกเซสชันการออกกำลังกายที่วางแผนไว้จากรายการเซสชันที่มีอยู่

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

นอกจากนี้ อุปกรณ์ที่สวมใส่ได้ยังบันทึกอัตราการเต้นของหัวใจตลอดการวิ่งด้วย คุณใช้ข้อมูลโค้ดต่อไปนี้ เพื่อสร้างระเบียนภายในช่วงเป้าหมายได้

ในแอปจริง ส่วนหลักของข้อมูลโค้ดนี้อาจอยู่ในแฮนเดิล สำหรับข้อความจากอุปกรณ์ที่สวมใส่ได้ ซึ่งจะเขียนการวัดไปยัง Health Connect เมื่อรวบรวม

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

ประเมินเป้าหมายด้านประสิทธิภาพ

ในวันถัดจากวันที่ผู้ใช้ออกกำลังกาย คุณสามารถดึงข้อมูลการออกกำลังกายที่บันทึกไว้ ตรวจสอบ เป้าหมายการออกกำลังกายที่วางแผนไว้ และประเมินข้อมูลประเภทอื่นๆ เพื่อ ดูว่าบรรลุเป้าหมายที่ตั้งไว้หรือไม่

โดยปกติแล้ว คุณจะพบข้อมูลโค้ดเช่นนี้ในงานที่ทำเป็นระยะๆ เพื่อประเมิน เป้าหมายประสิทธิภาพ หรือเมื่อโหลดรายการแบบฝึกหัดและแสดง การแจ้งเตือนเกี่ยวกับเป้าหมายประสิทธิภาพในแอป

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

เซสชันการออกกำลังกาย

เซสชันการออกกำลังกายอาจประกอบด้วยกิจกรรมต่างๆ ตั้งแต่การวิ่งไปจนถึงแบดมินตัน

เขียนข้อมูลเซสชันการออกกำลังกาย

วิธีสร้างคำขอแทรกที่มีเซสชันมีดังนี้

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

อ่านเซสชันการออกกำลังกาย

ตัวอย่างวิธีอ่านเซสชันการออกกำลังกายมีดังนี้

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

เขียนข้อมูลประเภทย่อย

เซสชันยังประกอบด้วยข้อมูลประเภทย่อยที่ไม่บังคับ ซึ่งจะช่วยเพิ่มข้อมูลเพิ่มเติมให้กับเซสชันได้ด้วย

เช่น เซสชันการออกกำลังกายอาจรวมคลาส ExerciseSegment, ExerciseLap และ 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
)