Integrar widgets do Android ao Google Assistente

1. Visão geral

No primeiro codelab das Ações no app, você aprendeu a vincular o Google Assistente a um app fitness implementando intents integradas (BIIs, na sigla em inglês) da categoria "Saúde e fitness".

Com as Ações no app, as pessoas podem acessar recursos específicos de aplicativos usando o Assistente. Basta pedir algo como Ok Google, comece uma corrida no AppDeExemplo. Além de iniciar apps, o Assistente pode mostrar ao usuário um widget interativo do Android e atender a solicitações de BIIs qualificadas.

Uma tela que mostra o Google Assistente retornando um widget após\numa consulta do usuário que acionou o capability de BII GET_EXERCISE_OBSERVATION de um app.

O que você vai criar

Neste codelab, você vai aprender a retornar widgets do Android para atender a pedidos de usuários no Google Assistente, além de:

  • Usar parâmetros de BII para personalizar widgets.
  • Fazer apresentações de conversão de texto em voz (TTS) no Google Assistente para seus widgets.
  • Utilizar a referência de intents integradas para determinar quais BIIs são compatíveis com o fulfillment de widgets.

Pré-requisitos

Antes de continuar, verifique se o ambiente está pronto para o desenvolvimento de Ações no app. Ele precisa ter:

  • Um terminal para executar comandos do shell com git instalado.
  • A versão estável mais recente do Android Studio.
  • Um dispositivo Android físico ou virtual com acesso à Internet.
  • Uma Conta do Google conectada ao Android Studio, ao Google app e ao Assistente.

Dispositivos físicos precisam estar conectados à sua máquina de desenvolvimento local.

2. Como funciona

O Google Assistente usa processamento de linguagem natural (PLN) para entender e associar o pedido do usuário a uma BII do Assistente. Em seguida, o Assistente mapeia a intent para o capability (responsável por implementar a BII), que você registra para ela no app. Depois, o Google Assistente atende o que o usuário pediu mostrando o widget do Android que o app gera (usando os detalhes encontrados no capability).

Neste codelab, você define um capability que registra a compatibilidade com a BII GET_EXERCISE_OBSERVATION. Nesse capability, você instrui o Google Assistente a gerar uma intent do Android para a classe de widgets FitActions e, com isso, atender às solicitações dessa BII. Você atualiza a classe para gerar um widget personalizado, que o Google Assistente mostra ao usuário, e uma apresentação de TTS para o Assistente anunciar.

O diagrama a seguir mostra esse fluxo:

Um diagrama mostrando o fulfillment de um widget do Assistente.

Widget FitActions

O app de exemplo FitActions inclui um widget de informações sobre o treino que os usuários podem adicionar à tela inicial. Esse widget é ideal para atender a consultas do usuário que acionam a BII GET_EXERCISE_OBSERVATION.

Como o widget funciona

Todo widget adicionado à tela inicial dá um ping no dispositivo Broadcast Receiver. Esse serviço recupera informações sobre o widget com base na definição do receptor do widget no recurso AndroidManifest.xml do app e usa esses dados para gerar um objeto RemoteViews que representa o widget.

O app de exemplo define o receptor widgets.StatsWidgetProvider, que corresponde à classe 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>

A classe de StatsWidgetProvider, StatsWidgetProvider.kt, gerencia a criação do objeto StatsWidget. Ela tem estas responsabilidades:

  • Criar e preencher instâncias do widget com dados sobre exercícios do banco de dados do app.
  • Usar formatDataAndSetWidget() para formatar dados sobre treinos, facilitando o entendimento.
  • Utilizar setNoActivityDataWidget() para informar valores padrão se os dados sobre treinos não estiverem disponíveis.

Adicionar suporte ao Google Assistente

Neste codelab, você atualiza o aplicativo de exemplo para processar a funcionalidade das Ações no app. Essas mudanças incluem:

  1. Configurar o capability de BII GET_EXERCISE_OBSERVATION para retornar uma instância do objeto StatsWidget.
  2. Atualizar a classe StatsWidget para aproveitar os recursos das Ações no app, como:
    • Usar parâmetros de BII, permitindo que os usuários confiram detalhes sobre os treinos pedindo algo como Ok Google, mostre minhas estatísticas de corrida no AppDeExemplo.
    • Fornecer strings para a apresentação de TTS.
    • Gerenciar casos especiais, por exemplo, quando a consulta do usuário não inclui um parâmetro do tipo de treino.

3. Preparar seu ambiente de desenvolvimento

