借助与应用有关的 Action 将动态快捷方式扩展到 Google 助理

1. 概览

在上一个 Codelab 中,您使用静态快捷方式在示例应用中实现了常用的内置 intent (BII)。Android 开发者使用与应用有关的 Action 将应用功能扩展到 Google 助理。

静态快捷方式与应用捆绑在一起,只能通过发布应用的新版本进行更新。为应用中的动态元素(例如用户生成的内容)启用语音功能可通过使用动态快捷方式实现。用户执行相关操作(例如在任务跟踪应用中创建新记事)后,应用会推送动态快捷方式。借助与应用有关的 Action,您可以将这些快捷方式与 BII 绑定以启用语音功能,这样用户就可以通过说出“Hey Google,在 ExampleApp 中打开我的购物清单”这样的语音指令,从 Google 助理访问相关内容。

三个进程屏幕,显示的是 Google 助理正在启动动态快捷方式。

图 1. 三个进程屏幕,显示的是用户创建的任务,以及 Google 助理正在启动该任务项的动态快捷方式。

您将构建的内容

在此 Codelab 中,您将在一个示例待办事项列表 Android 应用中启用动态语音快捷方式,让用户能够要求 Google 助理打开他们在应用中创建的任务列表项。为此,您可以使用 Android 架构模式,特别是代码库、服务定位器和 ViewModel 模式。

前提条件

此 Codelab 以上一个 Codelab 中介绍的与应用有关的 Action 的概念为基础,尤其是 BII 和静态快捷方式。如果您刚开始接触与应用有关的 Action,建议您先完成上一个 Codelab,然后再继续。

此外,请确保您的开发环境具有以下配置,然后再继续:

  • 已安装 git 的终端,用于运行 shell 命令。
  • 最新的稳定版 Android Studio。
  • 一台可连接到互联网的 Android 实体设备或虚拟设备。
  • 已登录 Android Studio、Google 应用和 Google 助理应用的 Google 帐号。

2. 了解运作方式

为语音访问启用动态快捷方式涉及以下步骤:

  • 将动态快捷方式绑定到符合条件的 BII。
  • 通过添加 Google 快捷方式集成库,让 Google 助理能够提取快捷方式。
  • 每当用户完成相关的应用内任务时推送快捷方式。

绑定快捷方式

为了能够通过 Google 助理访问动态快捷方式,需要将动态快捷方式绑定到相关的 BII。当具有快捷方式的 BII 触发时,Google 助理会将用户请求中的参数与绑定的快捷方式中定义的关键字进行匹配。例如:

  • 绑定到 GET_THING BII 的快捷方式可让用户直接通过 Google 助理请求特定应用内内容。*“Hey Google,在 ExampleApp 中打开我的购物清单。”
  • 绑定到 ORDER_MENU_ITEM BII 的快捷方式可让用户再次执行之前的订单。*“Hey Google,从 ExampleApp 照常下单。”

如需查看完整的 BII 分类列表,请参阅内置 intent 参考文档。

为 Google 助理提供快捷方式

将快捷方式绑定到 BII 后,下一步是将 Google 快捷方式集成库添加到您的项目中,让 Google 助理能够提取这些快捷方式。添加此库后,Google 助理会识别您应用推送的每个快捷方式,让用户能够通过在 Google 助理中使用快捷方式的触发短语来启动这些快捷方式。

3. 准备开发环境

此 Codelab 使用的示例待办事项列表应用专为 Android 而打造。通过此应用,用户可以向列表添加项、按类别搜索任务列表项,以及按完成状态过滤任务。完成此部分后,即可下载并准备示例应用。

下载基础文件

运行以下命令,以克隆示例应用的 GitHub 代码库:

git clone https://github.com/actions-on-google/app-actions-dynamic-shortcuts.git

克隆完代码库后,按照以下步骤在 Android Studio 中将其打开:

  1. 在 Welcome to Android Studio 对话框中,点击 Import project。
  2. 选择您克隆代码库的文件夹。

或者,您也可以查看表示已完成 Codelab 的示例应用的版本,只需克隆其 GitHub 代码库的 codelab-complete 分支即可:

git clone https://github.com/actions-on-google/app-actions-dynamic-shortcuts.git --branch codelab-complete

更新 Android 应用 ID

更新应用的应用 ID 可以唯一标识测试设备上的应用,同时避免在向 Play 管理中心上传应用时出现“Duplicate package name”错误。若要更新应用 ID,请打开 app/build.gradle:

android {
...
  defaultConfig {
    applicationId "com.MYUNIQUENAME.android.fitactions"
    ...
  }
}

将 applicationId 字段中的“MYUNIQUENAME”替换为您独有的名称。

添加 Shortcuts API 依赖项

将以下 Jetpack 库添加到 app/build.gradle 资源文件中:

app/build.gradle

dependencies {
   ...
   // Shortcuts library
   implementation "androidx.core:core:1.6.0"
   implementation 'androidx.core:core-google-shortcuts:1.0.1'
   ...
}

在您的设备上测试应用

在对应用做出更多更改之前,最好先了解一下示例应用的功能。若要在模拟器上运行应用,请按以下步骤操作:

  1. 在 Android Studio 中,依次选择“Run”>“Run app”,或点击工具栏中的 Run 图标 Android Studio 中的“Run app”图标。
  2. 在 Select Deployment Target 对话框中,选择相应设备,然后点击 OK。虽然与应用有关的 Action 在搭载 Android 5(API 级别 21)的设备上能够运行,但我们推荐使用的操作系统版本为 Android 10(API 级别 30)或更高版本。
  3. 长按主屏幕按钮,以设置 Google 助理并验证其能否正常运行。您需要在设备上登录 Google 助理(如果您尚未登录的话)。

如需详细了解 Android 虚拟设备,请参阅创建和管理虚拟设备。

快速浏览应用以了解其功能。点按加号图标可创建新的任务项;借助右上角的菜单项,您可以按完成状态搜索和过滤任务项。

4. 创建快捷方式代码库类

我们示例应用中的多个类将调用 ShortcutManagerCompat API 来推送和管理动态快捷方式。为减少代码冗余,您需要实现一个代码库,让您的项目类可以轻松管理动态快捷方式。

代码库设计模式提供了一个整洁有序的 API 来管理快捷方式。代码库的优势在于底层 API 的详细信息在最小的 API 后统一进行抽象化处理。请按照以下步骤实现代码库:

  1. 创建 ShortcutsRepository 类来抽象化 ShortcutManagerCompat API。
  2. 将 ShortcutsRepository 方法添加到应用的服务定位器。
  3. 在主应用中注册 ShortcutRepository 服务。

创建代码库

在 com.example.android.architecture.blueprints.todoapp.data.source 软件包中创建名为 ShortcutsRepository 的新 Kotlin 类。此软件包已整理到 app/src/main/java 文件夹中。您将使用此类实现一个接口,用于提供一组最少的方法来涵盖我们的 Codelab 用例。

一个 Android Studio 窗口,显示了 ShortcutsRepository 类所在的位置。

图 2. Android Studio 的“Project Files”窗口,显示了 ShortcutsRepository 类所在的位置。

请将以下代码粘贴到新类中:

package com.example.android.architecture.blueprints.todoapp.data.source

import android.content.Context
import android.content.Intent
import androidx.annotation.WorkerThread
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import com.example.android.architecture.blueprints.todoapp.data.Task
import com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity

private const val GET_THING_KEY = "q"

/**
* ShortcutsRepository provides an interface for managing dynamic shortcuts.
*/
class ShortcutsRepository(val context: Context) {

   private val appContext = context.applicationContext

   /**
    * Pushes a dynamic shortcut. The task ID is used as the shortcut ID.
    * The task's title and description are used as shortcut's short and long labels.
    * The resulting shortcut corresponds to the GET_THING capability with task's
    * title used as BII's "name" argument.
    *
    * @param task Task object for which to create a shortcut.
    */
   @WorkerThread
   fun pushShortcut(task: Task) {
      // TODO
   }

   private fun createShortcutCompat(task: Task): ShortcutInfoCompat {
      //...
   }

   /**
    *  Updates a dynamic shortcut for the provided task. If the shortcut
    *  associated with this task doesn't exist, this method throws an error.
    *  This operation may take a few seconds to complete.
    *
    * @param tasks list of tasks to update.
    */
   @WorkerThread
   fun updateShortcuts(tasks: List<Task>) {
       //...
   }

   /**
    * Removes shortcuts if IDs are known.
    *
    * @param ids list of shortcut IDs
    */
   @WorkerThread
   fun removeShortcutsById(ids: List<String>) {
       //...
   }

   /**
    * Removes shortcuts associated with the tasks.
    *
    * @param tasks list of tasks to remove.
    */
   @WorkerThread
   fun removeShortcuts(tasks: List<Task>) {
       //...
   }
}

