Расширьте динамические ярлыки Google Assistant с помощью App Actions

1. Обзор

В предыдущей лаборатории кода вы использовали статические ярлыки для реализации часто используемых встроенных намерений (BII) в примере приложения. Разработчики Android используют App Actions, чтобы расширить функциональность приложения для Google Assistant.

Статические ярлыки входят в состав приложения и могут быть обновлены только путем выпуска новых версий приложения. Включение голосовых функций для динамических элементов в приложении, таких как пользовательский контент, достигается с помощью динамических ярлыков . Приложения добавляют динамические ярлыки после того, как пользователи выполняют соответствующие действия, например создают новую заметку в приложении для отслеживания задач. С помощью действий приложения вы включаете эти голосовые ярлыки, привязывая их к BII, позволяя пользователям получать доступ к своему контенту из Ассистента, говоря что-то вроде: «Эй, Google, открой мой список покупок в exampleApp».

Три прогрессивных экрана, на которых Google Assistant запускает динамический ярлык.

Рис. 1. Три прогрессивных экрана, показывающие задачу, созданную пользователем, и Google Assistant, запускающий динамический ярлык для этого элемента задачи.

Что ты построишь

В этой лаборатории кода вы включите динамические сочетания клавиш для голоса в примере приложения Android со списком дел, что позволит пользователям просить Ассистента открыть элементы списка задач, которые они создают в приложении. Это достигается с помощью шаблонов архитектуры Android, в частности шаблонов репозитория , локатора сервисов и ViewModel .

Предварительные условия

Эта лаборатория кода основана на концепциях действий приложений, рассмотренных в предыдущей лаборатории кода , в частности на BII и статических ярлыках. Если вы новичок в App Actions, мы рекомендуем завершить эту лабораторию кода, прежде чем продолжить.

Кроме того, прежде чем продолжить, убедитесь, что ваша среда разработки имеет следующую конфигурацию:

  • Терминал для запуска команд оболочки с установленным git.
  • Последняя стабильная версия Android Studio .
  • Физическое или виртуальное устройство Android с доступом в Интернет.
  • Учетная запись Google, в которой выполнен вход в Android Studio, приложение Google и приложение Google Assistant.

2. Поймите, как это работает

Включение динамического ярлыка для голосового доступа включает следующие шаги:

  • Привязка динамического ярлыка к подходящему BII.
  • Включение Ассистента для приема ярлыков путем добавления библиотеки интеграции ярлыков Google .
  • Нажатие ярлыка всякий раз, когда пользователь выполняет соответствующую задачу в приложении.

Привязка ярлыков

Чтобы динамический ярлык был доступен из Ассистента, его необходимо привязать к соответствующему BII. Когда запускается BII с ярлыком, Ассистент сопоставляет параметры в запросе пользователя с ключевыми словами, определенными в связанном ярлыке. Например:

  • Ярлык, привязанный к GET_THING BII, может позволить пользователям запрашивать определенный контент в приложении непосредственно из Ассистента. * «Окей, Google, открой мой список покупок в exampleApp».
  • Ярлык, привязанный к BII ORDER_MENU_ITEM , может позволить пользователям воспроизводить предыдущие заказы. * «Окей, Google, закажи мой обычный товар в SampleApp».

Полный список BII с разбивкой по категориям можно найти в справочнике по встроенным намерениям .

Предоставление ярлыков Ассистенту

После привязки ярлыков к BII следующим шагом будет предоставление Ассистенту возможности использовать эти ярлыки, добавив в проект библиотеку интеграции ярлыков Google . При наличии этой библиотеки Ассистент будет знать о каждом ярлыке, запущенном вашим приложением, что позволит пользователям запускать эти ярлыки с помощью триггерной фразы ярлыка в Ассистенте.

3. Подготовьте среду разработки

В этой кодовой лаборатории используется пример приложения со списком дел, созданного для Android. С помощью этого приложения пользователи могут добавлять элементы в списки, искать элементы списка задач по категориям и фильтровать задачи по статусу завершения. Загрузите и подготовьте образец приложения, заполнив этот раздел.

