걸음 수 추적

헬스 커넥트는 StepsRecord를 사용하여 걸음 수를 기록하는 걸음 수 데이터 유형을 제공합니다. 걸음 수는 건강 및 피트니스 추적의 기본적인 측정 단위입니다.

모바일 걸음 수 읽기

Android 14 (API 수준 34) 및 SDK 확장 프로그램 버전 20 이상에서 헬스 커넥트는 온디바이스 걸음 수 계산을 제공합니다. 앱에 READ_STEPS 권한이 부여되면 헬스 커넥트에서 Android 기반 기기의 걸음 수를 캡처하기 시작하고 사용자에게 헬스 커넥트 걸음 수 항목에 걸음 수 데이터가 자동으로 추가되어 표시됩니다.

온디바이스 걸음 수 측정이 지원되는지 확인하려면 기기가 Android 14 (API 수준 34)를 실행하고 SDK 확장 프로그램 버전이 20 이상인지 확인해야 합니다. 다음 코드를 사용할 수 있습니다.

val isStepTrackingAvailable =
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
        SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 20

헬스 커넥트에서 캡처한 모바일 걸음 수의 DataOrigin은 패키지 이름 android로 설정됩니다. 앱이 aggregate를 사용하여 집계된 걸음 수를 읽기만 하고 DataOrigin로 필터링하지 않으면 온디바이스 걸음 수가 합계에 자동으로 포함됩니다.

앱에서 기기 내 걸음 수를 읽어야 하거나 소스 애플리케이션 또는 기기별로 분류된 걸음 수 데이터를 표시하는 경우 DataOriginandroid인 레코드를 쿼리하면 됩니다. 앱이 걸음 수 데이터의 출처를 표시하는 경우 android 패키지의 데이터를 현재 기기에 귀속시켜야 합니다. '내 휴대전화'와 같은 라벨을 사용하거나 Settings.Global.getString(resolver, Settings.Global.DEVICE_NAME)로 기기 이름을 가져오거나 레코드의 메타데이터에서 Device 필드를 검사하여 이를 확인할 수 있습니다.

다음 예는 android 데이터 출처를 필터링하여 집계된 모바일 걸음 수 데이터를 읽는 방법을 보여줍니다.

suspend fun readStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = setOf(DataOrigin("android"))
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

온디바이스 걸음 수 측정

기기 내 걸음 수 측정 기능에 대해 자세히 알아보세요.

  • 센서 사용: 헬스 커넥트는 SensorManagerTYPE_STEP_COUNTER 센서를 활용합니다. 이 센서는 전력 소비가 낮도록 최적화되어 있어 지속적인 백그라운드 걸음 수 추적에 적합합니다.
  • 데이터 세분성: 배터리 수명을 보존하기 위해 걸음 수 데이터는 일반적으로 일괄 처리되어 헬스 커넥트 데이터베이스에 분당 한 번 이하로 기록됩니다.
  • 저작자 표시: 앞에서 언급한 것처럼 이 온디바이스 기능으로 기록된 모든 단계는 DataOrigin에서 android 패키지 이름에 기여한 것으로 표시됩니다.
  • 활성화: 기기 내 걸음 수 측정 메커니즘은 기기의 하나 이상의 애플리케이션에 헬스 커넥트 내에서 READ_STEPS 권한이 부여된 경우에만 활성화됩니다.

헬스 커넥트 사용 가능 여부 확인

헬스 커넥트를 사용하기 전에 앱에서 사용자의 기기에서 헬스 커넥트를 사용할 수 있는지 확인해야 합니다. 헬스 커넥트가 일부 기기에 사전 설치되어 있지 않거나 사용 중지되어 있을 수 있습니다. HealthConnectClient.getSdkStatus() 메서드를 사용하여 사용 가능 여부를 확인할 수 있습니다.

