HealthConnectClient


public interface HealthConnectClient

Known direct subclasses
FakeHealthConnectClient

Fake HealthConnectClient to be used in tests for components that use it as a dependency.


Interface to access health and fitness records.

Summary

Nested types

public static class HealthConnectClient.Companion

Constants

default static final int

The Health Connect SDK APIs are available.

default static final int

The Health Connect SDK is unavailable on this device at the time.

default static final int

The Health Connect SDK APIs are currently unavailable, the provider is either not installed or needs to be updated.

Public methods

abstract @NonNull AggregationResult

Reads AggregateMetrics according to requested read criteria: Records from AggregateRequest.dataOriginFilter and within AggregateRequest.timeRangeFilter.

abstract @NonNull List<@NonNull AggregationResultGroupedByDuration>

Reads AggregateMetrics according to requested read criteria specified in AggregateGroupByDurationRequest.

abstract @NonNull List<@NonNull AggregationResultGroupedByPeriod>

Reads AggregateMetrics according to requested read criteria specified in AggregateGroupByPeriodRequest.

default @NonNull MedicalDataSource

Creates a MedicalDataSource using a CreateMedicalDataSourceRequest.

default void
@ExperimentalPersonalHealthRecordApi
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
deleteMedicalDataSourceWithData(@NonNull String id)

Deletes a MedicalDataSource and all data contained within it.

default void

Deletes a list of MedicalResources by the provided list of MedicalResourceIds.

default void

Deletes MedicalResources based on given filters in request.

abstract void
deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull TimeRangeFilter timeRangeFilter
)

Deletes any Record of the given recordType in the given timeRangeFilter (automatically filtered to Record belonging to the calling application).

abstract void
deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull List<@NonNull String> recordIdsList,
    @NonNull List<@NonNull String> clientRecordIdsList
)

Deletes one or more Record by their identifiers.

abstract @NonNull ChangesResponse
getChanges(@NonNull String changesToken)

Retrieves changes in Health Connect, from a specific point in time represented by provided changesToken.

abstract @NonNull String

Retrieves a changes-token, representing a point in time in the underlying Android Health Platform for a given ChangesTokenRequest.

default @NonNull HealthConnectFeatures

Access operations related to feature availability.

default static final @NonNull Intent
getHealthConnectManageDataIntent(
    @NonNull Context context,
    @NonNull String providerPackageName
)

Intent to open Health Connect data management screen on this phone.

default static final @NonNull String

Intent action to open Health Connect settings on this phone.

default @NonNull List<@NonNull MedicalDataSource>

Gets MedicalDataSources for the provided list of ids.

default @NonNull List<@NonNull MedicalDataSource>

Gets the requested MedicalDataSources using GetMedicalDataSourcesRequest.

default static final @NonNull HealthConnectClient
getOrCreate(@NonNull Context context, @NonNull String providerPackageName)

Retrieves an IPC-backed HealthConnectClient instance binding to an available implementation.

abstract @NonNull PermissionController

Access operations related to permissions.

default static final int
getSdkStatus(@NonNull Context context, @NonNull String providerPackageName)

Determines whether the Health Connect SDK is available on this device at the moment.

abstract @NonNull InsertRecordsResponse

Inserts one or more Record and returns newly assigned androidx.health.connect.client.records.metadata.Metadata.id generated.

default @NonNull List<@NonNull MedicalResource>

Reads a collection of MedicalResources given a list of MedicalResourceIds.

default @NonNull ReadMedicalResourcesResponse

Reads MedicalResources by request, either ReadMedicalResourcesInitialRequest or ReadMedicalResourcesPageRequest.

abstract @NonNull ReadRecordResponse<@NonNull T>
<T extends Record> readRecord(
    @NonNull KClass<@NonNull T> recordType,
    @NonNull String recordId
)

Reads one Record point with its recordType and recordId.

abstract @NonNull ReadRecordsResponse<@NonNull T>
<T extends Record> readRecords(@NonNull ReadRecordsRequest<@NonNull T> request)

Retrieves a collection of Records.

abstract void

Updates one or more Record of given UIDs to newly specified values.

default @NonNull List<@NonNull MedicalResource>
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
@ExperimentalPersonalHealthRecordApi
upsertMedicalResources(
    @NonNull List<@NonNull UpsertMedicalResourceRequest> requests
)

Inserts or updates a list of MedicalResources using UpsertMedicalResourceRequests.

Extension functions