Загрузите базовые файлы

Выполните следующую команду, чтобы клонировать репозиторий GitHub примера приложения:

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

После клонирования репозитория выполните следующие действия, чтобы открыть его в Android Studio:

  1. В диалоговом окне «Добро пожаловать в Android Studio» нажмите «Импортировать проект» .
  2. Выберите папку, в которую вы клонировали репозиторий.

Кроме того, вы можете просмотреть версию примера приложения, представляющую завершенную кодовую лабораторию, клонировав ветку codelab-complete его репозитория Github:

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

Обновите идентификатор приложения Android

Обновление идентификатора приложения однозначно идентифицирует приложение на тестовом устройстве и позволяет избежать ошибки «Дублируется имя пакета», если приложение загружено в Play Console. Чтобы обновить идентификатор приложения, откройте app/build.gradle :

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

Замените «MYUNIQUENAME» в поле applicationId на что-то уникальное для вас.

Добавить зависимости API ярлыков

Добавьте следующие библиотеки Jetpack в файл ресурсов app/build.gradle :

приложение/build.gradle

dependencies {
   ...
   // Shortcuts library
   implementation "androidx.core:core:1.6.0"
   implementation 'androidx.core:core-google-shortcuts:1.0.1'
   ...
}

Проверьте приложение на своем устройстве

Прежде чем вносить дополнительные изменения в приложение, полезно получить представление о том, на что способен пример приложения. Чтобы запустить приложение на эмуляторе, выполните следующие действия:

  1. В Android Studio выберите «Выполнить» > «Запустить приложение» или нажмите «Выполнить» . Значок запуска приложения в Android Studio на панели инструментов.
  2. В диалоговом окне «Выбор цели развертывания» выберите устройство и нажмите «ОК» . Рекомендуемая версия ОС — Android 10 (уровень API 30) или выше, хотя действия приложений работают на устройствах, начиная с Android 5 (уровень API 21) .
  3. Нажмите и удерживайте кнопку «Домой», чтобы настроить Ассистента и убедиться, что он работает. Вам нужно будет войти в Ассистент на своем устройстве, если вы еще этого не сделали.

Дополнительную информацию о виртуальных устройствах Android см. в разделе Создание виртуальных устройств и управление ими .

Кратко изучите приложение, чтобы узнать, на что оно способно. Нажатие на значок «Плюс» создает новый элемент задачи, а пункты меню в правом верхнем углу позволяют искать и фильтровать элементы задач по статусу завершения.

4. Создайте класс репозитория ярлыков.

Несколько классов в нашем примере приложения будут вызывать API ShortcutManagerCompat для отправки динамических ярлыков и управления ими. Чтобы уменьшить избыточность кода, вы реализуете репозиторий, позволяющий классам вашего проекта легко управлять динамическими ярлыками.

Шаблон проектирования репозитория предоставляет понятный API для управления ярлыками. Преимущество репозитория заключается в том, что детали базового API единообразно абстрагируются за минимальным API. Реализуйте репозиторий, выполнив следующие действия:

  1. Создайте класс ShortcutsRepository для абстрагирования API ShortcutManagerCompat .
  2. Добавьте методы ShortcutsRepository в локатор сервисов приложения.
  3. Зарегистрируйте службу ShortcutRepository в основном приложении.

Создать репозиторий

Создайте новый класс Kotlin с именем ShortcutsRepository в пакете com.example.android.architecture.blueprints.todoapp.data.source . Вы можете найти этот пакет в папке app/src/main/java . Вы будете использовать этот класс для реализации интерфейса, предоставляющего минимальный набор методов, охватывающих наш вариант использования кодовой лаборатории.

Окно Android Studio, отображающее расположение класса ShortcutsRepository.

Рис. 2. Окно «Файлы проекта Android Studio», отображающее расположение класса ShortcutsRepository.

