Как отслеживать сеансы сна

Это руководство совместимо с платформой "Здоровье и спорт" версии 1.1.0-alpha11.

Платформа "Здоровье и спорт" предоставляет тип данных Сеанс сна, чтобы хранить информацию о сне пользователя, например о ночном сне или дневном сне. Для представления этих сеансов используется тип данных SleepSessionRecord.

Сеансы позволяют отслеживать показатели, связанные со временем, например непрерывные данные о пульсе или местоположении.

Сеансы SleepSessionRecord содержат данные о фазах сна, например AWAKE, SLEEPING и DEEP.

Данные подтипа относятся к сеансу и имеют смысл только при чтении с родительским сеансом. Например, фаза сна.

Связанные данные – это данные, которые регистрируются независимо, но относятся к временному диапазону сеанса. Например, если пользователь записывает пульс во время сеанса сна, то данные о пульсе будут считаться связанными данными. В отличие от данных подтипа, которые являются частью записи сеанса, связанные данные состоят из отдельных записей, у каждой из которых есть собственный UUID.

Проверьте доступность платформы "Здоровье и спорт"

Прежде чем пытаться использовать Здоровье и спорт, ваше приложение должно проверить, доступна ли эта платформа на устройстве пользователя. Приложение "Здоровье и спорт" может быть не предустановлено на некоторых устройствах или отключено. Проверить доступность можно с помощью метода HealthConnectClient.getSdkStatus().

Как проверить, доступна ли платформа "Здоровье и спорт"

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME
    val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName)

    if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) {
      // Health Connect is not available. Guide the user to install/enable it.
      // For example, show a dialog.
      return // early return as there is no viable integration
    }
    if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) {
      // Health Connect is available but requires an update.
      // Optionally redirect to package installer to find a provider, for example:
      val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding"
      context.startActivity(
        Intent(Intent.ACTION_VIEW).apply {
          setPackage("com.android.vending")
          data = Uri.parse(uriString)
          putExtra("overlay", true)
          putExtra("callerId", context.packageName)
        }
      )
      return
    }
    // Health Connect is available, obtain a HealthConnectClient instance
    val healthConnectClient = HealthConnectClient.getOrCreate(context)
    // Issue operations with healthConnectClient
}

В зависимости от статуса, возвращенного getSdkStatus(), вы можете предложить пользователю установить или обновить приложение "Здоровье и спорт" из Google Play.

Доступность функции

Для этого типа данных нет флага доступности функции.

Требуемые права доступа

Доступ к сеансу сна защищен следующими разрешениями:

  • android.permission.health.READ_SLEEP
  • android.permission.health.WRITE_SLEEP

Чтобы добавить в приложение возможность отслеживать сеансы сна, сначала запросите разрешения для типа данных SleepSession.

Чтобы записывать сеансы сна, необходимо объявить следующее разрешение:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_SLEEP" />
...
</application>

Чтобы считывать данные о сеансах сна, необходимо запросить следующие разрешения:

<application>
  <uses-permission
android:name="android.permission.health.READ_SLEEP" />
...
</application>

Запрос разрешений у пользователя

После создания экземпляра клиента приложению необходимо запросить у пользователя разрешения. Пользователи должны иметь возможность в любой момент предоставить или отклонить разрешения. Для этого создайте набор разрешений для нужных типов данных. Убедитесь, что разрешения из набора объявлены в манифесте Android.

val permissions =
    setOf(
        HealthPermission.getReadPermission(SleepSessionRecord::class),
        HealthPermission.getWritePermission(SleepSessionRecord::class)
    )
Используйте getGrantedPermissions, чтобы проверить, есть ли у вашего приложения необходимые разрешения. Если нет, используйте createRequestPermissionResultContract, чтобы запросить эти разрешения. Откроется экран с разрешениями для сервиса "Здоровье и спорт".
val permissions = setOf(
        HealthPermission.getReadPermission(StepsRecord::class),
        HealthPermission.getWritePermission(StepsRecord::class),
        HealthPermission.getReadPermission(HeartRateRecord::class),
        HealthPermission.getWritePermission(HeartRateRecord::class)
    )

val requestPermissionsLauncher = rememberLauncherForActivityResult(
    contract = PermissionController.createRequestPermissionResultContract()
) { grantedPermissions ->
    if (grantedPermissions.containsAll(permissions)) {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") }
    } else {
        coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") }
    }
}
Поскольку пользователи могут в любой момент предоставить или отозвать разрешения, ваше приложение должно проверять их каждый раз перед использованием и обрабатывать ситуации, когда разрешение теряется.

