Managing Sleep Data with Samsung Health and Health Connect
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. noteandroid 14 health connect is part of the system and is installed by default. however in earlier versions, it's necessary to check if health connect is present 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") } notealthough data is inserted to and stored in health connect in utc time with time zone offset, data retrieved from health connect is displayed in local time. 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.