Estendi le scorciatoie dinamiche all'Assistente Google con Azioni app

1. Panoramica

Nel codelab precedente, hai utilizzato scorciatoie statiche per implementare intent integrati (BII) di uso comune in un'app di esempio. Gli sviluppatori Android utilizzano Azioni app per estendere la funzionalità dell'app a Google Assistant.

Le scorciatoie statiche sono raggruppate con un'app e possono essere aggiornate solo rilasciando nuove versioni dell'app. L'attivazione della funzionalità vocale per gli elementi dinamici di un'app, come i contenuti generati dagli utenti, viene eseguita utilizzando le scorciatoie dinamiche. Le app inviano scorciatoie dinamiche dopo che gli utenti eseguono azioni pertinenti, ad esempio la creazione di una nuova nota in un'app di monitoraggio delle attività. Con le Azioni app, puoi attivare queste scorciatoie per la voce associandole a un intent integrato, consentendo agli utenti di accedere ai propri contenuti dall'assistente dicendo ad esempio "Hey Google, apri la mia lista della spesa su EsempioApp".

Tre schermate progressive che mostrano l'Assistente Google che avvia una scorciatoia dinamica.

Figura 1. Tre schermate progressive che mostrano un'attività creata dall'utente e l'Assistente Google che avvia una scorciatoia dinamica all'elemento dell'attività.

Cosa creerai

In questo codelab, attiverai le scorciatoie dinamiche per la voce in un'app Android di esempio per la lista delle cose da fare, consentendo agli utenti di chiedere all'assistente di aprire gli elementi della lista delle attività che creano nell'app. Per farlo, utilizzerai i pattern dell'architettura Android, in particolare i pattern repository, service locator e ViewModel.

Prerequisiti

Questo codelab si basa sui concetti di Azioni app trattati nel codelab precedente, in particolare sugli intent integrati e sulle scorciatoie statiche. Se non hai mai utilizzato le Azioni app, ti consigliamo di completare il codelab prima di continuare.

Inoltre, prima di procedere, assicurati che il tuo ambiente di sviluppo abbia la seguente configurazione:

  • Un terminale per eseguire comandi shell con Git installato.
  • L'ultima release stabile di Android Studio.
  • Un dispositivo Android fisico o virtuale con accesso a internet.
  • Un Account Google collegato ad Android Studio, all'app Google e all'app Assistente Google.

2. Comprendere il funzionamento

L'attivazione di una scorciatoia dinamica per l'accesso vocale prevede i seguenti passaggi:

  • Associazione di una scorciatoia dinamica a un intent integrato idoneo.
  • Consentire all'assistente di importare le scorciatoie aggiungendo la libreria Google Shortcuts Integration.
  • Invio di una scorciatoia ogni volta che un utente completa l'attività in-app pertinente.

Scorciatoie di binding

Affinché una scorciatoia dinamica sia accessibile dall'assistente, deve essere associata a un intent integrato pertinente. Quando viene attivato un intent integrato con una scorciatoia, l'assistente abbina i parametri nella richiesta dell'utente alle parole chiave definite nella scorciatoia associata. Ad esempio:

  • Una scorciatoia associata all'intent integrato GET_THING potrebbe consentire agli utenti di richiedere contenuti in-app specifici direttamente dall'assistente. * "Hey Google, apri la mia lista della spesa su ExampleApp."
  • Una scorciatoia associata all'intent integrato START_EXERCISE potrebbe consentire agli utenti di visualizzare le proprie sessioni di allenamento. * "Hey Google, chiedi a EsempioApp di iniziare il mio allenamento abituale."

Per un elenco completo e classificato delle BII, consulta il riferimento agli intent integrati.

Fornire scorciatoie all'assistente

Dopo aver associato le scorciatoie a un intent integrato, il passaggio successivo consiste nell'attivare l'assistente per l'importazione di queste scorciatoie aggiungendo la libreria Google Shortcuts Integration al tuo progetto. Con questa libreria, l'assistente sarà a conoscenza di ogni scorciatoia inviata dalla tua app, consentendo agli utenti di avviarle utilizzando la frase di attivazione della scorciatoia nell'assistente.

