Configura Acciones en apps para implementar el Asistente de Google con atajos dinámicos

1. Descripción general

En el codelab anterior, configuraste atajos estáticos para implementar intents integrados (BII) de uso general en una app de ejemplo. Los desarrolladores de Android utilizan Acciones en apps para habilitar las funciones de una app con el Asistente de Google.

Los atajos estáticos se empaquetan en una app y solo se pueden actualizar mediante el lanzamiento de versiones nuevas de esa app. La habilitación de una función de voz para elementos dinámicos en una app, como el contenido generado por usuarios, se logra mediante atajos dinámicos. Las apps envían atajos dinámicos después de que los usuarios realizan acciones relevantes, como crear una nota nueva en una app de control de tareas. Con Acciones en apps, puedes vincular esos atajos a un BII a fin de que se habiliten para utilizarse con comandos por voz, lo que permite que los usuarios accedan a su contenido desde el Asistente pronunciando frases como "Hey Google, abre mi lista de compra de comestibles en App de Ejemplo".

Tres pantallas progresivas en las que se muestra cómo el Asistente de Google inicia un atajo dinámico.

Figura 1: Tres pantallas progresivas en las que se muestra cómo el Asistente de Google inicia un atajo dinámico a una tarea creada por el usuario

Qué harás

En este codelab, habilitarás atajos dinámicos que permitirán utilizar comandos por voz en una app de ejemplo de listas de tareas pendientes para Android. De esta manera, los usuarios podrán pedirle al Asistente que abra elementos de una lista de tareas que hayan creado en la app. Para ello, tendrás que utilizar patrones de la arquitectura de Android, específicamente los de repositorio, localizador de servicios y ViewModel.

Requisitos previos

Este codelab se basa en los conceptos de Acciones en apps que se trataron en el codelab anterior, en particular los BII y los atajos estáticos. Si es la primera vez que utilizas Acciones en apps, te recomendamos que completes ese codelab antes de continuar.

Además, asegúrate de que tu entorno de desarrollo tenga la siguiente configuración antes de continuar:

  • Una terminal para ejecutar comandos de shell con Git instalado
  • La versión estable más reciente de Android Studio
  • Un dispositivo Android físico o virtual con acceso a Internet
  • Una Cuenta de Google para acceder a Android Studio, la app de Google y la app del Asistente de Google

2. Comprende el funcionamiento

Para habilitar un atajo dinámico que permita el acceso con comandos por voz, se deben seguir estos pasos:

Vinculación de atajos

Para que un atajo dinámico esté disponible en el Asistente, es necesario vincularlo a un BII relevante. Cuando se activa un BII que incluye un atajo, el Asistente busca coincidencias los parámetros de la solicitud del usuario y las palabras clave definidas en el atajo vinculado. Por ejemplo:

  • Un atajo vinculado al BII GET_THING podría permitir que los usuarios soliciten contenido específico en la app directamente desde el Asistente. * "Hey Google, abre mi lista de compra de comestibles en App de Ejemplo".
  • Un atajo vinculado al BII ORDER_MENU_ITEM podría permitir que los usuarios repitan pedidos anteriores. * "Hey Google, pide mi comida habitual de App de Ejemplo".

Consulta la referencia de intents integrados para obtener una lista completa de BII por categoría.

Integración de atajos en el Asistente

Después de vincular tus atajos a un BII, el siguiente paso es agregar a tu proyecto la biblioteca de Google Shortcuts Integration con el fin de habilitar el Asistente para que integre esos atajos. Con esta biblioteca configurada, el Asistente detectará los atajos que envíe tu app. De esta manera, los usuarios podrán ejecutar esos atajos en el Asistente utilizando la frase de activación correspondiente a cada uno de ellos.

3. Prepara tu entorno de desarrollo

En este codelab, se utiliza una app de ejemplo de listas de tareas pendientes compilada para Android. Con esa app, los usuarios pueden agregar elementos a listas, buscar elementos en las listas de tareas por categoría y filtrar tareas según el estado de finalización. Completa esta sección para descargar y preparar la app de ejemplo.

Descarga los archivos fundamentales

Ejecuta el siguiente comando para clonar el repositorio de GitHub de la app de ejemplo:

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

Una vez que hayas clonado el repositorio, sigue estos pasos para abrirlo en Android Studio:

  1. En el diálogo Welcome to Android Studio, haz clic en Import project.
  2. Selecciona la carpeta donde clonaste el repositorio.

También puedes ver una versión de la app de ejemplo que representa el codelab completado. Solo tienes que clonar la rama codelab-complete del repositorio de GitHub de esa app:

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

Actualiza el ID de aplicación para Android

La actualización del ID de aplicación identifica de forma exclusiva la app en tu dispositivo de prueba y evita que se muestre el error "Duplicate package name" si la app se sube a Play Console. Para actualizar el ID de aplicación, abre app/build.gradle:

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