Вставьте следующий код в новый класс:

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

Затем обновите метод pushShortcut , чтобы он вызывал API ShortcutManagerCompat . Обновите класс ShortcutsRepository , используя следующий код:

ЯрлыкиRepository.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))
}

В предыдущем примере кода мы передали appContext в API. Это свойство класса, содержащее контекст приложения . Важно использовать контекст приложения (в отличие от контекста активности ), чтобы избежать утечек памяти, поскольку контекст может сохраняться дольше, чем жизненный цикл активности узла.

Кроме того, API требует, чтобы мы передали объект ShortcutInfoCompat для объекта Task. В предыдущем примере кода мы достигаем этого, вызывая частный метод createShortcutCompat , который мы обновим для создания и возврата объекта ShortcutInfoCompat . Для этого обновите заглушку createShortcutCompat , добавив следующий код:

ЯрлыкиRepository.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()
}

Остальные заглушки функций в этом классе предназначены для обновления и удаления динамических ярлыков. Включите эти функции, обновив их следующим кодом:

ЯрлыкиRepository.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 })
}

Добавить класс в локатор сервисов

После создания класса ShortcutsRepository следующим шагом будет сделать экземпляры объектов этого класса доступными для остальной части приложения. Это приложение управляет зависимостями классов, реализуя шаблон локатора сервисов . Откройте класс локатора сервисов с помощью браузера классов в Android Studio. Для этого выберите «Навигация» > « Класс» и введите «ServiceLocator». Щелкните полученный файл Kotlin, чтобы открыть его в своей IDE.

В верхней части ServiceLocator.kt вставьте следующий код, чтобы импортировать пакеты ShortcutsRepository и 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

Добавьте члены и методы службы ShortcutRepository , вставив следующий код в тело 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)
       }
   }
 }

Зарегистрируйте службу ярлыков

Последний шаг — зарегистрировать новую службу ShortcutsRepository в приложении. В Android Studio откройте TodoApplication.kt и скопируйте следующий код в начало файла:

TodoApplication.kt

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

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

Затем зарегистрируйте службу, добавив в тело класса следующий код:

TodoApplication.kt

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

   //...

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

   //...
}

Создайте приложение и убедитесь, что оно продолжает работать.

5. Нажмите новый ярлык

Создав службу ярлыков, вы готовы приступить к использованию ярлыков. Поскольку пользователи создают контент (элементы задач) в этом приложении и ожидают, что смогут вернуться к ним позже, мы обеспечим голосовой доступ к этому контенту, нажимая динамический ярлык, привязанный к GET_THING BII, каждый раз, когда пользователь создает новый задача. Это позволяет Помощнику направлять пользователей непосредственно к запрошенному элементу задачи, когда они запускают BII, спрашивая что-то вроде: « Эй, Google, открой мой список покупок в SampleApp».

Эту функцию можно включить в примере приложения, выполнив следующие действия:

  1. Импорт сервиса ShortcutsRepository в класс AddEditTaskViewModel , отвечающий за управление объектами списка задач.
  2. Нажатие динамического ярлыка, когда пользователь создает новую задачу.

Импортировать репозиторий ярлыков

Сначала нам нужно сделать сервис ShortcutsRepository доступным для AddEditTaskViewModel . Для этого импортируйте службу в ViewModelFactory — фабричный класс, который приложение использует для создания экземпляров объектов ViewModel , включая AddEditTaskViewModel .

Откройте браузер классов в Android Studio, выбрав «Навигация» > « Класс» и набрав «ViewModelFactory». Щелкните полученный файл Kotlin, чтобы открыть его в своей IDE.

В верхней части ViewModelFactory.kt вставьте следующий код, чтобы импортировать пакеты ShortcutsRepository и SuppressLint :

ViewModelFactory.kt

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

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

Затем замените тело ViewModelFactory следующим кодом:

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
}