3. Prepara l'ambiente di sviluppo

Questo codelab utilizza un'app di esempio per la lista delle cose da fare creata per Android. Con questa app, gli utenti possono aggiungere elementi agli elenchi, cercare elementi degli elenchi di attività per categoria e filtrare le attività in base allo stato di completamento. Scarica e prepara l'app di esempio completando questa sezione.

Scaricare i file di base

Esegui il comando seguente per clonare il repository GitHub dell'app di esempio:

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

Dopo aver clonato il repository, segui questi passaggi per aprirlo in Android Studio:

  1. Nella finestra di dialogo Benvenuto in Android Studio, fai clic su Importa progetto.
  2. Seleziona la cartella in cui hai clonato il repository.

In alternativa, puoi visualizzare una versione dell'app di esempio che rappresenta il codelab completato clonando il ramo codelab-complete del relativo repository GitHub:

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

Aggiornare l'ID applicazione Android

L'aggiornamento dell'ID applicazione dell'app identifica in modo univoco l'app sul dispositivo di test ed evita l'errore "Nome del pacchetto duplicato" se l'app viene caricata su Play Console. Per aggiornare l'ID applicazione, apri app/build.gradle:

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

Sostituisci "MYUNIQUENAME" nel campo applicationId con un nome univoco.

Aggiungere le dipendenze dell'API Shortcuts

Aggiungi le seguenti librerie Jetpack al file di risorse 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'
   ...
}

Testare l'app sul dispositivo

Prima di apportare altre modifiche all'app, è utile farsi un'idea di cosa può fare l'app di esempio. Per eseguire l'app sull'emulatore, segui questi passaggi:

  1. In Android Studio, seleziona Esegui > Esegui app o fai clic su EseguiIcona Esegui app in Android Studio nella barra degli strumenti.
  2. Nella finestra di dialogo Seleziona target di deployment, seleziona un dispositivo e fai clic su Ok. La versione del sistema operativo consigliata è Android 10 (livello API 30) o versioni successive, anche se le Azioni app funzionano sui dispositivi con Android 5 (livello API 21).
  3. Tieni premuto il pulsante Home per configurare l'assistente e verificare che funzioni. Se non l'hai ancora fatto, dovrai accedere all'assistente sul tuo dispositivo.

Per ulteriori informazioni sui dispositivi virtuali Android, vedi Creare e gestire dispositivi virtuali.

Esplora brevemente l'app per vedere cosa può fare. Toccando l'icona Più viene creato un nuovo elemento attività e le voci di menu in alto a destra consentono di cercare e filtrare gli elementi attività in base allo stato di completamento.

4. Crea una classe di repository di scorciatoie

Diverse classi nella nostra app di esempio chiameranno l'API ShortcutManagerCompat per eseguire il push e gestire le scorciatoie dinamiche. Per ridurre la ridondanza del codice, implementerai un repository per consentire alle classi del progetto di gestire facilmente le scorciatoie dinamiche.

Il pattern di progettazione del repository fornisce un'API pulita per la gestione delle scorciatoie. Il vantaggio di un repository è che i dettagli dell'API sottostante vengono astratti in modo uniforme dietro un'API minima. Implementa il repository seguendo questi passaggi:

  1. Crea una classe ShortcutsRepository per astrarre l'API ShortcutManagerCompat.
  2. Aggiungi i metodi ShortcutsRepository al service locator dell'app.
  3. Registra il servizio ShortcutRepository nell'applicazione principale.

Crea il repository

Crea una nuova classe Kotlin denominata ShortcutsRepository nel pacchetto com.example.android.architecture.blueprints.todoapp.data.source. Puoi trovare questo pacchetto organizzato nella cartella app/src/main/java. Utilizzerai questa classe per implementare un'interfaccia che fornisce un insieme minimo di metodi che coprono il caso d'uso del nostro codelab.

Finestra di Android Studio che mostra la posizione della classe ShortcutsRepository.

Figura 2. Finestra dei file di progetto di Android Studio che mostra la posizione della classe ShortcutsRepository.