Reemplaza "MYUNIQUENAME" en el campo applicationId por algo que sea único para ti.

Agrega las dependencias de la API de atajos

Agrega las siguientes bibliotecas de Jetpack al archivo del 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'
   ...
}

Prueba la app en tu dispositivo

Antes de realizar más cambios en la app, es conveniente ejecutarla en tu emulador para tener una idea de lo que puede hacer la app de ejemplo. Para ello, sigue estos pasos:

  1. En Android Studio, selecciona Run > Run app, o haz clic en Run Ícono del botón de ejecución de la app en Android Studio en la barra de herramientas.
  2. En el diálogo Select Deployment Target, selecciona un dispositivo y haz clic en OK. La versión de SO recomendada es Android 10 (nivel de API 30) o una posterior, aunque las Acciones en apps también funcionan en dispositivos que tengan hasta la versión Android 5 (nivel de API 21).
  3. Mantén presionado el botón de inicio para configurar el Asistente y verificar que funcione. Si aún no lo hiciste, tendrás que acceder al Asistente en tu dispositivo.

Para obtener más información sobre los dispositivos virtuales de Android, consulta Cómo crear y administrar dispositivos virtuales.

Explora la app brevemente para ver lo que puede hacer. Si presionas el ícono de signo más, puedes crear una tarea nueva. Además, los elementos de menú en la esquina superior derecha te permiten buscar y filtrar tareas por estado de finalización.

4. Crea una clase de repositorio de atajos

En la app de ejemplo, hay varias clases que llamarán a la API ShortcutManagerCompat para enviar y administrar atajos dinámicos. A fin de reducir la redundancia en el código, tendrás que implementar un repositorio para habilitar las clases de tu proyecto de forma tal que sea posible administrar los atajos dinámicos fácilmente.

El patrón de diseño del repositorio proporciona una API bien definida para administrar atajos. La ventaja de un repositorio es que abstrae de forma uniforme los detalles de la API subyacente para presentarlos en una API sencilla. Sigue estos pasos para implementar el repositorio:

  1. Crea una clase ShortcutsRepository para abstraer la API ShortcutManagerCompat.
  2. Agrega métodos de ShortcutsRepository al localizador de servicios de la app.
  3. Registra el servicio ShortcutRepository en la aplicación principal.

Crea el repositorio

En el paquete com.example.android.architecture.blueprints.todoapp.data.source, crea una nueva clase en Kotlin llamada ShortcutsRepository (puedes encontrar el paquete organizado en la carpeta app/src/main/java). Utilizarás esta clase para implementar una interfaz que proporcione un conjunto reducido de métodos relacionados con el caso de uso de nuestro codelab.

Ventana de Android Studio que muestra la ubicación de la clase ShortcutsRepository.

Figura 2: Ventana Project Files de Android Studio que muestra la ubicación de la clase ShortcutsRepository

Pega el siguiente código en la nueva clase:

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

A continuación actualiza el método pushShortcut para llamar a la API ShortcutManagerCompat. Utiliza el siguiente código para actualizar la clase ShortcutsRepository:

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

En la muestra de código anterior, pasamos appContext a la API. Esta es una propiedad de clase que contiene un Contexto de la aplicación. Es importante usar un Contexto de la aplicación (en lugar de un Contexto de la actividad) para evitar fugas de memoria, ya que la retención del contexto podría durar más tiempo que el ciclo de vida de la actividad del host.

Además, la API requiere que pasemos un objeto ShortcutInfoCompat para el objeto Task. En la muestra de código anterior, conseguimos esto llamando al método privado createShortcutCompat, que actualizaremos para crear y mostrar un objeto ShortcutInfoCompat. Para ello, tienes que actualizar el stub de createShortcutCompat con el siguiente código:

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

Los stubs restantes de funciones en esta clase permiten actualizar y borrar atajos dinámicos. Para habilitar esas funciones, actualízalas con el siguiente 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 })
}

Agrega la clase al localizador de servicios

Con la clase ShortcutsRepository creada, el siguiente paso es hacer que las instancias de los objetos de esta clase estén disponibles para el resto de la app, que administra las dependencias de clase mediante la implementación del patrón del localizador de servicios. Abre la clase del localizador de servicios con el navegador de clases en Android Studio. Para ello, ve a Navigate > Class y escribe "ServiceLocator". Haz clic en el archivo Kotlin resultante para abrirlo en tu IDE.

En la parte superior de ServiceLocator.kt, pega el siguiente código para importar los paquetes ShortcutsRepository y 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 agregar los métodos y miembros del servicio ShortcutRepository, pega el siguiente código en el cuerpo 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)
       }
   }
 }

Registra el servicio de atajos

