이 가이드는 Health Connect 버전 1.1.0-alpha12와 호환됩니다.
운동 경로는 사용자가 관련 운동 활동의 GPS 경로를 추적하고 다른 앱에 운동 지도를 공유할 수 있도록 지원합니다.
이 가이드에서는 사용자에게 권한을 요청하는 방법을 설명하고 앱이 운동 세션의 일환으로 경로 데이터를 쓰기 위한 권한을 받는 방식을 설명합니다.
다음은 운동 경로 읽기 및 쓰기 기능의 간단한 요약입니다.
- 앱에서 운동 경로를 위한 새 쓰기 권한을 만듭니다.
- 경로를 필드로 갖는 운동 세션을 쓰면 삽입이 이루어집니다.
- 읽기:
- 세션 소유자의 경우, 세션 읽기를 사용하여 데이터가 액세스됩니다.
- 서드 파티 앱의 경우, 사용자에게 일회성 경로 읽기를 부여하도록 안내하는 대화상자를 통해 데이터가 액세스됩니다.
사용자에게 권한 요청
클라이언트 인스턴스를 만든 후 앱은 사용자에게 권한을 요청해야 합니다. 사용자는 언제든지 권한을 부여하거나 거부할 수 있어야 합니다.
이렇게 하려면 필요한 데이터 유형의 권한 집합을 만듭니다. 먼저 집합의 권한이 Android 매니페스트에 선언되어 있는지 확인합니다.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
)
getGrantedPermissions
를 사용하여 앱에 필요한 권한이 이미 부여되었는지 확인합니다. 그렇지 않은 경우 createRequestPermissionResultContract
를 사용하여 이러한 권한을 요청합니다. 헬스 커넥트 권한 화면이 표시됩니다.
// 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
)이 있습니다.
앱에 운동 경로 기능을 추가하려면 먼저 특정 데이터 유형에 대한 쓰기 권한을 요청하세요.
각 경로는 특정 운동 세션에 연결되어 있으므로(세션 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 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))
}