الخطط التدريبية

يتوافق هذا الدليل مع الإصدار 1.1.0-alpha11 من Health Connect.

يوفّر 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

يجب الإفصاح عن هذه الأذونات في Play Console لتطبيقك، وكذلك في ملف بيان تطبيقك:

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

أنت مسؤول عن الإفصاح عن جميع الأذونات المناسبة التي تريد استخدامها في أجهزتك وتطبيقاتك. يجب أيضًا التحقّق من أنّه قد منح كل إذن قبل استخدامه.

ترتبط خطط التدريب بجلسات التمارين الرياضية. لذلك، على المستخدم منح الإذن باستخدام كل نوع سجلّ مرتبط بخطة تدريب من أجل الاستفادة بالكامل من هذه الميزة في Health Connect.

على سبيل المثال، إذا كانت خطة التدريب تقيس معدل ضربات قلب المستخدم أثناء سلسلة من عمليات الركض، قد يحتاج المطوِّر إلى الإفصاح عن الأذونات التالية ومنحها للمستخدم من أجل كتابة جلسة التمارين وقراءة النتائج لتقييمها لاحقًا:

  • android.permission.health.READ_EXERCISE
  • android.permission.health.READ_EXERCISE_ROUTE
  • 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_ROUTE WRITE_EXERCISE_ROUTE
READ_HEART_RATE WRITE_HEART_RATE

المعلومات المضمّنة في سجلّ جلسة تمارين مخطّط

  • عنوان الجلسة
  • قائمة بمجموعات التمارين المخطَّط لها
  • وقت بدء الجلسة وانتهائها
  • نوع التمرين
  • ملاحظات حول النشاط
  • البيانات الوصفية.
  • رقم تعريف جلسة التمارين المكتملة: يتم تسجيل هذا العنصر تلقائيًا بعد اكتمال جلسة التمارين المتعلّقة بهذه الجلسة المخطّط لها.

المعلومات المضمّنة في سجلّ مجموعة تمارين مخطّط

يحتوي القسم المُخطَّط للتمارين على قائمة بخطوات التمارين، لدعم تكرار مجموعات مختلفة من الخطوات (على سبيل المثال، يمكنك إجراء تسلسل من تمارين تنمية القوة للذراع وتمارين Burpees وتمارين المعدة المخصّصة للنساء خمس مرات متتالية).

المعلومات المضمّنة في سجلّ خطوات تمرين مخطَّط

عمليات التجميع المسموح بها

لا تتوفّر عمليات تجميع متوافقة لنوع البيانات هذا.

مثال على الاستخدام

لنفترض أنّ أحد المستخدمين يخطّط لركض لمدة 90 دقيقة بعد يومَين من الآن. سيتضمّن هذا الركض ثلاث لفات حول بحيرة بمعدّل نبضات قلب مستهدَف يتراوح بين 90 و110 نبضة في الدقيقة.

  1. جلسة تمارين مخطَّط لها يتضمّن المستخدم ما يلي في تطبيق خطة التدريب:
    1. وقت بدء التنفيذ وانتهائه المخطَّطَين
    2. نوع التمارين الرياضية (الركض)
    3. عدد الجولات (مرات التكرار)
    4. مستوى الأداء المستهدَف لمعدّل ضربات القلب (بين 90 و110 نبضة في الدقيقة)
  2. يتم تجميع هذه المعلومات في مجموعات من التمارين الرياضية والخطوات، ويكتبها تطبيق خطة التدريب في Health Connect على شكل PlannedExerciseSessionRecord.
  3. ينفّذ المستخدم الجلسة المخطّط لها (قيد التنفيذ).
  4. يتم تسجيل بيانات التمارين المرتبطة بالجلسة على النحو التالي:
    1. من خلال جهاز قابل للارتداء أثناء الجلسة على سبيل المثال، معدّل ضربات القلب. يتم تسجيل هذه البيانات في Health Connect كنوع السجلّ ل activity. في هذه الحالة، HeartRateRecord.
    2. يدويًا من قِبل المستخدم بعد انتهاء الجلسة على سبيل المثال، الإشارة إلى بداية ونهاية التنفيذ الفعلي يتم كتابة هذه البيانات في Health Connect كـ ExerciseSessionRecord.
  5. في وقت لاحق، يقرأ تطبيق خطة التدريب البيانات من Health Connect لتقييم الأداء الفعلي مقارنةً بالأهداف التي حدّدها المستخدم في جلسة التمارين المخطّط لها.