Incolla il seguente codice nella nuova 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>) {
       //...
   }
}

Successivamente, aggiorna il metodo pushShortcut per chiamare l'API ShortcutManagerCompat. Aggiorna la classe ShortcutsRepository con il seguente codice:

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

Nell'esempio di codice precedente abbiamo passato appContext all'API. Si tratta di una proprietà della classe che contiene un contesto dell'applicazione. È importante utilizzare un contesto dell'applicazione (anziché un contesto dell'attività) per evitare perdite di memoria, poiché il contesto potrebbe essere conservato più a lungo del ciclo di vita dell'attività host.

Inoltre, l'API richiede di trasmettere un oggetto ShortcutInfoCompat per l'oggetto Task. Nel campione di codice precedente, questo risultato viene ottenuto chiamando il metodo privato createShortcutCompat, che aggiorneremo per creare e restituire un oggetto ShortcutInfoCompat. Per farlo, aggiorna lo stub createShortcutCompat con il seguente codice:

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

Gli stub di funzione rimanenti in questa classe si occupano dell'aggiornamento e dell'eliminazione delle scorciatoie dinamiche. Attiva queste funzioni aggiornandole con il seguente codice:

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

Aggiungere una classe al localizzatore di servizi

Dopo aver creato la classe ShortcutsRepository, il passaggio successivo consiste nel rendere disponibili gli oggetti istanziati di questa classe al resto dell'app. Questa app gestisce le dipendenze delle classi implementando il pattern service locator. Apri la classe del localizzatore di servizi utilizzando il browser delle classi in Android Studio andando su Navigate (Naviga) > Class (Classe) e digitando "ServiceLocator". Fai clic sul file Kotlin risultante per aprirlo nell'IDE.

Nella parte superiore di ServiceLocator.kt, incolla il seguente codice per importare i pacchetti 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

Aggiungi i membri e i metodi del servizio ShortcutRepository incollando il seguente codice nel corpo di 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)
       }
   }
 }

Registrare il servizio scorciatoia

Il passaggio finale consiste nel registrare il nuovo servizio ShortcutsRepository con l'applicazione. In Android Studio, apri TodoApplication.kt e copia il seguente codice nella parte superiore del file:

TodoApplication.kt

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

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

Quindi, registra il servizio aggiungendo il seguente codice al corpo della classe:

TodoApplication.kt

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

   //...

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

   //...
}

Crea l'app e assicurati che continui a essere eseguita.

5. Push di una nuova scorciatoia

Ora che hai creato il servizio di scorciatoie, puoi iniziare a inviare le scorciatoie. Poiché gli utenti generano contenuti (elementi di attività) in questa app e si aspettano di poterli consultare in un secondo momento, attiveremo l'accesso a questi contenuti tramite comandi vocali inviando una scorciatoia dinamica associata all'intent integrato GET_THING ogni volta che un utente crea una nuova attività. In questo modo, l'assistente può indirizzare gli utenti direttamente all'elemento dell'attività richiesto quando attivano l'intent integrato chiedendo ad esempio: "Hey Google, apri la mia lista della spesa su SampleApp".

Per attivare questa funzionalità nell'app di esempio:

  1. Importazione del servizio ShortcutsRepository nella classe AddEditTaskViewModel, responsabile della gestione degli oggetti dell'elenco delle attività.
  2. Invio di una scorciatoia dinamica quando l'utente crea una nuova attività.

Importa ShortcutsRepository

Innanzitutto, dobbiamo rendere disponibile il servizio ShortcutsRepository per AddEditTaskViewModel. Per farlo, importa il servizio in ViewModelFactory, la classe factory che l'app utilizza per creare istanze di oggetti ViewModel, incluso AddEditTaskViewModel.

Apri il browser delle classi in Android Studio andando su Navigate > Class e digitando "ViewModelFactory". Fai clic sul file Kotlin risultante per aprirlo nell'IDE.

Nella parte superiore di ViewModelFactory.kt, incolla il seguente codice per importare i pacchetti 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

