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.

Este guia é compatível com a versão 1.1.0-alpha12 ou mais recente do app Conexão Saúde.

Ler passos em dispositivos móveis

Com o Android 14 (nível 34 da API) e a versão 20 ou mais recente da extensão do SDK, a Conexão Saúde oferece contagem de passos no dispositivo. Se algum app tiver recebido a permissão READ_STEPS, a Conexão Saúde vai começar a capturar passos do dispositivo Android, e os usuários vão ver os dados de passos adicionados automaticamente às entradas de Passos da 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 (nível 34 da API) e tem pelo menos a versão 20 da extensão do SDK:

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

Se o app ler contagens de passos agregadas usando aggregate e não filtrar por DataOrigin, os passos no dispositivo serão incluídos automaticamente no total, e nenhuma mudança será necessária para a atualização de junho de 2026.

Mudança na atribuição de passos no dispositivo

A partir da atualização de junho de 2026, as etapas monitoradas nativamente pelo Conexão Saúde serão atribuídas a um nome de pacote sintético (SPN, na sigla em inglês), como com.android.healthconnect.phone.jd5bdd37e1a8d3667a05d0abebfc4a89e.

Anteriormente, as etapas integradas eram atribuídas ao nome do pacote android. Os dados históricos de passos registrados antes de junho de 2026 mantêm o nome do pacote android.

Os SPNs são específicos do dispositivo e têm escopo por aplicativo para proteger a privacidade do usuário:

  • Estável:o SPN do dispositivo atual é estável para o aplicativo.
  • Escopo do aplicativo:aplicativos diferentes no mesmo dispositivo veem SPNs diferentes para dados de passos no dispositivo.

Consultar etapas no dispositivo

Como os SPNs têm escopo e são específicos do dispositivo, não codifique os valores de SPN. Em vez disso, use a API getCurrentDeviceDataSource para recuperar o SPN do dispositivo atual.

Embora a contagem de passos no dispositivo exija a versão 20 ou mais recente da extensão do SDK, a API getCurrentDeviceDataSource() está disponível no Android 14 (nível 34 da API) com a versão 22 ou mais recente da extensão do SDK. Para usar essa API, defina compileSdkExtension como 22 ou mais recente no arquivo build.gradle.kts ou build.gradle do módulo:

Kotlin

android {
    compileSdk = 35
    compileSdkExtension = 22
}

Groovy

android {
    compileSdk 35
    compileSdkExtension 22
}

A API getCurrentDeviceDataSource() ainda não está disponível na biblioteca Jetpack da Conexão Saúde. Os exemplos a seguir usam a API do framework do Android, que requer um callback Executor e um OutcomeReceiver:

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.OutcomeReceiver

val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)
healthConnectManager?.getCurrentDeviceDataSource(
    context.mainExecutor,
    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
        override fun onResult(result: DeviceDataSource) {
            val currentDeviceSpn = result.deviceDataOrigin.packageName
        }

        override fun onError(error: HealthConnectException) {
            // Handle error
        }
    }
)

Se o app precisar ler as etapas no dispositivo ou se ele mostrar dados de etapas divididos por aplicativo ou dispositivo de origem, consulte os registros em que o DataOrigin é android ou corresponde ao SPN do dispositivo. Se o app mostrar a atribuição de dados de passos, use metadata.device para identificar o dispositivo de origem de registros individuais. Para etapas no dispositivo identificadas por um SPN em dados agregados, use metadados do dispositivo, como model ou manufacturer de DeviceDataSource para atribuição, ou use um rótulo genérico, como "Seu smartphone", para etapas no dispositivo.

O exemplo a seguir mostra como ler dados agregados de contagem de passos no dispositivo filtrando por android e pelo SPN do dispositivo atual:

import android.content.Context
import android.health.connect.DeviceDataSource
import android.health.connect.HealthConnectException
import android.health.connect.HealthConnectManager
import android.os.Build
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.records.metadata.DataOrigin
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
import java.time.Instant
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine

suspend fun readDeviceStepsByTimeRange(
    healthConnectClient: HealthConnectClient,
    context: Context,
    startTime: Instant,
    endTime: Instant
) {
    // 1. Check if SDK Extension 22+ is available for getCurrentDeviceDataSource()
    val isDataSourceApiAvailable =
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
            SdkExtensions.getExtensionVersion(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) >= 22

    try {
        val healthConnectManager = context.getSystemService(HealthConnectManager::class.java)

        // 2. Safely fetch the package name only if the API is available
        val currentDeviceSpn = if (isDataSourceApiAvailable && healthConnectManager != null) {
            suspendCancellableCoroutine { continuation ->
                healthConnectManager.getCurrentDeviceDataSource(
                    context.mainExecutor,
                    object : OutcomeReceiver<DeviceDataSource, HealthConnectException> {
                        override fun onResult(result: DeviceDataSource) {
                            continuation.resume(result.deviceDataOrigin.packageName)
                        }

                        override fun onError(error: HealthConnectException) {
                            continuation.resumeWithException(error)
                        }
                    }
                )
            }
        } else {
            null
        }

        val dataOriginFilters = mutableSetOf(DataOrigin("android"))

        // 3. Explicit null-safety check using .let
        currentDeviceSpn?.let {
            dataOriginFilters.add(DataOrigin(it))
        }

        val response = healthConnectClient.aggregate(
            AggregateRequest(
                metrics = setOf(StepsRecord.COUNT_TOTAL),
                timeRangeFilter = TimeRangeFilter.between(startTime, endTime),
                dataOriginFilter = dataOriginFilters
            )
        )

        val stepCount = response[StepsRecord.COUNT_TOTAL]

    } catch (e: Exception) {
        // Now this catch block only handles actual runtime exceptions,
        // rather than Errors from missing methods.
    }
}

Contagem de passos no dispositivo

  • Uso de sensores: a 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 economizar bateria, os dados de passos geralmente são agrupados e gravados no banco de dados do Conexão Saúde no máximo uma vez por minuto.
  • Atribuição: as etapas registradas por esse recurso antes de junho de 2026 são atribuídas ao nome do pacote android no DataOrigin. Após essa data, eles serão atribuídos a um SPN específico do dispositivo. Consulte Mudança de atribuição para passos no dispositivo.
  • Ativação: o mecanismo de contagem de passos no dispositivo só é ativado quando pelo menos um aplicativo no dispositivo recebe a permissão READ_STEPS na Conexão Saúde.

Verificar a disponibilidade do app 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 do Conexão Saúde

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata"
    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 passos ao app, solicite permissões para o tipo de dados Steps.

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

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

Para ler as etapas, você precisa solicitar 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. Os usuários precisam concedê-las ou negá-las a qualquer momento. Para fazer isso, crie um conjunto de permissões para os tipos de dados necessários. Verifique se as permissões no conjunto foram declaradas no manifesto do Android primeiro.

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.
val permissions = setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class),
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class)
    )

val requestPermissionsLauncher = rememberLauncherForActivityResult(
    contract = PermissionController.createRequestPermissionResultContract()
) { grantedPermissions ->
    if (grantedPermissions.containsAll(permissions)) {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") }
    } else {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") }
    }
}
Como os usuários podem conceder ou revogar permissões a qualquer momento, seu app precisa verificar as permissões sempre antes de usá-las e lidar com situações em que elas são perdidas.

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

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 deslocamento de fuso horário para o horário de início.
  • endZoneOffset: o deslocamento de fuso horário para o horário de término.

Agregações compatíveis

Os seguintes valores agregados estão disponíveis para StepsRecord:

Os seguintes valores agregados estão disponíveis para StepsCadenceRecord:

Exemplo de uso

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

Gravar dados de passos

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

val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime)
val stepsRecord = StepsRecord(
    count = 120,
    startTime = startTime,
    endTime = endTime,
    startZoneOffset = zoneOffset,
    endZoneOffset = zoneOffset,
    metadata = Metadata.autoRecorded(
        device = Device(type = Device.TYPE_WATCH)
    )
)
healthConnectClient.insertRecords(listOf(stepsRecord))

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(startTime: Instant, endTime: Instant): Long {
    val response = healthConnectClient.aggregate(
        AggregateRequest(
            metrics = setOf(StepsRecord.COUNT_TOTAL),
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
    return response[StepsRecord.COUNT_TOTAL] ?: 0L
}

Ler dados brutos

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

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        StepsRecord::class,
        timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
    )
)
response.records.forEach { record ->
    /* Process records */
}