default final void
<T extends Record> HealthConnectClientExt.deleteRecords(
    @NonNull HealthConnectClient receiver,
    @NonNull TimeRangeFilter timeRangeFilter
)

Deletes any Record of type T in the given timeRangeFilter (automatically filtered to Record belonging to the calling application).

default final void
<T extends Record> HealthConnectClientExt.deleteRecords(
    @NonNull HealthConnectClient receiver,
    @NonNull List<@NonNull String> recordIdsList,
    @NonNull List<@NonNull String> clientRecordIdsList
)

Deletes one or more Record by their identifiers.

default final @NonNull ReadRecordResponse<@NonNull T>
<T extends Record> HealthConnectClientExt.readRecord(
    @NonNull HealthConnectClient receiver,
    @NonNull String recordId
)

Reads one Record point of type T and with the specified recordId.

Constants

SDK_AVAILABLE

Added in 1.1.0-beta02
default static final int SDK_AVAILABLE = 3

The Health Connect SDK APIs are available.

Apps can subsequently call getOrCreate to get an instance of HealthConnectClient.

SDK_UNAVAILABLE

Added in 1.1.0-beta02
default static final int SDK_UNAVAILABLE = 1

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

Added in 1.1.0-beta02
default static final int SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED = 2

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 methods

aggregate

abstract @NonNull AggregationResult aggregate(@NonNull AggregateRequest request)

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
@NonNull AggregateRequest request

AggregateRequest object specifying AggregateMetrics to aggregate and other filters.

Returns
@NonNull AggregationResult

the AggregationResult that contains aggregated values.

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

abstract @NonNull List<@NonNull AggregationResultGroupedByDurationaggregateGroupByDuration(
    @NonNull AggregateGroupByDurationRequest request
)

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
@NonNull AggregateGroupByDurationRequest request

AggregateGroupByDurationRequest object specifying AggregateMetrics to aggregate and other filters.

Returns
@NonNull List<@NonNull AggregationResultGroupedByDuration>

a list of AggregationResultGroupedByDurations, each contains aggregated values and start/end time of the row. The list is sorted by time in ascending order.

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

abstract @NonNull List<@NonNull AggregationResultGroupedByPeriodaggregateGroupByPeriod(@NonNull AggregateGroupByPeriodRequest request)

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
@NonNull AggregateGroupByPeriodRequest request

AggregateGroupByPeriodRequest object specifying AggregateMetrics to aggregate and other filters.

Returns
@NonNull List<@NonNull AggregationResultGroupedByPeriod>

a list of AggregationResultGroupedByPeriods, each contains aggregated values and start/end time of the row. The list is sorted by time in ascending order.

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")
default @NonNull MedicalDataSource createMedicalDataSource(@NonNull CreateMedicalDataSourceRequest request)

Creates a MedicalDataSource using a CreateMedicalDataSourceRequest.

Regarding permissions:

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
@NonNull CreateMedicalDataSourceRequest request

request containing details of the MedicalDataSource to be created

deleteMedicalDataSourceWithData

Added in 1.1.0-beta02
@ExperimentalPersonalHealthRecordApi
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
default void deleteMedicalDataSourceWithData(@NonNull String id)

Deletes a MedicalDataSource and all data contained within it.

Regarding permissions:

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
@NonNull String id

The id of the data source to delete.

Throws
kotlin.IllegalArgumentException

if id is invalid, does not exist, or owned by another app.

deleteMedicalResources

Added in 1.1.0-beta02
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
@ExperimentalPersonalHealthRecordApi
default void deleteMedicalResources(@NonNull List<@NonNull MedicalResourceId> ids)

Deletes a list of MedicalResources by the provided list of MedicalResourceIds.

  • If any ID in ids is invalid, the API will throw an IllegalArgumentException, and nothing will be deleted.

  • If any ID in ids does not exist, that ID will be ignored, while deletion on other IDs will be performed.

Regarding permissions:

  • Caller must hold PERMISSION_WRITE_MEDICAL_DATA in order to call this API, even then, it can only delete its own data. If any of the items in ids belongs 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
@NonNull List<@NonNull MedicalResourceId> ids

The ids to delete.

deleteMedicalResources

Added in 1.1.0-beta02
@RequiresPermission(value = "android.permission.health.WRITE_MEDICAL_DATA")
@ExperimentalPersonalHealthRecordApi
default void deleteMedicalResources(@NonNull DeleteMedicalResourcesRequest request)

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:

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
@NonNull DeleteMedicalResourcesRequest request

