Android Sleep API Codelab

1. 简介

构建内容

在此 Codelab 中,您将构建 Android 应用来检测用户的睡眠时间。该应用将:

  • 请求权限
  • 注册 Android Sleep API
  • 检索 API 事件并在界面上予以显示
  • 不再需要该 API 时取消注册

所需条件

  • 已安装 Android Build Tools v21 或更高版本的最新版 Android Studio
  • 搭载 Android 10(API 级别 29)或更高版本的设备

2. 准备工作

克隆初始项目代码库

为帮助您尽快入门,我们准备了一个初始项目,您可以在此项目的基础上进行构建。通过在终端/命令行中输入 git --version,检查您是否已安装 git。如果尚未安装,请按照这些说明进行安装。然后,只需运行以下命令即可克隆项目。

 git clone https://github.com/android/codelab-android-sleep/

导入项目

启动 Android Studio,然后在欢迎屏幕中选择“Open an existing Android Studio project”,以打开项目目录。

项目加载完成后,您可能还会看到一条提醒,指出 Git 将不会跟踪所有本地更改。您可以点击右上角的 IgnoreX。(您所做的任何更改都不会保存到 Git 代码库中。)

如果您采用的是 Android 视图,那么在项目窗口的左上角应该会看到类似下图所示的内容。(如果您采用的是 Project 视图,那么需要展开项目才能看到这些内容。)

1401a11c10711a60.png

您可以看到两个文件夹图标(startcomplete)。它们都称为“模块”。

请注意,首次打开项目时,Android Studio 可能需要数秒时间在后台编译项目。在此期间,您会在 Android Studio 底部的状态栏中看到一个旋转图标:

4bc64eb3b99eb0ae.png

建议您等待此过程完成后再更改代码。这样,Android Studio 就可以提取所有必要的组件。

此外,如果您看到“Reload for language changes to take effect?”的提示或类似内容,请选择“Yes”。

了解初始项目

现在,您已经完成准备工作,可以开始添加运动状态识别功能了。我们将使用 start 模块,这是此 Codelab 的起点。换句话说,您将在每个步骤向 start 中添加代码。

complete 模块可用于检查您的工作,或在您遇到问题时提供参考。

关键组件概览:

  • MainActivity.kt:用户启动应用时渲染应用主屏幕。
  • SleepReceiver.kt:从 API 中提取睡眠事件并将其存储到数据库中。
  • BootReceiver.kt:在设备启动完成后重新注册 Sleep API,以便继续监听 Sleep API 事件。

运行初始项目

现在,我们来运行应用。

  1. 将您的 Android 设备连接到计算机。
  2. 在工具栏中,从下拉选择器中选择 start 配置,选择您的设备,然后点击旁边的绿色三角形(运行)按钮:

177045a302bf57d8.png

您应在设备上看到应用:

30efe28b9757e1e7.png

311c83ca64556d94.png

我们尚未添加任何代码来跟踪睡眠,后续部分将详细介绍这一操作。

在下一部分中,我们将了解所需的库和权限。

3. 查看库并添加权限

查看 build.gradle 和 AndroidManifest.xml

如需在应用中使用 Sleep API,您必须在应用清单中声明依赖 Google Location and Activity Recognition API 并指定 com.google.android.gms.permission.ACTIVITY_RECOGNITION 权限。

  1. start 模块的 build.gradle 文件中,搜索 TODO: Review play services library required for activity recognition。此步骤无需采取任何操作。只需查看我们要求声明的依赖项即可。代码应如下所示:
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:18.0.0'
  1. start 模块的 AndroidManifest.xml 中,搜索 TODO: Add activity recognition and boot complete permissions,并将以下代码添加到 <manifest> 元素中。
<!-- TODO: Add activity recognition and receive boot complete permissions. -->
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

现在,您的代码应如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.example.sleepcodelab">
...
<!-- TODO: Add activity recognition and receive boot complete permissions. -->
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  ...
</manifest>

从注解中可以看出,您需要针对 API 版本 29 添加运行时权限。

大功告成!您的应用现在支持睡眠运动状态识别功能。我们只需添加此代码即可获得此功能。

查看运动状态识别权限检查

我们需要根据需要检查并请求运行时权限:

  • MainActivity.kt 中,我们将检查运动状态识别权限。
  • 如果未授予权限,我们将调用 displayPermissionSettingsSnackBar()。该信息条将显示相应权限需求,并允许用户在系统设置中批准权限。

start 模块的 MainActivity.kt 中,搜索 TODO: Review Activity Recognition permission check。您应该会看到以下代码段。

请注意,这一部分无需采取任何操作。

// TODO: Review Activity Recognition permission checking.
private fun activityRecognitionPermissionApproved(): Boolean {
   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
}

启用/停用睡眠跟踪功能

