Integrar atalhos dinâmicos ao Google Assistente com as Ações no app

1. Visão geral

No codelab anterior, você usou atalhos estáticos para implementar intents integradas (BIIs, na sigla em inglês) usadas com frequência em um app de exemplo. Os desenvolvedores Android utilizam as Ações no app para integrar uma funcionalidade do app ao Google Assistente.

Os atalhos estáticos são incluídos em um app e só podem ser atualizados com o lançamento de novas versões dele. A ativação dos comandos de voz para elementos dinâmicos em um app, como conteúdo gerado pelo usuário, é realizada com atalhos dinâmicos. Os apps enviam atalhos dinâmicos depois que as pessoas realizam ações relevantes, como criar uma observação em um aplicativo de rastreamento de tarefas. Com as Ações no app, você ativa comandos de voz nesses atalhos vinculando cada um deles a uma BII. Assim, os usuários podem acessar o conteúdo do Google Assistente dizendo algo como Ok Google, abra minha lista de compras no AppDeExemplo.

Três telas progressivas que mostram o Google Assistente iniciando um atalho dinâmico.

Figura 1. Três telas progressivas que mostram uma tarefa criada pelo usuário e o Google Assistente iniciando um atalho dinâmico para a tarefa.

O que você vai criar

Neste codelab, você vai ativar comandos de voz nos atalhos dinâmicos em um app de lista de tarefas para Android, permitindo que os usuários peçam ao Google Assistente para abrir os itens da lista criados no aplicativo. Basta usar os padrões de arquitetura do Android, especificamente aqueles do repositório, do localizador de serviços e da classe ViewModel.

Pré-requisitos

Este codelab usa os conceitos das Ações no app estudados no codelab anterior, principalmente sobre BIIs e atalhos estáticos. Se você não conhece bem as Ações no app, recomendamos concluir aquele codelab antes de continuar.

Além disso, verifique se o ambiente de desenvolvimento tem a seguinte configuração:

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

2. Como funciona

Para ativar comandos de voz em um atalho dinâmico:

  • Vincule um atalho dinâmico a uma BII qualificada.
  • Permita ao Assistente ingerir os atalhos adicionando a biblioteca Google Shortcuts Integration.
  • Envie um atalho sempre que alguém concluir uma tarefa relevante no app.

Vincular atalhos

O Google Assistente só consegue acessar atalhos dinâmicos vinculados a BIIs relevantes. Quando uma BII com um atalho é acionada, o Google Assistente associa os parâmetros no pedido do usuário às palavras-chave definidas no atalho vinculado. Exemplo:

  • Um atalho vinculado à BII GET_THING permite que os usuários solicitem conteúdo no app diretamente do Assistente. Por exemplo, Ok Google, abra minha lista de compras no AppDeExemplo.
  • Um atalho vinculado à BII ORDER_MENU_ITEM permite que os usuários façam o mesmo pedido novamente. Por exemplo, Ok Google, peça o de sempre no AppDeExemplo.

Acesse a referência de intents integradas para consultar a lista completa e categorizada de BIIs.

Criar atalhos para o Google Assistente

Depois de vincular os atalhos a uma BII, a próxima etapa é permitir ao Google Assistente ingerir esses atalhos adicionando a biblioteca Google Shortcuts Integration ao seu projeto. Com ela, o Google Assistente vai reconhecer cada atalho enviado pelo app. Assim, as pessoas poderão iniciar esses atalhos usando a frase de acionamento deles.

3. Preparar seu ambiente de desenvolvimento

Este codelab usa um app de lista de tarefas para Android como exemplo. Com esse aplicativo, os usuários adicionam itens a listas, fazem pesquisas por categoria e filtram tarefas por status de conclusão. Faça o download e prepare o app de exemplo concluindo esta seção.

Fazer o download dos arquivos de base

Execute o comando a seguir para clonar o repositório do GitHub do app de exemplo:

git clone https://github.com/actions-on-google/app-actions-dynamic-shortcuts.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. Selecione a pasta em que você clonou o repositório.

Também é possível conferir uma versão do app de exemplo que representa o codelab pronto clonando a ramificação de codelab-complete do repositório do GitHub:

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

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.

