המדריך הזה תואם לגרסה 1.1.0-alpha12 של Health Connect.
מסלולי אימון מאפשרים למשתמשים לעקוב אחרי מסלול GPS של פעילויות גופניות רלוונטיות ולשתף מפות של האימונים שלהם עם אפליקציות אחרות.
בדיקת הזמינות של Health Connect
לפני שמנסים להשתמש ב-Health Connect, האפליקציה צריכה לוודא שהשירות זמין במכשיר של המשתמש. יכול להיות שאפליקציית Health Connect לא מותקנת מראש בחלק מהמכשירים או שהיא מושבתת.
אפשר לבדוק את הזמינות באמצעות ה-method 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_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>
צריך גם להצהיר על הרשאת אימון, כי כל מסלול משויך לסשן אימון (סשן אחד = אימון אחד).
כדי לבקש הרשאות, צריך להשתמש ב-method PermissionController.createRequestPermissionResultContract() כשמחברים את האפליקציה ל-Health Connect בפעם הראשונה. אלה כמה הרשאות שכדאי לבקש:
- קריאה של נתוני בריאות ונתונים של אימוני כושר, כולל נתוני מסלול:
HealthPermission.getReadPermission(ExerciseSessionRecord::class) - הרשאה לכתוב נתוני בריאות ונתונים של אימוני כושר, כולל נתוני מסלול:
HealthPermission.getWritePermission(ExerciseSessionRecord::class) - כתיבה של נתוני מסלול האימון:
HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE
בקשת הרשאות מהמשתמש
אחרי שיוצרים מופע של לקוח, האפליקציה צריכה לבקש הרשאות מהמשתמש. צריך לאפשר למשתמשים להעניק או לדחות הרשאות בכל שלב.
כדי לעשות זאת, יוצרים קבוצת הרשאות לסוגי הנתונים הנדרשים. קודם צריך לוודא שההרשאות בקבוצה מוצהרות במניפסט של Android.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
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)
}
}
המשתמשים יכולים לתת או לבטל הרשאות בכל שלב, ולכן האפליקציה צריכה לבדוק אם ההרשאות ניתנו בכל פעם לפני שהיא משתמשת בהן, ולטפל במקרים שבהם הרשאה כלשהי בוטלה.
המידע שכלול ברשומה של אימון
כל רשומה של אימון מכילה את הפרטים הבאים:
- סוג הפעילות הגופנית, למשל רכיבה על אופניים.
- מסלול האימון, שמכיל מידע כמו קו רוחב, קו אורך וגובה.
צבירות נתמכות
הערכים המצטברים הבאים זמינים עבור ExerciseSessionRecord:
דוגמת שימוש
בקטעי הקוד הבאים מוצגות דוגמאות לקריאה ולכתיבה של מסלול אימון.
קריאת הנתונים של מסלול האימון
האפליקציה לא יכולה לקרוא נתונים של מסלול אימון שנוצרו על ידי אפליקציות אחרות כשהיא פועלת ברקע.
כשהאפליקציה פועלת ברקע ומנסה לקרוא מסלול אימון שנוצר על ידי אפליקציה אחרת, Health Connect מחזיר תגובה מסוג ExerciseRouteResult.ConsentRequired, גם אם לאפליקציה שלכם יש גישה מסוג Always allow לנתוני מסלול האימון.
לכן, מומלץ מאוד לבקש מסלולים רק אחרי שהמשתמש מקיים אינטראקציה מכוונת עם האפליקציה, כשהוא פעיל בממשק המשתמש של האפליקציה.
מידע נוסף על קריאות ברקע זמין בדוגמה לקריאה ברקע.
קטע הקוד הבא מדגים איך לקרוא נתוני סשן ב-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( 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()
)
מחיקת סשן אימון
יש שתי דרכים למחוק אימון:
- לפי טווח תאריכים.
- לפי מזהה משתמש (UID).
כך מוחקים סוג משנה של נתונים לפי טווח תאריכים:
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) }
אפשר גם למחוק סוג משנה של נתונים לפי UID. הפעולה הזו תמחק רק את סשן התרגול, ולא את הנתונים שמשויכים אליו:
suspend fun deleteExerciseSessionByUid( healthConnectClient: HealthConnectClient, exerciseRecord: ExerciseSessionRecord, ) { healthConnectClient.deleteRecords( ExerciseSessionRecord::class, recordIdsList = listOf(exerciseRecord.metadata.id), clientRecordIdsList = emptyList() ) }