【Android】Android Sleep APIことはじめ

# Sleep API
以下抄訳↓

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

https://developers.google.com/location-context/sleep

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

# 実際に試してみる

## 下準備

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

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

```gradle:build.gradle
android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

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

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

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

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

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

```kotlin: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" />

```

```kotlin: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などを利用しても :ok_woman: 


#### 2. データを眺める

[SleepClassifyEvent](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent)と[SleepSegmentEvent](https://developers.google.com/android/reference/com/google/android/gms/location/SleepSegmentEvent)が取得可能です。
それぞれ以下抄訳↓

- [SleepClassifyEvent](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent)は睡眠の信頼度、およびデバイスの動きや周囲光レベルなどのサポートデータを含む睡眠分類イベントを表します。分類イベントは、10分ごとに定期的な間隔で報告されます。

- [SleepSegmentEvent](https://developers.google.com/android/reference/com/google/android/gms/location/SleepSegmentEvent)はユーザーの睡眠開始、終了時間やスリープ状態にあるかを検出した時間などを検出できます。

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

SleepClasifyの[getTimestampMillis](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent#getTimestampMillis())を計測してみました


| [timestampMillis(UNIX time)](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent#public-long-gettimestampmillis) | timestampMillis(日付表記) | [confidence](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent#getConfidence()) |[light](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent#public-int-getlight)|[motion](https://developers.google.com/android/reference/com/google/android/gms/location/SleepClassifyEvent#public-int-getmotion)|端末操作の有無|
|-------------:|--------------------:|------------:|------------:|------------:|------------:|
| 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`も計測してみてください！