start 模块的 MainActivity.kt 中,搜索 TODO: Enable/Disable Sleep tracking and ask for permissions if needed。将 onClickRequestSleepData() 方法替换为以下代码。

// TODO: Enable/Disable sleep tracking and ask for permissions if needed.
fun onClickRequestSleepData(view: View) {
   if (activityRecognitionPermissionApproved()) {
       if (subscribedToSleepData) {
           unsubscribeToSleepSegmentUpdates(applicationContext, sleepPendingIntent)
       } else {
           subscribeToSleepSegmentUpdates(applicationContext, sleepPendingIntent)
       }
   } else {
       requestPermissionLauncher.launch(permission.ACTIVITY_RECOGNITION)
   }
}

如果运动状态识别权限请求获得批准,并且用户订阅了睡眠数据,我们就会订阅睡眠更新。否则,我们会退订。

如果该权限请求未获批准,我们会将用户转到启动画面 activity,其中解释了我们为何需要该权限并允许他们启用该权限。

4. 订阅 Sleep API 更新

创建 PendingIntent

start 模块的 MainActivity.kt 中,搜索 TODO: Create a PendingIntent for Sleep API events。将其替换为以下代码段。

// TODO: Create a PendingIntent for Sleep API events
sleepPendingIntent =
   SleepReceiver.createSleepReceiverPendingIntent(context = applicationContext)

现在,我们可以在 Sleep API 数据可用时获取更新。

请求 Sleep API 更新

start 模块的 MainActivity.kt 中,搜索 TODO: Request Sleep API updates。将其替换为以下代码段。

// TODO: Request Sleep API updates
val task = ActivityRecognition.getClient(context).requestSleepSegmentUpdates(
   pendingIntent,
   // Registers for both SleepSegmentEvent and SleepClassifyEvent data.
   SleepSegmentRequest.getDefaultSleepSegmentRequest()
)

task.addOnSuccessListener {
   mainViewModel.updateSubscribedToSleepData(true)
   Log.d(TAG, "Successfully subscribed to sleep data.")
}
task.addOnFailureListener { exception ->
   Log.d(TAG, "Exception when subscribing to sleep data: $exception")
}

成功注册 Sleep API 更新后,您的应用将在已注册的 PendingIntent 中接收睡眠检测通知。

启动完成后重新订阅

start 模块的 receiver/BootReceiver.kt 中,搜索 TODO: Request Sleep API on boot complete。将其替换为以下代码段。

// TODO: Request Sleep API upon boot complete
val subscribedToSleepData = repository.subscribedToSleepDataFlow.first()
if (subscribedToSleepData) {
   subscribeToSleepSegmentUpdates(
       context = context,
       pendingIntent = SleepReceiver.createSleepReceiverPendingIntent(context)
   )
}

上述代码可让应用在设备重新启动后继续接收 Sleep API 更新。

5. 处理事件

当发生睡眠分类或睡眠时间检测事件时,您的应用会收到一个 Intent 回调。您可以从此 intent 中提取一系列 SleepSegmentEventSleepClassifyEvent 对象。

我们来添加处理这些事件的代码。

start 模块的 receiver/SleepReceiver.kt 中,搜索 TODO: Extract sleep information from PendingIntent。在注解之后添加以下代码。

// TODO: Extract sleep information from PendingIntent.
if (SleepSegmentEvent.hasEvents(intent)) {
   val sleepSegmentEvents: List<SleepSegmentEvent> =
       SleepSegmentEvent.extractEvents(intent)
   addSleepSegmentEventsToDatabase(repository, sleepSegmentEvents)
} else if (SleepClassifyEvent.hasEvents(intent)) {
   val sleepClassifyEvents: List<SleepClassifyEvent> =
       SleepClassifyEvent.extractEvents(intent)
   addSleepClassifyEventsToDatabase(repository, sleepClassifyEvents)
}

此操作会将 SleepSegmentEventSleepClassifyEvent 数据保存到 Room 数据库中。MainActivity 使用 LiveData 通过 ViewModel 访问该数据库信息。如需详细了解这些类如何协同运行,请参阅Codelab 课程“带 View 的 Room”

大功告成,此步骤已完成!请尝试运行应用。

6. 安装应用并查看代码

使用数据线将您的设备连接到工作站。点击 Android Studio 中的“Run”,即可在您的设备上安装并运行应用。授予运动状态识别权限并点按“SUBSCRIBE TO SLEEP DATA”按钮后,第一个 SleeveClassificationEvent 会在大约 10 分钟后显示。SleepSegmentEvent 应该会在一天内显示。

您随时可以浏览完整代码,查看您所完成的工作,并更好地了解代码如何协同运行。

选择性查看代码

您可以选择性查看以下代码。它们提供有关 Sleep API 和数据库实体之间数据映射的详细信息:

  • data/db/SleepClassifyEventEntity.kt
  • data/db/SleepSegmentEventEntity.kt

参考文档