Para usar as APIs do framework do Android para registros de saúde pessoais (PHRs), você precisa:
- Configurar o ambiente usando o Android Studio
- Instanciar
HealthConnectManager
- Processar permissões
- Criar origens de dados
Configurar o ambiente usando o Android Studio
Para ter acesso às APIs mais recentes, você também precisa ter acesso às ferramentas mais recentes:
- Instale a versão mais recente do Android Studio.
- No Android Studio, acesse Tools > SDK Manager.
- Na guia "SDK Platforms", selecione Android Baklava.
- Esta versão inclui o nível 36 da API e as APIs PHR.
- Na guia "SDK Tools", selecione as Android SDK Build-Tools mais recentes disponíveis.
- Clique em "OK" para instalar o SDK.
- Use a versão mais recente disponível do Plug-in do Android para Gradle.
Declarar níveis de API
Para acessar as APIs de PHR, você precisa segmentar a versão adequada do
Android. Isso é feito declarando os níveis da API em app/build.gradle
.
...
compileSdk = 36
defaultConfig {
targetSdk = 36
...
Saiba mais sobre como configurar os níveis de API na documentação uses-sdk-element e Configurar o SDK do Android 16.
Declarar permissões
Adicionar novas permissões de PHR em AndroidManifest.xml
.
Declare permissões apenas para os tipos de dados que você pretende usar no
aplicativo. A lista completa de permissões relacionadas a informações médicas é mostrada no
bloco de código. As permissões não relacionadas à PHR para a Conexão Saúde não estão incluídas.
...
<!-- Medical permissions -->
<uses-permission android:name="android.permission.health.WRITE_MEDICAL_DATA"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_CONDITIONS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_IMMUNIZATIONS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_LABORATORY_RESULTS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_MEDICATIONS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PERSONAL_DETAILS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PRACTITIONER_DETAILS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PREGNANCY"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_PROCEDURES"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_SOCIAL_HISTORY"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_VISITS"/>
<uses-permission android:name="android.permission.health.READ_MEDICAL_DATA_VITAL_SIGNS"/>
...
Se um aplicativo tentar usar uma API que exige uma permissão e
essa permissão não for declarada no manifesto do app, uma SecurityException
será gerada.
Para uma exceção do tipo SecurityException
, uma mensagem de erro útil estará
disponível em exception.localizedMessage
.
Criar uma instância do HealthConnectManager
HealthConnectManager
é a classe responsável por processar
interações de permissões, bem como leituras e gravações no repositório de dados
local da Conexão Saúde. Vamos conhecer os métodos de instância de HealthConnectManager
nas próximas seções. Como o HealthConnectManager expõe um serviço do sistema,
não é possível instanciar essa classe diretamente. Use
getSystemService
.
Em particular, observe que o serviço do sistema está intimamente
vinculado ao contexto em que é instanciado e não pode ser
acessível fora desse contexto.
import android.health.connect.HealthConnectManager
...
val healthConnectManager: HealthConnectManager = requireNotNull(applicationContext.getSystemService(HealthConnectManager::class.java))
Processar permissões
O usuário do seu app precisa conceder a permissão para acessar os dados do app Conexão Saúde. Para fazer isso, inicie uma nova atividade com as permissões especificadas e consuma a lista resultante de permissões concedidas. A interface da nova atividade permite que o usuário selecione as permissões necessárias para conceder ao app. Solicite permissões apenas para os tipos de dados que você pretende que o aplicativo use.
val MEDICAL_PERMISSIONS = arrayOf(
"android.permission.health.READ_MEDICAL_DATA_ALLERGIES_INTOLERANCES",
"android.permission.health.READ_MEDICAL_DATA_CONDITIONS",
"android.permission.health.READ_MEDICAL_DATA_IMMUNIZATIONS",
"android.permission.health.READ_MEDICAL_DATA_LABORATORY_RESULTS",
"android.permission.health.READ_MEDICAL_DATA_MEDICATIONS",
"android.permission.health.READ_MEDICAL_DATA_PERSONAL_DETAILS",
"android.permission.health.READ_MEDICAL_DATA_PRACTITIONER_DETAILS",
"android.permission.health.READ_MEDICAL_DATA_PREGNANCY",
"android.permission.health.READ_MEDICAL_DATA_PROCEDURES",
"android.permission.health.READ_MEDICAL_DATA_SOCIAL_HISTORY",
"android.permission.health.READ_MEDICAL_DATA_VISITS",
"android.permission.health.READ_MEDICAL_DATA_VITAL_SIGNS",
"android.permission.health.WRITE_MEDICAL_DATA",
)
...
private lateinit var mRequestPermissionLauncher: ActivityResultLauncher<Array<String>>
...
mRequestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
permissionMap: Map<String, Boolean> ->
requestPermissionResultHandler(permissionMap)
}
}
...
private fun requestPermissionResultHandler(permissionMap: Map<String, Boolean>) {
// Evaluate permissionMap and handle any missing permissions
}
...
mRequestPermissionLauncher.launch(MEDICAL_PERMISSIONS)
Tentar ler ou gravar sem uma permissão necessária vai resultar em
uma HealthConnectException
.
Para uma exceção do tipo HealthConnectException
, uma mensagem de erro útil estará
disponível em exception.localizedMessage
.
Criar origens de dados
Para gravar dados de saúde na Conexão Saúde, seu app precisa primeiro criar uma fonte de dados para armazenar as informações. Uma fonte de dados geralmente representa uma API ou um sistema médico específico.
No exemplo, criamos uma fonte de dados chamada My Hospital
e especificamos
a versão do FHIR.
import android.health.connect.CreateMedicalDataSourceRequest
import android.health.connect.HealthConnectManager
import android.health.connect.datatypes.FhirVersion
import android.health.connect.datatypes.MedicalDataSource
import android.net.Uri
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.os.asOutcomeReceiver
import kotlinx.coroutines.suspendCancellableCoroutine
...
private suspend fun createMedicalDataSource(
fhirBaseUri: Uri,
displayName: String,
fhirVersion: FhirVersion,
): String {
val dataSource =
suspendCancellableCoroutine<MedicalDataSource> { continuation ->
healthConnectManager.createMedicalDataSource(
CreateMedicalDataSourceRequest.Builder(fhirBaseUri,
displayName,
fhirVersion).build(),
Runnable::run,
continuation.asOutcomeReceiver(),
)
}
Log.d("CREATE_DATA_SOURCE", "Created source: $dataSource")
return "Created data source: $displayName"
}
...
createMedicalDataSource(
Uri.parse("example.fhir.com/R4/123"),
"My Hospital",
FhirVersion.parseFhirVersion("4.0.1"),
)
Gravar registros
Prepare exemplos de registros FHIR em JSON. Há várias fontes na Web com exemplos de dados no formato FHIR.
{
"resourceType": "Immunization",
"id": "immunization-1",
"status": "completed",
"vaccineCode": {
"coding": [
{
"system": "http://hl7.org/fhir/sid/cvx",
"code": "115"
},
{
"system": "http://hl7.org/fhir/sid/ndc",
"code": "58160-842-11"
}
],
"text": "Tdap"
},
"patient": {
"reference": "Patient/patient_1",
"display": "Example, Anne"
},
"encounter": {
"reference": "Encounter/encounter_unk",
"display": "GP Visit"
},
"occurrenceDateTime": "2018-05-21",
"primarySource": true,
"manufacturer": {
"display": "Sanofi Pasteur"
},
"lotNumber": "1",
"site": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ActSite",
"code": "LA",
"display": "Left Arm"
}
],
"text": "Left Arm"
},
"route": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration",
"code": "IM",
"display": "Injection, intramuscular"
}
],
"text": "Injection, intramuscular"
},
"doseQuantity": {
"value": 0.5,
"unit": "mL"
},
"performer": [
{
"function": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0443",
"code": "AP",
"display": "Administering Provider"
}
],
"text": "Administering Provider"
},
"actor": {
"reference": "Practitioner/practitioner_1",
"type": "Practitioner",
"display": "Dr Maria Hernandez"
}
}
]
}
Insira os dados:
import android.health.connect.UpsertMedicalResourceRequest
import android.health.connect.datatypes.MedicalResource
...
private suspend fun loadJSONFromAsset(assetName: String): String {
...
private suspend fun upsertMedicalResources(
requests: List<UpsertMedicalResourceRequest>
): List<MedicalResource> {
Log.d("UPSERT_RESOURCES", "Writing ${requests.size} resources")
val resources =
suspendCancellableCoroutine<List<MedicalResource>> { continuation ->
healthConnectManager.upsertMedicalResources(
requests,
Runnable::run,
continuation.asOutcomeReceiver(),
)
}
Log.d("UPSERT_RESOURCES", "Wrote ${resources.size} resources")
return resources
}
...
private suspend fun insertResource(insertedDataSourceId: String, resource: String): String {
val insertedResources =
upsertMedicalResources(
listOf(
UpsertMedicalResourceRequest.Builder(
insertedDataSourceId,
FhirVersion.parseFhirVersion("4.0.1"),
resource,
)
.build()
)
)
return insertedResources.joinToString(
separator = "\n",
transform = MedicalResource::toString,
)
}
...
val immunizationResource =
loadJSONFromAsset("immunization_1.json")
insertResource(dataSource.id, immunizationResource)
upsertMedicalResources
usa como argumento uma lista de
UpsertMedicalResourceRequest
. Se qualquer UpsertMedicalResourceRequest
individual
transmitido na chamada não for inserido, nenhuma gravação será confirmada no
repositório para a lista inteira de UpsertMedicalResourceRequest
.
Se qualquer solicitação tiver IDs MedicalDataSource
inválidos, a API vai gerar um
IllegalArgumentException
. Se uma solicitação for considerada inválida por qualquer outro
motivo, o autor da chamada vai receber um HealthConnectException
.
A chave exclusiva de uma solicitação específica é a combinação do ID da fonte de dados, do tipo de recurso FHIR e do ID do recurso FHIR. Se esses três itens em uma solicitação corresponderem a um registro existente, uma atualização será acionada. Caso contrário, um novo registro será criado.
Ler registros
Leia os registros por tipo e processe os resultados como achar melhor.
import android.health.connect.ReadMedicalResourcesInitialRequest
import android.health.connect.ReadMedicalResourcesResponse
import android.health.connect.datatypes.MedicalResource.MEDICAL_RESOURCE_TYPE_IMMUNIZATIONS
...
private suspend fun readImmunization(): List<MedicalResource> {
var receiver: OutcomeReceiver<ReadMedicalResourcesResponse, HealthConnectException>
val request: ReadMedicalResourcesInitialRequest =
ReadMedicalResourcesInitialRequest.Builder(MEDICAL_RESOURCE_TYPE_IMMUNIZATIONS).build()
val resources =
suspendCancellableCoroutine<ReadMedicalResourcesResponse> { continuation ->
receiver = continuation.asOutcomeReceiver()
healthConnectManager.readMedicalResources(request, Runnable::run, receiver)
}
.medicalResources
Log.d("READ_MEDICAL_RESOURCES", "Read ${resources.size} resources")
return resources
}