### Start a Workout with Goals Source: https://developer.android.com/health-and-fitness/health-services/active-data Configure and start a running exercise with one-time calorie and milestone distance goals. Ensure GPS is enabled if location data is required. ```kotlin const val CALORIES_THRESHOLD = 250.0 const val DISTANCE_THRESHOLD = 1_000.0 // meters suspend fun startExercise() { // Types for which we want to receive metrics. val dataTypes = setOf( DataType.HEART_RATE_BPM, DataType.CALORIES_TOTAL, DataType.DISTANCE ) // Create a one-time goal. val calorieGoal = ExerciseGoal.createOneTimeGoal( DataTypeCondition( dataType = DataType.CALORIES_TOTAL, threshold = CALORIES_THRESHOLD, comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL ) ) // Create a milestone goal. To make a milestone for every kilometer, set the initial // threshold to 1km and the period to 1km. val distanceGoal = ExerciseGoal.createMilestone( condition = DataTypeCondition( dataType = DataType.DISTANCE_TOTAL, threshold = DISTANCE_THRESHOLD, comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL ), period = DISTANCE_THRESHOLD ) val config = ExerciseConfig( exerciseType = ExerciseType.RUNNING, dataTypes = dataTypes, isAutoPauseAndResumeEnabled = false, isGpsEnabled = true, exerciseGoals = mutableListOf>(calorieGoal, distanceGoal) ) exerciseClient.startExerciseAsync(config).await() } ``` -------------------------------- ### Start Custom Synthetic Exercise Source: https://developer.android.com/health-and-fitness/health-services/simulated-data Start a custom synthetic exercise with specific parameters. Allows fine-grained control over metrics like duration, heart rate, and speed. ```bash adb shell am broadcast \ -a "whs.synthetic.user.START_EXERCISE" \ --ei exercise_options_heart_rate 90 \ --ef exercise_options_average_speed 1.2 \ --ez exercise_options_use_location true \ com.google.android.wearable.healthservices ``` -------------------------------- ### Write a Sleep Session Record (Basic) Source: https://developer.android.com/health-and-fitness/health-connect/features/sleep-sessions Create and insert a SleepSessionRecord with a title, start and end times, and timezone offsets. This serves as a fundamental example for recording sleep periods. ```kotlin SleepSessionRecord( title = "weekend sleep", startTime = startTime, endTime = endTime, startZoneOffset = ZoneOffset.UTC, endZoneOffset = ZoneOffset.UTC, ) ``` -------------------------------- ### Start Synthetic Walking Exercise Source: https://developer.android.com/health-and-fitness/health-services/simulated-data Initiate a synthetic walking exercise. This command generates realistic data for walking activities. ```bash # start the "walking" synthetic exercise $ adb shell am broadcast \ -a "whs.synthetic.user.START_WALKING" \ com.google.android.wearable.healthservices ``` -------------------------------- ### Start a New Exercise Session Source: https://developer.android.com/health-and-fitness/health-connect/experiences/workouts Generates a unique client ID, creates an ExerciseSessionRecord with start and end times, and inserts it into Health Connect. Ensure the client ID is stable for potential sync retries. ```kotlin val sessionClientId = UUID.randomUUID().toString() val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime) val session = ExerciseSessionRecord( startTime = startTime, startZoneOffset = zoneOffset, endTime = startTime.plusSeconds(3600), endZoneOffset = zoneOffset, exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING, metadata = Metadata(clientRecordId = sessionClientId) ) healthConnectClient.insertRecords(listOf(session)) HealthConnectManager.kt ``` -------------------------------- ### Read Exercise Sessions and Associated Data Source: https://developer.android.com/health-and-fitness/health-connect/features/exercise-routes This example demonstrates how to read exercise session records within a specified time range. It also shows how to fetch associated data, such as distance records, for each session. ```kotlin suspend fun readExerciseSessions( healthConnectClient: HealthConnectClient, startTime: Instant, endTime: Instant ) { val response = healthConnectClient.readRecords( ReadRecordsRequest( ExerciseSessionRecord::class, timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) for (exerciseRecord in response.records) { // Process each exercise record // Optionally pull in with other data sources of the same time range. val distanceRecord = healthConnectClient .readRecords( ReadRecordsRequest( DistanceRecord::class, timeRangeFilter = TimeRangeFilter.between( exerciseRecord.startTime, exerciseRecord.endTime ) ) ) .records } } ``` -------------------------------- ### WorkManager Setup in Application Class Source: https://developer.android.com/health-and-fitness/fitness/basic-app/read-step-count-data This snippet demonstrates how to configure WorkManager by extending the Application class and enqueuing a periodic work request to run the StepCounterWorker every 15 minutes. ```kotlin @HiltAndroidApp @RequiresApi(Build.VERSION_CODES.S) internal class PulseApplication : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory override fun onCreate() { super.onCreate() val myWork = PeriodicWorkRequestBuilder( 15, TimeUnit.MINUTES).build() WorkManager.getInstance(this) .enqueueUniquePeriodicWork("MyUniqueWorkName", ExistingPeriodicWorkPolicy.UPDATE, myWork) } override val workManagerConfiguration: Configuration get() = Configuration.Builder() .setWorkerFactory(workerFactory) .setMinimumLoggingLevel(android.util.Log.DEBUG) .build() } ``` -------------------------------- ### Instantiate StepsRecord with Metadata Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/metadata When writing data to Health Connect, you must specify metadata details using one of the factory methods for Metadata. This example shows how to instantiate a StepsRecord with the Metadata object. ```kotlin StepsRecord( startTime = Instant.ofEpochMilli(1234L), startZoneOffset = null, endTime = Instant.ofEpochMilli(1236L), endZoneOffset = null, metadata = Metadata(), count = 10 ) ``` -------------------------------- ### Define a Daily Steps Goal Source: https://developer.android.com/health-and-fitness/guides/health-services/monitor-background Create a passive goal to trigger notifications when a specific daily step count is reached. This example defines a goal for 10,000 steps. ```kotlin val dailyStepsGoal by lazy { val condition = DataTypeCondition( dataType = DataType.STEPS_DAILY, threshold = 10_000, // Trigger every 10000 steps comparisonType = ComparisonType.GREATER_THAN_OR_EQUAL ) PassiveGoal(condition) } ``` -------------------------------- ### Get Exercise Capabilities Source: https://developer.android.com/health-and-fitness/health-services/active-data Query the device capabilities to determine supported exercise types and their specific features, data types, and goals. This should be done on app startup. ```kotlin val healthClient = HealthServices.getClient(this /*context*/) val exerciseClient = healthClient.exerciseClient lifecycleScope.launch { val capabilities = exerciseClient.getCapabilitiesAsync().await() if (ExerciseType.RUNNING in capabilities.supportedExerciseTypes) { runningCapabilities = capabilities.getExerciseTypeCapabilities(ExerciseType.RUNNING) } } ``` -------------------------------- ### Get Boot Timestamp Source: https://developer.android.com/health-and-fitness/guides/health-services/monitor-background Calculate the boot timestamp to accurately interpret data point timestamps. This is essential for ordering events correctly when data is received in batches. ```kotlin val bootInstant = Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime()) ``` -------------------------------- ### Write Blood Pressure Record to Health Connect Source: https://developer.android.com/health-and-fitness/health-connect/experiences/vitals Example of creating and writing a single BloodPressureRecord to Health Connect. Ensure the Health Connect client is properly initialized. ```kotlin suspend fun writeBloodPressureRecord(healthConnectClient: HealthConnectClient) { val record = BloodPressureRecord( time = Instant.now(), zoneOffset = ZoneOffset.UTC, systolic = Pressure.millimetersOfMercury(120.0), diastolic = Pressure.millimetersOfMercury(80.0), bodyPosition = BloodPressureRecord.BODY_POSITION_SITTING_DOWN, measurementLocation = BloodPressureRecord.MEASUREMENT_LOCATION_LEFT_WRIST ) healthConnectClient.insertRecords(listOf(record)) } ``` -------------------------------- ### Write Nutrition Data Source: https://developer.android.com/health-and-fitness/health-connect/write-data Example of how to set nutrition data, including energy and various nutrient values with their respective units of measurement. All nutrients are represented in units of Mass, while energy is in a unit of Energy. ```kotlin val endTime = Instant.now() val startTime = endTime.minus(Duration.ofMinutes(1)) 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 = startTime, endTime = endTime, startZoneOffset = ZoneOffset.UTC, endZoneOffset = ZoneOffset.UTC, metadata = Metadata( device = Device(type = Device.TYPE_PHONE) ) ) ``` -------------------------------- ### Read Exercise Sessions Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/exercise-routes This function demonstrates how to read exercise sessions within a specified time range. It iterates through the retrieved records and shows an example of fetching associated distance data for each session. ```kotlin suspend fun readExerciseSessions( healthConnectClient: HealthConnectClient, startTime: Instant, endTime: Instant ) { val response = healthConnectClient.readRecords( ReadRecordsRequest( ExerciseSessionRecord::class, timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) for (exerciseRecord in response.records) { // Process each exercise record // Optionally pull in with other data sources of the same time range. val distanceRecord = healthConnectClient .readRecords( ReadRecordsRequest( DistanceRecord::class, timeRangeFilter = TimeRangeFilter.between( exerciseRecord.startTime, exerciseRecord.endTime ) ) ) .records } } ``` -------------------------------- ### Check Health Connect Availability Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/exercise-routes Verifies if Health Connect is installed and available on the user's device. Guides the user to install or update if necessary. ```kotlin fun checkHealthConnectAvailability(context: Context) { val providerPackageName = "com.google.android.apps.healthdata" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName) if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) { // Health Connect is not available. Guide the user to install/enable it. // For example, show a dialog. return // early return as there is no viable integration } if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) { // Health Connect is available but requires an update. // Optionally redirect to package installer to find a provider, for example: val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding" context.startActivity( Intent(Intent.ACTION_VIEW).apply { setPackage("com.android.vending") data = Uri.parse(uriString) putExtra("overlay", true) putExtra("callerId", context.packageName) } ) return } // Health Connect is available, obtain a HealthConnectClient instance val healthConnectClient = HealthConnectClient.getOrCreate(context) // Issue operations with healthConnectClient } ``` -------------------------------- ### Request Steps Permissions Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/steps Create a set of permissions for reading and writing steps, and use a launcher to request them from the user. ```kotlin val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class) ) HealthConnectManager.kt ``` -------------------------------- ### Example FHIR Resource JSON Source: https://developer.android.com/health-and-fitness/guides/medical-records/write-data This is an example of a FHIR JSON string for an `AllergyIntolerance` resource, which maps to the `FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE` in Medical Records. ```json { "resourceType": "AllergyIntolerance", "id": "allergyintolerance-1", "criticality": "high", "code": { "coding": [ { "system": "http://snomed.info/sct", "code": "91936005", "display": "Penicillin allergy" } ], "text": "Penicillin allergy" }, "recordedDate": "2020-10-09T14:58:00+00:00", "asserter": { "reference": "Patient/patient-1" }, "lastOccurrence": "2020-10-09", "patient": { "reference": "Patient/patient-1", "display": "B., Alex" } ... } ``` -------------------------------- ### Launch Matchmaking Flow Source: https://developer.android.com/health-and-fitness/health-connect/ui/matchmaking Use this code to create a launcher for the matchmaking flow and launch it with a `MatchmakingRequest`. The result handler checks if the matchmaking was successful. ```kotlin val matchmakingLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { // Matchmaking finished successfully. // User successfully granted at least one permission. } else { // User canceled flow or didn't grant permissions. } } fun launchMatchmaking(healthConnectClient: HealthConnectClient) { val request = MatchmakingRequest(recordTypes = setOf(StepsRecord::class)) val intent = healthConnectClient.createMatchmakingIntent(request) matchmakingLauncher.launch(intent) } ``` -------------------------------- ### Create a HeartRateRecord with Samples Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/write-data Demonstrates how to create a `HeartRateRecord` for a single minute, including multiple heart rate samples within that minute. This is useful for structuring time-series data. ```kotlin val startTime = Instant.now().truncatedTo(ChronoUnit.MINUTES) val endTime = startTime.plus(Duration.ofMinutes(1)) val heartRateRecord = HeartRateRecord( startTime = startTime, startZoneOffset = ZoneOffset.UTC, endTime = endTime, endZoneOffset = ZoneOffset.UTC, // Create a new record every minute, containing a list of samples. samples = listOf( HeartRateRecord.Sample( time = startTime + Duration.ofSeconds(15), beatsPerMinute = 80, ), HeartRateRecord.Sample( time = startTime + Duration.ofSeconds(30), beatsPerMinute = 82, ), HeartRateRecord.Sample( time = startTime + Duration.ofSeconds(45), beatsPerMinute = 85, ) ), metadata = Metadata( device = Device(type = Device.TYPE_WATCH) )) HealthConnectManager.kt ``` -------------------------------- ### Run Configuration for Wear OS Source: https://developer.android.com/health-and-fitness/guides/basic-fitness-app/integrate-wear-os This Run configuration allows you to deploy and run your Wear OS module on an emulator or a real device, providing a basic 'hello world' experience. ```java Run configuration for Wear OS ``` -------------------------------- ### Install Health Connect Toolbox APK Source: https://developer.android.com/health-and-fitness/guides/health-connect/test/health-connect-toolbox Install the Health Connect Toolbox APK on a connected device using ADB. Ensure you replace `{Version Number}` with the actual version from your downloaded ZIP file. ```bash $ adb install HealthConnectToolbox-{Version Number}.apk ``` -------------------------------- ### Get Changes Token for Health Connect Source: https://developer.android.com/health-and-fitness/health-connect/sync-data Obtain a changes token from Health Connect to request data changes. Supply the required record types to the ChangesTokenRequest. It's recommended to get separate tokens per data type to avoid exceptions if a permission is revoked. ```kotlin val changesToken = healthConnectClient.getChangesToken( ChangesTokenRequest(recordTypes = setOf(WeightRecord::class)) ) ``` -------------------------------- ### Instantiate AppDatabase with Room Source: https://developer.android.com/health-and-fitness/fitness/basic-app/read-step-count-data Create an abstract class extending `RoomDatabase` to instantiate the database and provide access to the DAOs. ```kotlin @Database(entities = [StepCount::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun stepsDao(): StepsDao } ``` -------------------------------- ### Get MedicalResource records by ID Source: https://developer.android.com/health-and-fitness/health-connect/medical-records/read-data Retrieve a `MedicalResource` using its ID. ```APIDOC ## Get MedicalResource records by ID ### Description You can also retrieve a `MedicalResource` using an ID. ### Method POST (Implied by `healthConnectClient.readMedicalResources(List)`) ### Endpoint `/medical-resources/read-by-ids` (Implied) ### Parameters #### Request Body - **medicalResourceIds** (List) - Required - A list of `MedicalResourceId` objects to retrieve. - **dataSourceId** (string) - Required - The ID of the data source. - **fhirResourceType** (string) - Required - The FHIR resource type. - **fhirResourceId** (string) - Required - The FHIR resource ID. ### Request Example ```json [ { "dataSourceId": "some-data-source-id", "fhirResourceType": "Observation", "fhirResourceId": "some-fhir-resource-id" } ] ``` ### Response #### Success Response (200) - **List** - A list of `MedicalResource` objects matching the provided IDs. #### Response Example ```json [ { "id": { "dataSourceId": "some-data-source-id", "fhirResourceType": "Observation", "fhirResourceId": "some-fhir-resource-id" }, "type": "LABORATORY_RESULTS", "creationTime": "2023-10-27T10:00:00Z", "lastModifiedTime": "2023-10-27T10:00:00Z" } ] ``` ``` -------------------------------- ### Get MedicalDataSource records by ID Source: https://developer.android.com/health-and-fitness/health-connect/medical-records/read-data Retrieve all `MedicalDataSource` with `id` matching any of the given IDs. ```APIDOC ## Get MedicalDataSource records by ID ### Description Or, request by `id` if you know it. ### Method POST (Implied by `healthConnectClient.getMedicalDataSources(List)`) ### Endpoint `/medical-data-sources/get-by-id` (Implied) ### Parameters #### Request Body - **ids** (List) - Required - A list of `MedicalDataSource` IDs to retrieve. ### Request Example ```json [ "some-data-source-id-1", "some-data-source-id-2" ] ``` ### Response #### Success Response (200) - **List** - A list of `MedicalDataSource` objects matching the provided IDs. #### Response Example ```json [ { "id": "some-data-source-id-1", "packageName": "com.example.app1", "name": "App 1 Data Source", "version": "1.0" }, { "id": "some-data-source-id-2", "packageName": "com.example.app2", "name": "App 2 Data Source", "version": "1.1" } ] ``` ``` -------------------------------- ### Manifest Configuration for InitializationProvider Source: https://developer.android.com/health-and-fitness/fitness/basic-app/read-step-count-data This XML snippet shows how to add an element to your app's manifest file to initialize the content provider that controls access to your app's step counter database immediately upon app startup. ```xml ``` -------------------------------- ### Get MedicalDataSource records by package name Source: https://developer.android.com/health-and-fitness/health-connect/medical-records/read-data Retrieve all `MedicalDataSource`s created by any of the specified package names. ```APIDOC ## Get MedicalDataSource records by package name ### Description Use `GetMedicalDataSourcesRequest` to request by package name (app). ### Method POST (Implied by `healthConnectClient.getMedicalDataSources(GetMedicalDataSourcesRequest)`) ### Endpoint `/medical-data-sources/get-by-package-name` (Implied) ### Parameters #### Request Body - **packageNames** (List) - Required - A list of package names to retrieve `MedicalDataSource`s for. ### Request Example ```json { "packageNames": ["com.example.app1", "com.example.app2"] } ``` ### Response #### Success Response (200) - **List** - A list of `MedicalDataSource` objects matching the provided package names. #### Response Example ```json [ { "id": "some-data-source-id-1", "packageName": "com.example.app1", "name": "App 1 Data Source", "version": "1.0" }, { "id": "some-data-source-id-2", "packageName": "com.example.app2", "name": "App 2 Data Source", "version": "1.1" } ] ``` ``` -------------------------------- ### Create StepsRecord with Client Record Version Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/write-data Demonstrates creating a StepsRecord with `clientRecordId` and `clientRecordVersion` metadata. This is used for version-controlled upserts where Health Connect compares versions to determine if an update should occur. ```kotlin val endTime = Instant.now() val startTime = endTime.minus(Duration.ofMinutes(15)) val stepsRecord = StepsRecord( count = 100L, startTime = startTime, startZoneOffset = ZoneOffset.UTC, endTime = endTime, endZoneOffset = ZoneOffset.UTC, metadata = Metadata( clientRecordId = "Your supplied record ID", clientRecordVersion = 0L, // Your supplied record version device = Device(type = Device.TYPE_WATCH) ) ) HealthConnectManager.kt ``` -------------------------------- ### Mock Metadata with Testing Library Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/metadata Shows how to use `MetadataTestHelper` to mock metadata values for testing purposes. This simulates the automatic population of metadata by Health Connect during record insertion. ```kotlin private val TEST_METADATA = Metadata.unknownRecordingMethod( clientRecordId = "clientId", clientRecordVersion = 1L, device = Device(type = Device.TYPE_UNKNOWN), ).populatedWithTestValues(id = "test") ``` -------------------------------- ### Read Raw Steps Data Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/steps Retrieve raw `StepsRecord` data between a start and end time. The records can then be processed. ```kotlin val response = healthConnectClient.readRecords( ReadRecordsRequest( StepsRecord::class, timeRangeFilter = TimeRangeFilter.between(startTime, endTime) ) ) response.records.forEach { record -> /* Process records */ } ``` -------------------------------- ### Prepare Exercise Asynchronously Source: https://developer.android.com/health-and-fitness/health-services/active-data Use `prepareExerciseAsync()` to warm up sensors like heart rate and GPS before starting a workout. This method allows sensors to stabilize and data to be received without affecting the workout timer. Ensure necessary permissions and location settings are enabled before calling. ```kotlin val warmUpConfig = WarmUpConfig( ExerciseType.RUNNING, setOf( DataType.HEART_RATE_BPM, DataType.LOCATION ) ) // Only necessary to call prepareExerciseAsync if body sensor (API level 35 // or lower), heart rate (API level 36+), or location permissions are given. exerciseClient.prepareExerciseAsync(warmUpConfig).await() // Data and availability updates are delivered to the registered listener. ``` -------------------------------- ### Configure Exercise with Event Types Source: https://developer.android.com/health-and-fitness/health-services/active-data/exercise-events Declare an ExerciseConfig to include specific exercise event types, such as GOLF_SHOT_EVENT, when starting an exercise. ```Kotlin val config = ExerciseConfig( exerciseType = ExerciseType.GOLF, dataTypes = setOf(....), // ... exerciseEventTypes = setOf(ExerciseEventType.GOLF_SHOT_EVENT), ) ``` -------------------------------- ### Read MedicalResource records Source: https://developer.android.com/health-and-fitness/health-connect/medical-records/read-data Filter a get request by specifying the `medicalResourceType`. Make sure to use paged requests and be mindful of rate limiting. ```APIDOC ## Read MedicalResource records ### Description Filter a get request by specifying the `medicalResourceType`. Make sure to use paged requests and be mindful of rate limiting. ### Method POST (Implied by `healthConnectClient.readMedicalResources(request)` where request can be `ReadMedicalResourcesInitialRequest` or `ReadMedicalResourcesPageRequest`) ### Endpoint `/medical-resources/read` (Implied) ### Parameters #### Query Parameters - **medicalResourceType** (string) - Required - The type of medical resource to retrieve. - **medicalDataSourceIds** (Set) - Optional - A set of medical data source IDs to filter by. If empty, all resources of the given type across all data sources are retrieved. - **pageSize** (int) - Optional - The number of resources to retrieve per page. Defaults to a system value. - **pageToken** (string) - Optional - A token to retrieve the next page of results. ### Request Example ```json { "medicalResourceType": "LABORATORY_RESULTS", "medicalDataSourceIds": [], "pageSize": 100 } ``` ### Response #### Success Response (200) - **medicalResources** (List) - A list of medical resources. - **nextPageToken** (string) - A token to retrieve the next page of results. Null if there are no more pages. #### Response Example ```json { "medicalResources": [ { "id": { "dataSourceId": "some-data-source-id", "fhirResourceType": "Observation", "fhirResourceId": "some-fhir-resource-id" }, "type": "LABORATORY_RESULTS", "creationTime": "2023-10-27T10:00:00Z", "lastModifiedTime": "2023-10-27T10:00:00Z" } ], "nextPageToken": "some-next-page-token" } ``` ``` -------------------------------- ### Write Subtype Data for Exercise Sessions Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/exercise-routes This example shows how to enrich an ExerciseSessionRecord with subtype data, including ExerciseSegment, ExerciseLap, and ExerciseRoute. Ensure subtype data is correctly instantiated with appropriate start/end times and values. ```kotlin val segments = listOf( ExerciseSegment( startTime = Instant.parse("2022-01-02T10:10:10Z"), endTime = Instant.parse("2022-01-02T10:10:13Z"), segmentType = ActivitySegmentType.BENCH_PRESS, repetitions = 373 ) ) val laps = listOf( ExerciseLap( startTime = Instant.parse("2022-01-02T10:10:10Z"), endTime = Instant.parse("2022-01-02T10:10:13Z"), length = 0.meters ) ) ExerciseSessionRecord( exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS, startTime = Instant.parse("2022-01-02T10:10:10Z"), endTime = Instant.parse("2022-01-02T10:10:13Z"), startZoneOffset = ZoneOffset.UTC, endZoneOffset = ZoneOffset.UTC, segments = segments, laps = laps, route = route, metadata = Metadata.manualEntry() ) ``` -------------------------------- ### Check Heart Rate Capability Source: https://developer.android.com/health-and-fitness/health-services/active-data/measure-client Check if the device supports providing heart rate data before registering for updates. This example uses coroutines and ListenableFuture. ```Kotlin val healthClient = HealthServices.getClient(this /*context*/) val measureClient = healthClient.measureClient lifecycleScope.launch { val capabilities = measureClient.getCapabilitiesAsync().await() supportsHeartRate = DataType.HEART_RATE_BPM in capabilities.supportedDataTypesMeasure } ``` -------------------------------- ### Schedule Background Read Worker Source: https://developer.android.com/health-and-fitness/health-connect/read-data Example of scheduling a WorkManager worker to perform background reads from Health Connect. Ensure the FEATURE_READ_HEALTH_DATA_IN_BACKGROUND is available before enqueuing. ```kotlin class ScheduleWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) { override suspend fun doWork(): Result { val healthConnectClient = HealthConnectClient.getOrCreate(applicationContext) // Perform background read logic here return Result.success() } } ``` ```kotlin @OptIn(ExperimentalFeatureAvailabilityApi::class) fun enqueueBackgroundReadWorker(context: Context, healthConnectClient: HealthConnectClient) { if (healthConnectClient .features .getFeatureStatus( HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE ) { val periodicWorkRequest = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS) .build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( "read_health_connect", ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest ) } } ``` -------------------------------- ### Mock Metadata for Testing Source: https://developer.android.com/health-and-fitness/health-connect/metadata Shows how to use `Metadata.unknownRecordingMethod` and `populatedWithTestValues` from the testing library to mock metadata for testing purposes. This simulates Health Connect's automatic population of metadata during record insertion. ```kotlin private val TEST_METADATA = Metadata.unknownRecordingMethod( clientRecordId = "clientId", clientRecordVersion = 1L, device = Device(type = Device.TYPE_UNKNOWN), ).populatedWithTestValues(id = "test") ``` -------------------------------- ### Get Medical Data Sources by ID Source: https://developer.android.com/health-and-fitness/guides/medical-records/read-data Retrieve `MedicalDataSource`s by their unique IDs. This method is efficient when you already know the IDs of the data sources you are interested in. ```kotlin val medicalDataSources: List = healthConnectClient.getMedicalDataSources(listOf(medicalDataSource.id, anotherId)) ``` -------------------------------- ### Set Heart Rate Series Data Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/write-data Demonstrates how to create and populate a HeartRateRecord with a series of samples. Ensure you have the necessary Health Connect client setup. ```kotlin val endTime = Instant.now() val startTime = endTime.minus(Duration.ofMinutes(5)) val heartRateRecord = HeartRateRecord( startTime = startTime, startZoneOffset = ZoneOffset.UTC, endTime = endTime, endZoneOffset = ZoneOffset.UTC, // records 10 arbitrary data, to replace with actual data samples = List(10) { index -> HeartRateRecord.Sample( time = startTime + Duration.ofSeconds(index.toLong()), beatsPerMinute = 100 + index.toLong(), ) }, metadata = Metadata( device = Device(type = Device.TYPE_WATCH) )) HealthConnectManager.kt ``` -------------------------------- ### Write Steps Data Source: https://developer.android.com/health-and-fitness/guides/health-connect/develop/get-started Structure your data into a record and write it using `insertRecords`. Ensure you have the necessary data types defined and permissions granted. ```kotlin val zoneOffset = ZoneOffset.systemDefault().rules.getOffset(startTime) val stepsRecord = StepsRecord( count = 120, startTime = startTime, endTime = endTime, startZoneOffset = zoneOffset, endZoneOffset = zoneOffset, metadata = Metadata( device = Device(type = Device.TYPE_WATCH), recordingMethod = Metadata.RECORDING_METHOD_AUTOMATICALLY_RECORDED ) ) healthConnectClient.insertRecords(listOf(stepsRecord)) ``` -------------------------------- ### Implement CompanionDeviceService Source: https://developer.android.com/health-and-fitness/health-connect/sync-data Create a class that extends CompanionDeviceService to handle wearable device connections and data reception via BLE GATT callbacks. This service writes received data to Health Connect. ```kotlin private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private var healthConnectClient: HealthConnectClient? = null private var bluetoothGatt: BluetoothGatt? = null override fun onDeviceAppeared(address: String) { super.onDeviceAppeared(address) healthConnectClient = HealthConnectClient.getOrCreate(this) serviceScope.launch { val granted = healthConnectClient?.permissionController?.getGrantedPermissions() // 1. Check permissions ONCE when the device connects if (granted?.contains(HealthPermission.getWritePermission(HeartRateRecord::class)) ?: false) { // This is where you'd actually start the Bluetooth connection // bluetoothGatt = gattCallback.connect(...) } // 2. Do your initial database read readExerciseSessionAndRoute() } } private val gattCallback = object : BluetoothGattCallback() { override fun onCharacteristicChanged( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray ) { super.onCharacteristicChanged(gatt, characteristic, value) // 3. ONLY process the incoming data here val rawData = value serviceScope.launch { // parseWearableData(rawData) // insertExerciseRoute() or writeToHealthConnect() } } } MyWearableService.kt ``` -------------------------------- ### Check Current Exercise Status Source: https://developer.android.com/health-and-fitness/health-services/active-data Before starting a new exercise, check if an exercise is already being tracked by the device. This helps in managing potential conflicts and informing the user. ```kotlin lifecycleScope.launch { val exerciseInfo = exerciseClient.getCurrentExerciseInfoAsync().await() when (exerciseInfo.exerciseTrackedStatus) { OTHER_APP_IN_PROGRESS -> // Warn user before continuing, will stop the existing workout. OWNED_EXERCISE_IN_PROGRESS -> // This app has an existing workout. NO_EXERCISE_IN_PROGRESS -> // Start a fresh workout. } } ```