Интегрируйте действия приложения с виджетами Android.

Рисунок 1. Запуск виджета для GET_EXERCISE_OBSERVATION .

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

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

Преимущества

Виджеты — это миниатюрные представления приложений, которые можно встроить в поверхности Android, например на панель запуска или экран блокировки. С помощью App Actions вы повышаете эффективность своих виджетов, предоставляя им возможность отображаться в Ассистенте:

  1. Обнаружение: активно отображайте виджеты в ответ на запросы пользователей на естественном языке.
  2. Вовлеченность: отображайте виджеты в контексте громкой связи, например, когда Ассистент предоставляет персональные результаты на экране блокировки или в Android Auto .
  3. Сохранение: пользователи могут закреплять виджеты, отображаемые в Ассистенте, на своей панели запуска. Для закрепления функций требуется библиотека расширений виджетов .

Как Ассистент отображает виджеты

Пользователи могут вызывать виджеты в Ассистенте двумя способами:

Явный вызов

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

  • «Окей, Google, покажи виджет exampleApp».
  • «Виджеты из exampleApp».

Ассистент отображает эти виджеты с общим описанием: «ExampleApp говорит, вот виджет». Хотя Assistant изначально возвращает виджеты, запрошенные таким образом, без каких-либо действий со стороны разработчика приложения, этот метод вызова требует от пользователя явного знания виджета, чтобы запросить его. Чтобы упростить обнаружение виджетов, используйте метод выполнения намерений, подробно описанный в следующем разделе.

Исполнение намерения

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

  • Доступ к параметрам: Ассистент передает параметры намерений , извлеченные из пользовательского запроса, в ваш виджет, обеспечивая возможность персонализированных ответов .
  • Пользовательские представления TTS: вы можете предоставить строку преобразования текста в речь (TTS), чтобы Помощник мог объявить при отображении вашего виджета.
  • Закрепление виджета: Ассистент отображает кнопку «Добавить этот виджет» рядом с вашим виджетом, позволяя пользователям легко закреплять ваши виджеты на панели запуска.

Реализация виджетов

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

  1. Реализуйте виджет Android, выполнив действия, описанные в разделе Создание простого виджета .
  2. В файле ресурсов shortcuts.xml вашего приложения добавьте элемент <app-widget> к своей возможности, содержащий сведения о выполнении и теги BII <parameter> . Обновите свой виджет для обработки параметров.
  3. Добавьте необходимую библиотеку расширений виджетов , которая позволит Ассистенту передавать имена и параметры BII вашим виджетам. Он также позволяет настраивать введение TTS и функцию закрепления виджетов.

В следующем разделе описывается схема <app-widget> для shortcuts.xml .

Схема виджета

Элементы <app-widget> определяются как элементы <capability> в shortcuts.xml . Для них требуются следующие атрибуты, если они не указаны как необязательные:

Тег `shortcuts.xml` Содержится в Атрибуты
<app-widget> <capability>
  • android:identifier
  • android:targetClass
<parameter> <app-widget>
<extra> <app-widget>
  • android:name (применимо только для TTS)
  • android:value (необязательно)

Описание схемы виджета

<приложение-виджет>

Элемент выполнения виджета верхнего уровня.

Атрибуты:

  • android:identifier : идентификатор для этого выполнения. Это значение должно быть уникальным для элементов выполнения <app-widget> и <intent> , определенных в <capability> .
  • android:targetClass : полное имя класса AppWidgetProvider для обработки намерения.

<параметр>

Сопоставляет параметр BII со значением <parameter> намерения. Вы можете определить ноль или более параметров для каждого элемента <app-widget> . Во время выполнения Ассистент передает параметры, обновляя дополнительные параметры для экземпляра виджета в виде пар ключ-значение в следующем формате:

  • Ключ: ключ android:key определенный для параметра.
  • Значение: значение, которое BII извлекает из голосового ввода пользователя.

