يتوافق هذا الدليل مع الإصدار 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
لإضافة إمكانية تتبُّع التمارين المخطَّط لها إلى تطبيقك، ابدأ بطلب أذونات الكتابة لنوع البيانات 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 أولاً.
// 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 |
المعلومات المضمّنة في سجلّ جلسة تمرين مخطَّط لها
- تمثّل هذه السمة عنوان الجلسة.
- قائمة فترات التمارين المخطَّط لها
- وقت بدء الجلسة ووقت انتهائها
- تمثّل هذه السمة نوع التمرين.
- ملاحظات حول النشاط
- البيانات الوصفية.
- رقم تعريف جلسة التمرين المكتملة: تتم كتابة هذا الرقم تلقائيًا بعد اكتمال جلسة تمرين ذات صلة بجلسة التمرين المخطَّط لها.
المعلومات المضمّنة في سجلّ فترة التمرين المخطَّط لها
تحتوي كتلة التمارين المخطَّط لها على قائمة بخطوات التمارين، وذلك للسماح بتكرار مجموعات مختلفة من الخطوات (على سبيل المثال، تنفيذ سلسلة من تمارين ثني الذراعين وتمارين بيربي وتمارين البطن خمس مرات متتالية).
- وصف المربّع
- تمثّل هذه السمة قائمة بخطوات التمارين المخطَّط لها.
- عدد مرات التكرار
المعلومات المضمّنة في سجلّ خطوة التمرين المخطَّط له
- تمثّل هذه السمة وصف الخطوة.
- فئة التمرين:
- نوع التمرين:
- قائمة بأهداف الأداء
- هدف الإكمال:
عمليات التجميع المتاحة
لا تتوفّر عمليات تجميع متوافقة لنوع البيانات هذا.
مثال على الاستخدام
لنفترض أنّ أحد المستخدمين يخطّط للركض لمدة 90 دقيقة بعد يومين من الآن. ستتضمّن هذه الجولة ثلاث لفات حول بحيرة مع معدّل نبضات قلب مستهدف يتراوح بين 90 و110 نبضة في الدقيقة.
- يحدّد المستخدم جلسة تمارين رياضية مُخطَّط لها تتضمّن ما يلي في تطبيق خطة تدريب:
- وقت بدء التشغيل ووقت انتهائه المخطَّط لهما
- نوع التمرين (الركض)
- عدد اللفات (التكرارات)
- الأداء المستهدَف لمعدّل نبضات القلب (بين 90 و110 نبضة في الدقيقة)
- يتم تجميع هذه المعلومات في وحدات تمارين وخطوات، ويكتبها تطبيق خطة التدريب في Health Connect كـ
PlannedExerciseSessionRecord
. - ينفّذ المستخدم الجلسة المخطّط لها (التشغيل).
- يتم تسجيل بيانات التمرين الرياضي المرتبطة بالجلسة بإحدى الطريقتَين التاليتَين:
- من خلال جهاز قابل للارتداء أثناء الجلسة على سبيل المثال، معدّل نبضات القلب
تتم كتابة هذه البيانات في Health Connect كنوع سجل للنشاط. في هذه الحالة،
HeartRateRecord
. - يدويًا من قِبل المستخدم بعد الجلسة على سبيل المثال، تحديد بداية ونهاية عملية التنفيذ الفعلية. تتم كتابة هذه البيانات في Health Connect كـ
ExerciseSessionRecord
.
- من خلال جهاز قابل للارتداء أثناء الجلسة على سبيل المثال، معدّل نبضات القلب
تتم كتابة هذه البيانات في Health Connect كنوع سجل للنشاط. في هذه الحالة،
- في وقت لاحق، يقرأ تطبيق خطة التدريب البيانات من Health Connect لتقييم الأداء الفعلي مقارنةً بالأهداف التي حدّدها المستخدم في جلسة التمرين المخطَّط لها.
التخطيط للتمارين وتحديد الأهداف
يمكن للمستخدم التخطيط لممارسة الرياضة في المستقبل وتحديد أهداف. اكتب هذا التمرين في Health Connect كـ جلسة تمارين رياضية مخطَّط لها.
في المثال الموضّح في مثال على الاستخدام، يخطّط المستخدم للركض لمدة 90 دقيقة بعد يومين من الآن. ستتضمّن هذه الجولة ثلاث لفات حول بحيرة مع معدّل نبضات قلب مستهدف يتراوح بين 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()
تسجيل بيانات التمارين والنشاط
وبعد يومين، يسجّل المستخدم جلسة التمرين الفعلية. اكتب هذه البيانات في 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
)