Integrar as Ações no app com widgets do Android

Figura 1. Iniciando um widget para GET_EXERCISE_OBSERVATION.

Para muitas intents, o melhor é fornecer ao usuário uma resposta simples, uma confirmação breve ou uma experiência interativa rápida. Você pode mostrar um widget de app Android no Google Assistente para atender a esses tipos de intents.

Este guia ensina como responder consultas no Google Assistente usando widgets e como melhorar essa experiência com a biblioteca Widgets Extension das Ações no app.

Vantagens

Os widgets são visualizações em miniatura de apps que podem ser incorporadas em plataformas do Android, como a tela de início ou de bloqueio. Com as Ações no app, você aumenta o impacto dos seus widgets, fazendo com que eles possam aparecer no Google Assistente:

  1. Descoberta: mostre widgets de forma proativa em resposta às consultas dos usuários em linguagem natural.
  2. Engajamento: mostre widgets em contextos viva-voz, como quando o Google Assistente fornece resultados personalizados na tela de bloqueio e no Android Auto.
  3. Retenção: permita que os usuários fixem na tela de início os widgets mostrados no Google Assistente. Essa função exige a biblioteca Widgets Extension.

Como o Google Assistente mostra widgets

Os usuários têm duas maneiras de invocar widgets no Google Assistente:

  • Solicitar um widget explicitamente pelo nome
  • Fazer uma consulta com o Google Assistente, que aciona uma intent integrada (BII, na sigla em inglês) ou uma intent personalizada configurada para fulfillment de widget

Invocação explícita

Para invocar explicitamente os widgets de qualquer app instalado, os usuários podem fazer pedidos como estes ao Google Assistente:

  • "Ok Google, mostre o widget do ExampleApp."
  • "Widgets do ExampleApp."

O Google Assistente mostra esses widgets com a introdução genérica: "Aqui está o widget do ExampleApp". O Google Assistente retorna os widgets solicitados dessa maneira de forma nativa, sem nenhum trabalho do desenvolvedor do app, mas esse método de invocação exige que o usuário tenha conhecimento explícito do widget a ser solicitado. Para simplificar a descoberta de widgets, use o método de fulfillment da intent detalhado na seção a seguir.

Fulfillment da intent

Seus widgets podem ser encontrados com mais facilidade quando são usados para atender às consultas que os usuários fazem no Google Assistente usando linguagem natural. Por exemplo, você pode retornar um widget sempre que um usuário acionar a BII GET_EXERCISE_OBSERVATION no seu app fitness perguntando "Ok Google, quantos quilômetros eu corri esta semana no ExampleApp?". Além de simplificar a descoberta, a integração de widgets com as Ações no app oferece estas vantagens:

  • Acesso a parâmetros: o Google Assistente fornece os parâmetros de intent extraídos da consulta do usuário para seu widget, resultando em respostas personalizadas.
  • Apresentações personalizadas de TTS: você pode fornecer uma string de conversão de texto em voz (TTS) para o Google Assistente anunciar ao mostrar seu widget.
  • Fixação de widgets: o Google Assistente mostra um botão Adicionar este widget ao lado do seu widget para que ele possa ser fixado na tela de início de forma simples.

Implementar widgets

Para implementar o fulfillment de widgets para as intents, siga estas etapas:

  1. Implemente um widget do Android seguindo as etapas descritas em Criar um widget simples.
  2. No arquivo shortcuts.xml do seu app, adicione um elemento <app-widget> ao recurso que contém detalhes de fulfillment e tags de <parameter> da BII. Atualize o widget para processar os parâmetros.
  3. (Obrigatório) Adicione a biblioteca Widgets Extension, que permite que o Google Assistente transmita nomes e parâmetros de BIIs aos seus widgets. Ela também ativa as apresentações de TTS personalizadas e a funcionalidade de fixação de widgets.

A seção a seguir descreve o esquema de <app-widget> para o shortcuts.xml.

Esquema do widget

Os elementos <app-widget> são definidos como fulfillments dentro dos elementos <capability> no arquivo shortcuts.xml. A menos que sejam considerados opcionais, eles exigem os seguintes atributos:

Tag no shortcuts.xmlContida emAtributos
<app-widget> <capability>
  • android:identifier
  • android:targetClass
<parameter> <app-widget>
<extra> <app-widget>
  • android:name (aplicável somente para TTS)
  • android:value (opcional)

Descrição do esquema do widget

<app-widget>

Elemento fulfillment do widget de nível superior.

Atributos:

  • android:identifier: identificador exclusivo do fulfillment. Esse valor precisa ser único entre os elementos de fulfillment <app-widget> e <intent> definidos em um elemento <capability>.
  • android:targetClass: o nome completo da classe da sua AppWidgetProvider para processar a intent.

<parameter>

Associa um parâmetro de BII a um valor <parameter> de intent. É possível definir zero ou mais parâmetros para cada elemento <app-widget>. Durante o fulfillment, o Google Assistente transmite os parâmetros atualizando os extras da instância de widget como pares de chave-valor com o seguinte formato:

  • Chave: o android:key definido para o parâmetro.
  • Valor: aquele que a BII extrai da entrada de voz de um usuário.

Para acessar esses extras, chame getAppWidgetOptions() no objeto AppWidgetManager associado que retorna um Bundle contendo o nome da BII e os parâmetros dela. Consulte Extrair valores de parâmetros para ver mais detalhes.

Se quiser mais informações sobre a correspondência de parâmetros de BII, consulte Correspondência e dados de parâmetros.

<extra>

Tag opcional que declara que uma apresentação personalizada de TTS precisa ser usada para esse widget. Essa tag requer os seguintes valores de atributo:

  • android:name: "hasTts"
  • android:value: "true"

Exemplo de código

O XML a seguir demonstra uma configuração de fulfillment do widget para um recurso da BII GET_EXERCISE_OBSERVATION:

shortcuts.xml

<capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
  <app-widget
    android:identifier="GET_EXERCISE_OBSERVATION_1"
    android:targetClass="com.exampleapp.providers.exampleAppWidgetProvider"
    android:targetPackage="com.exampleapp">
    <parameter
      android:name="exerciseObservation.aboutExercise.name"
      android:key="exercisename">
    </parameter>
    <extra android:name="hasTts" android:value="true"/>
  </app-widget>
</capability>

Você pode especificar vários elementos <app-widget> ou usar uma combinação de elementos <app-widget> e <intent> por recurso. Essa abordagem permite fornecer uma experiência personalizada com base em diferentes combinações de parâmetros fornecidos pelos usuários. Por exemplo, caso o usuário não especifique um local de desembarque na consulta, ele pode ser direcionado para a atividade no app que mostra opções para definir os locais de embarque e desembarque. Consulte a seção Intents substitutas para mais informações sobre como definir essas intents.

Extrair valores de parâmetros

Na classe AppWidgetProvider de exemplo a seguir, a função particular updateAppWidget() é usada para extrair o nome e os parâmetros da BII do Bundle de opções de widget:

Kotlin

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
class MyAppWidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    private fun updateAppWidget(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val widgetText: CharSequence = context.getString(R.string.appwidget_text)

        // Construct the RemoteViews object
        val views = RemoteViews(context.packageName, R.layout.my_app_widget)
        views.setTextViewText(R.id.appwidget_text, widgetText)

        // Extract the name and parameters of the BII from the widget options.
        val optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId)
        val bii = optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII) // "actions.intent.CREATE_TAXI_RESERVATION"
        val params = optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS)
        if (params != null && params.containsKey("dropoff")) {
            val dropoffLocation = params.getString("dropoff")
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Java

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {

        CharSequence widgetText = context.getString(R.string.appwidget_text);

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_app_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Extract the name and parameters of the BII from the widget options.
        Bundle optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId);
        String bii =
                optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII); // "actions.intent.CREATE_TAXI_RESERVATION"
        Bundle params =
                optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS);

        if (params != null && params.containsKey(("dropoff"))){
            String dropoffLocation = params.getString("dropoff");
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

Biblioteca Widgets Extension

A biblioteca Widgets Extension das Ações no app melhora os widgets para experiências do Google Assistente que usam a voz. Ela permite que os widgets recebam informações importantes de fulfillment da BII de acionamento, incluindo o nome da BII e os parâmetros de intent extraídos da consulta do usuário. Com essa biblioteca do Maven, você pode fornecer uma apresentação personalizada de conversão de texto em voz (TTS, na sigla em inglês) para cada widget, permitindo que o Google Assistente anuncie um resumo do conteúdo que está sendo renderizado visualmente. Ela também ativa a fixação na tela de início, fazendo com que seja mais fácil salvar nessa tela os widgets mostrados no Google Assistente.

Para começar, adicione a biblioteca à seção de dependências do arquivo build.gradle do módulo do app:

app/build.gradle

dependencies {
    //...
    implementation "com.google.assistant.appactions:widgets:0.0.1"
}

Apresentações personalizadas

Depois de importar a biblioteca Widgets Extension, você pode fornecer apresentações personalizadas de TTS para seus widgets. Se quiser adicionar sua definição à AppWidgetProvider do widget, abra essa classe no ambiente de desenvolvimento integrado e importe a biblioteca Widgets Extension:

Kotlin

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

Java

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;
Em seguida, use a biblioteca para definir as strings de apresentação e atualizar o widget:
ExampleAppWidget

Kotlin

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
object MyAppWidget : AppWidgetProvider() {
    fun updateAppWidget(
        context: Context?,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
            .setResponseSpeech("Hello world") // TTS to be played back to the user.
            .setResponseText("Hello world!") // Response text to be displayed in Assistant.
            .build()

        // Update widget with TTS.
        appActionsWidgetExtension.updateWidget(appWidgetId)

        // Update widget UI.
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Java

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

  static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
    int appWidgetId) {

    AppActionsWidgetExtension appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
      .setResponseSpeech("Hello world")  // TTS to be played back to the user.
      .setResponseText("Hello world!")  // Response text to be displayed in Assistant.
      .build();

      // Update widget with TTS.
      appActionsWidgetExtension.updateWidget(appWidgetId);

      // Update widget UI.
      appWidgetManager.updateAppWidget(appWidgetId, views);
    }

}

Fixação na tela de início

Essa biblioteca permite que o botão Adicionar este widget seja mostrado com o widget no Google Assistente. A fixação requer que a seguinte definição de receptor seja adicionada ao AndroidManifest.xml:

AndroidManifest.xml

<application>
  <receiver android:name="com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetBroadcastReceiver"
    android:exported="false">
    <intent-filter>
      <action android:name="com.google.assistant.appactions.widgets.COMPLETE_PIN_APP_WIDGET" />
    </intent-filter>
  </receiver>
  <service
    android:name=
    "com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
      <action
        android:name="com.google.assistant.appactions.widgets.PIN_APP_WIDGET" />
    </intent-filter>
  </service>
</application>

Disponibilidade de inventário

As BIIs com suporte ao inventário inline ou ao inventário da Web podem ampliar esses inventários para os fulfillments do widget.

Inventário inline

O exemplo de código a seguir demonstra um recurso START_EXERCISE da BII configurado para inventário inline e fulfillment do widget:

shortcuts.xml

<capability
  android:name="actions.intent.START_EXERCISE">
  <app-widget
    android:identifier="START_EXERCISE_1"
    android:targetClass="com.example.exampleapp.StartExerciseAppWidgetProvider">
    <parameter
      android:name="exercise.name"
      android:key="exerciseName"
      app:shortcutMatchRequired="true">
    </parameter>
  </app-widget>
</capability>

<shortcut android:shortcutId="RunningShortcut">
  <intent
    android:action="android.intent.action.VIEW"
    android:targetClass="com.example.exampleapp.StartExcerciseActivity" />
  <capability-binding
    android:capability="actions.intent.START_EXERCISE"
    android:parameter="exercise.name"
    android:value="running;runs" />
</shortcut>

No exemplo anterior, quando um usuário pedir para o Google Assistente "Começar a correr com o ExampleApp", o pacote de opções para o fulfillment <app-widget> vai conter o seguinte par de chave-valor:

  • Chave: “exerciseName”
  • Valor: “RunningShortcut”

Inventário da Web

Consulte o exemplo de código a seguir para acessar um recurso ativado para inventário da Web e fulfillment de widget:

shortcuts.xml

<shortcuts>
  <capability
    android:name="actions.intent.START_EXERCISE">
    <app-widget
      android:identifier="START_EXERCISE_1"
      android:targetClass="com.example.exampleapp.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="exercise.name"
        android:key="exerciseName"
        android:mimeType="text/*">
        <data android:pathPattern="https://exampleapp.com/exercise/.*" />
      </parameter>
    </app-widget>
  </capability>
</shortcuts>

Teste

Use a App Actions Test Tool (um recurso do plug-in do Google Assistente para Android Studio) para testar widgets em um dispositivo físico ou virtual. Siga estas etapas para usar a ferramenta de teste:

  1. Conecte o dispositivo de teste em que o app está sendo executado.
  2. No Android Studio, acesse Tools > App Actions > App Actions Test Tool.
  3. Clique em Create preview.
  4. No Android Studio, execute o app no dispositivo de teste.
  5. Use o app Google Assistente no seu dispositivo para testar a ação no app. Por exemplo, você pode dizer algo como "Ok Google, quantos quilômetros eu corri esta semana no ExampleApp?".
  6. Observe o comportamento do app ou use o depurador do Android Studio para verificar o resultado da ação desejada.

Diretrizes de qualidade

Esta seção destaca os principais requisitos e as práticas recomendadas quando você integra Ações no app com widgets.

Conteúdo em widgets

  • (Obrigatório) Não mostre anúncios nos seus widgets.
  • O conteúdo do widget precisa se concentrar totalmente em atender à intent. Não é recomendado atender a várias intents com um único widget ou adicionar conteúdo irrelevante.

Como processar a autenticação

  • (Obrigatório) Quando a autenticação do usuário for necessária para concluir um fluxo de usuários, retorne um widget que explica que o usuário precisa continuar no app. As Ações no app não têm suporte para a autenticação de usuários inline no Google Assistente.
  • Caso os usuários estejam permitindo que o app mostre dados usando widgets, você pode retornar um widget de erro no momento da execução para usuários não autorizados.

Intents substitutas

  • (Obrigatório) No shortcuts.xml, sempre forneça uma <intent> substituta com o fulfillment do widget para um recurso específico. Uma intent substituta é um elemento <intent> sem valores de <parameter> obrigatórios. Isso permite que o Google Assistente realize uma ação quando a consulta do usuário não tiver parâmetros exigidos pelos outros elementos de fulfillment definidos no recurso. A exceção é quando não há parâmetros obrigatórios para o recurso. Nesse caso, apenas o fulfillment do widget é necessário.
  • A intent substituta precisa abrir o app na tela relevante do aplicativo, não na tela inicial.

O exemplo de código a seguir demonstra um <capability> com uma <intent> substituta que tem suporte a um fulfillment de <app-widget> principal:

shortcuts.xml

<shortcuts>
  <capability
    android:name="actions.intent.CREATE_TAXI_RESERVATION">
    <!-- Widget with required parameter, specified using the "android:required" attribute. -->
    <app-widget
      android:identifier="CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="taxiReservation.dropoffLocation.name"
        android:key="dropoff"
        android:required="true">
      </parameter>
    </app-widget>
    <!-- Fallback intent with no parameters required to successfully execute. -->
    <intent
      android:identifier="CREATE_TAXI_RESERVATION_3"
      android:action="myapplication.intent.CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.TaxiReservationActivity">
    </intent>
  </capability>
</shortcuts>

Declaração sobre o uso de dados pelo Google Play

Esta seção lista os dados do usuário final coletados pela versão mais recente da biblioteca Widgets Extension.

Esse SDK envia as respostas de conversão de texto em voz (TTS) fornecidas pelo desenvolvedor que são ditas ao usuário pelo Google Assistente usando a tecnologia de fala dele. Essas informações não são armazenadas pelo Google.

As Ações no app também podem coletar metadados do app cliente para as seguintes finalidades:

  • Monitorar as taxas de adoção de diferentes versões do SDK.
  • Quantificar o uso dos recursos do SDK nos apps.