Managing Sleep Data with Samsung Health and Health Connect

Samsung Developer

Sleep is an important factor in our everyday life. Good sleeping habits play a major role in physical health and overall well-being. With Galaxy Watch and Samsung Health, users can track their sleep, evaluate its quality, and get coaching to develop healthy habits. When the user wakes up, the sleep data is analyzed and the user can review key sleep statistics and how much time they spent in each sleep stage. Sleep coaching functionality compares data to previous days, so the user can track how their daily routine improvements influence their sleep quality.

Galaxy Watch devices can also measure blood oxygen during sleep, and users with a Galaxy Watch5 or higher can also track their skin temperature. When a phone is used together with the watch for sleep tracking, snoring detection is also possible.

Sleep tracking

You can leverage Samsung Health advanced sleep tracking and Health Connect API to create applications that can read users’ real sleep data and create custom sleep session information that can be synchronized to Samsung Health.

This blog demonstrates how to use the Health Connect API to read data from Health Connect, work with session data, and insert data to Health Connect, using sleep data as an example. To follow along with the steps in this blog, download the sample application:

Sleep Recorder version 1.0
(128,0KB) Jan 15, 2024

For more information about Samsung Health and Health Connect, see Accessing Samsung Health Data through Health Connect..

Sleep data synchronization with Health Connect

The Health Connect platform collects health-related data and synchronizes it with Samsung Health, enabling you to use it in your applications.

Sleep data is created on a smartwatch when the user wakes up. The data must be transferred to a paired mobile device for processing. Data transfer is initiated when the mobile device is connected. The processed data creates a sleep record, which Samsung Health synchronizes to Health Connect. Transfer and synchronization tasks can be delayed, depending on processor availability.

Prerequisites

Implementing functionality that uses health data in an application requires Health Connect library, HealthConnectionClient, and permissions.

Add Health Connect API library dependencies

To use the Health Connect API features in your application:

dependencies {
    // Add Health Connect library
    implementation "androidx.health.connect:connect-client:1.1.0-alpha06"
}

Configure the “AndroidManifest.xml” file

  • Declare the required permissions:
<uses-permission android:name="android.permission.health.WRITE_SLEEP"/>
<uses-permission android:name="android.permission.health.READ_SLEEP" />
  • Add <intent-filter> in the <activity> section:
<intent-filter>
	<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
  • Add the <activity-alias> element required in Android 14:
<activity-alias
	android:name="ViewPermissionUsageActivity"
    android:exported="true"
    android:targetActivity=".MainMenuActivity"
    android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
    <intent-filter>
    	<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
        <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
    </intent-filter>
</activity-alias>
  • Add <uses-permission> elements:
<uses-permission android:name="android.permission.health.WRITE_SLEEP"/>
<uses-permission android:name="android.permission.health.READ_SLEEP" />
  • Add the <queries> element:
<queries>
	<package android:name="com.google.android.apps.healthdata" />
</queries>

Note that in this application we also use:

  <package android:name="com.sec.android.app.shealth" />

Adding this element is necessary, since we are going to open the Samsung Health app. However, if you are interested in using only the Health Connect API – the above part is not required.

Get a HealthConnect client

The HealthConnectClient class is an entry point to the Health Connect API. It automatically manages the connection to the underlying storage layer and handles all IPC and serialization of the outgoing requests and the incoming responses.

It is a good practice to ensure that the device running your application actually supports the Health Connect API library. The library is available only when the Health Connect application is installed on the device.