The request that contains the filters to delete.

deleteRecords

Added in 1.1.0-beta02
abstract void deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull TimeRangeFilter timeRangeFilter
)

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
@NonNull KClass<@NonNull Record> recordType

Which type of Record to delete, such as StepsRecord::class

@NonNull TimeRangeFilter timeRangeFilter

The TimeRangeFilter to delete from

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

Added in 1.1.0-beta02
abstract void deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull List<@NonNull String> recordIdsList,
    @NonNull List<@NonNull String> clientRecordIdsList
)

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
@NonNull KClass<@NonNull Record> recordType

Which type of Record to delete, such as Steps::class

@NonNull List<@NonNull String> recordIdsList

List of androidx.health.connect.client.records.metadata.Metadata.id of Record to delete

@NonNull List<@NonNull String> clientRecordIdsList

List of client record IDs of Record to delete

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

abstract @NonNull ChangesResponse getChanges(@NonNull String changesToken)

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
@NonNull String changesToken

A Changes-Token that represents a specific point in time in Android Health Platform.

Returns
@NonNull ChangesResponse

a ChangesResponse with changes since provided changesToken.

Throws
android.os.RemoteException

For any IPC transportation failures.

java.lang.SecurityException

For requests with unpermitted access.

See also
getChangesToken

getChangesToken

abstract @NonNull String getChangesToken(@NonNull ChangesTokenRequest request)

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
@NonNull ChangesTokenRequest request

Includes interested types of record to observe changes and optional filters.

Returns
@NonNull String

a changes-token

Throws
android.os.RemoteException

For any IPC transportation failures.

java.lang.SecurityException

For requests with unpermitted access.

See also
getChanges

getFeatures

Added in 1.1.0-beta02
default @NonNull HealthConnectFeatures getFeatures()

Access operations related to feature availability.

getHealthConnectManageDataIntent

Added in 1.1.0-beta02
default static final @NonNull Intent getHealthConnectManageDataIntent(
    @NonNull Context context,
    @NonNull String providerPackageName
)

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
@NonNull Context context

the context

@NonNull String providerPackageName

optional alternative package provider to choose for backend implementation

Returns
@NonNull Intent

Intent to open Health Connect data management screen.

getHealthConnectSettingsAction

Added in 1.1.0-beta02
default static final @NonNull String getHealthConnectSettingsAction()

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.

getMedicalDataSources

@ExperimentalPersonalHealthRecordApi
default @NonNull List<@NonNull MedicalDataSourcegetMedicalDataSources(@NonNull List<@NonNull String> ids)

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 ids is 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.

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
@NonNull List<@NonNull String> ids

ids of the MedicalDataSources to retrieve

Returns
@NonNull List<@NonNull MedicalDataSource>

MedicalDataSources matching the provided ids

getMedicalDataSources

@ExperimentalPersonalHealthRecordApi
default @NonNull List<@NonNull MedicalDataSourcegetMedicalDataSources(@NonNull GetMedicalDataSourcesRequest request)

Gets the requested MedicalDataSources using GetMedicalDataSourcesRequest. Number of data sources returned by this API will depend based on below factors:

  • If an empty GetMedicalDataSourcesRequest is 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.packageNames is 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:

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
@NonNull GetMedicalDataSourcesRequest request

containing details of the MedicalDataSources to retrieve

Returns
@NonNull List<@NonNull MedicalDataSource>

MedicalDataSources matching the provided request

getOrCreate

Added in 1.1.0-beta02
default static final @NonNull HealthConnectClient getOrCreate(@NonNull Context context, @NonNull String providerPackageName)

Retrieves an IPC-backed HealthConnectClient instance binding to an available implementation.

Parameters
@NonNull Context context

the context

@NonNull String providerPackageName

optional alternative package provider to choose for backend implementation

Returns
@NonNull HealthConnectClient

instance of HealthConnectClient ready for issuing requests

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

getPermissionController

Added in 1.1.0-beta02
abstract @NonNull PermissionController getPermissionController()

Access operations related to permissions.

getSdkStatus

Added in 1.1.0-beta02
default static final int getSdkStatus(@NonNull Context context, @NonNull String providerPackageName)

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
@NonNull Context context

the context

@NonNull String providerPackageName

optional package provider to choose for backend implementation

insertRecords

abstract @NonNull InsertRecordsResponse insertRecords(@NonNull List<@NonNull Record> records)

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.