接下来,更新 pushShortcut 方法以调用 ShortcutManagerCompat API。使用以下代码更新 ShortcutsRepository 类:

ShortcutsRepository.kt

/**
* Pushes a dynamic shortcut for the task. The task's ID is used as a shortcut
* ID. The task's title and description are used as shortcut's short and long
* labels. The created shortcut corresponds to GET_THING capability with task's
* title used as BII's "name" argument.
*
* @param task Task object for which to create a shortcut.
*/

@WorkerThread
fun pushShortcut(task: Task) {
   ShortcutManagerCompat.pushDynamicShortcut(appContext, createShortcutCompat(task))
}

在前面的代码示例中,我们已将 appContext 传递给 API。这是一个包含应用上下文的类属性。由于上下文的保留期限可能会比宿主 activity 生命周期长,因此请务必使用应用上下文(而不是 Activity 上下文),以免内存泄漏。

此外,API 还要求我们为 Task 对象传递 ShortcutInfoCompat 对象。在前面的代码示例中,我们通过调用 createShortcutCompat 私有方法完成此操作。我们将更新该方法,以创建并返回 ShortcutInfoCompat 对象。为此,请使用以下代码更新 createShortcutCompat 桩:

ShortcutsRepository.kt

private fun createShortcutCompat(task: Task): ShortcutInfoCompat {
   val intent = Intent(appContext, TasksActivity::class.java)
   intent.action = Intent.ACTION_VIEW
   // Filtering is set based on currentTitle.
   intent.putExtra(GET_THING_KEY, task.title)

   // A unique ID is required to avoid overwriting an existing shortcut.
   return ShortcutInfoCompat.Builder(appContext, task.id)
           .setShortLabel(task.title)
           .setLongLabel(task.title)
           // Call addCapabilityBinding() to link this shortcut to a BII. Enables user to invoke a shortcut using its title in Assistant.
           .addCapabilityBinding(
                   "actions.intent.GET_THING", "thing.name", listOf(task.title))
           .setIntent(intent)
           .setLongLived(false)
       .build()
}

此类中的其余函数桩用于处理动态快捷方式的更新和删除事宜。若要启用这些函数,请使用以下代码对其进行更新:

ShortcutsRepository.kt

/**
* Updates a Dynamic Shortcut for the task. If the shortcut associated with this task
* doesn't exist, throws an error. This operation may take a few seconds to complete.
*
* @param tasks list of tasks to update.
*/
@WorkerThread
fun updateShortcuts(tasks: List<Task>) {
   val scs = tasks.map { createShortcutCompat(it) }
   ShortcutManagerCompat.updateShortcuts(appContext, scs)
}

/**
* Removes shortcuts if IDs are known.
* @param ids list of shortcut IDs
*/
@WorkerThread
fun removeShortcutsById(ids: List<String>) {
   ShortcutManagerCompat.removeDynamicShortcuts(appContext, ids)
}

/**
* Removes shortcuts associated with the tasks.
*
* @param tasks list of tasks to remove.
*/
@WorkerThread
fun removeShortcuts(tasks: List<Task>) {
   ShortcutManagerCompat.removeDynamicShortcuts (appContext,
           tasks.map { it.id })
}

将类添加到服务定位器

创建 ShortcutsRepository 类后,下一步便是将此类的实例化对象提供给应用的其余部分。此应用通过实现服务定位器模式来管理类依赖项。在 Android Studio 中使用类浏览器打开服务定位器类,具体方法是依次前往 Navigate > Class,然后输入“ServiceLocator”。点击生成的 Kotlin 文件,在您的 IDE 中将其打开。

在 ServiceLocator.kt 的顶部,粘贴以下代码以导入 ShortcutsRepository 和 SuppressLint 软件包:

ServiceLocator.kt

package com.example.android.architecture.blueprints.todoapp

// ...Other import statements
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository
import android.annotation.SuppressLint

将以下代码粘贴到 ServiceLocator.kt 的正文中,即可添加 ShortcutRepository 服务成员和方法:

ServiceLocator.kt

object ServiceLocator {

   // ...
   // Only the code immediately below this comment needs to be copied and pasted
   // into the body of ServiceLocator.kt:

   @SuppressLint("StaticFieldLeak")
   @Volatile
   var shortcutsRepository: ShortcutsRepository? = null