Check for Health Connect availability. If it is missing, display an error and redirect the user to the app store if installation of the app is possible:
when (HealthConnectClient.getSdkStatus(this)) {
  HealthConnectClient.SDK_UNAVAILABLE -> {
  // Error message
  }
  HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED -> {
  // Error message
  try {
  	startActivity(
      	Intent(
          	Intent.ACTION_VIEW,
          	Uri.parse("market://details?id=com.google.android.apps.healthdata"),
      	),
  	)
  } catch (e: ActivityNotFoundException) {
  	startActivity(
      	Intent(
          	Intent.ACTION_VIEW,
          	Uri.parse("https://play.google.com/store/apps/details?id=com.google.android.apps.healthdata"),
      	),
  	)
  }
}

If Health Connect is available, get a HealthConnectClient class instance:

private val healthConnectClient by lazy { HealthConnectClient.getOrCreate(context) }

Request user permissions

Your application must request permission from the user to use their health data.

  • Create a set of permissions for the required data types. The permissions must match those you defined in the AndroidManifest.xml file.
  val permissions = setOf(
    HealthPermission.getWritePermission(SleepSessionRecord::class),
    HealthPermission.getReadPermission(SleepSessionRecord::class),
)
  • Check whether the user has granted the required permissions:
suspend fun hasAllPermissions(): Boolean {
	if (HealthConnectClient.sdkStatus(context) != HealthConnectClient.SDK_AVAILABLE) {
    	return false
    }
    return healthConnectClient.permissionController.getGrantedPermissions()
		.containsAll(permissions)
}
  • If not, launch the permission request:
if (!healthConnectManager.hasAllPermissions()) {
	requestPermissions.launch(healthConnectManager.permissions)
}
  • Create the permissions prompt:
private fun createRequestPermissionsObject() {
	requestPermissions =
		registerForActivityResult(healthConnectManager.requestPermissionActivityContract) { granted ->
  			lifecycleScope.launch {
  				if (granted.isNotEmpty() && healthConnectManager.hasAllPermissions()) {
  					runOnUiThread {
						Toast.makeText(
                        	this@MainMenuActivity,
                         	R.string.permission_granted,
                            Toast.LENGTH_SHORT,
                          ) .show()
					}
				} else {
                	runOnUiThread {
                    	AlertDialog.Builder(this@MainMenuActivity)
                    	.setMessage(R.string.permissions_not_granted)
                  		.setPositiveButton(R.string.ok, null)
                		.show()
           		}
  			}
    	}
	}
}

Retrieve sleep data from Health Connect

In the sample application, to display sleep data, select a date and tap READ DATA. A list of sleep sessions for that day is displayed. When you select a session, the application retrieves and displays its sleep stage information from Health Connect.

To retrieve and display sleep session data:

  • Define the desired time range and send it to Health Connect Manager. Since a sleep session can start on the day before the selected date, be sure the application also retrieves sleep sessions from the previous day and later ignores the sessions that do not match the desired time range.
val startTime = chosenDay.minusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()
val endTime = startTime.plus(2, ChronoUnit.DAYS).minus(1, ChronoUnit.MILLIS)
val sleepSessions = healthConnectManager.readSleepSessionRecords(
	startTime,
    endTime,
)
  • Retrieve the list of sleep session records in Health Connect Manager. Create a ReadRecordsRequest object and send it to Health Connect:
val request = ReadRecordsRequest(
	recordType = SleepSessionRecord::class,
    timeRangeFilter = TimeRangeFilter.between(start, end),
)
val response = healthConnectClient.readRecords(request)
return response.records
  • Display the records in a ListView on the application screen:
for (session in sleepSessions) {
	sleepSessionRanges.add(TimeRange.between(session.startTime, session.endTime))
    val sessionStart = session.startTime.atZone(ZoneId.systemDefault()).format(
    	DateTimeFormatter.ISO_LOCAL_TIME,
  	)
    val sessionEnd = session.endTime.atZone(ZoneId.systemDefault()).format(
    	DateTimeFormatter.ISO_LOCAL_TIME,
    )
    sleepStagesLists.add(session.stages)
    sleepSessionsAdapter.add("Start: $sessionStart\t\tEnd: $sessionEnd GMT ${session.startZoneOffset}")
}
  • When a session is selected, display its sleep stage details:
for (stage in sleepStagesLists[sleepSessionIndex]) {
	val stageStart = stage.startTime.atZone(ZoneId.systemDefault()).format(
    	DateTimeFormatter.ISO_LOCAL_TIME,
    )
    val stageEnd = stage.endTime.atZone(ZoneId.systemDefault()).format(
    	DateTimeFormatter.ISO_LOCAL_TIME,
    )
    val stageName = HealthConnectManager.enumToStageMap.getValue(stage.stage)
    sleepStagesAdapter.add("$stageStart\t-\t$stageEnd\t\t$stageName")
}
You can extract Health Connect sleep data from any source, including data from Galaxy Watch. The following figure shows a sleep session in Samsung Health and the same data presented in the sample application.

Sleep data in Samsung Health and sample application

Create and insert sleep session data

Health Connect not only allows you to read sleep data that is collected through Samsung Health, it also enables you to manually insert sleep data that can be synchronized to Samsung Health.

To manually insert sleep data to Health Connect, you must prepare both a sleep session and sleep stage data.
A session is a time interval during which a user performs an activity, such as sleeping. To prepare a session, you need to know its start and end time. In the sample application, an optional time zone offset is also implemented, since data in Health Connect database is stored in UTC.

If the session start hour and minute is later than the end hour and minute, the session is interpreted as starting on the previous day. In the following figure, the session is interpreted to have started at 22:00 on 2023-12-10 and ended at 06:00 on 2023-12-11.

Sleep session duration

In the following part of the application, sleep stages can be added within the sleep session. To add a sleep stage, define its start time, end time, and name. For compatibility with Samsung Health, use the sleep stage names Awake, Light, REM, and Deep. Each defined sleep stage is visible in the list. Ideally, sleep stages cover the entire sleep session, but this is not a requirement for synchronizing with Samsung Health.

Sleep stage creation

To create and add a sleep session to Health Connect:

  • Check that the user has granted the necessary permissions:
if (!healthConnectManager.hasAllPermissions()) {
	showDialogInfo(R.string.permissions_not_granted_api_call)
    return@launch
}
  • Define the sleep session start and end time:
val startTime = sleepSessionTimeRange.startDateTimeMillis
val endTime = sleepSessionTimeRange.endDateTimeMillis
val timezoneOffset = dateOfSleepEnd.offset
  • To add sleep stage data to the session, create a SleepStage record for each stage
var sleepStagesList: ArrayList<SleepSessionRecord.Stage>

val sleepStage = SleepSessionRecord.Stage(
startTime = sleepStageStart,
                endTime = sleepStageEnd,
                stage = HealthConnectManager.stageToEnumMap.getValue(
                    activitySleepStagesBinding.spinStage.selectedItem.toString(),
                ),
)
sleepStagesList.add(sleepStage)
  • In Health Connect manager, create a sleep session record and include the sleep stage list:
suspend fun insertSleepSessionRecord(
	sleepStartTime: Instant,
    sleepEndTime: Instant,
    timezoneOffset: ZoneOffset,
    stages: ArrayList<SleepSessionRecord.Stage>,
): Boolean {
	var wasInsertSuccessful = false
    try {
    	val sleepSessionRecord = SleepSessionRecord(
        	sleepStartTime,
            timezoneOffset,
            sleepEndTime,
            timezoneOffset,
            "Sleep Record sample",
            "This is a sleep record sample recorded by SleepRecorder app",
            stages,
        )
  • Insert the sleep session record into Health Connect:
var wasInsertSuccessful = false
try {
    wasInsertSuccessful =
        healthConnectClient.insertRecords(listOf(sleepSessionRecord)).recordIdsList.isNotEmpty()
} catch (e: Exception) {
    Log.i(APP_TAG, "Error inserting record: " + e.message)
}

The sleep session is now in Health Connect and visible in Samsung Health after data synchronization. In Samsung Health, go to the “Sleep” screen. The inserted sleep session can be reviewed there with visualizations and analysis just like any other sleep session, such as those tracked on a Galaxy Watch. Below is a sleep session from the sample application:

Sleep session

Conclusion

This blog has demonstrated how you can develop an application that retrieves data from and inserts data into Health Connect, making it visible in the Samsung Health application after data synchronization.