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

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

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

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

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

Подтип – это данные, которые "принадлежат" сеансу и имеют смысл только при чтении с родительским сеансом. Например, фаза сна.

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

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

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

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

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata"
    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 доступны следующие агрегированные значения:

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

Ниже приведены рекомендации по работе с сеансами сна в платформе "Здоровье и спорт".

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

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.
  • Сеансы полезны, если пользователь хочет, чтобы данные были связаны с сеансом (и отслеживались как его часть), а не записывались непрерывно.

Сеансы сна

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

  • 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)