Доступ к этим дополнительным функциям можно получить, вызвав метод getAppWidgetOptions() для связанного объекта AppWidgetManager , который возвращает Bundle , содержащий имя запускающего BII и его параметры. Подробности см. в разделе Извлечение значений параметров .

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

<дополнительно>

Необязательный тег, объявляющий, что для этого виджета используется собственное введение TTS . Для этого тега требуются следующие значения атрибутов:

  • android:name : "hasTts"
  • android:value : "true"

Пример кода

Следующий пример из файла shortcuts.xml демонстрирует конфигурацию выполнения виджета для возможности GET_EXERCISE_OBSERVATION BII:

<capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
  <app-widget
    android:identifier="GET_EXERCISE_OBSERVATION_1"
    android:targetClass="com.exampleapp.providers.exampleAppWidgetProvider"
    android:targetPackage="com.exampleapp">
    <parameter
      android:name="exerciseObservation.aboutExercise.name"
      android:key="exercisename">
    </parameter>
    <extra android:name="hasTts" android:value="true"/>
  </app-widget>
</capability>

Вы можете указать несколько элементов <app-widget> или использовать комбинацию элементов <app-widget> и <intent> для каждой возможности. Такой подход позволяет обеспечить индивидуальный подход на основе различных комбинаций параметров, предоставленных пользователями. Например, если пользователь не указывает место высадки в своем запросе, вы можете направить его к действию в вашем приложении, которое показывает варианты настройки мест посадки и высадки. Дополнительные сведения об определении резервных намерений см. в разделе «Намерения отката» .

Извлечение значений параметров

В следующем примере класса AppWidgetProvider частная функция updateAppWidget() используется для извлечения имени и параметров BII из параметров виджета Bundle :

