将 Android widget 与 Google 助理集成

1. 概览

第一个涉及与应用有关的 Action 的 Codelab 中,您学习了如何通过实现“健康与健身”BII 类别中的内置 intent (BII) 将 Google 助理的功能扩展到示例健身应用。

借助与应用有关的 Action,用户只需说出“Hey Google,在 ExampleApp 中开始跑步”这样的语音指令,就能通过 Google 助理直接启动特定的应用功能。除了启动应用之外,Google 助理还可以向用户显示交互式 Android widget,以执行对符合条件的 BII 的请求。

一个显示以下情况的屏幕:Google 助理返回了一个 widget,\n以响应触发了一项应用 GET_EXERCISE_OBSERVATION BII capability 的用户询问。

您将构建的内容

在此 Codelab 中,您将学习如何返回 Android widget 以执行 Google 助理用户请求。您还将学习:

  • 用于 widget 个性化设置的用户 BII 参数。
  • 在 Google 助理中为 widget 提供文字转语音 (TTS) 简介。
  • 根据内置 intent 参考文档确定哪些 BII 支持 widget 执行方式。

前提条件

在继续学习之前,请确保您的开发环境已可以进行与应用有关的 Action 的开发。您的开发环境应具备:

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

如果您使用的是实体设备,请将其连接到本地开发机器。

2. 了解运作方式

Google 助理使用自然语言理解 (NLU) 技术读取用户的请求,并将其与 Google 助理内置 intent (BII) 进行匹配。然后,Google 助理将该 intent 映射到您在应用中为该 intent 注册的capability(用于实现该 BII)。最后,Google 助理利用在此 capability 中找到的详细信息显示您的应用生成的 Android widget,从而执行用户的请求。

在此 Codelab 中,您将定义一项 capability 来注册对 GET_EXERCISE_OBSERVATION BII 的支持。在此 capability 中,您将指示 Google 助理向 FitActions widget 类生成 Android intent,用于执行对此 BII 的请求。您将更新此类,以生成供 Google 助理向用户显示的个性化 widget,以及 Google 助理可以播报的 TTS 简介。

下图演示了此流程:

一张演示 Google 助理 widget 执行方式的流程图。

FitActions widget

FitActions 示例应用包含一个锻炼信息 widget,用户可将其添加到自己的主屏幕。此 widget 非常适合执行可触发 GET_EXERCISE_OBSERVATION BII 的用户询问。

widget 的运作方式

用户将某个 widget 添加到主屏幕时,该 widget 会对设备广播接收器执行 ping 操作。此服务会从应用的 AndroidManifest.xml 资源内该 widget 的接收器定义中检索 widget 的相关信息。它会使用这些信息生成表示该 widget 的 RemoteViews 对象。

示例应用定义了与 StatsWidgetProvider 类相对应的接收器 widgets.StatsWidgetProvider

<!-- app/src/main/AndroidManifest.xml -->

<receiver
  android:name=".widgets.StatsWidgetProvider"
  android:exported="false">
  <intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
  <meta-data
    android:name="android.appwidget.provider"
    android:resource="@xml/stats_widget" />
</receiver>

StatsWidgetProvider 类(即 StatsWidgetProvider.kt)用于管理 StatsWidget 对象创建流程,具有以下责任:

  • 创建 widget 实例,并使用应用数据库中的锻炼数据填充这些实例。
  • 使用 formatDataAndSetWidget() 设置锻炼数据的格式,以方便阅读。
  • 在无法获得锻炼数据时使用 setNoActivityDataWidget() 提供默认值。

添加 Google 助理支持

在此 Codelab 中,您将更新示例应用以处理与应用有关的 Action 的功能。这些更改包括:

  1. 配置 GET_EXERCISE_OBSERVATION BII capability,以返回 StatsWidget 对象的实例。
  2. 更新 StatsWidget 类,以使用与应用有关的 Action 的功能,例如:
    • 使用 BII 参数,让用户可通过说出“Hey Google,在 ExampleApp 中显示我的跑步统计数据”这样的语音指令来查看具体的锻炼统计信息。
    • 提供 TTS 简介字符串。
    • 管理特殊情况,例如当用户的询问未包含锻炼类型参数时。

3. 准备开发环境

下载基础文件

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

git clone --branch start-widget-codelab https://github.com/actions-on-google/appactions-fitness-kotlin.git

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

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

若要查看表示已完成 Codelab 的应用的版本,请使用 --branch master 标记克隆示例应用的代码库。