Поддерживаемые агрегации

Для параметра SleepSessionRecord доступны следующие агрегированные значения:

Общие рекомендации

Ниже приведены рекомендации по работе с сеансами сна в Health Connect.

  • Сеансы используются для добавления данных о конкретном сеансе сна: для сна:

suspend fun writeSleepSession(healthConnectClient: HealthConnectClient) {
    healthConnectClient.insertRecords(
        listOf(
            SleepSessionRecord(
                startTime = Instant.parse("2022-05-10T23:00:00.000Z"),
                startZoneOffset = ZoneOffset.of("-08:00"),
                endTime = Instant.parse("2022-05-11T07:00:00.000Z"),
                endZoneOffset = ZoneOffset.of("-08:00"),
                title = "My Sleep",
                metadata = Metadata.activelyRecorded(device = Device(type = Device.TYPE_WATCH))
            ),
        )
    )
}

  • Данные подтипа должны быть выровнены в сеансе с последовательными временными метками, которые не перекрываются. но пробелы допускаются.
  • Данные подтипа не содержат UUID, но связанные данные имеют разные UUID.
  • Сеансы полезны, если пользователь хочет, чтобы данные были связаны с сеансом и отслеживались как его часть, а не регистрировались непрерывно.

Сеансы сна

Вы можете читать и записывать данные о сне в Health Connect. Данные о сне отображаются в виде сеансов и могут быть разделены на восемь фаз:

  • UNKNOWN – не указано или неизвестно, спит ли пользователь.
  • AWAKE: пользователь проснулся в течение цикла сна, а не днем.
  • SLEEPING – общее описание сна.
  • OUT_OF_BED – пользователь встал с кровати во время сеанса сна.
  • AWAKE_IN_BED – пользователь бодрствует в постели.
  • LIGHT – пользователь находится в фазе поверхностного сна.
  • DEEP – пользователь находится в фазе глубокого сна.
  • REM – пользователь находится в фазе быстрого сна.

Эти значения представляют собой тип сна пользователя в определенном временном диапазоне. Записывать этапы сна необязательно, но рекомендуется, если такая возможность есть.

Запись данных о сеансах сна

Тип данных SleepSessionRecord состоит из двух частей:

  1. Общий сеанс, охватывающий весь период сна.
  2. Отдельные фазы сна, например поверхностный или глубокий сон.

Вот как добавить сеанс сна без фаз:

val zoneRules = ZoneId.systemDefault().rules

// Calculate the specific offset for both start and end times
val startOffset = zoneRules.getOffset(startTime)
val endOffset = zoneRules.getOffset(endTime)

SleepSessionRecord(
    title = "weekend sleep",
    startTime = startTime,
    endTime = endTime,
    startZoneOffset = startOffset,
    endZoneOffset = endOffset,
    metadata = Metadata.activelyRecorded(device = Device(type = Device.TYPE_WATCH))
)

Вот как добавить фазы, которые охватывают весь период сеанса сна:

val stages = listOf(
    SleepSessionRecord.Stage(
        startTime = START_TIME,
        endTime = END_TIME,
        stage = SleepSessionRecord.STAGE_TYPE_SLEEPING,
    )
)

SleepSessionRecord(
        title = "weekend sleep",
        startTime = START_TIME,
        endTime = END_TIME,
        startZoneOffset = START_ZONE_OFFSET,
        endZoneOffset = END_ZONE_OFFSET,
        stages = stages,
)

Как читать данные о сеансах сна

Для каждого сеанса сна, возвращенного в ответе, необходимо проверить, есть ли данные о фазах сна:

val response =
    healthConnectClient.readRecords(
        ReadRecordsRequest(
            SleepSessionRecord::class,
            timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
        )
    )
for (sleepRecord in response.records) {
    // Retrieve relevant sleep stages from each sleep record
    val sleepStages = sleepRecord.stages
}

Как удалить сеанс сна

Вот как удалить сеанс. В этом примере мы использовали сеанс сна:

val timeRangeFilter = TimeRangeFilter.between(sleepRecord.startTime, sleepRecord.endTime)
healthConnectClient.deleteRecords(SleepSessionRecord::class, timeRangeFilter)