【Android】Android Sleep APIことはじめ


Sleep API

以下抄訳↓

  • Sleep APIを活用すると、アプリでユーザーがいつ寝て、いつ起きているかを判断することができます。
  • 周囲の明るさ、デバイスの動きなどに関連する情報を収集して、ユーザーが眠りに落ちて目を覚ます時間を推測します。
  • 情報の更新を購読することができます。

様々な機能に組み合わせることで効力を発揮しそうなAPIですね。

実際に試してみる

下準備

1. target sdkを29以上に設定する

※29以下だと取得ができないので、minsSDKは29に設定してます

build.gradle
android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "xxx"
        minSdkVersion 29
        targetSdkVersion 30
        ~~~
    }
    ~~~~
}

2. 使用するmoduleのManifestへACTIVITY_RECOGNITIONのpermissionを追加する

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

3. 睡眠行動の更新をリクエストする

permissionのcheckを行いrequestSleepSegmentUpdatesをリクエストする

MainActivity.kt

private val sleepPendingIntent: PendingIntent by lazy {
     SleepDataReceiver.sleepReceiverPendingIntent(context = requireContext())
}

private val requestPermissionLauncher: ActivityResultLauncher<String> =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
            if (isGranted) {
                // granted
            } else {
                // not granted
            }
        }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     super.onViewCreated(view, savedInstanceState)

     subscribeBtn.setOnClickListener {
         if (checkActivityRecognitionPermission()) {
              // request
              subscribeToSleepSegmentUpdates(sleepPendingIntent)
         } else {
              requestPermissionLauncher.launch(Manifest.permission.ACTIVITY_RECOGNITION)
         }
     }
}

private fun subscribeToSleepSegmentUpdates(pendingIntent: PendingIntent) {
     val task = ActivityRecognition.getClient(
          requireContext()).requestSleepSegmentUpdates(
          pendingIntent,
          SleepSegmentRequest.getDefaultSleepSegmentRequest()
     )

     task.addOnSuccessListener {
       Log.d(TAG, "Successfully")
     }
}

実際に利用してみる

1. 睡眠情報を受け取れるようにReceiverを用意

xml.AndroidManifest.xml

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

SleepDataReceiver.kt
class SleepDataReceiver: BroadcastReceiver() {

    companion object {
        private const val REQUEST_CODE = 100
        fun sleepReceiverPendingIntent(context: Context): PendingIntent {
            return PendingIntent.getBroadcast(
                context,
                REQUEST_CODE,
                Intent(context, SleepDataReceiver::class.java),
                PendingIntent.FLAG_CANCEL_CURRENT
            )
        }
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        if (SleepSegmentEvent.hasEvents(intent)) {
            val sleepSegmentEvents: List<SleepSegmentEvent> =
                SleepSegmentEvent.extractEvents(intent)
//            コールバックを用意してSleepSegmentEventsを流す
        } else if (SleepClassifyEvent.hasEvents(intent)) {
            val sleepClassifyEvents: List<SleepClassifyEvent> =
                SleepClassifyEvent.extractEvents(intent)
//            コールバックを用意してSleepClassifyEventを流す
        }
    }
}

コールバックはどんな形でもOKです。
DataStore + Flow + LiveDataなどを利用しても

2. データを眺める

SleepClassifyEventSleepSegmentEventが取得可能です。
それぞれ以下抄訳↓

  • SleepClassifyEventは睡眠の信頼度、およびデバイスの動きや周囲光レベルなどのサポートデータを含む睡眠分類イベントを表します。分類イベントは、10分ごとに定期的な間隔で報告されます。

  • SleepSegmentEventはユーザーの睡眠開始、終了時間やスリープ状態にあるかを検出した時間などを検出できます。

試しに10分間隔で受信するSleepClassifyEventのログを出してみました。

SleepClasifyのgetTimestampMillisを計測してみました

timestampMillis(UNIX time) timestampMillis(日付表記) confidence light motion 端末操作の有無
1616651011 2021-03-25 14:43:31 3 5 6
1616651370 2021-03-25 14:49:30 1 5 6
1616651858 2021-03-25 14:57:38 2 5 4
1616652348 2021-03-25 15:05:48 4 5 6
1616652836 2021-03-25 15:13:56 1 5 5
1616653325 2021-03-25 15:22:05 22 5 4

気付き

  • 10分間隔はあくまでおおよそで、実際はある程度誤差がある
  • confidenceは基準値低めにしておいても良さそう
  • lightの精度は抜群

最後に

confidence以外の精度はかなり高い印象を受けました。
通知を不定期に送る機能と組み合わせ、ユーザーが寝ているであろう時間には送らないようにすることも可能になるかと思います。

気になる方は是非SleepSegmentEventも計測してみてください!