更新 Android 应用 ID

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

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

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

安装测试插件

借助 Google 助理插件,您可以在测试设备上测试与应用有关的 Action。该插件的运作方式是通过 Android 设备上的 Google 应用向 Google 助理发送信息。如果您还没有安装该插件,请按以下步骤进行安装:

  1. 依次前往 File > Settings(在 MacOS 上,依次前往 Android Studio > Preferences)。
  2. 在“Plugins”部分中,前往 Marketplace 并搜索“Google Assistant”。此外,您也可以手动下载并安装该测试工具。
  3. 安装该工具,然后重启 Android Studio。

在您的设备上测试应用

在对应用做出更多更改之前,最好先了解一下示例应用的功能。

在测试设备上运行应用:

  1. 在 Android Studio 中,选择您的实体设备或虚拟设备,然后依次选择 Run > Run app,或点击工具栏中的 Run 图标 Android Studio 中的“Run app”图标。
  2. 长按主屏幕按钮,以设置 Google 助理并验证其能否正常运行。您需要在设备上登录 Google 助理(如果您尚未登录的话)。

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

快速浏览应用以了解其功能。该应用预填充了 10 项锻炼活动并在第一个视图中显示此信息。

试用现有的 widget

  1. 点按 Home 按钮,前往测试设备的主屏幕。
  2. 长按主屏幕上的空白区域,然后选择 Widgets
  3. 向下滚动 widget 列表,找到 FitActions
  4. 长按 FitActions 图标并将其 widget 放置在主屏幕上。

一张屏幕截图,显示了设备主屏幕上的 FitActions widget。

4. 添加与应用有关的 Action

在此步骤中,您将添加 GET_EXERCISE_OBSERVATION BII capability。您将通过在 shortcuts.xml 中添加新的 capability 元素来实现此操作。此 capability 用于指定如何触发此 BII capability、如何使用 BII 参数,以及要调用哪些 Android intent 来执行该请求。

  1. 使用以下配置向示例项目 shortcuts.xml 资源添加新的 capability 元素:
    <!-- fitnessactions/app/src/main/res/xml/shortcuts.xml -->
    
    <capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
      <app-widget
        android:identifier="GET_EXERCISE_OBSERVATION"
        android:targetClass="com.devrel.android.fitactions.widgets.StatsWidgetProvider"
        android:targetPackage="PUT_YOUR_APPLICATION_ID_HERE">
        <parameter
          android:name="exerciseObservation.aboutExercise.name"
          android:key="aboutExerciseName"
          android:required="true">
        </parameter>
        <extra android:name="hasTts" android:value="true"/>
      </app-widget>
      <!-- Add Fallback Intent-->
    </capability>
    
    android:targetPackage 值(即 PUT_YOUR_APPLICATION_ID_HERE)替换为您的唯一 applicationId

此 capability 会将 GET_EXERCISE_OBSERVATION BII 映射到 app-widget intent,以便在该 BII 被触发时,相应 widget 会实例化并向用户显示。

在触发 widget 之前,Google 助理会从用户询问中提取支持的 BII 参数。此 Codelab 需要使用 BII 参数 exerciseObservation.aboutExercise.name,该参数表示用户请求的锻炼类型。该应用支持三种锻炼类型:“跑步”“步行”和“骑车”。您应提供内嵌目录,以便将这些支持的值告知 Google 助理。

  1. 将以下配置(位于 GET_EXERCISE_OBSERVATION capability 上方)添加到 shortcuts.xml 中,以定义这些目录元素:
    <!-- shortcuts.xml -->
    
    <!-- shortcuts are bound to the GET_EXERCISE_OBSERVATION capability and
         represent the types of exercises supported by the app. -->
    
    <shortcut
      android:shortcutId="running"
      android:shortcutShortLabel="@string/activity_running">
      <capability-binding android:key="actions.intent.GET_EXERCISE_OBSERVATION">
        <parameter-binding
          android:key="exerciseObservation.aboutExercise.name"
          android:value="@array/runningSynonyms"/>
      </capability-binding>
    </shortcut>
    
    <shortcut
      android:shortcutId="walking"
      android:shortcutShortLabel="@string/activity_walking">
      <capability-binding android:key="actions.intent.GET_EXERCISE_OBSERVATION">
        <parameter-binding
          android:key="exerciseObservation.aboutExercise.name"
          android:value="@array/walkingSynonyms"/>
      </capability-binding>
    </shortcut>
    
    <shortcut
      android:shortcutId="cycling"
      android:shortcutShortLabel="@string/activity_cycling">
      <capability-binding android:key="actions.intent.GET_EXERCISE_OBSERVATION">
        <parameter-binding
          android:key="exerciseObservation.aboutExercise.name"
          android:value="@array/cyclingSynonyms"/>
      </capability-binding>
    </shortcut>
    
    <capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
      <!-- ... -->
    </capability>
    

