Desenvolver experiências de sono com a Conexão Saúde

Se você quiser criar uma experiência de monitoramento do sono no seu app, use a Conexão Saúde para fazer o seguinte:

  • Gravar sessões de sono
  • Gravar dados de estágio do sono
  • Gravar dados de sono, como frequência cardíaca, saturação de oxigênio e ritmo respiratório
  • Ler dados de sono de outros apps

Este guia descreve como criar esses recursos de sono, abordando tipos de dados, execução em segundo plano, permissões, fluxos de trabalho recomendados e práticas recomendadas.

Visão geral: como criar um monitoramento abrangente do sono

Para criar uma experiência abrangente de monitoramento do sono usando a Conexão Saúde, siga estas etapas principais:

  • Implementar corretamente as permissões com base nas permissões de saúde.
  • Gravação de sessões usando SleepSessionRecord.
  • Gravar tipos de dados como estágios do sono, frequência cardíaca e saturação de oxigênio de maneira consistente durante a sessão.
  • Gerenciar a execução em segundo plano corretamente para verificar a captura contínua de dados durante a noite.
  • Leitura de dados da sessão de leitura para resumos e análises pós-sono.

Esse fluxo de trabalho permite a interoperabilidade com outros apps da Conexão Saúde e verifica o acesso aos dados controlado pelo usuário.

Antes de começar

Antes de implementar os recursos de suspensão:

Principais conceitos

A Conexão Saúde representa os dados de sono usando alguns componentes principais. Um SleepSessionRecord funciona como o registro central do sono, contendo detalhes como horários de início ou término e estágios do sono. Durante uma sessão, vários tipos de dados, como HeartRateRecord ou OxygenSaturationRecord, podem ser registrados.

Sessões de sono

Os dados de sono são representados por SleepSessionRecord. Cada registro armazena:

  • startTime
  • endTime
  • stages: uma lista de SleepSessionRecord.Stage, incluindo sono profundo, leve, REM e acordado.
  • Metadados opcionais da sessão (título, observações)

Os apps podem gravar vários tipos de dados associados a uma sessão.

Tipos de dados

Os tipos de dados comuns registrados durante uma sessão de sono incluem:

Cada tipo de dado é armazenado como um registro individual.

Considerações de desenvolvimento

Os apps de monitoramento do sono geralmente precisam ser executados por longos períodos, com frequência em segundo plano quando a tela está desligada. Ao criar recursos de sono, é importante considerar como gerenciar a execução em segundo plano e solicitar as permissões necessárias para dados de sono.

Execução em segundo plano

Os apps de monitoramento do sono geralmente são executados durante a noite com a tela desligada. Nesse estado, use:

  • Serviços em primeiro plano para coleta de dados
  • WorkManager para gravação ou sincronização adiada
  • Estratégias de processamento em lote para gravações regulares de registros de dados granulares, como frequência cardíaca

Mantenha a continuidade mantendo o ID da sessão consistente em todas as gravações.

Permissões

Seu app precisa solicitar as permissões relevantes da Conexão Saúde antes de ler ou gravar dados de sono. Para conferir uma lista completa de tipos de dados, consulte Tipos de dados do Conexão Saúde. As permissões comuns para o sono incluem sessões de sono e métricas como frequência cardíaca ou saturação de oxigênio.

O acesso ao sono é protegido pelas seguintes permissões:

  • android.permission.health.READ_SLEEP
  • android.permission.health.WRITE_SLEEP

Para adicionar a capability de sono ao app, comece solicitando permissões para o tipo de dado SleepSession.

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

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

Para ler dados de sono, você precisa solicitar as seguintes permissões:

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