Parameters
@NonNull List<@NonNull Record> records

List of records to insert

Returns
@NonNull 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
default @NonNull List<@NonNull MedicalResourcereadMedicalResources(@NonNull List<@NonNull MedicalResourceId> ids)

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:

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 ids is too large or any ID is deemed as invalid.

readMedicalResources

@ExperimentalPersonalHealthRecordApi
default @NonNull ReadMedicalResourcesResponse readMedicalResources(@NonNull ReadMedicalResourcesRequest request)

Reads MedicalResources by request, either ReadMedicalResourcesInitialRequest or ReadMedicalResourcesPageRequest.

A typical flow to read all MedicalResources that satisfy a certain criteria would be:

  1. Create a ReadMedicalResourcesInitialRequest with desired criteria, and make a request. If successful, a ReadMedicalResourcesResponse should be returned.

  2. Use returned ReadMedicalResourcesResponse.nextPageToken to create a ReadMedicalResourcesPageRequest and make another request. Again, if successful, a ReadMedicalResourcesResponse should be returned.

  3. Repeat step 2 until ReadMedicalResourcesResponse.nextPageToken is null.

Regarding permissions, only permitted MedicalResources are returned. Specifically:

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

abstract @NonNull ReadRecordResponse<@NonNull T> <T extends Record> readRecord(
    @NonNull KClass<@NonNull T> recordType,
    @NonNull String recordId
)

Reads one Record point with its recordType and recordId.

Parameters
@NonNull KClass<@NonNull T> recordType

Which type of Record to read, such as Steps::class

@NonNull String recordId

androidx.health.connect.client.records.metadata.Metadata.id of Record to read

Returns
@NonNull ReadRecordResponse<@NonNull T>

The Record data point.

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

abstract @NonNull ReadRecordsResponse<@NonNull T> <T extends Record> readRecords(@NonNull ReadRecordsRequest<@NonNull T> request)

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 extends Record>

the type of Record

@NonNull ReadRecordsRequest<@NonNull T> request

ReadRecordsRequest object specifying time range and other filters

Returns
@NonNull ReadRecordsResponse<@NonNull T>

a response containing a collection of 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.

Example code to read basic data like step counts:

updateRecords

abstract void updateRecords(@NonNull List<@NonNull Record> records)

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.

Parameters
@NonNull List<@NonNull Record> records

List of records to update

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
default @NonNull List<@NonNull MedicalResourceupsertMedicalResources(
    @NonNull List<@NonNull UpsertMedicalResourceRequest> requests
)

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:

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:

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
@NonNull List<@NonNull UpsertMedicalResourceRequest> requests

List of upsert requests.

Throws
kotlin.IllegalArgumentException

if any request is failed to be processed for any reason such as invalid UpsertMedicalResourceRequest.dataSourceId

java.lang.SecurityException

if caller does not hold PERMISSION_WRITE_MEDICAL_DATA.

Extension functions

HealthConnectClientExt.deleteRecords

default final void <T extends Record> HealthConnectClientExt.deleteRecords(
    @NonNull HealthConnectClient receiver,
    @NonNull TimeRangeFilter timeRangeFilter
)

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 extends Record>

Which type of Record to delete, such as StepsRecord.

@NonNull TimeRangeFilter timeRangeFilter

The TimeRangeFilter to delete from

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:

HealthConnectClientExt.deleteRecords

default final void <T extends Record> HealthConnectClientExt.deleteRecords(
    @NonNull HealthConnectClient receiver,
    @NonNull List<@NonNull String> recordIdsList,
    @NonNull List<@NonNull String> clientRecordIdsList
)

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 extends Record>

Which type of Record to delete, such as StepsRecord.

@NonNull List<@NonNull String> recordIdsList

List of androidx.health.connect.client.records.metadata.Metadata.id of Record to delete

@NonNull List<@NonNull String> clientRecordIdsList

List of client record IDs of Record to delete

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:

HealthConnectClientExt.readRecord

default final @NonNull ReadRecordResponse<@NonNull T> <T extends Record> HealthConnectClientExt.readRecord(
    @NonNull HealthConnectClient receiver,
    @NonNull String recordId
)

Reads one Record point of type T and with the specified recordId.

Parameters
<T extends Record>

Which type of Record to read, such as StepsRecord.

@NonNull String recordId

androidx.health.connect.client.records.metadata.Metadata.id of Record to read

Returns
@NonNull ReadRecordResponse<@NonNull T>

The Record data point.

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