添加回退 intent

回退 intent 用于处理因用户询问中缺少该 capability 所需的参数而导致询问无法执行的情况。GET_EXERCISE_OBSERVATION capability 需要使用由 android:required="true" 属性指定的 exerciseObservation.aboutExercise.name 参数。对于这类情况,Google 助理会要求您定义回退 intent,以便成功执行请求,即使询问中未提供任何参数也是如此。

  1. shortcuts.xml 中,使用以下配置向 GET_EXERCISE_OBSERVATION capability 添加回退 intent:
    <!-- shortcuts.xml -->
    
    <capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
    
      <app-widget>
        <!-- ... -->
      </app-widget>
    
      <!-- Fallback intent with no parameters needed to successfully execute.-->
      <intent
        android:identifier="GET_EXERCISE_OBSERVATION_FALLBACK"
        android:action="android.intent.action.VIEW"
        android:targetClass="com.devrel.android.fitactions.widgets.StatsWidgetProvider">
      </intent>
    </capability>
    

在此示例配置中,回退执行方式是一个 Extra 数据中不含参数的 Android intent。

5. 为 Google 助理启用 widget

创建 GET_EXERCISE_OBSERVATION capability 后,更新 widget 类以支持与应用有关的 Action 的语音调用。

添加 Widgets Extension 库

与应用有关的 Action 的 Widgets Extension 库可增强您的 widget,以实现语音优先的 Google 助理体验。具体来说,该库可让您为 widget 提供自定义的 TTS 简介。

  1. 将 Widgets Extension 库依赖项添加到示例应用的 /app/build.gradle 资源:
    // app/build.gradle
    
    dependencies {
      //...
      implementation "com.google.assistant.appactions:widgets:0.0.1"
    }
    
    点击 Android Studio 内显示的警告框中的 Sync Now。在每次 build.gradle 更改后同步有助于避免在构建应用时出错。

添加 widget service

Service 是一种可在后台执行长时间运行的操作的应用组件。您的应用需要提供 servive 来处理 widget 请求。

  1. 使用以下配置向示例应用的 AndroidManifest.xml 资源添加一项 service:
    <!-- AndroidManifest.xml -->
    <service
       android:name=".widgets.StatsWidgetProvider"
       android:enabled="true"
       android:exported="true">
       <intent-filter>
           <action
               android:name="com.google.assistant.appactions.widgets.PIN_APP_WIDGET" />
       </intent-filter>
    </service>
    
    

在触发 widget 执行方式的语音询问期间,Google 助理会使用此 service 向应用发送请求。此 service 会收到相应请求及 BII 数据。此 service 会使用这些数据生成要在 Google 助理内呈现的 RemoteView widget 对象。

更新 widget 类

您的应用现已配置为将 GET_EXERCISE_OBSERVATION capability 请求路由到您的 widget 类。接下来,更新 StatsWidget.kt 类,以使用 BII 参数值生成针对用户请求的个性化 widget 实例。

  1. 打开 StatsWidget.kt 类,导入与应用有关的 Action 的 Widgets Extension 库:
    // StatsWidget.kt
    
    // ... Other import statements
    import com.google.assistant.appactions.widgets.AppActionsWidgetExtension
    
    
  2. 添加以下私有变量,在确定应填充到 widget 中的信息时使用这些变量:
    // StatsWidget.kt
    
    private val hasBii: Boolean
    private val isFallbackIntent: Boolean
    private val aboutExerciseName: String
    private val exerciseType: FitActivity.Type
    
  3. 添加 init 函数,让该类可以使用从 Google 助理传递过来的 widget 选项数据:
    // StatsWidget.kt
    
    init {
      val optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId)
      val bii = optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII)
      hasBii = !bii.isNullOrBlank()
      val params = optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS)
    
      if (params != null) {
        isFallbackIntent = params.isEmpty
        if (isFallbackIntent) {
          aboutExerciseName = context.resources.getString(R.string.activity_unknown)
        } else {
            aboutExerciseName = params.get("aboutExerciseName") as String
          }
      } else {
          isFallbackIntent = false
          aboutExerciseName = context.resources.getString(R.string.activity_unknown)
      }
      exerciseType = FitActivity.Type.find(aboutExerciseName)
    }
    
    

