HealthConnectClient
interface HealthConnectClient
FakeHealthConnectClient |
Fake |
Interface to access health and fitness records.
Summary
Constants |
|
|---|---|
const Int |
SDK_AVAILABLE = 3The Health Connect SDK APIs are available. |
const Int |
SDK_UNAVAILABLE = 1The Health Connect SDK is unavailable on this device at the time. |
const Int |
The Health Connect SDK APIs are currently unavailable, the provider is either not installed or needs to be updated. |
Public companion functions |
|
|---|---|
Intent |
getHealthConnectManageDataIntent(Intent to open Health Connect data management screen on this phone. |
HealthConnectClient |
getOrCreate(context: Context, providerPackageName: String)Retrieves an IPC-backed |
Int |
getSdkStatus(context: Context, providerPackageName: String)Determines whether the Health Connect SDK is available on this device at the moment. |
Public companion properties |
|
|---|---|
String |
Intent action to open Health Connect settings on this phone. |
Public functions |
|
|---|---|
suspend AggregationResult |
aggregate(request: AggregateRequest)Reads |
suspend List<AggregationResultGroupedByDuration> |
Reads |
suspend List<AggregationResultGroupedByPeriod> |
Reads |
open suspend MedicalDataSource |
@ExperimentalPersonalHealthRecordApiCreates a |
open suspend Unit |
@ExperimentalPersonalHealthRecordApiDeletes a |
open suspend Unit |
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")Deletes a list of |
open suspend Unit |
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")Deletes |
suspend Unit |
deleteRecords(Deletes any |
suspend Unit |
deleteRecords(Deletes one or more |
suspend ChangesResponse |
getChanges(changesToken: String)Retrieves changes in Health Connect, from a specific point in time represented by provided |
suspend String |
getChangesToken(request: ChangesTokenRequest)Retrieves a changes-token, representing a point in time in the underlying Android Health Platform for a given |
open suspend List<MedicalDataSource> |
Gets |
open suspend List<MedicalDataSource> |
Gets the requested |
suspend InsertRecordsResponse |
insertRecords(records: List<Record>)Inserts one or more |
open suspend List<MedicalResource> |
Reads a collection of |
open suspend ReadMedicalResourcesResponse |
Reads |
suspend ReadRecordResponse<T> |
<T : Record> readRecord(recordType: KClass<T>, recordId: String)Reads one |
suspend ReadRecordsResponse<T> |
<T : Record> readRecords(request: ReadRecordsRequest<T>)Retrieves a collection of |
suspend Unit |
updateRecords(records: List<Record>)Updates one or more |
open suspend List<MedicalResource> |
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")Inserts or updates a list of |
Public properties |
|
|---|---|
open HealthConnectFeatures |
Access operations related to feature availability. |
PermissionController |
Access operations related to permissions. |
Extension functions |
|
|---|---|
suspend inline Unit |
<T : Record> HealthConnectClient.deleteRecords(Deletes any |
suspend inline Unit |
<T : Record> HealthConnectClient.deleteRecords(Deletes one or more |
suspend inline ReadRecordResponse<T> |
<T : Record> HealthConnectClient.readRecord(recordId: String)Reads one |
Constants
SDK_AVAILABLE
const val SDK_AVAILABLE = 3: Int
The Health Connect SDK APIs are available.
Apps can subsequently call getOrCreate to get an instance of HealthConnectClient.
SDK_UNAVAILABLE
const val SDK_UNAVAILABLE = 1: Int
The Health Connect SDK is unavailable on this device at the time. This can be due to the device running a lower than required Android Version.
Apps should hide any integration points to Health Connect in this case.
SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED
const val SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED = 2: Int
The Health Connect SDK APIs are currently unavailable, the provider is either not installed or needs to be updated.
Apps may choose to redirect to package installers to find a suitable APK.
Public companion functions
getHealthConnectManageDataIntent
fun getHealthConnectManageDataIntent(
context: Context,
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME
): Intent
Intent to open Health Connect data management screen on this phone. Developers should use this if they want to re-direct the user to Health Connect data management.
| Parameters | |
|---|---|
context: Context |
the context |
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME |
optional alternative package provider to choose for backend implementation |
| Returns | |
|---|---|
Intent |
Intent to open Health Connect data management screen. |
getOrCreate
fun getOrCreate(
context: Context,
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME
): HealthConnectClient
Retrieves an IPC-backed HealthConnectClient instance binding to an available implementation.
| Parameters | |
|---|---|
context: Context |
the context |
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME |
optional alternative package provider to choose for backend implementation |
| Returns | |
|---|---|
HealthConnectClient |
instance of |
| Throws | |
|---|---|
kotlin.UnsupportedOperationException |
if service not available due to SDK version too low or running in a profile |
kotlin.IllegalStateException |
if the SDK is not available |
| See also | |
|---|---|
getSdkStatus |
getSdkStatus
fun getSdkStatus(
context: Context,
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME
): Int
Determines whether the Health Connect SDK is available on this device at the moment.
import androidx.health.connect.client.HealthConnectClient val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName) if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) { return // early return as there is no viable integration } if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) { // 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 } val healthConnectClient = HealthConnectClient.getOrCreate(context) // Issue operations with healthConnectClient
| Parameters | |
|---|---|
context: Context |
the context |
providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME |
optional package provider to choose for backend implementation |
| Returns | |
|---|---|
Int |
One of |
Public companion properties
ACTION_HEALTH_CONNECT_SETTINGS
val ACTION_HEALTH_CONNECT_SETTINGS: String
Intent action to open Health Connect settings on this phone. Developers should use this if they want to re-direct the user to Health Connect.
Public functions
aggregate
suspend fun aggregate(request: AggregateRequest): AggregationResult
Reads AggregateMetrics according to requested read criteria: Records from AggregateRequest.dataOriginFilter and within AggregateRequest.timeRangeFilter.
import androidx.health.connect.client.records.DistanceRecord import androidx.health.connect.client.request.AggregateRequest import androidx.health.connect.client.time.TimeRangeFilter val response = healthConnectClient.aggregate( AggregateRequest( metrics = setOf(DistanceRecord.DISTANCE_TOTAL), timeRangeFilter = TimeRangeFilter.between(startTime, endTime), ) ) // The result may be null if no data is available in the time range. val distanceTotalInMeters = response[DistanceRecord.DISTANCE_TOTAL]?.inMeters
Example code to retrieve statistical aggregates like maximum or minimum heart rate:
import androidx.health.connect.client.records.HeartRateRecord import androidx.health.connect.client.request.AggregateRequest import androidx.health.connect.client.time.TimeRangeFilter val response = healthConnectClient.aggregate( AggregateRequest( setOf(HeartRateRecord.BPM_MAX, HeartRateRecord.BPM_MIN), timeRangeFilter = TimeRangeFilter.between(startTime, endTime), ) ) // The result may be null if no data is available in the time range. val minimumHeartRate = response[HeartRateRecord.BPM_MIN] val maximumHeartRate = response[HeartRateRecord.BPM_MAX]
| Parameters | |
|---|---|
request: AggregateRequest |
|
| Returns | |
|---|---|
AggregationResult |
the |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. Example code to aggregate cumulative data like distance: |
aggregateGroupByDuration
suspend fun aggregateGroupByDuration(request: AggregateGroupByDurationRequest): List<AggregationResultGroupedByDuration>
Reads AggregateMetrics according to requested read criteria specified in AggregateGroupByDurationRequest.
This method is similar to aggregate but instead of returning one AggregationResult for the entire query's time interval, it returns a list of AggregationResultGroupedByDuration, with each row keyed by start and end time. For example: steps for today bucketed by hours.
An AggregationResultGroupedByDuration is returned only if there are Record to aggregate within start and end time of the row.
import androidx.health.connect.client.records.StepsRecord import androidx.health.connect.client.request.AggregateGroupByDurationRequest import androidx.health.connect.client.time.TimeRangeFilter val response = healthConnectClient.aggregateGroupByDuration( AggregateGroupByDurationRequest( metrics = setOf(StepsRecord.COUNT_TOTAL), timeRangeFilter = TimeRangeFilter.between(startTime, endTime), timeRangeSlicer = Duration.ofMinutes(1), ) ) for (monthlyResult in response) { // The result may be null if no data is available in the time range. val totalSteps = monthlyResult.result[StepsRecord.COUNT_TOTAL] }
| Parameters | |
|---|---|
request: AggregateGroupByDurationRequest |
|
| Returns | |
|---|---|
List<AggregationResultGroupedByDuration> |
a list of |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. Example code to retrieve cumulative step count for each minute within provided time range: |
aggregateGroupByPeriod
suspend fun aggregateGroupByPeriod(request: AggregateGroupByPeriodRequest): List<AggregationResultGroupedByPeriod>
Reads AggregateMetrics according to requested read criteria specified in AggregateGroupByPeriodRequest.
This method is similar to aggregate but instead of returning one AggregationResult for the entire query's time interval, it returns a list of AggregationResultGroupedByPeriod, with each row keyed by start and end time. For example: steps for this month bucketed by day.
An AggregationResultGroupedByPeriod is returned only if there are Record to aggregate within start and end time of the row.
import androidx.health.connect.client.records.StepsRecord import androidx.health.connect.client.request.AggregateGroupByPeriodRequest import androidx.health.connect.client.time.TimeRangeFilter val response = healthConnectClient.aggregateGroupByPeriod( AggregateGroupByPeriodRequest( metrics = setOf(StepsRecord.COUNT_TOTAL), timeRangeFilter = TimeRangeFilter.between(startTime, endTime), timeRangeSlicer = Period.ofMonths(1), ) ) for (monthlyResult in response) { // The result may be null if no data is available in the time range. val totalSteps = monthlyResult.result[StepsRecord.COUNT_TOTAL] }
| Parameters | |
|---|---|
request: AggregateGroupByPeriodRequest |
|
| Returns | |
|---|---|
List<AggregationResultGroupedByPeriod> |
a list of |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. Example code to retrieve cumulative step count for each month within provided time range: |
createMedicalDataSource
@ExperimentalPersonalHealthRecordApi
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
open suspend fun createMedicalDataSource(request: CreateMedicalDataSourceRequest): MedicalDataSource
Creates a MedicalDataSource using a CreateMedicalDataSourceRequest.
Regarding permissions:
-
Caller must hold
PERMISSION_WRITE_MEDICAL_DATAin order to call this API, otherwise aSecurityExceptionwill be thrown. -
With
PERMISSION_WRITE_MEDICAL_DATAgranted, caller is permitted to call this API in either foreground or background.
Medical data is represented using the Fast Healthcare Interoperability Resources (FHIR) standard.
A MedicalDataSource needs to be created before any MedicalResources for that source can be inserted. Separate MedicalDataSources should be created for medical records coming from different sources (e.g. different FHIR endpoints, different healthcare systems), unless the data has been reconciled and all records have a unique combination of resource type and resource id.
The CreateMedicalDataSourceRequest.displayName must be unique across all medical data sources created by an app, otherwise an IllegalArgumentException will be thrown. See CreateMedicalDataSourceRequest.fhirBaseUri for more details on the FHIR base URI. The data source can not be updated after creation.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.request.CreateMedicalDataSourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Create a `MedicalDataSource` // Note that `displayName` must be unique across `MedicalDataSource`s val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) )
| Parameters | |
|---|---|
request: CreateMedicalDataSourceRequest |
request containing details of the |
| Returns | |
|---|---|
MedicalDataSource |
the created |
| Throws | |
|---|---|
java.lang.SecurityException |
if caller does not hold |
deleteMedicalDataSourceWithData
@ExperimentalPersonalHealthRecordApi
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
open suspend fun deleteMedicalDataSourceWithData(id: String): Unit
Deletes a MedicalDataSource and all data contained within it.
Regarding permissions:
-
Caller must hold
PERMISSION_WRITE_MEDICAL_DATAin order to call this API, otherwise aSecurityExceptionwill be thrown. -
With
PERMISSION_WRITE_MEDICAL_DATAgranted, caller is permitted to call this API in either foreground or background. -
Caller may only delete data sources it created.
Medical data is represented using the Fast Healthcare Interoperability Resources (FHIR) standard.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.request.CreateMedicalDataSourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or creates a `MedicalDataSource` // Each `MedicalDataSource` is assigned an `id` by the system on creation val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Delete the `MedicalDataSource` that has the specified `id` healthConnectClient.deleteMedicalDataSourceWithData(medicalDataSource.id)
| Parameters | |
|---|---|
id: String |
The id of the data source to delete. |
| Throws | |
|---|---|
kotlin.IllegalArgumentException |
if |
deleteMedicalResources
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
@ExperimentalPersonalHealthRecordApi
open suspend fun deleteMedicalResources(ids: List<MedicalResourceId>): Unit
Deletes a list of MedicalResources by the provided list of MedicalResourceIds.
-
If any ID in
idsis invalid, the API will throw anIllegalArgumentException, and nothing will be deleted. -
If any ID in
idsdoes not exist, that ID will be ignored, while deletion on other IDs will be performed.
Regarding permissions:
-
Caller must hold
PERMISSION_WRITE_MEDICAL_DATAin order to call this API, even then, it can only delete its own data. If any of the items inidsbelongs to another app, they will be ignored. -
Deletes are permitted in the foreground or background.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.records.MedicalResource import androidx.health.connect.client.records.MedicalResourceId import androidx.health.connect.client.request.CreateMedicalDataSourceRequest import androidx.health.connect.client.request.UpsertMedicalResourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Insert `MedicalResource`s into the `MedicalDataSource` val medicalResources: List<MedicalResource> = healthConnectClient.upsertMedicalResources( listOf( UpsertMedicalResourceRequest( medicalDataSource.id, medicalDataSource.fhirVersion, medicationJsonToInsert, // a valid FHIR json string ) ) ) // Delete `MedicalResource`s matching the specified `dataSourceId`, `type` and `fhirResourceId` healthConnectClient.deleteMedicalResources( medicalResources.map { medicalResource: MedicalResource -> MedicalResourceId( dataSourceId = medicalDataSource.id, fhirResourceType = medicalResource.id.fhirResourceType, fhirResourceId = medicalResource.id.fhirResourceId, ) } )
| Parameters | |
|---|---|
ids: List<MedicalResourceId> |
The ids to delete. |
| Throws | |
|---|---|
java.lang.SecurityException |
if caller does not hold |
deleteMedicalResources
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
@ExperimentalPersonalHealthRecordApi
open suspend fun deleteMedicalResources(request: DeleteMedicalResourcesRequest): Unit
Deletes MedicalResources based on given filters in request.
Only MedicalResources inserted by the calling app will be deleted. If the request matches any other MedicalResources, these will be ignored.
Regarding permissions:
-
Caller must hold
PERMISSION_WRITE_MEDICAL_DATAin order to call this API. -
Deletes are permitted in the foreground or background.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.records.MedicalResource import androidx.health.connect.client.records.MedicalResource.Companion.MEDICAL_RESOURCE_TYPE_MEDICATIONS import androidx.health.connect.client.request.CreateMedicalDataSourceRequest import androidx.health.connect.client.request.DeleteMedicalResourcesRequest import androidx.health.connect.client.request.UpsertMedicalResourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Insert `MedicalResource`s into the `MedicalDataSource` val medicalResources: List<MedicalResource> = healthConnectClient.upsertMedicalResources( listOf( UpsertMedicalResourceRequest( medicalDataSource.id, medicalDataSource.fhirVersion, medicationJsonToInsert, // a valid FHIR json string ) ) ) // Delete all `MedicalResource`s that are in any pair of provided `dataSourceIds` and // `medicalResourceTypes` healthConnectClient.deleteMedicalResources( DeleteMedicalResourcesRequest( dataSourceIds = setOf(medicalDataSource.id), medicalResourceTypes = setOf(MEDICAL_RESOURCE_TYPE_MEDICATIONS), ) )
| Parameters | |
|---|---|
request: DeleteMedicalResourcesRequest |
The request that contains the filters to delete. |
| Throws | |
|---|---|
java.lang.SecurityException |
if caller does not hold |
deleteRecords
suspend fun deleteRecords(
recordType: KClass<Record>,
timeRangeFilter: TimeRangeFilter
): Unit
Deletes any Record of the given recordType in the given timeRangeFilter (automatically filtered to Record belonging to the calling application). Deletion of multiple Record is executed in a transaction - if one fails, none is deleted.
import androidx.health.connect.client.deleteRecords import androidx.health.connect.client.records.StepsRecord import androidx.health.connect.client.time.TimeRangeFilter healthConnectClient.deleteRecords<StepsRecord>( timeRangeFilter = TimeRangeFilter.between(startTime, endTime) )
| Parameters | |
|---|---|
recordType: KClass<Record> |
Which type of |
timeRangeFilter: TimeRangeFilter |
The |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. Example usage to delete written steps data in a time range: |
deleteRecords
suspend fun deleteRecords(
recordType: KClass<Record>,
recordIdsList: List<String>,
clientRecordIdsList: List<String>
): Unit
Deletes one or more Record by their identifiers. Deletion of multiple Record is executed in single transaction - if one fails, none is deleted.
import androidx.health.connect.client.deleteRecords import androidx.health.connect.client.records.StepsRecord healthConnectClient.deleteRecords<StepsRecord>( recordIdsList = listOf(uid1, uid2), clientRecordIdsList = emptyList(), )
| Parameters | |
|---|---|
recordType: KClass<Record> |
Which type of |
recordIdsList: List<String> |
List of |
clientRecordIdsList: List<String> |
List of client record IDs of |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. Deleting by invalid identifiers such as a non-existing identifier or deleting the same record multiple times will result in IPC failure. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. Example usage to delete written steps data by its unique identifier: |
getChanges
suspend fun getChanges(changesToken: String): ChangesResponse
Retrieves changes in Health Connect, from a specific point in time represented by provided changesToken.
The response returned may not provide all the changes due to IPC or memory limits, see ChangesResponse.hasMore. Clients can make more api calls to fetch more changes from Health Connect with updated ChangesResponse.nextChangesToken.
Provided changesToken may have expired if clients have not synced for extended period of time (such as a month). In this case ChangesResponse.changesTokenExpired will be set, and clients should generate a new changes-token via getChangesToken.
val response = client.getChanges(changesToken)
if (response.changesTokenExpired) {
// Consider re-sync and fetch new changes token.
} else {
// Process new insertion/deletions, either update local storage or upload to backends.
}
| Parameters | |
|---|---|
changesToken: String |
A Changes-Token that represents a specific point in time in Android Health Platform. |
| Returns | |
|---|---|
ChangesResponse |
a |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
| See also | |
|---|---|
getChangesToken |
getChangesToken
suspend fun getChangesToken(request: ChangesTokenRequest): String
Retrieves a changes-token, representing a point in time in the underlying Android Health Platform for a given ChangesTokenRequest. Changes-tokens are used in getChanges to retrieve changes since that point in time.
Changes-tokens represent a point in time after which the client is interested in knowing the changes for a set of interested types of Record and optional DataOrigin filters.
Changes-tokens are only valid for 30 days after they're generated. Calls to getChanges with an expired changes-token will lead to ChangesResponse.changesTokenExpired
| Parameters | |
|---|---|
request: ChangesTokenRequest |
Includes interested types of record to observe changes and optional filters. |
| Returns | |
|---|---|
String |
a changes-token |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
| See also | |
|---|---|
getChanges |
getMedicalDataSources
@ExperimentalPersonalHealthRecordApi
open suspend fun getMedicalDataSources(ids: List<String>): List<MedicalDataSource>
Gets MedicalDataSources for the provided list of ids.
The returned list of data sources will be in the same order as the ids.
Number of data sources returned by this API will depend based on below factors:
-
If an empty list of
idsis provided, no data sources will be returned. -
If an id is invalid or non-existent, no data source will be returned for that id, this means the returned list might have fewer elements than
ids. -
Callers will only get data sources they are permitted to get. See below.
There is no specific read permission for getting data sources. Instead, permission to read data sources is based on whether the caller has permission to read the data currently contained in that data source. Being permitted to get data sources is dependent on the following logic, in priority order, earlier statements take precedence.
-
A caller without any read medical permissions such as
PERMISSION_READ_MEDICAL_DATA_VACCINESor the write permissionPERMISSION_WRITE_MEDICAL_DATAis not permitted get anyMedicalDataSources, including ones that it created. -
A caller with the write permission is permitted to read its own
MedicalDataSources regardless whether it's in foreground or background. -
A caller with only a read permission, when in background, is only permitted to read its own
MedicalDataSources that contains at least oneMedicalResourcewith the type that the read permission covers. However, when the caller is in foreground or if it holdsPERMISSION_READ_HEALTH_DATA_IN_BACKGROUND, it can also read other apps'MedicalDataSources as long as each of thoseMedicalDataSourcecontains at least oneMedicalResourcewith the corresponding type. -
For example, if a caller
Acreated aMedicalDataSourceDS1, then usedDS1to insert aMedicalResourceMR1with typeMedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES, and another callerBcreatedDS2. IfAholdsPERMISSION_READ_MEDICAL_DATA_VACCINES, it can only readDS1in background. However, if it is in foreground or holdsPERMISSION_READ_HEALTH_DATA_IN_BACKGROUND, it can read bothDS1andDS2.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.request.CreateMedicalDataSourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` // Each `MedicalDataSource` is assigned an `id` by the system on creation val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Retrieve all `MedicalDataSource` with `id` matching any of the given ids val medicalDataSources: List<MedicalDataSource> = healthConnectClient.getMedicalDataSources(listOf(medicalDataSource.id, anotherId))
| Parameters | |
|---|---|
ids: List<String> |
ids of the |
| Returns | |
|---|---|
List<MedicalDataSource> |
|
getMedicalDataSources
@ExperimentalPersonalHealthRecordApi
open suspend fun getMedicalDataSources(request: GetMedicalDataSourcesRequest): List<MedicalDataSource>
Gets the requested MedicalDataSources using GetMedicalDataSourcesRequest. Number of data sources returned by this API will depend based on below factors:
-
If an empty
GetMedicalDataSourcesRequestis passed, all data sources for all apps are requested, and all which the caller is permitted to get will be returned. See below. -
If
GetMedicalDataSourcesRequest.packageNamesis not empty, then only the data sources created by those packages is being requested. All data sources created by those packages which the caller is permitted to get will be returned. See below.
There is no specific read permission for getting data sources. Instead, permission to read data sources is based on whether the caller has permission to read the data currently contained in that data source. Specifically:
-
A caller without any read medical permissions such as
PERMISSION_READ_MEDICAL_DATA_VACCINESor the write permissionPERMISSION_WRITE_MEDICAL_DATAis not permitted get anyMedicalDataSources, including ones that it created. -
A caller with the write permission is permitted to read its own
MedicalDataSources regardless whether it's in foreground or background. -
A caller with only a read permission, when in background, is only permitted to read its own
MedicalDataSources that contains at least oneMedicalResourcewith the type that the read permission covers. However, when the caller is in foreground or if it holdsPERMISSION_READ_HEALTH_DATA_IN_BACKGROUND, it can also read other apps'MedicalDataSources as long as each of thoseMedicalDataSourcecontains at least oneMedicalResourcewith the corresponding type. -
For example, a client
Acreated aMedicalDataSourceDS1, then usedDS1to insert aMedicalResourceMR1with typeMedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES. Similarly another clientBcreatedDS2, then usedDS2to insert anotherMedicalResourceMR2with the sameMedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES. IfAholdsPERMISSION_READ_MEDICAL_DATA_VACCINESand it is in background, it can only readDS1. However, ifAis in foreground or holdsPERMISSION_READ_HEALTH_DATA_IN_BACKGROUND, it can read bothDS1andDS2.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.request.CreateMedicalDataSourceRequest import androidx.health.connect.client.request.GetMedicalDataSourcesRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` // Each `MedicalDataSource` contains the `packageName` which created it val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Retrieve all `MedicalDataSource`s created by any of the specified package names // Package names may be found in other `MedicalDataSource`s or from arbitrary input val medicalDataSources: List<MedicalDataSource> = healthConnectClient.getMedicalDataSources( GetMedicalDataSourcesRequest(listOf(medicalDataSource.packageName, anotherPackageName)) )
| Parameters | |
|---|---|
request: GetMedicalDataSourcesRequest |
containing details of the |
| Returns | |
|---|---|
List<MedicalDataSource> |
|
insertRecords
suspend fun insertRecords(records: List<Record>): InsertRecordsResponse
Inserts one or more Record and returns newly assigned androidx.health.connect.client.records.metadata.Metadata.id generated. Insertion of multiple records is executed in a transaction - if one fails, none is inserted.
import androidx.health.connect.client.records.StepsRecord import androidx.health.connect.client.records.metadata.Metadata val stepsRecord = StepsRecord( count = 120, startTime = START_TIME, endTime = END_TIME, startZoneOffset = START_ZONE_OFFSET, endZoneOffset = END_ZONE_OFFSET, metadata = Metadata.manualEntry(), ) healthConnectClient.insertRecords(listOf(stepsRecord))
To insert more complex data like nutrition for a user who’s eaten a banana:
import androidx.health.connect.client.records.NutritionRecord import androidx.health.connect.client.records.metadata.Metadata import androidx.health.connect.client.units.grams import androidx.health.connect.client.units.kilocalories val banana = NutritionRecord( name = "banana", energy = 105.0.kilocalories, dietaryFiber = 3.1.grams, potassium = 0.422.grams, totalCarbohydrate = 27.0.grams, totalFat = 0.4.grams, saturatedFat = 0.1.grams, sodium = 0.001.grams, sugar = 14.0.grams, vitaminB6 = 0.0005.grams, vitaminC = 0.0103.grams, startTime = START_TIME, endTime = END_TIME, startZoneOffset = START_ZONE_OFFSET, endZoneOffset = END_ZONE_OFFSET, metadata = Metadata.manualEntry(), ) healthConnectClient.insertRecords(listOf(banana))
To insert some heart rate data:
import androidx.health.connect.client.records.HeartRateRecord import androidx.health.connect.client.records.metadata.Metadata val heartRateRecord = HeartRateRecord( startTime = START_TIME, startZoneOffset = START_ZONE_OFFSET, endTime = END_TIME, endZoneOffset = END_ZONE_OFFSET, metadata = Metadata.manualEntry(), // records 10 arbitrary data, to replace with actual data samples = List(10) { index -> HeartRateRecord.Sample( time = START_TIME + Duration.ofSeconds(index.toLong()), beatsPerMinute = 100 + index.toLong(), ) }, ) healthConnectClient.insertRecords(listOf(heartRateRecord))
androidx.health.connect.client.records.metadata.Metadata.clientRecordId can be used to deduplicate data with a client provided unique identifier. When a subsequent insertRecords is called with the same androidx.health.connect.client.records.metadata.Metadata.clientRecordId, whichever Record with the higher androidx.health.connect.client.records.metadata.Metadata.clientRecordVersion takes precedence.
| Returns | |
|---|---|
InsertRecordsResponse |
List of unique identifiers in the order of inserted records. |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. For example, to insert basic data like step counts: |
readMedicalResources
@ExperimentalPersonalHealthRecordApi
open suspend fun readMedicalResources(ids: List<MedicalResourceId>): List<MedicalResource>
Reads a collection of MedicalResources given a list of MedicalResourceIds.
The number and order of medical resources returned by this API is not guaranteed, depending on a number of factors:
-
If an empty list of IDs is provided, an empty list will be returned.
-
If any ID does not exist, no medical resource will be returned for that ID.
-
Only permitted
MedicalResources are returned. Specifically: -
A caller without any read medical permissions such as
PERMISSION_READ_MEDICAL_DATA_VACCINES, or the write medical permissionPERMISSION_WRITE_MEDICAL_DATAis not permitted to read anyMedicalResource, including ones that it created. -
A caller with the write permission is permitted to read its own
MedicalResources of any type regardless whether it's in foreground or background. -
A caller with only a read permission, when in background, is only permitted to read its own
MedicalResources of the corresponding type. For example, if a caller only holdsPERMISSION_READ_MEDICAL_DATA_VACCINES, then when in background it is only permitted to read its ownMedicalResources withMedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES. However, when it is in foreground or if it holdsPERMISSION_READ_HEALTH_DATA_IN_BACKGROUND, it is permitted to read all vaccinesMedicalResources, including ones that were written by other apps.
Each returned MedicalResource is not guaranteed to meet all requirements of the Fast Healthcare Interoperability Resources (FHIR) spec. If required, clients should perform their own validations.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.records.MedicalResource import androidx.health.connect.client.records.MedicalResourceId import androidx.health.connect.client.request.CreateMedicalDataSourceRequest import androidx.health.connect.client.request.UpsertMedicalResourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Insert `MedicalResource`s into the `MedicalDataSource` val medicalResources: List<MedicalResource> = healthConnectClient.upsertMedicalResources( listOf( UpsertMedicalResourceRequest( medicalDataSource.id, medicalDataSource.fhirVersion, medicationJsonToInsert, // a valid FHIR json string ) ) ) // Retrieve `fhirResourceType` type `MedicalResource`s with the specified `id`s from the // provided `MedicalDataSource` val retrievedMedicalResources: List<MedicalResource> = healthConnectClient.readMedicalResources( medicalResources.map { medicalResource: MedicalResource -> MedicalResourceId( dataSourceId = medicalDataSource.id, fhirResourceType = medicalResource.id.fhirResourceType, fhirResourceId = medicalResource.id.fhirResourceId, ) } )
| Throws | |
|---|---|
kotlin.IllegalArgumentException |
if the size of |
readMedicalResources
@ExperimentalPersonalHealthRecordApi
open suspend fun readMedicalResources(request: ReadMedicalResourcesRequest): ReadMedicalResourcesResponse
Reads MedicalResources by request, either ReadMedicalResourcesInitialRequest or ReadMedicalResourcesPageRequest.
A typical flow to read all MedicalResources that satisfy a certain criteria would be:
-
Create a
ReadMedicalResourcesInitialRequestwith desired criteria, and make a request. If successful, aReadMedicalResourcesResponseshould be returned. -
Use returned
ReadMedicalResourcesResponse.nextPageTokento create aReadMedicalResourcesPageRequestand make another request. Again, if successful, aReadMedicalResourcesResponseshould be returned. -
Repeat step 2 until
ReadMedicalResourcesResponse.nextPageTokenis null.
Regarding permissions, only permitted MedicalResources are returned. Specifically:
-
A caller without any read medical permissions such as
PERMISSION_READ_MEDICAL_DATA_VACCINES, or the write medical permissionPERMISSION_WRITE_MEDICAL_DATAis not permitted to read anyMedicalResource, including ones that it created. -
A caller with the write permission is permitted to read its own
MedicalResources of any type regardless whether it's in foreground or background. -
A caller with only a read permission, when in background, is only permitted to read its own
MedicalResources of the corresponding type. For example, if a caller only holdsPERMISSION_READ_MEDICAL_DATA_VACCINES, then when in background it is only permitted to read its ownMedicalResources withMedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES. However, when it is in foreground or if it holdsPERMISSION_READ_HEALTH_DATA_IN_BACKGROUND, it is permitted to read all vaccinesMedicalResources, including ones that were written by other apps.
Each returned MedicalResource is not guaranteed to meet all requirements of the Fast Healthcare Interoperability Resources (FHIR) spec. If required, clients should perform their own validations.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.records.MedicalResource import androidx.health.connect.client.records.MedicalResource.Companion.MEDICAL_RESOURCE_TYPE_LABORATORY_RESULTS import androidx.health.connect.client.request.CreateMedicalDataSourceRequest import androidx.health.connect.client.request.ReadMedicalResourcesInitialRequest import androidx.health.connect.client.request.ReadMedicalResourcesPageRequest import androidx.health.connect.client.request.ReadMedicalResourcesRequest import androidx.health.connect.client.response.ReadMedicalResourcesResponse // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Insert `MedicalResource`s into the `MedicalDataSource` healthConnectClient.upsertMedicalResources(exampleLabResults) // Read `MedicalResource`s back from the `MedicalDataSource` // Read 100 resources / page. See `pageSize` doc for defaults and limits. val pageSize = 100 // Prepare the initial read request. // All `MedicalResource`s in the given `MedicalDataSource`s and of given `medicalResourceType` // will be retrieved. val initialRequest: ReadMedicalResourcesRequest = ReadMedicalResourcesInitialRequest( MEDICAL_RESOURCE_TYPE_LABORATORY_RESULTS, setOf(medicalDataSource.id), pageSize = pageSize, ) // Continue reading pages until all `MedicalResource`s are read var pageToken: String? = null do { // Prepare paged request if needed val request: ReadMedicalResourcesRequest = if (pageToken == null) initialRequest else ReadMedicalResourcesPageRequest(pageToken, pageSize = pageSize) // Read `MedicalResource`s val response: ReadMedicalResourcesResponse = healthConnectClient.readMedicalResources(request) // Process `MedicalResource`s as desired val resources: List<MedicalResource> = response.medicalResources // Advance to next page pageToken = response.nextPageToken } while (pageToken != null)
readRecord
suspend fun <T : Record> readRecord(recordType: KClass<T>, recordId: String): ReadRecordResponse<T>
Reads one Record point with its recordType and recordId.
| Parameters | |
|---|---|
recordType: KClass<T> |
Which type of |
recordId: String |
|
| Returns | |
|---|---|
ReadRecordResponse<T> |
The |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. Update with invalid identifiers will result in IPC failure. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. |
readRecords
suspend fun <T : Record> readRecords(request: ReadRecordsRequest<T>): ReadRecordsResponse<T>
Retrieves a collection of Records.
import androidx.health.connect.client.readRecord import androidx.health.connect.client.records.StepsRecord import androidx.health.connect.client.request.ReadRecordsRequest import androidx.health.connect.client.time.TimeRangeFilter val response = healthConnectClient.readRecords( ReadRecordsRequest<StepsRecord>( timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) for (stepRecord in response.records) { // Process each step record }
| Parameters | |
|---|---|
<T : Record> |
the type of |
request: ReadRecordsRequest<T> |
|
| Returns | |
|---|---|
ReadRecordsResponse<T> |
a response containing a collection of |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. Example code to read basic data like step counts: |
updateRecords
suspend fun updateRecords(records: List<Record>): Unit
Updates one or more Record of given UIDs to newly specified values. Update of multiple records is executed in a transaction - if one fails, none is inserted.
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. Update with invalid identifiers will result in IPC failure. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. |
upsertMedicalResources
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
@ExperimentalPersonalHealthRecordApi
open suspend fun upsertMedicalResources(requests: List<UpsertMedicalResourceRequest>): List<MedicalResource>
Inserts or updates a list of MedicalResources using UpsertMedicalResourceRequests.
In each request, from UpsertMedicalResourceRequest.dataSourceId, fhir resource type and fhir resource ID extracted from UpsertMedicalResourceRequest.data, a MedicalResourceId will be constructed. If there already exists a MedicalResource with that ID in Health Connect, then it will be updated, otherwise a new MedicalResource will be inserted.
For each UpsertMedicalResourceRequest, one MedicalResource will be returned, regardless whether it's updated or inserted. The order of the MedicalResources in the returned list will be the same as their corresponding UpsertMedicalResourceRequests in the input list.
Note that a MedicalDataSource needs to be created using createMedicalDataSource before any MedicalResources can be upserted for this source.
Regarding permissions:
-
Caller must hold
PERMISSION_WRITE_MEDICAL_DATAin order to call this API, otherwise aSecurityExceptionwill be thrown. -
With
PERMISSION_WRITE_MEDICAL_DATAgranted, caller is permitted to call this API in either foreground or background.
Medical data is represented using the "https://hl7.org/fhir/" standard. The FHIR resource provided in UpsertMedicalResourceRequest.data is expected to be valid for the specified FHIR version according to the FHIR spec. Structural validation checks such as resource structure, field types and presence of required fields will be performed, however these checks may not cover all FHIR spec requirements and are dependant on the backing implementation of Health Connect.
Data written to Health Connect should be for a single individual only. However, the API allows for multiple Patient resources to be written to account for the possibility of multiple Patient resources being present in one individual's medical record.
Each UpsertMedicalResourceRequest also has to meet the following requirements:
-
UpsertMedicalResourceRequest.datamust contain an "id" field and a "resourceType" field. The "resource type" must be one of the items in the accepted list of resource types inFhirResource. -
The FHIR resource does not have a "contained" field (holds contained resources).
-
FHIR versionof each request must matchMedicalDataSource.fhirVersionofUpsertMedicalResourceRequest.dataSourceId's correspondingMedicalDataSource.
If any request is failed to be processed for any reason, none of the requests will be inserted or updated in one transaction.
This feature is dependent on the version of HealthConnect installed on the device. To check if it's available call HealthConnectFeatures.getFeatureStatus and pass FEATURE_PERSONAL_HEALTH_RECORD as an argument. An UnsupportedOperationException would be thrown if the feature is not available.
import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_PERSONAL_HEALTH_RECORD import androidx.health.connect.client.HealthConnectFeatures.Companion.FEATURE_STATUS_AVAILABLE import androidx.health.connect.client.records.FhirVersion import androidx.health.connect.client.records.MedicalDataSource import androidx.health.connect.client.records.MedicalResource import androidx.health.connect.client.request.CreateMedicalDataSourceRequest import androidx.health.connect.client.request.UpsertMedicalResourceRequest // Ensure `FEATURE_PERSONAL_HEALTH_RECORD` is available before calling PHR apis if ( healthConnectClient.features.getFeatureStatus(FEATURE_PERSONAL_HEALTH_RECORD) != FEATURE_STATUS_AVAILABLE ) { return } // Get or create a `MedicalDataSource` val medicalDataSource: MedicalDataSource = healthConnectClient.createMedicalDataSource( CreateMedicalDataSourceRequest( fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"), displayName = "Test Data Source", fhirVersion = FhirVersion(4, 0, 1), ) ) // Insert `MedicalResource`s into the `MedicalDataSource` val medicalResources: List<MedicalResource> = healthConnectClient.upsertMedicalResources( listOf( UpsertMedicalResourceRequest( medicalDataSource.id, medicalDataSource.fhirVersion, medicationJsonToInsert, // a valid FHIR json string ) ) ) // Update `MedicalResource`s in the `MedicalDataSource` val updatedMedicalResources: List<MedicalResource> = healthConnectClient.upsertMedicalResources( listOf( UpsertMedicalResourceRequest( medicalDataSource.id, medicalDataSource.fhirVersion, // a valid FHIR json string // if this resource has the same type and ID as in `medicationJsonToInsert`, // this `upsertMedicalResources()` call will update the previously inserted // `MedicalResource` updatedMedicationJsonToInsert, ) ) )
| Parameters | |
|---|---|
requests: List<UpsertMedicalResourceRequest> |
List of upsert requests. |
| Throws | |
|---|---|
kotlin.IllegalArgumentException |
if any request is failed to be processed for any reason such as invalid |
java.lang.SecurityException |
if caller does not hold |
Public properties
features
open val features: HealthConnectFeatures
Access operations related to feature availability.
permissionController
val permissionController: PermissionController
Access operations related to permissions.
Extension functions
deleteRecords
suspend inline fun <T : Record> HealthConnectClient.deleteRecords(
timeRangeFilter: TimeRangeFilter
): Unit
Deletes any Record of type T in the given timeRangeFilter (automatically filtered to Record belonging to the calling application). Deletion of multiple Record is executed in a transaction - if one fails, none is deleted.
import androidx.health.connect.client.deleteRecords import androidx.health.connect.client.records.StepsRecord import androidx.health.connect.client.time.TimeRangeFilter healthConnectClient.deleteRecords<StepsRecord>( timeRangeFilter = TimeRangeFilter.between(startTime, endTime) )
| Parameters | |
|---|---|
<T : Record> |
Which type of |
timeRangeFilter: TimeRangeFilter |
The |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. |
kotlin.IllegalStateException |
If service is not available. |
| See also | |
|---|---|
deleteRecords |
Example usage to delete written steps data in a time range: |
deleteRecords
suspend inline fun <T : Record> HealthConnectClient.deleteRecords(
recordIdsList: List<String>,
clientRecordIdsList: List<String>
): Unit
Deletes one or more Record by their identifiers. Deletion of multiple Record is executed in single transaction - if one fails, none is deleted.
import androidx.health.connect.client.deleteRecords import androidx.health.connect.client.records.StepsRecord healthConnectClient.deleteRecords<StepsRecord>( recordIdsList = listOf(uid1, uid2), clientRecordIdsList = emptyList(), )
| Parameters | |
|---|---|
<T : Record> |
Which type of |
recordIdsList: List<String> |
List of |
clientRecordIdsList: List<String> |
List of client record IDs of |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. Deleting by invalid identifiers such as a non-existing identifier or deleting the same record multiple times will result in IPC failure. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. |
kotlin.IllegalStateException |
If service is not available. |
| See also | |
|---|---|
deleteRecords |
Example usage to delete written steps data by its unique identifier: |
readRecord
suspend inline fun <T : Record> HealthConnectClient.readRecord(recordId: String): ReadRecordResponse<T>
Reads one Record point of type T and with the specified recordId.
| Parameters | |
|---|---|
<T : Record> |
Which type of |
recordId: String |
|
| Returns | |
|---|---|
ReadRecordResponse<T> |
The |
| Throws | |
|---|---|
android.os.RemoteException |
For any IPC transportation failures. Update with invalid identifiers will result in IPC failure. |
java.lang.SecurityException |
For requests with unpermitted access. |
java.io.IOException |
For any disk I/O issues. |
kotlin.IllegalStateException |
If service is not available. |
| See also | |
|---|---|
readRecord |