Fazer o download dos arquivos de base

Execute o comando abaixo para clonar o repositório do GitHub do app de exemplo (link em inglês):

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

Depois, siga estas etapas para abrir o conteúdo clonado no Android Studio:

  1. Na caixa de diálogo Welcome to Android Studio, clique em Import project.
  2. Encontre e selecione a pasta em que você clonou o repositório.

Para conferir uma versão do app que represente o codelab pronto, clone o repositório do app de exemplo usando a sinalização --branch master.

Atualizar o ID do app Android

Atualize o ID para identificar seu app de forma exclusiva no dispositivo de teste, evitando o erro "Duplicate package name" se o aplicativo for enviado ao Play Console. Para atualizar o ID do aplicativo, abra app/build.gradle:

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

Substitua "MYUNIQUENAME" no campo applicationId por algo exclusivo.

Instalar o plug-in de teste

Com o plug-in do Google Assistente, você pode testar as Ações no app em um dispositivo. Ele envia informações para o Assistente usando o Google app no seu aparelho Android. Para instalar o plug-in:

  1. Acesse File > Settings (Android Studio > Preferences no MacOS).
  2. Na seção "Plugins", acesse Marketplace e pesquise "Google Assistant". Também é possível fazer o download e instalar manualmente a ferramenta de teste.
  3. Instale a ferramenta e reinicie o Android Studio.

Testar o app no seu dispositivo

Antes de fazer outras mudanças, teste o app de exemplo para saber o que ele consegue fazer.

Execute o app no seu dispositivo de teste:

  1. No Android Studio, escolha o dispositivo físico ou virtual e selecione Run > Run app ou clique em RunÍcone do app de corrida no Android Studio. na barra de ferramentas.
  2. Toque e mantenha pressionado o botão home para configurar o Google Assistente e verificar se ele está funcionando. Faça login no Google Assistente usando seu dispositivo.

Para mais informações sobre dispositivos virtuais Android, consulte Criar e gerenciar dispositivos virtuais.

Use um pouco o app para saber o que ele oferece. Dez atividades de exercício já vêm preenchidas, e o aplicativo mostra essas informações na primeira visualização.

Testar o widget atual

  1. Toque no botão home para acessar a tela inicial do dispositivo de teste.
  2. Toque e mantenha pressionado um espaço vazio na tela inicial e selecione Widgets.
  3. Role a lista de widgets para baixo até chegar em FitActions.
  4. Toque no ícone FitActions. Mantenha pressionado e posicione o widget na tela inicial.

Captura de tela que mostra o widget FitActions na tela inicial do dispositivo.

4. Adicionar a Ação no app

Nesta etapa, você adiciona o capability de BII GET_EXERCISE_OBSERVATION. Para fazer isso, inclua um novo elemento capability no arquivo shortcuts.xml. Esse capability especifica como ele é acionado, como os parâmetros de BII são usados e quais intents do Android precisam ser invocadas para atender ao pedido.

  1. Adicione um novo elemento capability ao recurso shortcuts.xml do projeto de exemplo com esta configuração:
    <!-- 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>
    
    Substitua o valor de android:targetPackage, PUT_YOUR_APPLICATION_ID_HERE, pelo applicationId exclusivo.

Esse capability mapeia a BII GET_EXERCISE_OBSERVATION para a intent app-widget. Assim, quando a BII é acionada, o widget é instanciado e mostrado ao usuário.

Antes de acionar o widget, o Google Assistente extrai da consulta do usuário os parâmetros de BII aceitos. Este codelab requer o parâmetro de BII exerciseObservation.aboutExercise.name, que representa o tipo de exercício que a pessoa pediu. O app funciona com três tipos de exercícios: "corrida", "caminhada" e "ciclismo". Você fornece um inventário inline para informar esses valores ao Google Assistente.

  1. Para definir esses elementos de inventário, adicione ao arquivo shortcuts.xml a seguinte configuração acima do capability GET_EXERCISE_OBSERVATION:
    <!-- 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>
    

Adicionar uma intent substituta

As intents substitutas são usadas em situações em que não é possível atender a uma consulta do usuário porque faltam parâmetros que o capability exige. O capability GET_EXERCISE_OBSERVATION requer o parâmetro exerciseObservation.aboutExercise.name, que é especificado pelo atributo android:required="true". Nessas situações, o Google Assistente exige que você defina uma intent substituta para a solicitação, mesmo que nenhum parâmetro seja informado na consulta.

  1. Em shortcuts.xml, adicione uma intent substituta ao capability GET_EXERCISE_OBSERVATION com esta configuração:
    <!-- 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>
    

