운동 경로 추가

운동 경로는 사용자가 관련 운동 활동의 GPS 경로를 추적하고 다른 앱에 운동 지도를 공유할 수 있도록 지원합니다.

이 가이드에서는 앱에서 운동 세션의 일환으로 경로 데이터를 쓰기 위한 권한을 받는 방식을 설명합니다.

다음은 운동 경로 읽기 및 쓰기 기능의 간단한 요약입니다.

  1. 앱에서 운동 경로를 위한 새 쓰기 권한을 만듭니다.
  2. 경로를 필드로 갖는 운동 세션을 쓰면 삽입이 이루어집니다.
  3. 읽기:
    1. 세션 소유자의 경우, 세션 읽기를 사용하여 데이터가 액세스됩니다.
    2. 서드 파티 앱의 경우, 사용자에게 일회성 경로 읽기를 부여하도록 안내하는 대화상자를 통해 데이터가 액세스됩니다.

권한

운동 경로에는 자체 런타임 쓰기 권한(android.permission.health.WRITE_EXERCISE_ROUTE)이 있습니다.

앱에 운동 경로 기능을 추가하려면 먼저 특정 데이터 유형에 대한 쓰기 권한을 요청하세요.

Android 14 권한 요청

Android 14 권한 요청

Android 13 권한 요청

Android 13 권한 요청

각 경로는 특정 운동 세션에 연결되어 있으므로(세션 1개 = 운동 1개) 운동 권한을 선언해야 합니다.

운동 경로를 쓰기 위해 선언해야 하는 권한은 다음과 같습니다.

<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>

권한을 요청하려면 앱을 처음 헬스 커넥트에 연결할 때 PermissionController.createRequestPermissionResultContract() 메서드를 사용합니다. 다음과 같은 몇 가지 권한을 요청할 수 있습니다.

  • 경로 데이터를 포함한 건강 데이터 읽기: HealthPermission.getReadPermission(ExerciseSessionRecord::class)
  • 경로 데이터를 포함한 건강 데이터 쓰기: HealthPermission.getWritePermission(ExerciseSessionRecord::class)
  • 운동 경로 데이터 쓰기: HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE

경로 데이터 읽기 및 쓰기

앱은 경로를 필드로 갖는 세션을 씀으로써 경로를 삽입합니다.

사용자에게 쓰기 권한이 없고 경로가 설정되지 않은 경우에는 경로가 업데이트되지 않습니다.

경로 쓰기 권한이 있는 앱에서 경로 없이 세션 객체를 전달하여 세션을 업데이트하려고 시도하면 기존 경로가 삭제됩니다.

앱에서 서드 파티 앱이 제공하는 경로 데이터를 읽어야 할 때마다 사용자에게 읽기 작업을 허용할지 묻는 대화상자가 표시됩니다.

세션에서 경로 요청

헬스 커넥트에서 세션을 읽고 이 세션에서 경로를 요청하는 방법은 다음과 같습니다.

suspend fun readExerciseSessionAndRoute() {
    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()

    // 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.parse("2023-01-01T10:00:00.00Z")
    val sessionDuration = Duration.ofMinutes(20)

    val exerciseRoute =
      if (getPermissions.contains(PERMISSION_EXERCISE_ROUTE_WRITE) {
        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 = sessionStartTime.plus(sessionDuration)
              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 = /* starting time in milliseconds */,
            startZoneOffset = ZoneOffset.UTC,
            endTime = sessionStartTime.plus(sessionDuration),
            endZoneOffset = ZoneOffset.UTC,
            exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
            title = "Morning Bike Ride",
            exerciseRoute = exerciseRoute
        )

    healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
}