המדריך הזה תואם לגרסה 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 פעימות בדקה.
קטע קוד כזה יכול להימצא ב-handler של טופס באפליקציה שמתעדת מפגשי אימון מתוכננים ב-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 כנתוני פעילות גופנית.
בדוגמה הזו, משך הסשן של המשתמש זהה בדיוק למשך המתוכנן.
יכול להיות שתמצאו את קטע הקוד הבא ב-handler של טופס באפליקציה שמתעדת סשנים של אימונים ב-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))
בנוסף, מכשיר לביש מתעד את קצב הלב לאורך הריצה. אפשר להשתמש בקטע הקוד הבא כדי ליצור רשומות בטווח היעד.
באפליקציה אמיתית, יכול להיות שהחלקים העיקריים של קטע הקוד הזה יימצאו ב-handler של הודעה ממכשיר לביש, שיכתוב את המדידה ל-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
)