El último paso es registrar tu nuevo servicio ShortcutsRepository en la aplicación. En Android Studio, abre TodoApplication.kt y copia el siguiente código cerca de la parte superior del archivo:

TodoApplication.kt

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

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

A continuación agrega el siguiente código al cuerpo de la clase para registrar el servicio:

TodoApplication.kt

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

   //...

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

   //...
}

Compila la app y asegúrate de que siga ejecutándose.

5. Envía un atajo nuevo

Ahora que creaste el servicio de atajos, tienes todo listo para comenzar a enviar atajos. Dado que los usuarios generan contenido (elementos de tareas) en esta app y esperan poder consultarlo más adelante, habilitaremos el acceso por voz a ese contenido. Para ello, enviaremos un atajo dinámico que esté vinculado al BII GET_THING cada vez que un usuario cree una tarea nueva. De esta manera, el Asistente puede llevar a los usuarios directamente al elemento de tarea solicitado cuando activan el BII pronunciando frases como "Hey Google, abre mi lista de compra de comestibles en App de Ejemplo".

Para habilitar esta funcionalidad en la app de ejemplo, sigue estos pasos:

  1. Importa el servicio ShortcutsRepository en la clase AddEditTaskViewModel, que administra los objetos de la lista de tareas.
  2. Envía un atajo dinámico cuando el usuario cree una tarea nueva.

Importa ShortcutsRepository

Primero necesitamos configurar el servicio ShortcutsRepository de forma tal que esté disponible para AddEditTaskViewModel. Para ello, importa el servicio a ViewModelFactory, la clase de fábrica que usa la app para crear instancias de objetos ViewModel, lo que incluye AddEditTaskViewModel.

Para abrir el navegador de clases en Android Studio, ve a Navigate > Class y escribe "ViewModelFactory". Haz clic en el archivo Kotlin resultante para abrirlo en tu IDE.

En la parte superior de ViewModelFactory.kt, pega el siguiente código para importar los paquetes ShortcutsRepository y SuppressLint:

ViewModelFactory.kt

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

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

A continuación reemplaza el cuerpo de ViewModelFactory por el siguiente 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
}

Para finalizar los cambios de ViewModelFactory, sube una capa y pasa ShortcutsRepository al constructor de la fábrica. Abre el navegador de archivos de Android Studio. Para ello, ve a Navigate > File y escribe "FragmentExt.kt". Haz clic en el archivo Kotlin resultante, ubicado en el paquete util, para abrirlo en tu IDE.

Reemplaza el cuerpo de FragmentExt.kt por el siguiente 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)
}

Envía un atajo

Con la clase de abstracción ShortcutsRepository disponible para las clases ViewModel de la app de ejemplo, puedes actualizar AddEditTaskViewModel, la clase ViewModel que se encarga de crear notas, para enviar un atajo dinámico cada vez que un usuario crea una nota nueva.

En Android Studio, abre el navegador de clases y escribe "AddEditTaskViewModel". Haz clic en el archivo Kotlin resultante para abrirlo en tu IDE.

En primer lugar, agrega el paquete ShortcutsRepository a esa clase con la siguiente sentencia de importación:

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

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

A continuación actualiza el constructor de clase con el siguiente código para agregar la propiedad de clase shortcutsRepository:

AddEditTaskViewModel.kt

//...

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

    //...

Con la clase ShortcutsRepository agregada, crea una nueva función, pushShortcut(), para llamar a esa clase. Pega la siguiente función privada en el cuerpo de AddEditTaskViewModel:

AddEditTaskViewModel.kt

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

Por último, envía un nuevo atajo dinámico cada vez que se cree una tarea. Reemplaza el contenido de la función saveTask() por el siguiente 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)
    }
}

Prueba el código

Ya está todo listo para probar el código. En este paso, tienes que enviar un atajo dinámico habilitado por voz y también inspeccionarlo con la app del Asistente de Google.

Crea una vista previa

Crear una vista previa con el complemento Google Assistant permite que, en tu dispositivo de prueba, aparezcan atajos dinámicos en el Asistente.

Instala el complemento de prueba

Si aún no tienes el complemento Google Assistant, instálalo siguiendo estos pasos en Android Studio:

  1. Ve a **File > Settings (Android Studio > Preferences en MacOS).
  2. En la sección Plugins, accede a Marketplace y busca "Google Assistant".
  3. Instala la herramienta y reinicia Android Studio.

Crea la vista previa

Crea una vista previa siguiendo estos pasos en Android Studio:

  1. Haz clic en Tools > Google Assistant > "App Actions Test Tool".
  2. En el cuadro App name, define un nombre como "Lista de tareas pendientes".
  3. Haz clic en Create Preview. Si se te solicita, revisa y acepta las políticas y las Condiciones del Servicio de Acciones en apps.

Panel de creación de una vista previa en App Actions Test Tool.

