برنامه های آموزشی

Health Connect یک نوع داده تمرین برنامه‌ریزی‌شده ارائه می‌دهد تا برنامه‌های آموزشی را قادر به نوشتن برنامه‌های آموزشی و خواندن برنامه‌های تمرینی توسط برنامه‌های تمرینی کند. تمرین‌های ضبط‌شده (تمرینات) را می‌توان برای تجزیه و تحلیل عملکرد شخصی‌سازی‌شده بازخوانی کرد تا به کاربران در دستیابی به اهداف آموزشی خود کمک کند.

بررسی در دسترس بودن Health Connect

قبل از تلاش برای استفاده از Health Connect، برنامه شما باید تأیید کند که Health Connect در دستگاه کاربر موجود است. Health Connect ممکن است از قبل روی همه دستگاه‌ها نصب نشده باشد یا غیرفعال باشد. می‌توانید با استفاده از متد HealthConnectClient.getSdkStatus() در دسترس بودن آن را بررسی کنید.

نحوه بررسی در دسترس بودن Health Connect

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME
    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() ، می‌توانید در صورت لزوم کاربر را برای نصب یا به‌روزرسانی Health Connect از فروشگاه Google Play راهنمایی کنید.

در دسترس بودن ویژگی

برای تعیین اینکه آیا دستگاه کاربر از برنامه‌های آموزشی 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>

درخواست مجوز از کاربر

پس از ایجاد یک نمونه کلاینت، برنامه شما باید از کاربر درخواست مجوز کند. کاربران باید بتوانند در هر زمانی مجوزها را اعطا یا رد کنند.

برای انجام این کار، مجموعه‌ای از مجوزها را برای انواع داده‌های مورد نیاز ایجاد کنید. مطمئن شوید که مجوزهای موجود در مجموعه ابتدا در مانیفست اندروید شما تعریف شده‌اند.

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

اطلاعات موجود در پرونده جلسه تمرینی برنامه‌ریزی‌شده

  • عنوان جلسه.
  • فهرستی از بلوک‌های ورزشی برنامه‌ریزی‌شده .
  • زمان شروع و پایان جلسه.
  • نوع تمرین.
  • یادداشت‌هایی برای فعالیت
  • فراداده (متادیتا).
  • شناسه جلسه تمرینی تکمیل‌شده — این شناسه به‌طور خودکار پس از تکمیل یک جلسه تمرینی مرتبط با این جلسه تمرینی برنامه‌ریزی‌شده نوشته می‌شود.

اطلاعات موجود در یک رکورد بلوک تمرینی برنامه‌ریزی‌شده

یک بلوک تمرینی برنامه‌ریزی‌شده شامل فهرستی از مراحل تمرینی است تا از تکرار گروه‌های مختلف مراحل پشتیبانی کند (برای مثال، پنج بار پشت سر هم حرکات جلو بازو، برپی و کرانچ را انجام دهید).

اطلاعات موجود در یک گزارش گام ورزشی برنامه‌ریزی‌شده

تجمیع‌های پشتیبانی‌شده

هیچ تجمیع پشتیبانی‌شده‌ای برای این نوع داده وجود ندارد.

مثال استفاده

فرض کنید کاربری برای دو روز آینده یک دویدن ۹۰ دقیقه‌ای برنامه‌ریزی می‌کند. این دویدن شامل سه دور دور دریاچه با ضربان قلب هدف بین ۹۰ تا ۱۱۰ ضربه در دقیقه خواهد بود.

  1. یک جلسه تمرینی برنامه‌ریزی‌شده با موارد زیر توسط کاربر در یک برنامه برنامه تمرینی تعریف می‌شود:
    1. شروع و پایان برنامه ریزی شده برای اجرا
    2. نوع ورزش (دویدن)
    3. تعداد دورها (تکرارها)
    4. هدف عملکردی برای ضربان قلب (بین ۹۰ تا ۱۱۰ ضربه در دقیقه)
  2. این اطلاعات به بلوک‌های ورزشی و مراحل دسته‌بندی شده و توسط برنامه‌ی برنامه‌ی تمرینی به عنوان یک PlannedExerciseSessionRecord در Health Connect ثبت می‌شود.
  3. کاربر جلسه برنامه‌ریزی‌شده (در حال اجرا) را انجام می‌دهد.
  4. داده‌های تمرینی مربوط به جلسه به یکی از روش‌های زیر ثبت می‌شوند:
    1. توسط یک دستگاه پوشیدنی در طول جلسه. به عنوان مثال، ضربان قلب. این داده‌ها به عنوان نوع رکورد برای فعالیت در Health Connect نوشته می‌شوند. در این مورد، HeartRateRecord .
    2. به صورت دستی توسط کاربر پس از جلسه. به عنوان مثال، نشان دهنده شروع و پایان دویدن واقعی. این داده به عنوان یک ExerciseSessionRecord در Health Connect نوشته می‌شود.
  5. در زمانی دیگر، برنامه‌ی تمرینی، داده‌های Health Connect را می‌خواند تا عملکرد واقعی را در مقایسه با اهداف تعیین‌شده توسط کاربر در جلسه‌ی تمرینی برنامه‌ریزی‌شده ارزیابی کند.

تمرینات را برنامه‌ریزی کنید و اهداف را تعیین کنید

کاربر می‌تواند برای ورزش خود در آینده برنامه‌ریزی کند و اهدافی را تعیین کند. این را به عنوان یک جلسه ورزشی برنامه‌ریزی شده برای Health Connect بنویسید.

در مثالی که در بخش «مثال استفاده» توضیح داده شده است، کاربر برای دو روز آینده یک دویدن ۹۰ دقیقه‌ای برنامه‌ریزی می‌کند. این دویدن شامل سه دور دویدن دور دریاچه با ضربان قلب هدف بین ۹۰ تا ۱۱۰ ضربه در دقیقه خواهد بود.

قطعه کدی مانند این را می‌توان در مدیریت فرم برنامه‌ای که جلسات ورزشی برنامه‌ریزی شده را در 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()

ثبت داده‌های ورزش و فعالیت

دو روز بعد، کاربر جلسه تمرینی واقعی را ثبت می‌کند. این را به عنوان یک جلسه تمرینی در 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
                            }
                        }
                    }
                }
            }
        }
    }
}

جلسات تمرینی

جلسات ورزشی می‌تواند شامل هر چیزی از دویدن گرفته تا بدمینتون باشد.

جلسات تمرینی بنویسید

نحوه‌ی ساخت یک درخواست درج که شامل یک جلسه (session) است، به این صورت است:

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
)