헬스 커넥트 사용 가능 여부를 확인하는 방법

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()에서 반환된 상태에 따라 필요한 경우 Google Play 스토어에서 헬스 커넥트를 설치하거나 업데이트하도록 사용자에게 안내할 수 있습니다.

필수 권한

걸음 수에 대한 액세스는 다음 권한으로 보호됩니다.

  • android.permission.health.READ_STEPS
  • android.permission.health.WRITE_STEPS

앱에 걸음 수 기능을 추가하려면 먼저 Steps 데이터 유형에 대한 쓰기 권한을 요청하세요.

걸음 수를 쓰기 위해 선언해야 하는 권한은 다음과 같습니다.

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

단계를 읽으려면 다음 권한을 요청해야 합니다.

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

사용자에게 권한 요청

클라이언트 인스턴스를 만든 후 앱은 사용자에게 권한을 요청해야 합니다. 사용자는 언제든지 권한을 부여하거나 거부할 수 있어야 합니다.

이렇게 하려면 필요한 데이터 유형의 권한 집합을 만드세요. 세트에 있는 권한이 먼저 Android 매니페스트에 선언되어 있는지 확인합니다.

// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(StepsRecord::class),
  HealthPermission.getWritePermission(StepsRecord::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)
  }
}

사용자는 언제든지 권한을 부여하거나 취소할 수 있으므로 앱은 부여된 권한을 주기적으로 확인하고 권한이 손실된 시나리오를 처리해야 합니다.

Steps 레코드에 포함된 정보

StepsRecord에는 다음 정보가 포함됩니다.

  • count: 시간 간격 동안 걸은 걸음 수입니다(Long).
  • startTime: 측정 간격의 시작 시간입니다.
  • endTime: 측정 간격의 종료 시간입니다.
  • startZoneOffset: 시작 시간의 영역 오프셋입니다.
  • endZoneOffset: 종료 시간의 영역 오프셋입니다.

지원되는 집계

StepsRecord에 사용할 수 있는 집계 값은 다음과 같습니다.

StepsCadenceRecord에 사용할 수 있는 집계 값은 다음과 같습니다.

사용 예

다음 섹션에서는 StepsRecord 데이터를 읽고 쓰는 방법을 보여줍니다.

걸음 수 데이터 쓰기

앱은 StepsRecord 인스턴스를 삽입하여 걸음 수 데이터를 쓸 수 있습니다. 다음 예는 사용자가 걸은 1,000걸음을 기록하는 방법을 보여줍니다.

suspend fun writeStepsData(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant,
    startZoneOffset: ZoneOffset,
    endZoneOffset: ZoneOffset
) {
    try {
        val stepsRecord = StepsRecord(
            startTime = startTime,
            startZoneOffset = startZoneOffset,
            endTime = endTime,
            endZoneOffset = endZoneOffset,
            count = 1000
        )
        healthConnectClient.insertRecords(listOf(stepsRecord))
    } catch (e: Exception) {
        // Run error handling
    }
}

집계 데이터 읽기

걸음 수 데이터를 읽는 가장 일반적인 방법은 일정 기간 동안의 총 걸음 수를 집계하는 것입니다. 다음 예는 특정 시간 범위 내에서 사용자의 총 걸음 수를 읽는 방법을 보여줍니다.

suspend fun readStepsAggregate(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        // The result may be null if no data is available in the time range
        val stepCount = response[StepsRecord.COUNT_TOTAL]
    } catch (e: Exception) {
        // Run error handling here
    }
}

원시 데이터 읽기

다음 예는 시작 시간과 종료 시간 사이에 원시 StepsRecord 데이터를 읽는 방법을 보여줍니다.

suspend fun readStepsRaw(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    try {
        val response = healthConnectClient.readRecords(
            ReadRecordsRequest(
                recordType = StepsRecord::class,
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
            )
        )
        for (record in response.records) {
            // Process each record
        }
    } catch (e: Exception) {
        // Run error handling here
    }
}