Adicionar dependências da API Shortcuts

Inclua as seguintes bibliotecas do Jetpack no arquivo do recurso 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'
   ...
}

Testar o app no seu dispositivo

Antes de fazer outras mudanças, teste o app de exemplo para saber o que ele oferece. Para executar o aplicativo no seu emulador:

  1. No Android Studio, selecione Run > Run app ou clique em RunÍcone do app de corrida no Android Studio na barra de ferramentas.
  2. Na caixa de diálogo Select Deployment Target, escolha um dispositivo e clique em OK. O SO recomendado é o Android 10 (nível da API – 30) ou uma versão mais recente. No entanto, as Ações no app funcionam a partir do Android 5 (nível da API – 21).
  3. 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. Toque no ícone de adição para criar uma tarefa ou utilize os itens de menu no canto superior direito para pesquisar e filtrar as tarefas por status de conclusão.

4. Criar uma classe de repositório de atalhos

Várias classes do nosso app de exemplo vão chamar a API ShortcutManagerCompat para enviar e gerenciar atalhos dinâmicos. Para reduzir a redundância de código, você vai implementar um repositório, permitindo que as classes do projeto gerenciem com facilidade os atalhos dinâmicos.

O padrão de design do repositório oferece uma API limpa para administrar atalhos. A vantagem de um repositório é que os detalhes da API são extraídos de maneira uniforme por trás de uma API mínima. Siga as etapas abaixo para implementar o repositório:

  1. Crie uma classe ShortcutsRepository para extrair a API ShortcutManagerCompat.
  2. Adicione métodos de ShortcutsRepository ao localizador de serviços do app.
  3. Registre o serviço ShortcutRepository no aplicativo principal.

Criar o repositório

Crie uma classe Kotlin chamada ShortcutsRepository no pacote com.example.android.architecture.blueprints.todoapp.data.source. Esse pacote está organizado na pasta app/src/main/java. Você vai usar essa classe para implementar uma interface que oferece um conjunto mínimo de métodos para nosso caso de uso do codelab.

Janela do Android Studio com o local da classe ShortcutsRepository.

Figura 2. Janela "Project Files" do Android Studio com o local da classe ShortcutsRepository.

Cole o seguinte código na nova classe:

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>) {
       //...
   }
}

Em seguida, atualize o método pushShortcut para chamar a API ShortcutManagerCompat. Atualize a classe ShortcutsRepository com o seguinte código:

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

No exemplo de código anterior, transmitimos appContext para a API. Essa é uma propriedade de classe com um contexto de aplicativo. É importante usar um contexto de aplicativo (em vez de um contexto de atividade) para evitar vazamentos de memória, já que o contexto pode ser retido por mais tempo que o ciclo de vida da atividade do host.

Além disso, a API exige que um objeto ShortcutInfoCompat seja transmitido para o objeto da tarefa. No exemplo de código anterior, fazemos isso chamando o método particular createShortcutCompat, que vamos atualizar para criar e retornar um objeto ShortcutInfoCompat. Basta usar o seguinte código no stub 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()
}

Os stubs de função restantes nessa classe cuidam da atualização e da exclusão de atalhos dinâmicos. Para ativar essas funções, atualize com o seguinte código:

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

Adicionar uma classe ao localizador de serviços

Com a classe ShortcutsRepository criada, a próxima etapa é disponibilizar objetos instanciados dessa classe para o restante do app. Esse aplicativo gerencia as dependências de classe implementando o padrão do localizador de serviços. Abra a classe do localizador usando o navegador de classes no Android Studio: Navigate > Class e digite "ServiceLocator". Clique no arquivo Kotlin para abrir no seu ambiente de desenvolvimento integrado.

Na parte de cima do ServiceLocator.kt, cole o seguinte código para importar os pacotes ShortcutsRepository e 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

Para adicionar os membros e métodos do serviço ShortcutRepository, cole o seguinte código no corpo de ServiceLocator.kt:

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

Registrar o serviço de atalho

A etapa final é registrar o novo serviço ShortcutsRepository no aplicativo. No Android Studio, abra TodoApplication.kt e copie o código abaixo na parte de cima do arquivo:

TodoApplication.kt

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

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

Em seguida, registre o serviço adicionando o seguinte código ao corpo da classe:

TodoApplication.kt

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

   //...

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

   //...
}

Compile o app e verifique se ele continua em execução.

5. Enviar um novo atalho

Depois de criar o serviço de atalho, você poderá começar a enviar atalhos. Como os usuários geram conteúdo (itens de tarefa) nesse app e esperam retornar depois, permitimos o acesso por comandos de voz ao conteúdo enviando um atalho dinâmico vinculado à BII GET_THING sempre que alguém cria uma tarefa. Isso permite que o Google Assistente encaminhe os usuários ao item de tarefa quando acionarem a BII. Basta pedir algo como Ok Google, abra minha lista de compras no AppDeExemplo.

Para ativar essa funcionalidade no app de exemplo:

  1. Importe o serviço ShortcutsRepository para a classe AddEditTaskViewModel, responsável por gerenciar objetos da lista de tarefas.
  2. Envie um atalho dinâmico quando o usuário criar uma tarefa.

Importar ShortcutsRepository

Primeiro, temos que disponibilizar o serviço ShortcutsRepository para a classe AddEditTaskViewModel. Basta importar o serviço para ViewModelFactory, a classe de fábrica que o app usa para instanciar objetos ViewModel, incluindo AddEditTaskViewModel.

No Android Studio, acesse Navigate > Class e digite "ViewModelFactory" para abrir o navegador de classes. Clique no arquivo Kotlin para abrir no seu ambiente de desenvolvimento integrado.

Na parte de cima do ViewModelFactory.kt, cole o seguinte código para importar os pacotes ShortcutsRepository e SuppressLint:

ViewModelFactory.kt

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

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

Em seguida, substitua o corpo de ViewModelFactory pelo seguinte código:

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
}

Termine de fazer as mudanças em ViewModelFactory acessando uma camada superior e transmita ShortcutsRepository ao construtor de fábrica. No Android Studio, acesse Navigate > File e digite "FragmentExt.kt" para abrir o navegador de arquivos. Clique no arquivo Kotlin localizado no pacote util para abrir no ambiente de desenvolvimento integrado.

Substitua o corpo de FragmentExt.kt pelo seguinte código:

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

Enviar um atalho

Com a classe de extração ShortcutsRepository disponível para as classes ViewModel do app de exemplo, você atualiza AddEditTaskViewModel (a classe ViewModel responsável por criar notas) para enviar um atalho dinâmico sempre que alguém criar uma observação.

No Android Studio, abra o navegador de classes e digite "AddEditTaskViewModel". Clique no arquivo Kotlin para abrir no seu ambiente de desenvolvimento integrado.

Primeiro, adicione o pacote ShortcutsRepository a essa classe com a seguinte instrução de importação:

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

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

Depois, adicione a propriedade da classe shortcutsRepository atualizando o construtor de classe com o seguinte código:

AddEditTaskViewModel.kt

//...

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

    //...

Com a classe ShortcutsRepository adicionada, crie outra função (pushShortcut()) para chamar essa classe. Cole a seguinte função particular no corpo de AddEditTaskViewModel:

AddEditTaskViewModel.kt

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

Envie um novo atalho dinâmico sempre que uma tarefa for criada. Substitua o conteúdo da função saveTask() pelo seguinte código:

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

Testar o código

Chegou a hora de testar nosso código. Nesta etapa, você envia um atalho dinâmico ativado por comando de voz e faz a inspeção dele usando o app Google Assistente.

Criar uma prévia

Crie uma prévia usando o plug-in do Google Assistente e faça com que seus atalhos dinâmicos apareçam no Assistente no dispositivo de teste.

Instalar o plug-in de teste

Siga estas etapas para instalar o plug-in do Google Assistente no Android Studio:

  1. Acesse **File > Settings (Android Studio > Preferences no MacOS).
  2. Na seção Plugins, acesse Marketplace e pesquise "Google Assistant".
  3. Instale a ferramenta e reinicie o Android Studio.

Criar a prévia

Para criar uma prévia, siga estas etapas no Android Studio:

  1. Clique em Tools > Google Assistant > App Actions Test Tool.
  2. Na caixa App name, insira o nome, como "Lista de tarefas".
  3. 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.

