歩数を記録する

ヘルスコネクトは、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 センサーを利用します。このセンサーは低消費電力に最適化されているため、バックグラウンドでの継続的な歩数トラッキングに最適です。
  • データ粒度: バッテリー寿命を節約するため、歩数データは通常、バッチ処理され、ヘルスコネクト データベースに 1 分に 1 回以下の頻度で書き込まれます。
  • アトリビューション: 前述のとおり、このデバイス上の機能で記録されたすべてのステップは、DataOriginandroid パッケージ名に帰属します。
  • 有効化: デバイス上の歩数カウント メカニズムは、デバイス上の少なくとも 1 つのアプリにヘルスコネクト内で 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)
  }
}

ユーザーはいつでも権限を付与または取り消すことができるため、アプリは定期的に付与されている権限をチェックし、権限が失われた状況に対応できるように設計する必要があります。

歩数レコードに含まれる情報

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