   private fun createShortcutsRepository(context: Context): ShortcutsRepository {
       val newRepo = ShortcutsRepository(context.applicationContext)
       shortcutsRepository = newRepo
       return newRepo
   }

   fun provideShortcutsRepository(context: Context): ShortcutsRepository {
       synchronized(this) {
           return shortcutsRepository ?: shortcutsRepository ?: createShortcutsRepository(context)
       }
   }
 }

注册快捷方式服务

最后一步是向应用注册新的 ShortcutsRepository 服务。在 Android Studio 中,打开 TodoApplication.kt,然后将以下代码复制到该文件顶部附近:

TodoApplication.kt

package com.example.android.architecture.blueprints.todoapp
/// ... Other import statements

import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

接下来,将以下代码添加到类的正文中,以注册该服务:

TodoApplication.kt

//...
class TodoApplication : Application() {

   //...

   val shortcutsRepository: ShortcutsRepository
       get() = ServiceLocator.provideShortcutsRepository(this)

   //...
}

构建应用并确保其继续运行。

5. 推送新的快捷方式

创建快捷方式服务后,您就可以开始推送快捷方式了。由于用户在此应用中生成内容(任务项),并期望稍后能够返回到这些内容,因此每次用户创建新任务时,我们都将通过推送一个绑定到 GET_THING BII 的动态快捷方式来启用对此内容的语音访问。这样一来,当用户通过说出“Hey Google,在 SampleApp 中打开我的购物清单”这样的语音指令来触发 BII 后,Google 助理便可以直接启动用户请求的任务项。

您可以通过完成以下步骤,在示例应用中启用此功能:

  1. 将 ShortcutsRepository 服务导入到 AddEditTaskViewModel 类,后者负责管理任务列表对象。
  2. 在用户创建新任务时推送动态快捷方式。

导入 ShortcutsRepository

我们首先需要向 AddEditTaskViewModel 提供 ShortcutsRepository 服务。为此,请将该服务导入到 ViewModelFactory,这是应用用于实例化 ViewModel 对象的工厂类,包括 AddEditTaskViewModel。

在 Android Studio 中打开类浏览器,具体方法是依次前往 Navigate > Class,然后输入“ViewModelFactory”。点击生成的 Kotlin 文件,在您的 IDE 中将其打开。

在 ViewModelFactory.kt 的顶部,粘贴以下代码以导入 ShortcutsRepository 和 SuppressLint 软件包:

ViewModelFactory.kt

package com.example.android.architecture.blueprints.todoapp

// ...Other import statements
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

接下来,将 ViewModelFactory 的正文替换为以下代码:

ViewModelFactory.kt

/**
 * Factory for all ViewModels.
 */
@Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
    private val tasksRepository: TasksRepository,
    private val shortcutsRepository: ShortcutsRepository,
    owner: SavedStateRegistryOwner,
    defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {

    override fun <T : ViewModel> create(
        key: String,
        modelClass: Class<T>,
        handle: SavedStateHandle
    ) = with(modelClass) {
        when {
            isAssignableFrom(StatisticsViewModel::class.java) ->
                StatisticsViewModel(tasksRepository)
            isAssignableFrom(TaskDetailViewModel::class.java) ->
                TaskDetailViewModel(tasksRepository)
            isAssignableFrom(AddEditTaskViewModel::class.java) ->
                AddEditTaskViewModel(tasksRepository, shortcutsRepository)
            isAssignableFrom(TasksViewModel::class.java) ->
                TasksViewModel(tasksRepository, handle)
            else ->
                throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
        }
    } as T
}

向上移动一层,完成 ViewModelFactory 更改,并将 ShortcutsRepository 传递给工厂的构造函数。打开 Android Studio 的文件浏览器,具体方法是依次前往 Navigate > File,然后输入“FragmentExt.kt”。点击生成的 Kotlin 文件(位于 util 软件包中),在您的 IDE 中将其打开。

将 FragmentExt.kt 的正文替换为以下代码:

fun Fragment.getViewModelFactory(): ViewModelFactory {
   val taskRepository = (requireContext().applicationContext as TodoApplication).taskRepository
   val shortcutsRepository = (requireContext().applicationContext as TodoApplication).shortcutsRepository
   return ViewModelFactory(taskRepository, shortcutsRepository, this)
}

推送快捷方式

通过向示例应用的 ViewModel 类提供 ShortcutsRepository 抽象类,您可以更新 AddEditTaskViewModel(即负责创建记事的 ViewModel 类),在用户每次创建新记事时推送动态快捷方式。