Figura 3: Panel de creación de una vista previa en App Actions Test Tool

Durante las pruebas, los atajos dinámicos que envíes aparecerán en el Asistente organizados por nombre según lo que hayas indicado en App name para la vista previa.

Envía e inspecciona un atajo

Reinicia la app de ejemplo en tu dispositivo de prueba y sigue estos pasos:

  1. Crea una tarea nueva con el título "Iniciar codelab".
  2. Abre la app del Asistente de Google y di o escribe "Mis atajos".
  3. Presiona la pestaña Explorar. Deberías ver el atajo de ejemplo.
  4. Presiona el atajo para invocarlo. Deberías ver que la app se inicia con el nombre del atajo prepropagado en el cuadro de filtro, lo que facilita la búsqueda del elemento de tarea solicitado.

6. Actualiza y borra un atajo (opcional)

Además de enviar nuevos atajos dinámicos en el tiempo de ejecución, tu app puede actualizarlos para reflejar el estado actual de tu contenido y tus preferencias de usuario. Es conveniente que actualices los atajos existentes cada vez que un usuario modifique el elemento de destino; por ejemplo, al cambiarle el nombre a una tarea en nuestra app de ejemplo. Para evitar que el usuario vea atajos rotos, también tienes que borrar los atajos correspondientes cuando se haya quitado un recurso de destino.

Actualiza un atajo

Modifica AddEditTaskViewModel para actualizar un atajo dinámico cada vez que un usuario cambie los detalles de un elemento de tarea. En primer lugar, actualiza el cuerpo de la clase con el siguiente código para agregar una función de actualización que utiliza nuestra clase de repositorio:

AddEditTaskViewModel.kt

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

A continuación modifica la función saveTask() para llamar a nuestro nuevo método cada vez que se actualice una tarea.

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

Para probar el código, reinicia la app y sigue estos pasos:

  1. Cambia el nombre del título de tu elemento de tarea a "Finalizar codelab".
  2. Di "Hey Google, mis atajos" para abrir el Asistente de Google.
  3. Presiona la pestaña Explorar. Deberías ver una etiqueta corta actualizada para tu atajo de prueba.

Quita un atajo

Los atajos de nuestra app de ejemplo se deben quitar cada vez que un usuario borra una tarea. En la app de ejemplo, la lógica de eliminación de tareas se basa en la clase TaskDetailViewModel. Antes de actualizar esta clase, debemos actualizar ViewModelFactory nuevamente para pasar shortcutsRepository a TaskDetailViewModel.

Abre ViewModelFactory y reemplaza el contenido de su método de constructor con el siguiente 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
}

A continuación abre TaskDetailViewModel. Importa el módulo ShortcutsRepository y declara una variable de instancia para ese módulo con el siguiente 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, modifica la función deleteTask() para que llame a shortcutsRepository y quite el atajo según su ID cada vez que se borre una tarea con un taskId correspondiente:

TaskDetailViewModel.kt

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

Para probar el código, reinicia la app y sigue estos pasos:

  1. Borra tu tarea de prueba.
  2. Cambia el nombre del título de tu elemento de tarea a "Finalizar codelab".
  3. Di "Hey Google, mis atajos" para abrir el Asistente de Google.
  4. Presiona la pestaña Explorar. Verifica que ya no aparezca tu atajo de prueba.

7. Próximos pasos

¡Felicitaciones! Gracias a tu trabajo, los usuarios de nuestra app de ejemplo pueden pedirle al Asistente cosas como "Hey Google, abre mi lista de compra de comestibles en App de Ejemplo" para regresar fácilmente a las notas que crean en esa app. Los atajos fomentan la participación de los usuarios, ya que facilitan la repetición de acciones frecuentes en tu app.

Temas abordados

En este codelab, aprendiste a hacer lo siguiente:

  • Identificar casos de uso para enviar atajos dinámicos en una app
  • Reducir la complejidad del código mediante patrones de diseño de repositorios, inserción de dependencias y localizadores de servicios
  • Enviar atajos dinámicos habilitados por voz al contenido de la app generado por los usuarios
  • Actualizar y quitar atajos existentes

¿Qué sigue?

De ahora en adelante, puedes definir mejor tu app de listas de tareas. Para consultar el proyecto finalizado, accede al repositorio de la rama –codelab-complete en GitHub.

A continuación, presentamos algunas sugerencias para adquirir más conocimientos sobre cómo configurar esta app con Acciones en apps:

Para continuar tu recorrido de Actions on Google, explora estos recursos:

Síguenos en Twitter (@ActionsOnGoogle) para mantenerte al tanto de nuestros anuncios más recientes y utiliza el hashtag #appActions para compartir tus compilaciones.

Encuesta de comentarios

Por último, completa esta encuesta para enviarnos comentarios sobre tu experiencia con este codelab.