המדריך הזה תואם לגרסה 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" // 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_ROUTES
android.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.
// 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 ולבקש מסלול מהסשן הזה:
suspend fun readExerciseSessionAndRoute() {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofHours(1))
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getReadPermission(ExerciseSessionRecord::class))) {
// The user doesn't allow the app to read exercise session data.
return
}
val readResponse =
healthConnectClient.readRecords(
ReadRecordsRequest(
ExerciseSessionRecord::class,
TimeRangeFilter.between(startTime, endTime)
)
)
val exerciseRecord = readResponse.records.first()
val recordId = exerciseRecord.metadata.id
// See https://developer.android.com.acc.wang/training/basics/intents/result#launch
// for appropriately handling ActivityResultContract.
val requestExerciseRouteLauncher = fragment.registerForActivityResul
(ExerciseRouteRequestContract()) { exerciseRoute: ExerciseRoute? ->
if (exerciseRoute != null) {
displayExerciseRoute(exerciseRoute)
} else {
// Consent was denied
}
}
val exerciseSessionRecord =
healthConnectClient.readRecord(ExerciseSessionRecord::class, recordId).record
when (val exerciseRouteResult = exerciseSessionRecord.exerciseRouteResult) {
is ExerciseRouteResult.Data ->
displayExerciseRoute(exerciseRouteResult.exerciseRoute)
is ExerciseRouteResult.ConsentRequired ->
requestExerciseRouteLauncher.launch(recordId)
is ExerciseRouteResult.NoData -> Unit // No exercise route to show
else -> Unit
}
}
fun displayExerciseRoute(route: ExerciseRoute?) {
val locations = route.route.orEmpty()
for (location in locations) {
// Handle location.
}
}
כתיבה של נתוני מסלול האימון
הקוד הבא מדגים איך לתעד פעילות שכוללת מסלול אימון:
suspend fun InsertExerciseRoute(healthConnectClient: HealthConnectClient) {
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
getWritePermission(ExerciseSessionRecord::class))) {
// The user doesn't allow the app to write exercise session data.
return
}
val sessionStartTime = Instant.now()
val sessionDuration = Duration.ofMinutes(20)
val sessionEndTime = sessionStartTime.plus(sessionDuration)
val exerciseRoute =
if (grantedPermissions.contains(PERMISSION_WRITE_EXERCISE_ROUTE)) ExerciseRoute(
listOf(
ExerciseRoute.Location(
// Location times must be on or after the session start time
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(
// Location times must be before the session end time
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
// The user doesn't allow the app to write exercise route data.
null
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.manualEntry(
device = Device(type = Device.TYPE_PHONE)
),
)
val response = healthConnectClient.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"
),
// ... 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
)
מחיקת סשן אימון
יש שתי דרכים למחוק אימון:
- לפי טווח זמן.
- לפי מזהה משתמש (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()
)
}