Painel de criação da prévia na App Actions Test Tool

Figura 3. Painel de criação da prévia na App Actions Test Tool.

Durante os testes, os atalhos dinâmicos enviados ao Google Assistente vão aparecer nele, organizados de acordo com o nome do app escolhido para a prévia.

Enviar e inspecionar um atalho

Reinicie o app de exemplo no dispositivo de teste e siga estas etapas:

  1. Crie uma tarefa com o título "Iniciar o codelab".
  2. Abra o app Google Assistente e fale ou digite: "Meus atalhos".
  3. Toque na guia Explore. Você vai encontrar o atalho de exemplo.
  4. Invoque o atalho tocando nele. O app vai abrir com o nome do atalho pré-preenchido na caixa de filtro, facilitando a localização do item de tarefa pedido.

6. (Opcional) Atualizar e excluir um atalho

Além de enviar novos atalhos dinâmicos no ambiente de execução, o app pode atualizá-los de acordo com o conteúdo e as preferências do usuário. É recomendável atualizar os atalhos sempre que alguém modificar o item de destino (por exemplo, renomear uma tarefa no nosso app de exemplo). Além disso, sempre exclua um atalho correspondente quando o recurso de destino for removido para não mostrar atalhos corrompidos ao usuário.

Atualizar um atalho

Modifique AddEditTaskViewModel para atualizar um atalho dinâmico sempre que alguém mudar os detalhes de um item de tarefa. Primeiro, atualize o corpo da classe com o código abaixo para adicionar uma função de atualização que use nossa classe de repositório:

AddEditTaskViewModel.kt

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

Depois, modifique a função saveTask() para chamar nosso novo método sempre que uma tarefa for atualizada.

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

Reinicie o app e siga as etapas abaixo para testar o código:

  1. Renomeie o título do item de tarefa como "Concluir o codelab".
  2. Abra o Google Assistente dizendo "Ok Google, meus atalhos".
  3. Toque na guia Explore. Você vai notar um pequeno rótulo atualizado para o atalho de teste.

Remover um atalho

Os atalhos dos apps de exemplo precisam ser removidos sempre que alguém exclui uma tarefa. No aplicativo de exemplo, a lógica de exclusão de tarefas fica na classe TaskDetailViewModel. Antes de atualizar essa classe, é necessário modificar ViewModelFactory de novo para transmitir shortcutsRepository a TaskDetailViewModel.

Abra ViewModelFactory e substitua o conteúdo do método construtor pelo seguinte código:

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

Depois, abra TaskDetailViewModel. Importe o módulo ShortcutsRepository e declare uma variável de instância usando o seguinte código:

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() {
...
}

Por último, modifique a função deleteTask() para chamar shortcutsRepository e remover um atalho com base no ID sempre que uma tarefa com um taskId correspondente for excluída:

TaskDetailViewModel.kt

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

Para testar o código, reinicie o app e siga estas etapas:

  1. Exclua a tarefa de teste.
  2. Renomeie o título do item de tarefa como "Concluir o codelab".
  3. Abra o Google Assistente dizendo "Ok Google, meus atalhos".
  4. Toque na guia Explore. Confirme se o atalho de teste desapareceu.

7. Próximas etapas

Parabéns! Agora os usuários do nosso app de exemplo conseguem retornar às observações que criaram com o Assistente. Basta dizer: Ok Google, abra minha lista de compras no AppDeExemplo. Os atalhos incentivam mais interações dos usuários, facilitando a repetição das ações mais comuns no seu app.

O que aprendemos

Neste codelab, você aprendeu a:

  • Identificar casos de uso para enviar atalhos dinâmicos em um app.
  • Reduzir a complexidade do código usando padrões de design para repositórios, injeções de dependências e localizadores de serviços.
  • Enviar atalhos dinâmicos ativados por comandos de voz para conteúdo gerado pelo usuário no app.
  • Atualizar e remover atalhos.

O que vem a seguir

Você pode melhorar ainda mais o app de lista de tarefas. Para fazer referência ao projeto finalizado, consulte o repositório da ramificação -codelab-complete 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.