Nesta configuração de exemplo, o fulfillment substituto é uma intent do Android sem parâmetros nos dados de Extra.

5. Ativar o widget do Assistente

Com o capability GET_EXERCISE_OBSERVATION estabelecido, atualize a classe do widget para aceitar comandos de voz nas Ações no app.

Adicionar a 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. Com ela, também é possível oferecer uma apresentação personalizada de TTS para seus widgets.

  1. Adicione a dependência da biblioteca Widgets Extension ao recurso /app/build.gradle do app de exemplo:
    // app/build.gradle
    
    dependencies {
      //...
      implementation "com.google.assistant.appactions:widgets:0.0.1"
    }
    
    Clique em Sync Now na caixa de aviso que aparece no Android Studio. Faça a sincronização após cada mudança do build.gradle para evitar erros no desenvolvimento do app.

Adicionar o serviço de widgets

Um serviço é um componente de aplicativo que executa operações de longa duração em segundo plano. Seu app precisa fornecer um serviço para processar as solicitações dos widgets.

  1. Adicione um serviço ao recurso AndroidManifest.xml do app de exemplo com esta configuração:
    <!-- 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>
    
    

Durante uma consulta por voz que aciona o fulfillment do widget, o Google Assistente usa esse serviço para enviar solicitações ao app. O serviço recebe o pedido com os dados da BII e usa essas informações para gerar um objeto de widget de RemoteView, que é renderizado no Assistente.

Atualizar a classe do widget

Seu app agora consegue encaminhar solicitações do capability GET_EXERCISE_OBSERVATION à sua classe de widgets. Em seguida, atualize a classe StatsWidget.kt para gerar uma instância de widget personalizada de acordo com o pedido do usuário, utilizando valores de parâmetros de BII.

  1. Abra a classe StatsWidget.kt e importe a biblioteca Widget Extension das Ações no app:
    // StatsWidget.kt
    
    // ... Other import statements
    import com.google.assistant.appactions.widgets.AppActionsWidgetExtension
    
    
  2. Adicione as variáveis particulares usadas para determinar as informações que vão preencher o widget:
    // StatsWidget.kt
    
    private val hasBii: Boolean
    private val isFallbackIntent: Boolean
    private val aboutExerciseName: String
    private val exerciseType: FitActivity.Type
    
  3. Adicione a função init para permitir que a classe use os dados de opções do widget transmitidos pelo Google Assistente:
    // 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)
    }
    
    

Confira abaixo como essas atualizações permitem que a classe StatsWidget.kt responda a intents do Android geradas pelo capability GET_EXERCISE_OBSERVATION:

  • optionsBundle = pacote
    • Os pacotes são objetos usados nos limites de processamento, entre atividades com intents, e para armazenar o estado temporário durante as mudanças na configuração. O Google Assistente usa objetos Bundle para transmitir dados de configuração ao widget.
  • bii = actions.intent.GET_EXERCISE_OBSERVATION
    • Para saber o nome da BII no pacote, use AppActionsWidgetExtension.
  • hasBii = true
    • Verifica se há uma BII.
  • params = Bundle[{aboutExerciseName=running}]
    • Um pacote especial, gerado por Ações no app, está aninhado no Bundle das opções do widget. Ele contém os pares de chave-valor da BII. Nesse caso, o valor running foi extraído da consulta de exemplo Ok Google, mostre minhas estatísticas de corrida no AppDeExemplo.
  • isFallbackIntent = false
    • Verifica se os parâmetros de BII necessários nos Extras da intent estão presentes.
  • aboutExerciseName = running
    • Recebe o valor dos Extras da intent para aboutExerciseName.
  • exerciseType = RUNNING
    • Usa aboutExerciseName para procurar o objeto do tipo de banco de dados correspondente.

Agora que a classe StatsWidget processa dados da intent do Android referentes às Ações no app, atualize a lógica da criação de widgets para verificar se o widget foi acionado por uma dessas ações.

  1. Em StatsWidget.kt, substitua a função updateAppWidget() por este código:
    // 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()
    }
    
    

O código anterior faz referência a uma nova função, observeAndUpdateRequestedExercise, que gera dados do widget usando as informações do parâmetro exerciseType transmitidas pela intent do Android das Ações no app.

  1. Adicione a função observeAndUpdateRequestedExercise com este código:
    // 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()
           }
       }
    }
    
    