Poi, sostituisci il corpo di ViewModelFactory con il seguente codice:

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
}

Completa le modifiche di ViewModelFactory salendo di un livello e passa ShortcutsRepository al costruttore della fabbrica. Apri il browser dei file di Android Studio andando su Navigate > File e digitando "FragmentExt.kt". Fai clic sul file Kotlin risultante che si trova nel pacchetto util per aprirlo nell'IDE.

Sostituisci il corpo di FragmentExt.kt con il seguente codice:

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

Inviare una scorciatoia

Con la classe di astrazione ShortcutsRepository disponibile per le classi ViewModel dell'app di esempio, aggiorni AddEditTaskViewModel, la classe ViewModel responsabile della creazione di note, per eseguire il push di una scorciatoia dinamica ogni volta che un utente crea una nuova nota.

In Android Studio, apri il browser delle classi e digita "AddEditTaskViewModel". Fai clic sul file Kotlin risultante per aprirlo nell'IDE.

Innanzitutto, aggiungi il pacchetto ShortcutsRepository a questa classe con la seguente istruzione di importazione:

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

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

Poi aggiungi la proprietà della classe shortcutsRepository aggiornando il costruttore della classe con il seguente codice:

AddEditTaskViewModel.kt

//...

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

    //...

Con la classe ShortcutsRepository aggiunta, crea una nuova funzione, pushShortcut(), per chiamare questa classe. Incolla la seguente funzione privata nel corpo di AddEditTaskViewModel:

AddEditTaskViewModel.kt

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

Infine, invia una nuova scorciatoia dinamica ogni volta che viene creata un'attività. Sostituisci i contenuti della funzione saveTask() con il seguente codice:

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

Prova il codice

Finalmente possiamo testare il nostro codice. In questo passaggio, trasferisci una scorciatoia dinamica attivata con la voce e ispezionala utilizzando l'app Assistente Google.

Creare un'anteprima

La creazione di un'anteprima utilizzando il plug-in dell'Assistente Google consente alle scorciatoie dinamiche di essere visualizzate nell'assistente sul dispositivo di test.

Installa il plug-in di test

Se non hai ancora il plug-in Google Assistant, installalo seguendo questi passaggi in Android Studio:

  1. Vai a **File > Impostazioni (Android Studio > Preferenze su macOS).
  2. Nella sezione Plug-in, vai a Marketplace e cerca "Assistente Google".
  3. Installa lo strumento e riavvia Android Studio.

Creare l'anteprima

Crea un'anteprima seguendo questi passaggi in Android Studio:

  1. Fai clic su Strumenti > Assistente Google > "Strumento di test delle Azioni app".
  2. Nella casella Nome app, definisci un nome come "Elenco cose da fare".
  3. Fai clic su Crea anteprima. Se richiesto, leggi e accetta le norme e i termini di servizio di Azioni app.

Riquadro di creazione dell&#39;anteprima dello strumento di test delle Azioni app.

Figura 3. Il riquadro di creazione dell'anteprima dello strumento di test delle Azioni app.

Durante il test, le scorciatoie dinamiche che invii all'assistente vengono visualizzate nell'assistente organizzate in base al nome dell'app che hai fornito per l'anteprima.

Eseguire il push e ispezionare una scorciatoia

Riavvia l'app di esempio sul dispositivo di test ed esegui i seguenti passaggi :

  1. Crea una nuova attività con il titolo "Avvia codelab"
  2. Apri l'app Assistente Google e di' o digita "Le mie scorciatoie".
  3. Tocca la scheda Esplora. Dovresti visualizzare la scorciatoia di esempio.
  4. Tocca la scorciatoia per richiamarla. Dovresti vedere l'app avviarsi con il nome della scorciatoia precompilato nella casella del filtro, in modo da trovare facilmente l'elemento dell'attività richiesto.

6. (Facoltativo) Aggiornare ed eliminare una scorciatoia

