Android Sleep API Tutorial: Getting Started

Learn how to use the Android Sleep API in your Kotlin apps to track when the user is asleep, awake, how long they slept, and the confidence of the results. By Roberto Orgiu.

Leave a rating/review
Download materials
Save for later
Share

In this tutorial, you’ll learn how to interact with the Android Sleep API and react to the stream of events sent by the system. The Android Sleep API collects information such as surrounding brightness and device movement to make assumptions about when the user is asleep or awake.

This API is handy for tracking users’ sleep patterns to help them improve their sleep habits.

While building a simple sleep tracking app, you’ll how to:

  • Request Activity Recognition permissions for your app.
  • Register a Sleep Receiver to filter and analyze the different events sensed by the device.

With that, it’s time to jump right in! Try not to fall asleep. ;]

Note: This tutorial assumes you have a basic understanding of Android and Kotlin. If you need to catch up on this topic, check out Beginning Android Development with Kotlin.

Getting Started

Click Download Materials at the top or bottom of this tutorial to download the sample project.

Import the starter project in Android Studio. Build and run it. You’ll see a classic sample screen:

Empty Hello World screen

You won’t interact with the UI much in this tutorial, so building and running the app is more about checking the code’s correctness.

Note: You need a physical device with at least Android 10 to use the Sleep API and test the code you’ll write in this tutorial.

Using the Android Sleep API

To use this API, you’ll need two pieces of code:

  1. The ActivityRecognition client that lets you subscribe to sleep updates.
  2. A BrodcastReceiver, to receive the updates from the ActivityRecognition client.

Additionally, you need to ask the user for permission to listen to their sleep data, which they grant outside the app, in the Settings screen.

This API will provide two sets of information:

  • The confidence level that your user is sleeping, which the API reports periodically a few minutes after the previous emission.
  • The daily sleep segment the API emits once the device detects the user is awake.

To use this API, you first need to include the dependencies. Open app ‣ build.gradle and add the following line in the dependencies block:

implementation 'com.google.android.gms:play-services-location:18.0.0'

Now you’re all set to start writing some code.

Listening for Sleep Data

Before you can do anything else, you need to receive sleep data. The component that provides this information is the BroadcastReceiver.

Open SleepReceiver.kt. Notice SleepReceiver already extends BroadcastReceiver with an empty onReceive. This method is where you’ll add the logic that filters the data.

Open AndroidManifest.xml. You’ll see SleepReceiver is already declared inside, right below MainActivity. It looks like this:

<receiver
    android:name=".receiver.SleepReceiver"
    android:enabled="true"
    android:exported="true" />

You need to declare SleepReceiver in AndroidManifest.xml so the system knows BroadcastReceiver is available in your app.

Now that you have SleepReceiver set up, it’s time to write the logic that will filter the events coming from the Intent.

Filtering Sleep Data

Open SleepReceiver. Add the following statement inside onReceive():

if (SleepSegmentEvent.hasEvents(intent)) {
} else if (SleepClassifyEvent.hasEvents(intent)) {
}

Here, you check whether the Intent contains SleepSegmentEvents or SleepClassifyEvents.

Next, add the following companion object to the bottom of the class:

companion object {
   private const val TAG = "SLEEP_RECEIVER"
}

You’ll use this tag to log events to the console and filter the text you’ll show in a few lines.

Now, to focus on the first branch of the if statement!

Right below the if line, add:

val events =
    SleepSegmentEvent.extractEvents(intent)

Log.d(TAG, "Logging SleepSegmentEvents")

for (event in events) {
  Log.d(TAG,
      "${event.startTimeMillis} to ${event.endTimeMillis} with status ${event.status}")
}

In case the Intent you receive contains a SleepSegmentEvent, you extract it and print its starting and ending timestamps and its status to the console. This represents the UNIX timestamp when the device detected the user began sleeping, woke up and a status code that indicates if the system succeeded in collecting sleep data, respectively.

Now you can get the sleep segments, but you also want to classify the events. You can do this in the statement’s else branch by adding:

val events = SleepClassifyEvent.extractEvents(intent)

Log.d(TAG, "Logging SleepClassifyEvents")

for (event in events) {
    Log.d(TAG,
        "Confidence: ${event.confidence} - Light: ${event.light} - Motion: ${event.motion}"
    )
}

Once again, you need to extract the information about the event and print its data to the console, highlighting the confidence level of the classification, light quantity and value of the motion.

At this point, if you build and run the app, you won’t see any output in the console. You still need to take a few more steps before you can get the data!

Requesting Activity Recognition Permissions

To get the sleep data, you need to have permission from your user to access it. It’s not as straightforward as with other permissions since you need to open the settings for the data, and the user needs to grant your app the Activity Recognition permissions. But it’s not that complicated either.

First, open AndroidManifest.xml and add the following permission within manifest:

<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />

Next, you need to create a method inside MainActivity.kt that will open the settings screen. Open MainActivity and add the following, importing android.provider.Settings:

private fun requestActivityRecognitionPermission() {

   val intent = Intent().apply {
        action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
        data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK
   }

   startActivity(intent)
}

Here you create the Settings Intent, adding the Activity Recognition action and your app package extra. You then run the Intent.

Now, since you need to request permissions, you can leverage the ActivityResultContracts API, which will help reduce the boilerplate to listen for the activity result.

Start by adding the activity ktx library to app ‣ build.gradle:

implementation 'androidx.activity:activity-ktx:1.2.3'

Sync your gradle file. This adds ActivityResultContracts and ActivityResultLauncher to your project.

Then, add this property to MainActivity:

private val permissionRequester: ActivityResultLauncher<String> =
     registerForActivityResult(
         ActivityResultContracts.RequestPermission()
    ) { isGranted ->

       if (!isGranted) {
         requestActivityRecognitionPermission()
       } else {
         // Request goes here
       }
     }

Here, you create an object that will listen for the Activity result from your permission request and pass it to you once this object parses it.

It’s not time to build and run your app yet. If you try, it still won’t show any data. But it will soon!