No código anterior, use uma classe de repositório do app para recuperar dados de condicionamento físico no banco de dados local do aplicativo. Essa classe fornece uma API que simplifica o acesso ao banco de dados. O repositório expõe um objeto LiveData quando consulta o banco. No seu código, você observa esse LiveData para recuperar o exercício mais recente.

Ativar a TTS

Você pode fornecer uma string de TTS para o Google Assistente anunciar quando mostrar seu widget. Recomendamos incluir uma string para criar um contexto audível com os widgets. Esse recurso é oferecido pela biblioteca Widgets Extension das Ações no app, que permite definir o texto e as apresentações de TTS que acompanham seus widgets no Assistente.

Um bom lugar para fazer a apresentação de TTS é na função formatDataAndSetWidget, que formata os dados da atividade retornados do banco de dados do app.

  1. Em StatsWidget.kt, adicione este código à função 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)
          }
    }
    
    

O código anterior faz referência a dois recursos de string: um para fala e outro para texto. Confira as sugestões de TTS na seção Recomendação de estilo da conversão de texto em voz do vídeo sobre nossos widgets. O exemplo também faz referência a setTts, uma nova função que transmite as informações de TTS para a instância do widget.

  1. Adicione a nova função setTts a StatsWidget.kt usando este código:
    // 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)
    }
    

Para concluir a lógica de TTS, defina informações de TTS quando o banco de dados sobre exercícios não retornar dados para o tipo de treino solicitado.

  1. Atualize a função setNoActivityDataWidget() no StatsWidget.kt com este código:
    // 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. Testar a Ação no app

Durante o desenvolvimento, use o plug-in do Google Assistente para conferir as Ações no app em um dispositivo de teste. É possível ajustar os parâmetros de intent de uma Ação no app para testar como ela processa as várias formas possíveis de um usuário pedir algo ao Assistente.

Criar uma prévia

Para testar a Ação no app com o plug-in:

  1. Acesse Tools > Google Assistant > App Actions Test Tool. Talvez seja necessário fazer login no Android Studio usando sua Conta do Google.
  2. Clique em Create preview. Confira e aceite as políticas e os Termos de Serviço das Ações no app, se for o caso.

Testar um tipo de exercício esperado

Para redirecionar a um widget que mostra informações sobre a última corrida concluída no app, siga estas etapas na ferramenta de teste:

  1. Primeiro, a ferramenta pede para selecionar e configurar uma BII. Escolha actions.intent.GET_EXERCISE_OBSERVATION.
  2. Na caixa exerciseObservation mude o nome padrão do exercício de climbing para run.
  3. Clique em Run App Action.

Uma tela com um widget retornado usando o plug-in do Google Assistente.

Testar um tipo de exercício inesperado

Para testar um tipo de exercício inesperado na ferramenta:

  1. Na caixa exerciseObservation mude o valor name de Run para Climbing.
  2. Clique em Run App Action.

O Google Assistente vai retornar um widget com a informação "Nenhuma atividade encontrada".

Uma tela com um widget sem informações de exercícios retornadas usando o plug-in do Google Assistente.

Testar a intent substituta

As consultas que acionam a intent substituta precisam retornar um widget com informações sobre a última atividade registrada de qualquer tipo de exercício.

Para testar a intent substituta:

  1. Na caixa exerciseObservation, exclua o objeto aboutExercise.
  2. Clique em Run App Action.

O Google Assistente vai retornar um widget com informações sobre o último exercício concluído.

Uma tela com um widget que mostra a última atividade registrada usando o plug-in do Google Assistente.

7. Próximas etapas

Parabéns!

Agora você sabe como atender às consultas dos usuários com um widget do Android usando o Google Assistente.

O que aprendemos

Neste codelab, você aprendeu a:

  • Adicionar um widget de app a uma BII.
  • Modificar um widget para acessar parâmetros de extras do Android.

O que vem a seguir

Você pode melhorar ainda mais o app fitness. Para fazer referência ao projeto finalizado, consulte o repositório principal no GitHub (link em inglês).

Confira algumas sugestões para integrar esse aplicativo usando as Ações no app:

Para continuar sua jornada na plataforma Actions on Google, confira estes recursos:

Siga nosso perfil @ActionsOnGoogle no Twitter, fique por dentro das novidades e envie um tweet com #appactions para mostrar o que você criou.

Pesquisa de feedback

Para terminar, responda a esta pesquisa e dê seu feedback sobre o que achou do codelab.