Oltre a eseguire il push di nuove scorciatoie dinamiche in fase di runtime, la tua app può aggiornarle in modo che riflettano lo stato attuale dei contenuti e delle preferenze degli utenti. È buona norma aggiornare le scorciatoie esistenti ogni volta che un utente modifica l'elemento di destinazione, ad esempio rinominando un'attività nella nostra app di esempio. Devi anche eliminare una scorciatoia corrispondente ogni volta che la risorsa di destinazione viene rimossa per evitare di mostrare agli utenti scorciatoie non funzionanti.

Aggiornare una scorciatoia

Modifica AddEditTaskViewModel per aggiornare una scorciatoia dinamica ogni volta che un utente modifica i dettagli di un elemento dell'attività. Innanzitutto, aggiorna il corpo della classe con il seguente codice per aggiungere una funzione di aggiornamento che utilizzi la classe del repository:

AddEditTaskViewModel.kt

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

Successivamente, modifica la funzione saveTask() per chiamare il nuovo metodo ogni volta che viene aggiornata un'attività esistente.

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

Testa il codice riavviando l'app e seguendo questi passaggi:

  1. Rinomina il titolo dell'elemento dell'attività esistente in "Completa il codelab".
  2. Apri l'Assistente Google dicendo "Hey Google, le mie scorciatoie".
  3. Tocca la scheda Esplora. Dovresti vedere un'etichetta breve aggiornata per la scorciatoia di test.

Rimuovere una scorciatoia

Le nostre scorciatoie delle app di esempio devono essere rimosse ogni volta che un utente elimina un'attività. Nell'app di esempio, la logica di eliminazione delle attività si trova nella classe TaskDetailViewModel. Prima di aggiornare questo corso, dobbiamo aggiornare di nuovo ViewModelFactory per trasferire shortcutsRepository in TaskDetailViewModel.

Apri ViewModelFactory e sostituisci i contenuti del metodo costruttore con il seguente codice:

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

Poi apri TaskDetailViewModel. Importa il modulo ShortcutsRepository e dichiara una variabile di istanza per esso utilizzando il seguente codice:

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

Infine, modifica la funzione deleteTask() per chiamare shortcutsRepository per rimuovere una scorciatoia in base al suo ID ogni volta che viene eliminata un'attività con un taskId corrispondente:

TaskDetailViewModel.kt

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

Per testare il codice, riavvia l'app e segui questi passaggi:

  1. Elimina l'attività di test.
  2. Rinomina il titolo dell'elemento dell'attività esistente in "Completa il codelab".
  3. Apri l'Assistente Google dicendo "Hey Google, le mie scorciatoie".
  4. Tocca la scheda Esplora. Verifica che la scorciatoia di test non venga più visualizzata.

7. Passaggi successivi

Complimenti! Grazie a te, gli utenti della nostra app di esempio possono tornare facilmente alle note che creano chiedendo all'assistente cose come "Hey Google, apri la mia lista della spesa su Esempio". Le scorciatoie incoraggiano un coinvolgimento più profondo degli utenti, in quanto consentono loro di riprodurre facilmente le azioni utilizzate di frequente nella tua app.

Argomenti trattati

In questo codelab hai imparato a:

  • Identificare i casi d'uso per il push di scorciatoie dinamiche in un'app.
  • Ridurre la complessità del codice utilizzando i pattern di progettazione di repository, iniezione delle dipendenze e service locator.
  • Invia scorciatoie dinamiche attivate con la voce ai contenuti dell'app generati dagli utenti.
  • Aggiorna e rimuovi le scorciatoie esistenti.

Passaggi successivi

Da qui, puoi provare a perfezionare ulteriormente l'app Elenco attività. Per fare riferimento al progetto completato, consulta il ramo –codelab-complete del repository su GitHub.

Ecco alcuni suggerimenti per approfondire l'estensione di questa app con Azioni app:

Per continuare il tuo percorso con Azioni su Google, esplora queste risorse:

Seguici su Twitter @ActionsOnGoogle per rimanere al corrente sui nostri ultimi annunci e twitta con l'hashtag #appActions per condividere ciò che hai creato.

Sondaggio di feedback

Infine, compila questo sondaggio per fornire un feedback sulla tua esperienza con questo codelab.