Завершите изменения ViewModelFactory , поднявшись на один уровень выше, и передайте ShortcutsRepository конструктору фабрики. Откройте файловый браузер Android Studio, выбрав «Навигация» > «Файл» и набрав «FragmentExt.kt». Щелкните полученный файл Kotlin, расположенный в пакете утилит , чтобы открыть его в своей IDE.

Замените тело FragmentExt.kt следующим кодом:

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

Нажмите ярлык

Благодаря классу абстракции ShortcutsRepository , доступному для классов ViewModel примера приложения, вы обновляете AddEditTaskViewModel , класс ViewModel , отвечающий за создание заметок, чтобы отправлять динамический ярлык каждый раз, когда пользователь создает новую заметку.

В Android Studio откройте браузер классов и введите «AddEditTaskViewModel». Щелкните полученный файл Kotlin, чтобы открыть его в своей IDE.

Сначала добавьте пакет ShortcutsRepository в этот класс с помощью следующего оператора импорта:

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

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

Затем добавьте свойство класса shortcutsRepository , обновив конструктор класса следующим кодом:

Аддедиттасквиевмодел.кт

//...

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

    //...

Добавив класс ShortcutsRepository , создайте новую функцию pushShortcut() для вызова этого класса. Вставьте следующую приватную функцию в тело AddEditTaskViewModel :

Аддедиттасквиевмодел.кт

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

Наконец, используйте новый динамический ярлык при каждом создании задачи. Замените содержимое функции saveTask() следующим кодом:

Аддедиттасквиевмодел.кт

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

Проверьте свой код

Мы наконец готовы протестировать наш код! На этом этапе вы нажимаете динамический ярлык с голосовым управлением и проверяете его с помощью приложения Google Assistant.

Создать предварительный просмотр

Создание предварительного просмотра с помощью плагина Google Assistant позволяет вашим динамическим ярлыкам отображаться в Assistant на тестовом устройстве.

Установите тестовый плагин

Если у вас еще нет плагина Google Assistant, установите его, выполнив следующие действия в Android Studio:

  1. Перейдите в **Файл > Настройки (Android Studio > Настройки в MacOS).
  2. В разделе «Плагины» перейдите на Marketplace и найдите «Google Assistant».
  3. Установите инструмент и перезапустите Android Studio.

Создать предварительный просмотр

Создайте предварительный просмотр, выполнив следующие действия в Android Studio:

  1. Нажмите «Инструменты» > «Google Ассистент » > « Инструмент проверки действий приложения ».
  2. В поле «Имя приложения» укажите имя, например «Список дел».
  3. Нажмите «Создать предварительный просмотр» . Если потребуется, просмотрите и примите политику и условия обслуживания App Actions.

Панель создания предварительного просмотра инструмента тестирования действий приложения.

Рис. 3. Панель создания предварительного просмотра средства тестирования действий приложения.

Во время тестирования динамические ярлыки, которые вы отправляете в Ассистент, будут отображаться в Ассистенте, организованные по имени приложения, которое вы указали для предварительного просмотра.

Нажмите и проверьте ярлык

Перезапустите пример приложения на тестовом устройстве и выполните следующие действия:

  1. Создайте новую задачу с названием «Начать кодовую лабораторию».
  2. Откройте приложение Google Assistant и скажите или введите: «Мои ярлыки».
  3. Нажмите вкладку «Исследование» . Вы должны увидеть образец ярлыка.
  4. Коснитесь ярлыка, чтобы вызвать его. Вы должны увидеть запуск приложения с именем ярлыка, предварительно заполненным в поле фильтра, что упрощает поиск запрошенного элемента задачи.

6. (Необязательно) Обновите и удалите ярлык.

Помимо добавления новых динамических ярлыков во время выполнения, ваше приложение может обновлять их, чтобы отражать текущее состояние вашего пользовательского контента и предпочтений. Хорошей практикой является обновление существующих ярлыков каждый раз, когда пользователь изменяет целевой элемент, например переименовывает задачу в нашем примере приложения. Вам также следует удалять соответствующий ярлык при каждом удалении целевого ресурса, чтобы избежать отображения неработающих ярлыков пользователю.