Котлин

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
class MyAppWidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // There might be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    private fun updateAppWidget(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val widgetText: CharSequence = context.getString(R.string.appwidget_text)

        // Construct the RemoteViews object
        val views = RemoteViews(context.packageName, R.layout.my_app_widget)
        views.setTextViewText(R.id.appwidget_text, widgetText)

        // Extract the name and parameters of the BII from the widget options
        val optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId)
        val bii = optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII) // "actions.intent.CREATE_TAXI_RESERVATION"
        val params = optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS)
        if (params != null && params.containsKey("dropoff")) {
            val dropoffLocation = params.getString("dropoff")
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Ява

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There might be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {

        CharSequence widgetText = context.getString(R.string.appwidget_text);

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_app_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Extract the name and parameters of the BII from the widget options
        Bundle optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId);
        String bii =
                optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII); // "actions.intent.CREATE_TAXI_RESERVATION"
        Bundle params =
                optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS);

        if (params != null && params.containsKey(("dropoff"))){
            String dropoffLocation = params.getString("dropoff");
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

Библиотека расширений виджетов

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

Эта библиотека Maven позволяет вам предоставлять настраиваемое представление текста в речь (TTS) для каждого виджета, позволяя Assistant объявлять краткое описание контента, визуально отображаемого пользователям. Он также позволяет закреплять панель запуска , что позволяет пользователям легко сохранять виджеты, отображаемые в Ассистенте, на экранах панели запуска.

Начните с добавления библиотеки в раздел зависимостей файла build.gradle для вашего модуля приложения:

dependencies {
    //...
    implementation "com.google.assistant.appactions:widgets:0.0.1"
}

Пользовательские представления

После импорта библиотеки расширений виджетов вы можете предоставить собственные представления TTS для своих виджетов. Чтобы добавить свое определение в AppWidgetProvider виджета, откройте класс в своей IDE и импортируйте библиотеку расширений виджетов:

Котлин

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

Ява

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;
Затем используйте библиотеку, чтобы определить вводные строки и обновить виджет, как показано в этом `ExampleAppWidget`:

Котлин

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
object MyAppWidget : AppWidgetProvider() {
    fun updateAppWidget(
        context: Context?,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
            .setResponseSpeech("Hello world") // TTS to be played back to the user
            .setResponseText("Hello world!") // Response text to be displayed in Assistant
            .build()

        // Update widget with TTS
        appActionsWidgetExtension.updateWidget(appWidgetId)

        // Update widget UI
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Ява

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

  static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
    int appWidgetId) {

    AppActionsWidgetExtension appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
      .setResponseSpeech("Hello world")  // TTS to be played back to the user
      .setResponseText("Hello world!")  // Response text to be displayed in Assistant
      .build();

      // Update widget with TTS
      appActionsWidgetExtension.updateWidget(appWidgetId);

      // Update widget UI
      appWidgetManager.updateAppWidget(appWidgetId, views);
    }

}

Рекомендации по стилю TTS

Используйте следующие рекомендации по стилю, чтобы оптимизировать представление пользовательских виджетов для TTS и отображаемых подсказок.

Рекомендация Рекомендуется Не рекомендуется
Схватки
Используйте сокращения в подсказках TTS. Сообщения без сокращений звучат неестественно и роботизированно, а не естественно и разговорно. Такие слова, как «нельзя» и «нельзя», могут звучать как наказание и резкость.
ResponseSpeech (TTS)
Извините, я не могу найти бронирование.

ResponseText
Извините, я не могу найти бронирование.
ResponseSpeech (TTS)
Извините, я не могу найти бронирование.

ResponseText
Извините, я не могу найти бронирование.
Запятые
Добавьте ясности, используя запятые в списках из трех и более элементов. Без порядковой запятой отдельные элементы в вашем списке могут быть неправильно восприняты или прочитаны как группы. Например, в слове «Нарциссы, ромашки и подсолнухи» слова «ромашки и подсолнухи» звучат так, будто они собираются вместе. В «нарциссах, маргаритках и подсолнухах» все три явно разделены.
ResponseSpeech (TTS)
Среди наших самых популярных — желтые розы, нарциссы, ромашки и подсолнухи.

ResponseText
Среди наших самых популярных — желтые розы, нарциссы, ромашки и подсолнухи.
ResponseSpeech (TTS)
Среди наших самых популярных — желтые розы, нарциссы, ромашки и подсолнухи.

ResponseText
Среди наших самых популярных — желтые розы, нарциссы, ромашки и подсолнухи.
Цифры
Используйте цифры вместо текста, чтобы сделать визуальный контент более понятным.
ResponseSpeech (TTS)
Ваше кровяное давление 100 на 80.

ResponseText
Ваше кровяное давление 100/80.
ResponseSpeech (TTS)
Ваше кровяное давление 100/80.

ResponseText
Ваше кровяное давление сто выше восьмидесяти.
Символы
Используйте специальные символы вместо текста, чтобы сделать визуальный контент более понятным.
ResponseSpeech (TTS)
Ваша последняя покупка стоила 24,65 доллара США.

ResponseText
Ваша последняя покупка стоила 24,65 доллара США.
ResponseSpeech (TTS)
Ваша последняя покупка стоила двадцать четыре доллара шестьдесят пять центов.

ResponseText
Ваша последняя покупка стоила двадцать четыре доллара шестьдесят пять центов.
Избегайте тонкостей
Изящества делают ответы отстраненными и формальными. Откажитесь от них и ведите разговор дружелюбно и неформально.
ResponseSpeech (TTS)
Ваш заказ доставлен.

ResponseText
Ваш заказ доставлен.
ResponseSpeech (TTS)
Конечно, я могу вам это сказать. Ваш заказ доставлен.

ResponseText
Конечно, я могу вам это сказать. Ваш заказ доставлен.
Избегайте восклицательных знаков
Их можно воспринимать как крик.
ResponseSpeech (TTS)
Сегодня ты пробежал 1,5 мили.

ResponseText
Сегодня ты пробежал 1,5 мили.
ResponseSpeech (TTS)
Сегодня ты пробежал 1,5 мили!

ResponseText
Сегодня ты пробежал 1,5 мили!
Время
Используйте цифры: «5:15» вместо «пять пятнадцать» или «четверть пятого». Для 12-часового формата времени используйте AM или PM.
ResponseSpeech (TTS)
Ваша доставка должна прибыть к 8:15 утра.

ResponseText
Ваша доставка должна прибыть к 8:15 утра.
ResponseSpeech (TTS)
Ваша доставка должна прибыть сегодня к 15 минутам десятого утра.

ResponseText
Ваша доставка должна прибыть сегодня к 15 минутам десятого утра.
Не впадайте в монологи
Будьте информативны, но ответы должны быть краткими. Не вдавайтесь в сложные детали без явной выгоды для пользователя.
ResponseSpeech (TTS)
В прошлом месяце вы израсходовали 159 часов энергии.

ResponseText
В прошлом месяце вы израсходовали 159 часов энергии.
ResponseSpeech (TTS)
Экономия энергии очень важна для планеты и окружающей среды. В прошлом месяце вы израсходовали 159 часов энергии. За этот месяц вы израсходовали 58 часов энергии.

ResponseText
Экономия энергии очень важна для планеты и окружающей среды. В прошлом месяце вы израсходовали 159 часов энергии. За этот месяц вы израсходовали 58 часов энергии.
Используйте короткие и простые слова
Простой и понятный язык имеет самую широкую привлекательность, что делает его доступным для людей любого происхождения.
ResponseSpeech (TTS)
Ваше последнее измерение уровня сахара в крови было 126.

ResponseText
Последнее измерение уровня сахара в крови составило 126 мг/дл.
ResponseSpeech (TTS)
Предпоследний уровень глюкозы в крови был 126.

ResponseText
Предпоследний уровень глюкозы в крови был 126.

Закрепление лаунчера

Библиотека расширений виджетов позволяет отображать кнопку «Добавить этот виджет» вместе с вашим виджетом в Ассистенте. Чтобы включить закрепление, добавьте следующее определение получателя в AndroidManifest.xml :

<application>
  <receiver android:name="com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetBroadcastReceiver"
    android:exported="false">
    <intent-filter>
      <action android:name="com.google.assistant.appactions.widgets.COMPLETE_PIN_APP_WIDGET" />
    </intent-filter>
  </receiver>
  <service
    android:name=
    "com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
      <action
        android:name="com.google.assistant.appactions.widgets.PIN_APP_WIDGET" />
    </intent-filter>
  </service>
</application>

Доступность инвентаря

BII, поддерживающие встроенную инвентаризацию или веб-инвентаризацию, могут расширить эти инвентаризации для выполнения ваших виджетов.

Встроенный инвентарь

Следующий код из примера файла shortcuts.xml демонстрирует возможность START_EXERCISE BII, настроенную для встроенной инвентаризации и выполнения виджетов:

<capability
  android:name="actions.intent.START_EXERCISE">
  <app-widget
    android:identifier="START_EXERCISE_1"
    android:targetClass="com.example.exampleapp.StartExerciseAppWidgetProvider">
    <parameter
      android:name="exercise.name"
      android:key="exerciseName"
      app:shortcutMatchRequired="true">
    </parameter>
  </app-widget>
</capability>

<shortcut android:shortcutId="RunningShortcut">
  <intent
    android:action="android.intent.action.VIEW"
    android:targetClass="com.example.exampleapp.StartExcerciseActivity" />
  <capability-binding
    android:capability="actions.intent.START_EXERCISE"
    android:parameter="exercise.name"
    android:value="running;runs" />
</shortcut>

В предыдущем примере, когда пользователь запускает эту возможность, спрашивая Ассистента: «Начните работу с примером приложения», пакет опций для выполнения <app-widget> содержит следующую пару ключ-значение:

  • Ключ = “exerciseName”
  • Значение = “RunningShortcut”

Веб-инвентаризация

Следующий код из примера файла shortcuts.xml демонстрирует возможность веб-инвентаризации и выполнения виджетов:

<shortcuts>
  <capability
    android:name="actions.intent.START_EXERCISE">
    <app-widget
      android:identifier="START_EXERCISE_1"
      android:targetClass="com.example.exampleapp.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="exercise.name"
        android:key="exerciseName"
        android:mimeType="text/*">
        <data android:pathPattern="https://exampleapp.com/exercise/.*" />
      </parameter>
    </app-widget>
  </capability>
</shortcuts>

Действия тестового приложения

Используйте инструмент тестирования действий приложения — функцию плагина Google Assistant для Android Studio — для тестирования виджетов на физическом или виртуальном устройстве. Чтобы использовать инструмент тестирования, выполните следующие действия:

  1. Подключите тестовое устройство с работающим приложением.
  2. В Android Studio выберите «Инструменты» > «Действия приложения» > «Инструмент проверки действий приложения» .
  3. Нажмите Создать предварительный просмотр .
  4. Используя Android Studio, запустите приложение на тестовом устройстве.
  5. Используйте приложение «Ассистент» на тестовом устройстве, чтобы протестировать действие приложения. Например, вы можете сказать что-то вроде : «Окей, Google, сколько миль я пробежал на этой неделе с помощью приложения exampleApp?»
  6. Наблюдайте за поведением своего приложения или используйте отладчик Android Studio , чтобы проверить желаемый результат действия.

Рекомендации по качеству

В этом разделе освещаются основные требования и рекомендации по интеграции действий приложения с виджетами.

Контент в виджетах

  • ( Обязательно ) Не ​​показывать рекламу в виджетах.
  • Сосредоточьте содержимое виджета полностью на достижении цели. Не пытайтесь реализовать несколько целей с помощью одного виджета или добавлять нерелевантный контент.

Обработка аутентификации

  • ( Обязательно ) Если для завершения пользовательского потока необходима аутентификация пользователя, верните виджет, объясняющий, что пользователю необходимо продолжить работу в приложении. Встроенная аутентификация пользователя в Google Assistant не поддерживается для действий приложения.
  • Если пользователи разрешают вашему приложению отображать данные с помощью виджетов, вы можете вернуть виджет ошибки во время выполнения для неавторизованных пользователей.

Резервные намерения

  • ( Обязательно ) В файле shortcuts.xml всегда указывайте резервный <intent> в дополнение к выполнению виджета для заданной возможности. Резервное намерение — это элемент <intent> без обязательных значений <parameter> .

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

  • Используйте резервное намерение, чтобы открыть приложение на соответствующем экране, а не на главном экране.

Следующий код из примера файла shortcuts.xml демонстрирует <capability> с резервным <intent> , поддерживающим основное выполнение <app-widget> :

<shortcuts>
  <capability
    android:name="actions.intent.CREATE_TAXI_RESERVATION">
    <!-- Widget with required parameter, specified using the "android:required" attribute. -->
    <app-widget
      android:identifier="CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="taxiReservation.dropoffLocation.name"
        android:key="dropoff"
        android:required="true">
      </parameter>
    </app-widget>
    <!-- Fallback intent with no parameters required to successfully execute. -->
    <intent
      android:identifier="CREATE_TAXI_RESERVATION_3"
      android:action="myapplication.intent.CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.TaxiReservationActivity">
    </intent>
  </capability>
</shortcuts>

Раскрытие данных Google Play

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

Этот SDK отправляет предоставленные разработчиком ответы преобразования текста в речь (TTS), которые объявляются пользователю Google Assistant с использованием речевой технологии Assistant. Эта информация не хранится в Google.

Действия приложения также могут собирать метаданные клиентского приложения для следующих целей:

  • Для мониторинга темпов внедрения различных версий SDK.
  • Для количественной оценки использования функций SDK в приложениях.