הוספת מסלולי אימון

המדריך הזה תואם לגרסה 1.1.0-alpha12 של Health Connect.

מסלולי אימון מאפשרים למשתמשים לעקוב אחרי מסלול GPS של פעילויות אימון משויכות ולשתף מפות של האימונים שלהם עם אפליקציות אחרות.

במדריך הזה מוסבר איך לבקש הרשאות מהמשתמש, וגם איך אפליקציות מקבלות הרשאה לכתוב נתוני מסלולים כחלק מסשן אימון.

לפניכם סיכום קצר של הפונקציונליות של קריאה וכתיבה של מסלולי אימון:

  1. אפליקציות יוצרות הרשאת כתיבה חדשה למסלולי אימון.
  2. כדי להוסיף פעילות, כותבים סשן אימון עם מסלול בתור השדה שלו.
  3. קריאה:
    1. הבעלים של הסשן יכול לגשת לנתונים באמצעות קריאת סשן.
    2. מאפליקציה של צד שלישי, דרך תיבת דו-שיח שמאפשרת למשתמש להעניק הרשאה לקריאה חד-פעמית של מסלול.

בקשת הרשאות מהמשתמש

אחרי שיוצרים מכונה של לקוח, האפליקציה צריכה לבקש הרשאות מהמשתמש. המשתמשים צריכים להיות מורשים להעניק או לדחות הרשאות בכל שלב.

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

מכיוון שמשתמשים יכולים להעניק או לבטל הרשאות בכל שלב, האפליקציה צריכה לבדוק מדי פעם אילו הרשאות ניתנו ולטפל בתרחישים שבהם ההרשאה נשללת.

הרשאות קריאה וכתיבה של מסלולי אימון

למסלולי אימון יש הרשאת כתיבה משלהם בזמן ריצה (android.permission.health.WRITE_EXERCISE_ROUTE).

כדי להוסיף לאפליקציה את היכולת ליצור מסלולי אימון, צריך קודם לבקש הרשאות כתיבה לסוג נתונים ספציפי.

צריך גם להצהיר על הרשאה לפעילות גופנית, כי כל מסלול משויך לסשן אימון (סשן אחד = אימון אחד).

זו ההרשאה שצריך להצהיר עליה כדי שתוכלו לכתוב מסלולי אימון:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
  <uses-permission
android:name="android.permission.health.WRITE_EXERCISE" />
...
</application>

כדי לקרוא מסלולי אימון, צריך לבקש את ההרשאות הבאות:

<application>
  <uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
  <uses-permission
android:name="android.permission.health.READ_EXERCISE" />
...
</application>

כדי לבקש הרשאות, צריך להשתמש ב-method‏ PermissionController.createRequestPermissionResultContract() בפעם הראשונה שמקשרים את האפליקציה ל-Health Connect. כמה הרשאות שיכול להיות שתרצו לבקש:

  • קריאה של נתוני בריאות, כולל נתוני מסלול: HealthPermission.getReadPermission(ExerciseSessionRecord::class)
  • כתיבה של נתוני בריאות, כולל נתוני מסלול: HealthPermission.getWritePermission(ExerciseSessionRecord::class)
  • כתיבה של נתוני מסלול האימון: HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE

קריאה וכתיבה של נתוני מסלולים

אפליקציות מוסיפות מסלול על ידי כתיבת סשן עם מסלול בתור שדה.

אם למשתמש אין הרשאות כתיבה והמסלול לא מוגדר, המסלול לא יתעדכן.

אם לאפליקציה יש הרשאת כתיבה למסלול והיא מנסה לעדכן סשן על ידי העברת אובייקט סשן ללא מסלול, המסלול הקיים נמחק.

בכל פעם שהאפליקציה צריכה לקרוא נתוני מסלול שסופקו על ידי אפליקציה של צד שלישי, מוצגת תיבת דו-שיח שבה המשתמש מתבקש לאשר את פעולת הקריאה.

שליחת בקשה למסלול מסשן

כך קוראים סשן ב-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/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))
}