在 Android Studio 中,打开类浏览器,然后输入“AddEditTaskViewModel”。点击生成的 Kotlin 文件,在您的 IDE 中将其打开。

首先,使用以下 import 语句将 ShortcutsRepository 软件包添加到此类:

package com.example.android.architecture.blueprints.todoapp.addedittask

//Other import statements
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

接下来,使用以下代码更新类构造函数,以添加 shortcutsRepository 类属性:

AddEditTaskViewModel.kt

//...

/**
* ViewModel for the Add/Edit screen.
*/
class AddEditTaskViewModel(
   private val tasksRepository: TasksRepository,
   private val shortcutsRepository: ShortcutsRepository
) : ViewModel() {

    //...

添加 ShortcutsRepository 类后,创建新函数 pushShortcut() 来调用此类。将以下私有函数粘贴到 AddEditTaskViewModel 的正文中:

AddEditTaskViewModel.kt

//...
private fun pushShortcut(newTask: Task) = viewModelScope.launch {
   shortcutsRepository.pushShortcut(newTask)
}

最后,每当用户创建任务时推送新的动态快捷方式。将 saveTask() 函数的内容替换为以下代码:

AddEditTaskViewModel.kt

fun saveTask() {
    val currentTitle = title.value
    val currentDescription = description.value

    if (currentTitle == null || currentDescription == null) {
        _snackbarText.value = Event(R.string.empty_task_message)
        return
    }
    if (Task(currentTitle, currentDescription).isEmpty) {
        _snackbarText.value = Event(R.string.empty_task_message)
        return
    }

    val currentTaskId = taskId
    if (isNewTask || currentTaskId == null) {
        val task = Task(currentTitle, currentDescription)
        createTask(task)
        pushShortcut(task)
    } else {
        val task = Task(currentTitle, currentDescription, taskCompleted, currentTaskId)
        updateTask(task)
    }
}

测试代码

我们终于准备好测试代码了!在此步骤中,您将推送支持语音的动态快捷方式,并使用 Google 助理应用对其进行检查。

创建预览

使用 Google 助理插件创建预览后,您的动态快捷方式便会出现在测试设备上的 Google 助理中。

安装测试插件

如果您还没有安装 Google 助理插件,请按以下步骤在 Android Studio 中安装该插件:

  1. 依次前往 **“File”>“Settings”(在 MacOS 上,依次前往“Android Studio”>“Preferences”)。
  2. 在 Plugins 部分中,前往 Marketplace 并搜索“Google Assistant”。
  3. 安装该工具,然后重启 Android Studio。

创建预览

在 Android Studio 中按照以下步骤创建预览:

  1. 依次点击 Tools > Google Assistant >“App Actions Test Tool”。
  2. 在 App name 框中,指定一个名称,例如“Todo List”。
  3. 点击 Create Preview。如果出现提示,请查看并接受与应用有关的 Action 方面的政策和服务条款。

与应用有关的 Action 测试工具的预览创建窗格。

图 3. 与应用有关的 Action 测试工具的预览创建窗格。

在测试期间,您推送到 Google 助理的动态快捷方式会出现在 Google 助理中,按您为预览提供的应用名称整理。

推送和检查快捷方式

在测试设备上重新启动示例应用,然后执行以下步骤:

  1. 创建一个标题为“Start codelab”的新任务
  2. 打开 Google 助理应用,说出或输入“我的快捷方式”。
  3. 点按探索标签页。您应该会看到示例快捷方式。
  4. 点按该快捷方式以调用它。您应该会看到,应用启动时,过滤框中预先填充了快捷方式的名称,以便您轻松找到用户请求的任务项。

6.(可选)更新和删除快捷方式

除了在运行时推送新的动态快捷方式之外,您的应用还可以更新这些快捷方式,以反映用户内容和偏好设置的当前状态。最好在每次用户修改目标项(例如重命名示例应用中的任务)时更新现有快捷方式。此外,每当移除目标资源时,也应删除相应的快捷方式,以避免向用户显示损坏的快捷方式。

更新快捷方式

修改 AddEditTaskViewModel,以便在每次用户更改任务项的详细信息时更新动态快捷方式。首先,使用以下代码更新类的正文,以添加利用我们的代码库类的更新函数:

AddEditTaskViewModel.kt

private fun updateShortcut(newTask: Task) = viewModelScope.launch {
   shortcutsRepository.updateShortcuts(listOf(newTask))
}

接下来,修改 saveTask() 函数,以便在现有任务每次更新时调用我们的新方法。

AddEditTaskViewModel.kt

// Called when clicking on fab.
fun saveTask() {
   // ...
   // Note: the shortcuts are created/updated in a worker thread.
   if (isNewTask || currentTaskId == null) {
       //...
   } else {
       //...
       updateShortcut(task)
   }
}

若要测试您的代码,请重新启动应用并按以下步骤操作:

  1. 将现有任务项的标题重命名为“Finish codelab”。
  2. 说出“Hey Google,我的快捷方式”,打开 Google 助理。
  3. 点按探索标签页。您应该会看到测试快捷方式的已更新简短标签。

移除快捷方式

每当用户删除任务时,我们都应移除我们的示例应用快捷方式。在示例应用中,任务删除逻辑位于 TaskDetailViewModel 类中。在更新此类之前,我们需要再次更新 ViewModelFactory,以将 shortcutsRepository 传递到 TaskDetailViewModel。

打开 ViewModelFactory 并将其构造函数方法的内容替换为以下代码:

//...
class ViewModelFactory constructor(
       private val tasksRepository: TasksRepository,
       private val shortcutsRepository: ShortcutsRepository,
       owner: SavedStateRegistryOwner,
       defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {

   override fun <T : ViewModel> create(
           key: String,
           modelClass: Class<T>,
           handle: SavedStateHandle
   ) = with(modelClass) {
       when {
           isAssignableFrom(StatisticsViewModel::class.java) ->
               StatisticsViewModel(tasksRepository)
           isAssignableFrom(TaskDetailViewModel::class.java) ->
               TaskDetailViewModel(tasksRepository, shortcutsRepository)
           isAssignableFrom(AddEditTaskViewModel::class.java) ->
               AddEditTaskViewModel(tasksRepository, shortcutsRepository)
           isAssignableFrom(TasksViewModel::class.java) ->
               TasksViewModel(tasksRepository, handle)
           else ->
               throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
       }
   } as T
}

接下来,打开 TaskDetailViewModel。使用以下代码导入 ShortcutsRepository 模块并为其声明实例变量:

TaskDetailViewModel.kt

package com.example.android.architecture.blueprints.todoapp.taskdetail

...
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

/**
* ViewModel for the Details screen.
*/
class TaskDetailViewModel(
       //...
       private val shortcutsRepository: ShortcutsRepository
   ) : ViewModel() {
...
}

最后,修改 deleteTask() 函数来调用 shortcutsRepository,以便在每次删除具有相应 taskId 的任务时根据 ID 来移除快捷方式:

TaskDetailViewModel.kt

fun deleteTask() = viewModelScope.launch {
   _taskId.value?.let {
       //...
       shortcutsRepository.removeShortcutsById(listOf(it))
   }
}

若要测试您的代码,请重新启动应用并按以下步骤操作:

  1. 删除您的测试任务。
  2. 将现有任务项的标题重命名为“Finish codelab”。
  3. 说出“Hey Google,我的快捷方式”,打开 Google 助理。
  4. 点按探索标签页。确认您的测试快捷方式不再出现。

7. 后续步骤

恭喜!有了您的大力协助,我们示例应用的用户才能通过向 Google 助理发出“Hey Google,在 ExampleApp 中打开我的购物清单”这样的语音指令,轻松返回到他们创建的记事。快捷方式让用户可以轻松地在您的应用中再次执行常用操作,从而鼓励用户更深入地互动。

所学内容

在此 Codelab 中,您学习了如何执行以下操作:

  • 确定在应用中推送动态快捷方式的用例。
  • 使用代码库、依赖项注入和服务定位器设计模式降低代码复杂性。
  • 将支持语音的动态快捷方式推送到用户生成的应用内容。
  • 更新和移除现有快捷方式。

后续操作

之后,您可以尝试进一步优化您的任务列表应用。如需参考已完成的项目,请参阅 GitHub 上代码库的 –codelab-complete 分支。

我们在下面提供了一些建议,以便您进一步了解如何使用与应用有关的 Action 扩展该应用:

如需继续了解 Actions on Google,请浏览下列资源:

欢迎关注我们的 Twitter 帐号 @ActionsOnGoogle,及时了解我们的最新公告,还可以使用标签 #appActions 发推文,分享您构建的成果!

反馈调查问卷

最后,请填写此调查问卷,就您学习此 Codelab 的体验提供反馈意见。