我们来了解一下这些更新如何启用 StatsWidget.kt 类来响应由 GET_EXERCISE_OBSERVATION capability 生成的 Android intent:

  • optionsBundle = Bundle
    • Bundle 是一种对象,旨在用于各种进程边界中、具有 intent 的 activity 之间,以及用于在配置更改后存储瞬时状态。Google 助理使用 Bundle 对象将配置数据传递给 widget。
  • bii = actions.intent.GET_EXERCISE_OBSERVATION
    • 您可以使用 AppActionsWidgetExtension 从 Bundle 中获取 BII 的名称。
  • hasBii = true
    • 检查是否存在相应 BII。
  • params = Bundle[{aboutExerciseName=running}]
    • 该特殊 Bundle 由与应用有关的 Action 生成,会嵌套在 widget 选项 Bundle 内。它包含 BII 的键值对。在此示例中,值 running 提取自示例询问“Hey Google,在 ExampleApp 中显示我的跑步统计数据”。
  • isFallbackIntent = false
    • 检查 intent Extras 中是否存在必需的 BII 参数。
  • aboutExerciseName = running
    • 获取 aboutExerciseName 的 intent Extras 值。
  • exerciseType = RUNNING
    • 使用 aboutExerciseName 查找对应的数据库类型对象。

现在,StatsWidget 类可以处理传入的与应用有关的 Action 的 Android intent 数据;请更新 widget 创建流程逻辑,以检查该 widget 是否由与应用有关的 Action 触发。

  1. StatsWidget.kt 中,使用以下代码替换 updateAppWidget() 函数:
    // StatsWidget.kt
    
    fun updateAppWidget() {
       /**
        * Checks for App Actions BII invocation and if BII parameter data is present.
        * If parameter data is missing, use data from last exercise recorded to the
        *  fitness tracking database.
        */
       if (hasBii && !isFallbackIntent) {
           observeAndUpdateRequestedExercise()
       } else observeAndUpdateLastExercise()
    }
    
    

上述代码引用了新函数 observeAndUpdateRequestedExercise。此函数使用由与应用有关的 Action 的 Android intent 传递的 exerciseType 参数数据生成 widget 数据。

  1. 使用以下代码添加 observeAndUpdateRequestedExercise 函数:
    // StatsWidget.kt
    
    /**
    * Create and observe the last exerciseType activity LiveData.
    */
    private fun observeAndUpdateRequestedExercise() {
      val activityData = repository.getLastActivities(1, exerciseType)
    
       activityData.observeOnce { activitiesStat ->
           if (activitiesStat.isNotEmpty()) {
               formatDataAndSetWidget(activitiesStat[0])
               updateWidget()
           } else {
               setNoActivityDataWidget()
               updateWidget()
           }
       }
    }
    
    

在前面的代码中,使用在应用中找到的代码库类,从应用的本地数据库中检索健身数据。此类提供的 API 可简化对数据库的访问。代码库的运作方式是在对数据库执行查询时公开 LiveData 对象。在您的代码中,您可以观察此 LiveData 以检索最新的健身活动。

启用 TTS

您可以提供 TTS 字符串,以供 Google 助理在显示 widget 时读出。我们建议您添加此字符串,以在 widget 中提供可听的背景信息。此功能由与应用有关的 Action 的 Widgets Extension 库提供,您可以使用该库设置 Google 助理中伴随 widget 提供的文字和 TTS 简介。

提供 TTS 简介的理想位置是在 formatDataAndSetWidget 函数中,该函数会设置从应用数据库返回的 activity 数据的格式。

  1. StatsWidget.kt 中,将以下代码添加到 formatDataAndSetWidget 函数中:
    // StatsWidget.kt
    
    private fun formatDataAndSetWidget(
      activityStat: FitActivity,
    ) {
          // ...
    
          // Add conditional for hasBii for widget with data
          if (hasBii) {
             // Formats TTS speech and display text for Assistant
             val speechText = context.getString(
                 R.string.widget_activity_speech,
                 activityExerciseTypeFormatted,
                 formattedDate,
                 durationInMin,
                 distanceInKm
             )
             val displayText = context.getString(
                 R.string.widget_activity_text,
                 activityExerciseTypeFormatted,
                 formattedDate
             )
             setTts(speechText, displayText)
          }
    }
    
    

