Monitorar passos

O Conexão Saúde oferece um tipo de dados de passos para registrar contagens de passos usando o StepsRecord. Os passos são uma medida fundamental no monitoramento de saúde e condicionamento físico.

Ler passos em dispositivos móveis

Com o Android 14 (nível 34 da API) e a extensão do SDK versão 20 ou mais recente, a Conexão Saúde oferece contagem de passos no dispositivo. Se algum app tiver recebido a permissão READ_STEPS, o Conexão Saúde vai começar a capturar etapas do dispositivo Android, e os usuários vão ver os dados de etapas adicionados automaticamente às entradas de Etapas do Conexão Saúde.

Para verificar se a contagem de passos no dispositivo está disponível, confira se o dispositivo está executando o Android 14 (API de nível 34) e tem pelo menos a versão 20 da extensão do SDK. Use o seguinte código:

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

Os passos capturados pela Conexão Saúde têm o DataOrigin definido como o nome do pacote android. Se o app apenas ler contagens de passos agregadas usando aggregate e não filtrar por DataOrigin, as etapas no dispositivo serão incluídas automaticamente no total.

Se o app precisar ler etapas no dispositivo ou mostrar dados de etapas divididos por aplicativo ou dispositivo de origem, você poderá consultar registros em que o DataOrigin é android. Se o app mostrar atribuição para dados de etapas, atribua os dados do pacote Android ao dispositivo atual. Para isso, use um rótulo como "Seu smartphone", recupere o nome do dispositivo com Settings.Global.getString(resolver, Settings.Global.DEVICE_NAME) ou inspecione o campo Device nos metadados do registro.

O exemplo a seguir mostra como ler dados agregados de contagem de passos em dispositivos móveis filtrando a origem de dados 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
    }
}

Contagem de passos no dispositivo

Mais detalhes sobre o recurso de contagem de passos no dispositivo:

  • Uso de sensores: o app Conexão Saúde usa o sensor TYPE_STEP_COUNTER do SensorManager. Esse sensor é otimizado para baixo consumo de energia, o que o torna ideal para o rastreamento contínuo de passos em segundo plano.
  • Granularidade dos dados: para conservar a duração da bateria, os dados de passos geralmente são agrupados e gravados no banco de dados da Conexão Saúde no máximo uma vez por minuto.
  • Atribuição: como mencionado anteriormente, todas as etapas registradas por esse recurso no dispositivo são atribuídas ao nome do pacote android no DataOrigin.
  • Ativação: o mecanismo de contagem de passos no dispositivo só fica ativo quando pelo menos um aplicativo no dispositivo recebe a permissão READ_STEPS na Conexão Saúde.

Verificar a disponibilidade da Conexão Saúde

Antes de tentar usar o Conexão Saúde, seu app precisa verificar se ele está disponível no dispositivo do usuário. A Conexão Saúde pode não estar pré-instalada em todos os dispositivos ou pode estar desativada. É possível verificar a disponibilidade usando o método HealthConnectClient.getSdkStatus().

Como verificar a disponibilidade da Conexão Saúde

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
}

Dependendo do status retornado por getSdkStatus(), você pode orientar o usuário a instalar ou atualizar o Conexão Saúde na Google Play Store, se necessário.

Permissões necessárias

O acesso às etapas é protegido pelas seguintes permissões:

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

Para adicionar a capability de etapas ao app, comece solicitando permissões de gravação do tipo de dado Steps.

Confira a permissão necessária para poder gravar etapas:

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

Para ler as etapas, solicite as seguintes permissões:

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

Solicitar permissões do usuário

Depois de criar uma instância de cliente, seu app precisa solicitar permissões aos usuários. Eles precisam concedê-las ou negá-las a qualquer momento.

Para isso, crie um conjunto de permissões para os tipos de dados necessários. Verifique se as permissões no conjunto foram declaradas primeiro no manifesto do Android.

// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(StepsRecord::class),
  HealthPermission.getWritePermission(StepsRecord::class)
)

Use getGrantedPermissions para verificar se o app já tem as permissões necessárias concedidas. Caso contrário, use createRequestPermissionResultContract para solicitá-las. Isso mostra a tela de permissões da Conexão Saúde.

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

Como os usuários podem conceder ou revogar permissões a qualquer momento, seu app precisa checar com regularidade as que foram concedidas e lidar com situações em que elas são perdidas.

Informações incluídas em um registro de etapas

Cada StepsRecord contém as seguintes informações:

  • count: o número de passos dados no intervalo de tempo, como um Long.
  • startTime: o horário de início do intervalo de medição.
  • endTime: o horário de término do intervalo de medição.
  • startZoneOffset: o ajuste de horário da zona para o horário de início.
  • endZoneOffset: o ajuste de horário para o horário de término.

Agregações compatíveis

Estes são os valores agregados disponíveis para StepsRecord:

Estes são os valores agregados disponíveis para StepsCadenceRecord:

Exemplo de uso

As seções a seguir mostram como ler e gravar dados do StepsRecord.

Gravar dados de passos

Seu app pode gravar dados de contagem de passos inserindo instâncias de StepsRecord. O exemplo a seguir mostra como registrar 1.000 etapas realizadas por um usuário:

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

Ler dados agregados

A maneira mais comum de ler dados de passos é agregar o total de passos em um período. O exemplo abaixo mostra como ler a contagem total de passos de um usuário em um determinado período:

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

Ler dados brutos

O exemplo a seguir mostra como ler dados brutos de StepsRecord entre um horário de início e término:

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