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 realizar consultas de usuários no Google Assistente usando widgets e como melhorar sua experiência com o Google Assistente usando a biblioteca Widgets Extension das Ações no app.

Benefícios

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 o fulfillment de 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. Adicione a biblioteca Widgets Extension obrigatória, que permite que o Google Assistente transmita nomes e parâmetros de BIIs aos 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.xml"Contida 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 de fulfillment do widget de nível superior.

Atributos:

  • android:identifier: o identificador desse 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 é usada para esse widget. Essa tag requer os seguintes valores de atributo:

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

Exemplo de código

O exemplo a seguir de um arquivo shortcuts.xml demonstra uma configuração de fulfillment de widget para um recurso de BII GET_EXERCISE_OBSERVATION:

<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 might 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 might 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 Widget Extensions de 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:

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 introdução e atualizar o widget, conforme mostrado neste "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);
    }

}

Recomendações de estilo para TTS

Use as recomendações de estilo a seguir para otimizar as apresentações do widget personalizado para o TTS e as solicitações exibidas.

Recomendação Recomendado Não recomendado
Expressões mais naturais
Use expressões que soam mais naturais nas solicitações de TTS. Mensagens muito longas e formais ficam muito rígidas e robóticas, e não naturais e conversacionais. Falar palavras como "não foi possível" e "desculpe" pode soar pouco natural.
ResponseSpeech (TTS)
Não encontrei uma reserva.

ResponseText
Não consigo encontrar uma reserva.
ResponseSpeech (TTS)
Não encontrei uma reserva.

ResponseText
Desculpe, não foi possível encontrar uma reserva.
Listas
Para aumentar a clareza, use "e" para separar quando há três itens ou ao final de uma lista. Sem o "e", é possível que itens individuais na lista sejam ouvidos de forma incorreta ou lidos como grupos. Por exemplo, em "gafanhotos, margaridas, girassóis", "margaridas e girassóis" soa como se estivessem reunidos. Em "macarrão, margaridas e girassóis", os três são claramente separados.
ResponseSpeech (TTS)
As flores mais conhecidas incluem rosas amarelas, narcisos, margaridas e girassóis.

ResponseText
As flores mais conhecidas incluem rosas amarelas, narcisos, margaridas e girassóis.
ResponseSpeech (TTS)
As flore mais conhecidas incluem rosas amarelas, narcisos, margaridas, girassóis.

ResponseText
Os mais procurados incluem rosas amarelas, narcisos, margaridas, girassóis.
Numerais
Use números em vez de texto para facilitar a visualização do conteúdo.
ResponseSpeech (TTS)
Sua pressão arterial é de 100 por 80.

ResponseText
Sua pressão arterial é de 100/80.
ResponseSpeech (TTS)
Sua pressão arterial é de 100/80.

ResponseText
Sua pressão arterial é de cem por oitenta.
Símbolos
Use símbolos especializados em vez de texto para facilitar a visualização do conteúdo.
ResponseSpeech (TTS)
Sua última compra foi de R$ 24,65.

ResponseText
Sua última compra foi de R$ 24,65.
ResponseSpeech (TTS)
Sua última compra foi de vinte e quatro reais e sessenta e cinco centavos.

ResponseText
Sua última compra foi de vinte e quatro reais e sessenta e cinco centavos.
Evite cortesias
As cortesias tornam as respostas distantes e formais. Deixe de lado as formalidades e mantenha a conversa amigável e informal.
ResponseSpeech (TTS)
Seu pedido foi entregue.

ResponseText
Seu pedido foi entregue.
ResponseSpeech (TTS)
Claro, eu posso fornecer essa informação. Seu pedido foi entregue.

ResponseText
Claro, eu posso fornecer essa informação. Seu pedido foi entregue.
Evite pontos de exclamação
Eles podem ser entendidas como gritos.
ResponseSpeech (TTS)
Você correu 1,5 km hoje.

ResponseText
Você correu 1,5 km hoje.
ResponseSpeech (TTS)
Você correu 1,5 km hoje!

ResponseText
Você correu 1,5 km hoje!
Hora
Use numerais: "5:15", em vez de "cinco e quinze". Para o relógio de 12 horas, use AM ou PM.
ResponseSpeech (TTS)
Sua entrega deve chegar até as 8h15.

ResponseText
Sua entrega deve chegar até as 8h15.
ResponseSpeech (TTS)
Sua entrega deve chegar 15 minutos após as 8h de hoje.

ResponseText
Sua entrega deve chegar 15 minutos após as 8h de hoje.
Cuidado para não fazer monólogos
Seja informativo, mas mantenha as respostas concisas. Não entre em detalhes complicados que não tenham um benefício claro para o usuário.
ResponseSpeech (TTS)
No mês passado, você consumiu 159 horas de energia.

ResponseText
No mês passado, você consumiu 159 horas de energia.
ResponseSpeech (TTS)
Economizar energia é muito importante para o planeta e o meio ambiente. No mês passado, você consumiu 159 horas de energia. Neste mês, você consumiu 58 horas de energia.

ResponseText
Economizar energia é muito importante para o planeta e o meio ambiente. No mês passado, você consumiu 159 horas de energia. Neste mês, você consumiu 58 horas de energia.
Use palavras curtas e simples
Uma linguagem simples e breve é mais atrativa, o que a torna mais acessível para pessoas de todas as origens.
ResponseSpeech (TTS)
O resultado da sua última leitura de açúcar no sangue foi 126.

ResponseText
O resultado da sua última leitura de açúcar no sangue foi 126 mg/dL.
ResponseSpeech (TTS)
O penúltimo nível de glicose no sangue foi 126.

ResponseText
O penúltimo nível de glicose no sangue foi 126.

Fixação na tela de início

A biblioteca Widgets Extension permite que o botão Add this widget seja mostrado com seu widget no Google Assistente. Para ativar a fixação, adicione a seguinte definição de receptor a 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 código a seguir de um arquivo shortcuts.xml de exemplo demonstra um recurso da BII START_EXERCISE configurado para inventário inline e fulfillment do widget:

<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:

  • Tecla = “exerciseName”
  • Valor: “RunningShortcut”

Inventário da Web

O código a seguir de um exemplo de arquivo shortcuts.xml mostra um recurso ativado para inventário da Web e fulfillment do widget:

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

Testar Ações no app

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.
  • Concentre-se totalmente no conteúdo do widget para cumprir a intent. Não atenda a várias intents com um único widget nem adicione conteúdo irrelevante.

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.
  • Se os usuários permitirem que o app mostre dados usando widgets, você poderá retornar um widget de erro no tempo de 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.

  • Use a intent de fallback para abrir o app na tela relevante, não na tela inicial.

O código a seguir de um exemplo de arquivo shortcuts.xml demonstra uma <capability> com uma <intent> substituta que oferece suporte a um fulfillment principal do <app-widget>:

<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.