上述代码引用了两项字符串资源:一个用于语音,另一个用于文字。如需了解 TTS 方面的建议,请观看 widget 相关视频中的文字转语音样式建议部分。该示例还引用了 setTts,这是一个向 widget 实例提供 TTS 信息的新函数。

  1. 使用以下代码将此新 setTts 函数添加到 StatsWidget.kt
    // StatsWidget.kt
    
    /**
     * Sets TTS to widget
     */
    private fun setTts(
      speechText: String,
      displayText: String,
    ) {
      val appActionsWidgetExtension: AppActionsWidgetExtension =
          AppActionsWidgetExtension.newBuilder(appWidgetManager)
            .setResponseSpeech(speechText)  // TTS to be played back to the user
            .setResponseText(displayText)  // Response text to be displayed in Assistant
            .build()
    
      // Update widget with TTS
      appActionsWidgetExtension.updateWidget(appWidgetId)
    }
    

最后,当锻炼数据库针对所请求的锻炼类型返回空数据时,设置 TTS 信息以完成 TTS 逻辑。

  1. 使用以下代码更新 StatsWidget.kt 中的 setNoActivityDataWidget() 函数:
    // StatsWidget.kt
    
    private fun setNoActivityDataWidget() {
      // ...
      // Add conditional for hasBii for widget without data
      if (hasBii) {
        // formats speech and display text for Assistant
        // https://developers.google.com/assistant/app/widgets#library
        val speechText =
          context.getString(R.string.widget_no_activity_speech, aboutExerciseName)
        val displayText =
          context.getString(R.string.widget_no_activity_text)
    
        setTts(speechText, displayText)
      }
    }
    

6. 测试与应用有关的 Action

在开发期间,您可以使用 Google 助理插件在测试设备上预览 Google 助理与应用有关的 Action。您可以使用该工具调整与应用有关的 Action 的 intent 参数,以便测试您的 Action 如何处理用户在要求 Google 助理运行 Action 时可能采用的各种方式。

创建预览

若要使用该插件测试与应用有关的 Action,请执行以下操作:

  1. 依次前往 Tools > Google Assistant > App Actions Test Tool。系统可能会要求您使用自己的 Google 帐号登录 Android Studio。
  2. 点击 Create Preview。如果出现提示,请查看并接受与应用有关的 Action 方面的政策和服务条款。

测试预期的锻炼类型

返回一个 widget,其中显示应用中上次完成的运行的相关信息;在测试工具中的具体步骤如下:

  1. 在第一步中,该工具会让您选择并配置一个 BII;请选择 actions.intent.GET_EXERCISE_OBSERVATION
  2. exerciseObservation 框中,将默认锻炼名称从 climbing 更新为 run
  3. 点击 Run App Action

一个屏幕,显示了使用 Google 助理插件返回的 widget。

测试非预期的锻炼类型

若要在测试工具中测试非预期的锻炼类型,请执行以下操作:

  1. exerciseObservation 框中,将 name 值从 Run 更新为 Climbing
  2. 点击 Run App Action

Google 助理应返回一个 widget,其中会显示“No activity found”信息。

一个屏幕,显示了使用 Google 助理插件返回的不含任何锻炼信息的 widget。

测试回退 intent

触发回退 intent 的询问应返回一个 widget,其中会显示系统上一次记录的任意锻炼类型的活动的相关信息。

若要测试回退 intent,请执行以下操作:

  1. exerciseObservation 框中,删除 aboutExercise 对象。
  2. 点击 Run App Action

Google 助理应返回一个 widget,其中会显示上次完成的锻炼的相关信息。

一个屏幕,显示了使用 Google 助理插件返回且展示了上次所记录活动的 widget。

7. 后续步骤

恭喜!

现在,您能够借助 Google 助理利用 Android widget 来执行用户的询问了。

所学内容

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

  • 向 BII 中添加应用 widget。
  • 修改 widget 以获取 Android Extras 中的参数。

后续操作

之后,您可以尝试进一步优化您的健身应用。如需参考已完成的项目,请参阅 GitHub 上的主代码库

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

  • 请参阅与应用有关的 Action 的内置 intent 参考文档,探索更多方法来将您的应用扩展到 Google 助理。

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

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

反馈调查问卷

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