يتوافق هذا الدليل مع الإصدار 1.1.0-alpha12 من Health Connect أو الإصدارات الأحدث.
تتيح مسارات التمارين الرياضية للمستخدمين تتبُّع مسار GPS للأنشطة الرياضية المرتبطة بها ومشاركة خرائط تمارينهم مع تطبيقات أخرى.
التحقّق من توفّر Health Connect
قبل محاولة استخدام Health Connect، يجب أن يتأكّد تطبيقك من توفُّر Health Connect على جهاز المستخدم. قد لا يكون تطبيق Health Connect مثبَّتًا مسبقًا على جميع الأجهزة أو قد يكون غير مفعَّل.
يمكنك التحقّق من توفّره باستخدام طريقة HealthConnectClient.getSdkStatus().
كيفية التحقّق من توفّر Health Connect
fun checkHealthConnectAvailability(context: Context) { val providerPackageName = "com.google.android.apps.healthdata" 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_EXERCISE_ROUTESandroid.permission.health.WRITE_EXERCISE_ROUTE
READ_EXERCISE_ROUTES بصيغة الجمع، بينما
WRITE_EXERCISE_ROUTE بصيغة المفرد.
لإضافة ميزة مسار التمرين إلى تطبيقك، ابدأ بطلب
أذونات لنوع البيانات ExerciseSession.
في ما يلي الإذن الذي يجب الإفصاح عنه لتتمكّن من كتابة مسار التمرين:
<application>
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
...
</application>
لقراءة مسار التمرين، عليك طلب الأذونات التالية:
<application>
<uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
...
</application>
عليك أيضًا الإفصاح عن إذن التمرين، لأنّ كل مسار يرتبط بجلسة تمرين (جلسة واحدة = تمرين واحد).
لطلب الأذونات، استخدِم طريقة
PermissionController.createRequestPermissionResultContract() عند ربط تطبيقك
بتطبيق Health Connect لأول مرة. هناك العديد من الأذونات التي قد
ترغب في طلبها:
- قراءة بيانات الصحة واللياقة البدنية، بما في ذلك بيانات المسار:
HealthPermission.getReadPermission(ExerciseSessionRecord::class) - كتابة بيانات الصحة واللياقة البدنية، بما في ذلك بيانات المسار:
HealthPermission.getWritePermission(ExerciseSessionRecord::class) - كتابة بيانات مسار التمرين الرياضي:
HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE
طلب الأذونات من المستخدم
بعد إنشاء مثيل عميل، يحتاج تطبيقك إلى طلب أذونات من المستخدم. يجب السماح للمستخدمين بمنح الأذونات أو رفضها في أي وقت. لإجراء ذلك، أنشئ مجموعة من الأذونات لأنواع البيانات المطلوبة. تأكَّد من تضمين الأذونات في المجموعة في بيان Android أولاً.
val permissions = setOf( HealthPermission.getReadPermission(ExerciseSessionRecord::class), HealthPermission.getWritePermission(ExerciseSessionRecord::class) )
getGrantedPermissions
لمعرفة ما إذا كان تطبيقك قد حصل على الأذونات المطلوبة. وإذا لم يكن الأمر كذلك، استخدِم
createRequestPermissionResultContract
لطلب هذه الأذونات. سيؤدي ذلك إلى عرض شاشة أذونات Health Connect.
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class), HealthPermission.getReadPermission(HeartRateRecord::class), HealthPermission.getWritePermission(HeartRateRecord::class) ) val requestPermissionsLauncher = rememberLauncherForActivityResult( contract = PermissionController.createRequestPermissionResultContract() ) { grantedPermissions -> if (grantedPermissions.containsAll(permissions)) { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") } } else { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") } } }
المعلومات المضمّنة في سجلّ جلسة التمرين
يحتوي كل سجلّ لجلسة تمرين على المعلومات التالية:
- نوع التمرين، مثل ركوب الدراجات
- مسار التمرين، والذي يتضمّن معلومات مثل خط العرض وخط الطول والارتفاع
عمليات التجميع المتوافقة
تتوفّر القيم المجمّعة التالية لـ ExerciseSessionRecord:
مثال للاستخدام
توضّح مقتطفات الرموز التالية كيفية قراءة مسار تمرين وكتابته.
قراءة مسار التمرين الرياضي
لا يمكن لتطبيقك قراءة بيانات مسار التمرين الرياضي التي أنشأتها تطبيقات أخرى عندما يعمل في الخلفية.
عندما يعمل تطبيقك في الخلفية ويحاول قراءة مسار تمرين رياضي أنشأه تطبيق آخر، يعرض Health Connect الرد ExerciseRouteResult.ConsentRequired، حتى إذا كان تطبيقك لديه إذن السماح دائمًا بالوصول إلى بيانات مسار التمرين الرياضي.
لهذا السبب، ننصحك بشدة بطلب المسارات عند تفاعل المستخدم بشكل متعمّد مع تطبيقك، أي عندما يتفاعل المستخدم بشكل نشط مع واجهة المستخدم في تطبيقك.
لمزيد من المعلومات حول عمليات القراءة في الخلفية، يُرجى الاطّلاع على مثال على عملية القراءة في الخلفية.
يوضّح مقتطف الرمز التالي كيفية قراءة جلسة في Health Connect وطلب مسار من تلك الجلسة:
private suspend fun readExerciseSessionAndRoute() { val client = healthConnectClient ?: return val endTime = Instant.now() val startTime = endTime.minus(Duration.ofHours(1)) val grantedPermissions = client.permissionController.getGrantedPermissions() // 1. Verify basic Exercise Session permissions if (!grantedPermissions.contains( HealthPermission.getReadPermission(ExerciseSessionRecord::class) ) ) { return } // 2. Read the sessions val readResponse = client.readRecords( ReadRecordsRequest( ExerciseSessionRecord::class, TimeRangeFilter.between(startTime, endTime) ) ) val exerciseRecord = readResponse.records.firstOrNull() ?: return val recordId = exerciseRecord.metadata.id // 3. Read the specific record to check for the route val sessionResponse = client.readRecord(ExerciseSessionRecord::class, recordId) // 4. Handle the Route Result directly from the response when (val routeResult = sessionResponse.record.exerciseRouteResult) { is ExerciseRouteResult.Data -> { displayExerciseRoute(routeResult.exerciseRoute) } is ExerciseRouteResult.ConsentRequired -> { // Since you are in a Service, you cannot launch ActivityResultLauncher. // Send a notification to the user to grant route-specific consent. handleConsentRequired(recordId) } is ExerciseRouteResult.NoData -> Unit else -> Unit } } private fun displayExerciseRoute(route: ExerciseRoute) { val locations = route.route.orEmpty() for (location in locations) { println(location) } }
كتابة مسار تمرين رياضي
يوضّح الرمز التالي كيفية تسجيل جلسة تتضمّن مسار تمرين:
private suspend fun insertExerciseRoute() { val client = healthConnectClient ?: return val grantedPermissions = client.permissionController.getGrantedPermissions() // 1. Verify Session Write Permission val hasWriteSession = grantedPermissions.contains( HealthPermission.getWritePermission(ExerciseSessionRecord::class) ) if (!hasWriteSession) return val sessionStartTime = Instant.now() val sessionDuration = Duration.ofMinutes(20) val sessionEndTime = sessionStartTime.plus(sessionDuration) // 2. Build the route if route-specific write permission is granted val hasWriteRoute = grantedPermissions.contains(HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE) val exerciseRoute = if (hasWriteRoute) { ExerciseRoute( listOf( ExerciseRoute.Location( time = sessionStartTime, latitude = 6.5483, longitude = 0.5488, horizontalAccuracy = Length.meters(2.0), verticalAccuracy = Length.meters(2.0), altitude = Length.meters(9.0), ), ExerciseRoute.Location( time = sessionEndTime.minusSeconds(1), latitude = 6.4578, longitude = 0.6577, horizontalAccuracy = Length.meters(2.0), verticalAccuracy = Length.meters(2.0), altitude = Length.meters(9.2), ) ) ) } else { null } // 3. Create the session record val exerciseSessionRecord = ExerciseSessionRecord( startTime = sessionStartTime, startZoneOffset = ZoneOffset.UTC, endTime = sessionEndTime, endZoneOffset = ZoneOffset.UTC, exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING, title = "Morning Bike Ride", exerciseRoute = exerciseRoute, metadata = Metadata.activelyRecorded( device = Device(type = Device.TYPE_PHONE) ) ) // 4. Insert into Health Connect client.insertRecords(listOf(exerciseSessionRecord)) }
جلسات التمارين الرياضية
يمكن أن تتضمّن جلسات التمارين أي نشاط، من الجري إلى كرة الريشة.
كتابة بيانات جلسات التمارين الرياضية
في ما يلي كيفية إنشاء طلب إدراج يتضمّن جلسة:
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",
metadata = Metadata.manualEntry()
),
// ... 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,
metadata = Metadata.manualEntry()
)
حذف جلسة تمرين
هناك طريقتان لحذف جلسة تمرين:
- حسب النطاق الزمني
- حسب المعرّف الفريد
في ما يلي كيفية حذف بيانات النوع الفرعي وفقًا للنطاق الزمني:
suspend fun deleteExerciseSessionByTimeRange( healthConnectClient: HealthConnectClient, exerciseRecord: ExerciseSessionRecord, ) { val timeRangeFilter = TimeRangeFilter.between(exerciseRecord.startTime, exerciseRecord.endTime) healthConnectClient.deleteRecords(ExerciseSessionRecord::class, timeRangeFilter) // delete the associated distance record healthConnectClient.deleteRecords(DistanceRecord::class, timeRangeFilter) }
يمكنك أيضًا حذف بيانات النوع الفرعي حسب المعرّف الفريد. يؤدي ذلك إلى حذف جلسة التمرين فقط، وليس البيانات المرتبطة بها:
suspend fun deleteExerciseSessionByUid( healthConnectClient: HealthConnectClient, exerciseRecord: ExerciseSessionRecord, ) { healthConnectClient.deleteRecords( ExerciseSessionRecord::class, recordIdsList = listOf(exerciseRecord.metadata.id), clientRecordIdsList = emptyList() ) }