تخطيط التمارين وتحديد الأهداف

يمكن للمستخدم تخطيط التمارين الرياضية في المستقبل وتحديد الأهداف. اكتب هذه المعلومات في Health Connect كجلسة تمارين مخطَّط لها.

في المثال الموضّح في مثال على الاستخدام، يخطّط المستخدم لركض لمدة 90 دقيقة بعد يومَين. سيتضمّن هذا الركض ثلاث لفات حول بحيرة بمعدّل نبضات قلب مستهدَف يتراوح بين 90 و110 نبضة في الدقيقة.

يمكن العثور على مقتطف مثل هذا في معالِج النموذج لتطبيق يسجِّل جلسات التمارين الرياضية المخطَّط لها في Health Connect. ويمكن أيضًا العثور عليه في نقطة نقل البيانات لعمليات الدمج، مثلاً مع خدمة تقدّم تدريبًا.

// Ensure 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 = ExerciseSessionRecord.EXERCISE_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
)
val insertedPlannedExerciseSessions =
    healthConnectClient.insertRecords(listOf(plannedExerciseSessionRecord)).recordIdsList
val insertedPlannedExerciseSessionId = insertedPlannedExerciseSessions.first()

تسجيل بيانات التمارين الرياضية والأنشطة

بعد يومَين، يسجّل المستخدم جلسة التمارين الفعلية. اكتب هذه البيانات في Health Connect كبيانات جلسة تمارين رياضية.

في هذا المثال، تطابقت مدة جلسة المستخدِم مع المدة المخطّط لها تمامًا.

يمكن العثور على المقتطف التالي في معالِج النموذج لتطبيق يسجِّل جلسات التمارين في Health Connect. وقد يظهر أيضًا في معالجات نقل البيانات ونقلها لجهاز قابل للارتداء يمكنه رصد جلسات التمارين الرياضية وتسجيلها.

تم إعادة استخدام insertedPlannedExerciseSessionId هنا من المثال السابق. في التطبيق الحقيقي، سيحدّد المستخدِم رقم التعريف من خلال اختيار جلسة تمرين مخطّط من قائمة الجلسات الحالية.

// Ensure 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,
)
val insertedExerciseSessions =
    healthConnectClient.insertRecords(listOf(exerciseSessionRecord))

يسجِّل جهاز قابل للارتداء أيضًا معدّل نبضات القلب طوال فترة الركض. يمكن استخدام المقتطف التالي لإنشاء سجلّات ضمن النطاق المستهدَف.

في التطبيق الفعلي، قد يتم العثور على الأجزاء الأساسية من هذا المقتطف في معالِج لرسالة واردة من جهاز قابل للارتداء، والذي سيُسجِّل القياس في Health Connect عند جمعه.

// Ensure 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,
)
val insertedHeartRateRecords = healthConnectClient.insertRecords(listOf(heartRateRecord))

تقييم أهداف الأداء

في اليوم التالي لتمرين المستخدم، يمكنك استرداد التمرين المسجَّل والتحقّق من أيّ أهداف تمارين مخطّط لها وتقييم أنواع بيانات إضافية لتحديد ما إذا تمّ تحقيق الأهداف المحدّدة.

من المرجّح العثور على مقتطف مثل هذا في مهمة دورية لتقييم أهداف الأداء أو عند تحميل قائمة بالتمارين وعرض إشعار بشأن أهداف الأداء في أحد التطبيقات.

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