Обновить ярлык

Измените AddEditTaskViewModel чтобы обновлять динамический ярлык всякий раз, когда пользователь меняет сведения об элементе задачи. Сначала обновите тело класса, используя следующий код, чтобы добавить функцию обновления, использующую наш класс репозитория:

Аддедиттасквиевмодел.кт

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

Затем измените функцию saveTask() , чтобы она вызывала наш новый метод при каждом обновлении существующей задачи.

Аддедиттасквиевмодел.кт

// Called when clicking on fab.
fun saveTask() {
   // ...
   // Note: the shortcuts are created/updated in a worker thread.
   if (isNewTask || currentTaskId == null) {
       //...
   } else {
       //...
       updateShortcut(task)
   }
}

Проверьте свой код, перезапустив приложение и выполнив следующие действия:

  1. Переименуйте заголовок существующего элемента задачи на «Завершить работу над кодом».
  2. Откройте Google Assistant, сказав: «Окей, Google, мои ярлыки».
  3. Нажмите вкладку «Обзор» . Вы должны увидеть обновленную короткую метку для вашего тестового ярлыка.

Удалить ярлык

Ярлыки наших примеров приложений следует удалять каждый раз, когда пользователь удаляет задачу. В примере приложения логика удаления задач находится в классе TaskDetailViewModel . Прежде чем мы обновим этот класс, нам нужно снова обновить ViewModelFactory , чтобы передать shortcutsRepository в TaskDetailViewModel .

Откройте ViewModelFactory и замените содержимое метода конструктора следующим кодом:

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

Затем откройте TaskDetailViewModel . Импортируйте модуль ShortcutsRepository и объявите для него переменную экземпляра, используя следующий код:

Таскдетаилвиевмодел.кт

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

Наконец, измените функцию deleteTask() , чтобы она вызывала shortcutsRepository для удаления ярлыка на основе его идентификатора всякий раз, когда удаляется задача с соответствующим taskId :

Таскдетаилвиевмодел.кт

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

Чтобы проверить свой код, перезапустите приложение и выполните следующие действия:

  1. Удалите тестовое задание.
  2. Переименуйте заголовок существующего элемента задачи на «Завершить работу над кодом».
  3. Откройте Google Assistant, сказав: «Окей, Google, мои ярлыки».
  4. Нажмите вкладку «Исследование» . Убедитесь, что ваш тестовый ярлык больше не отображается.

7. Следующие шаги

Поздравляем! Благодаря вам пользователи нашего примера приложения могут легко вернуться к созданным ими заметкам, спросив Ассистента, например: «Окей, Google, открой мой список покупок в exampleApp». Ярлыки способствуют более глубокому вовлечению пользователей, позволяя им легко воспроизводить часто используемые действия в вашем приложении.

Что мы рассмотрели

В этой лаборатории вы узнали, как:

  • Определите варианты использования динамических ярлыков в приложении.
  • Уменьшите сложность кода, используя шаблоны проектирования репозитория, внедрения зависимостей и локатора сервисов.
  • Добавляйте голосовые динамические ярлыки к пользовательскому содержимому приложений.
  • Обновите и удалите существующие ярлыки.

Что дальше

Отсюда вы можете попробовать внести дальнейшие улучшения в свое приложение «Список задач». Чтобы получить ссылку на готовый проект, см. ветку repo –codelab-complete на GitHub.

Вот несколько советов по дальнейшему изучению расширения этого приложения с помощью действий приложения:

Чтобы продолжить свое путешествие по Actions on Google, изучите эти ресурсы:

Следуйте за нами в Твиттере @ActionsOnGoogle, чтобы быть в курсе наших последних объявлений, и напишите в Твиттере #appActions , чтобы поделиться тем, что вы создали!

Опрос обратной связи

Наконец, пожалуйста, заполните этот опрос , чтобы оставить отзыв о своем опыте работы с этой лабораторией кода.