Confira um exemplo de como solicitar permissões para uma sessão de sono que inclui dados de frequência cardíaca, saturação de oxigênio e frequência respiratória:

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(SleepSessionRecord::class),
  HealthPermission.getWritePermission(SleepSessionRecord::class),
  HealthPermission.getReadPermission(HeartRateRecord::class),
  HealthPermission.getWritePermission(HeartRateRecord::class),
  HealthPermission.getReadPermission(OxygenSaturationRecord::class),
  HealthPermission.getWritePermission(OxygenSaturationRecord::class),
  HealthPermission.getReadPermission(RespiratoryRateRecord::class),
  HealthPermission.getWritePermission(RespiratoryRateRecord::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 verificar as permissões sempre antes de usá-las e lidar com situações em que elas são perdidas.

Implementar uma sessão de sono

Nesta seção, descrevemos o fluxo de trabalho recomendado para registrar dados de sono.

Para alinhar tipos de dados como HeartRateRecord ou OxygenSaturationRecord com uma sessão de sono, registre-os com carimbos de data/hora que estejam entre startTime e endTime da sessão. A Conexão Saúde não usa um identificador de sessão para vincular sessões de sono a dados detalhados. Em vez disso, a associação é implícita por intervalos de tempo sobrepostos. Ao ler dados de sono, você pode usar o período de uma sessão para consultar tipos de dados associados, conforme mostrado em Leitura de dados de sono.

Escrever uma sessão

Embora dados granulares, como frequência cardíaca, possam ser registrados durante uma sessão de sono, o SleepSessionRecord só pode ser gravado na Conexão Saúde quando a sessão termina, por exemplo, quando o usuário acorda. O registro precisa incluir a sessão startTime, endTime e uma lista de objetos SleepSessionRecord.Stage gravados durante a sessão, já que SleepSessionRecord exige que endTime seja posterior a startTime.

Para gravar uma sessão de sono:

  1. Gere um ID de registro de cliente exclusivo.
  2. Quando o usuário acordar ou o monitoramento do sono for interrompido, colete todas as fases do sono e crie um SleepSessionRecord.
  3. Insira o registro usando insertRecords.

Exemplo:

val clientRecordId = UUID.randomUUID().toString()
val sessionStartTime = LocalDateTime.of(2023, 10, 30, 22, 0).toInstant(ZoneOffset.UTC)
val sessionEndTime = LocalDateTime.of(2023, 10, 31, 7, 0).toInstant(ZoneOffset.UTC)

val stages = mutableListOf<SleepSessionRecord.Stage>()
// Add recorded stages, for example:
stages.add(SleepSessionRecord.Stage(
    startTime = sessionStartTime.plusSeconds(3600),
    endTime = sessionStartTime.plusSeconds(7200),
    stage = SleepSessionRecord.STAGE_TYPE_LIGHT)
)
stages.add(SleepSessionRecord.Stage(
    startTime = sessionStartTime.plusSeconds(7200),
    endTime = sessionStartTime.plusSeconds(10800),
    stage = SleepSessionRecord.STAGE_TYPE_DEEP)
)
// ... other stages

val session = SleepSessionRecord(
    startTime = sessionStartTime,
    startZoneOffset = ZoneOffset.UTC,
    endTime = sessionEndTime,
    endZoneOffset = ZoneOffset.UTC,
    stages = stages,
    metadata = Metadata(clientRecordId = clientRecordId)
)

healthConnectClient.insertRecords(listOf(session))

Como ler dados de sono

Os apps podem ler sessões de sono e os dados associados para resumir atividades, fornecer insights de saúde ou sincronizar dados com um servidor externo. Por exemplo, é possível ler um SleepSessionRecord e consultar o HeartRateRecord que ocorreu durante esse mesmo intervalo de tempo.

Sessão de leitura com dados associados

É possível ler sessões de sono usando um ReadRecordsRequest com SleepSessionRecord como o tipo de registro, filtrado por um período. Para ler os dados associados a uma determinada sessão, faça uma segunda solicitação para o tipo de dados selecionado, como HeartRateRecord, filtrando pelo startTime e endTime da sessão de sono.

O exemplo a seguir mostra como ler sessões de sono com dados de frequência cardíaca associados para um determinado período:

suspend fun readSleepSessionsWithAssociatedData(
    healthConnectClient: HealthConnectClient,
    startTime: Instant,
    endTime: Instant
) {
    val response = healthConnectClient.readRecords(
        ReadRecordsRequest(
            recordType = SleepSessionRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )

    for (sleepRecord in response.records) {
        // Process each session
        val stages = sleepRecord.stages
        val notes = sleepRecord.notes

        // To read specific granular data (like heart rate) that occurred during
        // this session, use the session's startTime and endTime to filter
        // the request for that data type.
        val hrResponse = healthConnectClient.readRecords(
            ReadRecordsRequest(
                recordType = HeartRateRecord::class,
                timeRangeFilter = TimeRangeFilter.between(
                    sleepRecord.startTime,
                    sleepRecord.endTime
                )
            )
        )
        for (heartRateRecord in hrResponse.records) {
            for (sample in heartRateRecord.samples) {
                val bpm = sample.beatsPerMinute
            }
        }
    }
}

Práticas recomendadas

Siga estas diretrizes para melhorar a confiabilidade dos dados e a experiência do usuário:

  • Frequência de gravação
    • Rastreamento ativo(em primeiro plano): para o monitoramento ativo do sono, grave os dados à medida que eles ficam disponíveis ou em um intervalo máximo de 15 minutos.
    • Sincronização em segundo plano:use WorkManager para gravações adiadas. Procure um intervalo de 15 minutos para equilibrar os dados em tempo real e a eficiência da bateria.
    • Loteamento:não grave cada evento do sensor individualmente. Divida suas solicitações. O Conexão Saúde processa até 1.000 registros por solicitação de gravação.
  • Mantenha os IDs de sessão estáveis e exclusivos:use identificadores consistentes para suas sessões. Se uma sessão for editada ou atualizada, usar o mesmo ID vai impedir que ela seja tratada como uma nova sessão de sono separada.
  • Use o agrupamento em lote para tipos de dados:para reduzir a sobrecarga de entrada/saída e preservar a duração da bateria, agrupe seus pontos de dados em uma única chamada insertRecords em vez de gravar cada ponto individualmente.
  • Evite gravar dados duplicados: use IDs de cliente. Ao criar registros, defina um metadata.clientRecordId. O Conexão Saúde usa isso para identificar registros exclusivos. Se você tentar gravar um registro com um clientRecordId que já existe, a Conexão Saúde vai ignorar o duplicado ou atualizar o registro atual em vez de criar um novo. Definir um metadata.clientRecordId é a maneira mais eficaz de evitar duplicatas durante novas tentativas de sincronização ou reinstalações de apps.

    val record = RespiratoryRateRecord(
        rate = 16.0,
        time = time,
        zoneOffset = ZoneOffset.UTC,
        metadata = Metadata(
            // Use a unique ID from your own database
            clientRecordId = "respiratory_rate_20231030_1"
        )
    )
    
  • Verifique os dados atuais:antes de sincronizar, consulte o período para saber se já existem registros do seu app.

  • Verifique se os carimbos de data/hora não se sobrepõem:confira se uma nova sessão não começa antes da anterior terminar. Sessões sobrepostas podem causar conflitos nos painéis de condicionamento físico e nos cálculos de resumo.

  • Forneça justificativas claras para a permissão:use o fluxo Permission.createIntent para explicar por que o app precisa de acesso a dados de saúde, como "Para analisar seus padrões de sono".

  • Teste sessões de longa duração:monitore o consumo de bateria durante sessões de várias horas para verificar se o intervalo de agrupamento e o uso do sensor não descarregam o dispositivo.

  • Alinhe os carimbos de data/hora com as taxas de sensores:combine os carimbos de data/hora dos registros com a frequência real dos sensores para manter a alta fidelidade dos dados.

Teste

Para verificar a correção dos dados e uma experiência do usuário de alta qualidade, siga estas estratégias de teste e consulte a documentação oficial Testar principais casos de uso.

Ferramentas de verificação

  • Caixa de ferramentas do app Conexão Saúde:use esse app complementar para inspecionar manualmente registros, excluir dados de teste e simular mudanças no banco de dados. Essa é a melhor maneira de verificar se seus registros estão sendo armazenados corretamente.
  • Teste de unidade com FakeHealthConnectClient:use a biblioteca de teste para verificar como o app lida com casos extremos, como revogação de permissão ou exceções de API, sem precisar de um dispositivo físico.

Checklist de qualidade

Arquitetura típica

Uma implementação de monitoramento do sono geralmente inclui:

Componente Gerencia
Controlador de sessão Estado da sessão
Timer
Lógica de agrupamento em lotes
Controladores de tipos de dados
Coleta de dados
Camada de repositório (encapsula operações da Conexão Saúde): Inserir sessão
Inserir tipos de dados
Inserir estágios do sono
Ler resumos de sessões
Camada de interface (exibições): Duração
Tipos de dados em tempo real
Visualização do estágio do sono

Solução de problemas

Sintoma Possível causa Resolução
Tipos de dados ausentes (por exemplo, frequência cardíaca) Permissões de gravação ausentes ou filtros de período incorretos. Verifique se você solicitou e se o usuário concedeu a permissão específica para o tipo de dados. Verifique se o ReadRecordsRequest usa um TimeRangeFilter que corresponde à sessão. Consulte Permissões.
A sessão não grava Carimbos de data/hora sobrepostos. A Conexão Saúde pode rejeitar registros que se sobrepõem a dados existentes do mesmo app. Valide se o startTime de uma nova sessão é posterior ao endTime da anterior.
Nenhum dado de sensor registrado durante o sono O serviço em primeiro plano foi encerrado ou está inativo. Para coletar dados de sensores durante a noite com a tela desligada, use um serviço em primeiro plano com foregroundServiceType="health".
Registros duplicados aparecem clientRecordId ausente Atribua um clientRecordId exclusivo no Metadata de cada registro. Isso permite que a Conexão Saúde faça a remoção de duplicidade se os mesmos dados forem gravados duas vezes durante uma nova tentativa de sincronização. Consulte as práticas recomendadas.

Etapas comuns de depuração

  • Verificar o estado da permissão:sempre chame getPermissionStatus() antes de tentar uma operação de leitura ou gravação. Os usuários podem revogar permissões nas configurações do sistema a qualquer momento.
  • Verifique o modo de execução:se o app não estiver coletando dados em segundo plano, verifique se você declarou as permissões corretas em AndroidManifest.xml e se o usuário não colocou o app no modo "Restrição de bateria".