Чтобы реализовать приложение голосового взаимодействия (VIA), выполните следующие действия:
- Создайте скелет VIA.
- ( необязательно ) Реализуйте процесс настройки/входа в систему.
- ( необязательно ) Реализуйте экран настроек.
- Укажите требуемые разрешения в файле манифеста.
- Реализовать пользовательский интерфейс голосовой пластины.
- Реализовать распознавание голоса (необходимо включить реализацию API RecognitionService).
- Реализовать речевое воспроизведение (при желании можно реализовать API TextToSpeech).
- Реализуйте выполнение команд. См. этот контент в разделе «Выполнение команд» .
В следующих разделах описывается, как выполнить каждый из упомянутых выше шагов.
Создать скелет VIA
Манифесты
Приложение определяется как приложение с голосовым взаимодействием, если в манифест включено следующее:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myvoicecontrol"> ... <application ... > <service android:name=".MyInteractionService" android:label="@string/app_name" android:permission="android.permission.BIND_VOICE_INTERACTION" android:process=":interactor"> <meta-data android:name="android.voice_interaction" android:resource="@xml/interaction_service" /> <intent-filter> <action android:name= "android.service.voice.VoiceInteractionService" /> </intent-filter> </service> </application> </manifest>
В этом примере:
- Интерфейсы VIA должны предоставлять службу, расширяющую
VoiceInteractionService, с фильтром намерений для действияVoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService"). - Эта служба должна иметь разрешение на подпись системы
BIND_VOICE_INTERACTION. - Эта служба должна включать файл метаданных
android.voice_interaction, содержащий следующее:res/xml/interaction_service.xml
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android" android:sessionService= "com.example.MyInteractionSessionService" android:recognitionService= "com.example.MyRecognitionService" android:settingsActivity= "com.example.MySettingsActivity" android:supportsAssist="true" android:supportsLaunchVoiceAssistFromKeyguard="true" android:supportsLocalInteraction="true" />
Подробную информацию о каждом поле см. в R.styleable#VoiceInteractionService . Учитывая, что все VIA также являются службами распознавания речи, необходимо включить в манифест следующее:
AndroidManifest.xml
<manifest ...> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <application ...> ... <service android:name=".RecognitionService" ...> <intent-filter> <action android:name="android.speech.RecognitionService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.speech" android:resource="@xml/recognition_service" /> </service> </application> </manifest>
Службам распознавания голоса также требуются следующие метаданные:
res/xml/recognition_service.xml
<recognition-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.MyRecognizerSettingsActivity" />VoiceInteractionService, VoiceInteractionSessionService и VoiceInteractionSession
На следующей диаграмме показан жизненный цикл каждой из этих сущностей:

Рисунок 1. Жизненные циклы
Как уже упоминалось, VoiceInteractionService — это точка входа в VIA. Основные обязанности этой службы:
- Инициализируйте все процессы, которые должны выполняться, пока этот VIA активен. Например, обнаружение ключевых слов.
- Отчеты поддерживают голосовые команды (см. Голосовой помощник Tap-to-Read ).
- Запуск сеансов голосового взаимодействия с экрана блокировки (блокировки клавиатуры).
В простейшем виде реализация VoiceInteractionService будет выглядеть так:
public class MyVoiceInteractionService extends VoiceInteractionService { private static final List<String> SUPPORTED_VOICE_ACTIONS = Arrays.asList( CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION, CarVoiceInteractionSession.VOICE_ACTION_REPLY_NOTIFICATION, CarVoiceInteractionSession.VOICE_ACTION_HANDLE_EXCEPTION ); @Override public void onReady() { super.onReady(); // TODO: Setup hotword detector } @NonNull @Override public Set<String> onGetSupportedVoiceActions( @NonNull Set<String> voiceActions) { Set<String> result = new HashSet<>(voiceActions); result.retainAll(SUPPORTED_VOICE_ACTIONS); return result; } ... }
Реализация VoiceInteractionService#onGetSupportedVoiceActions() необходима для обработки функции Tap-to-Read голосового помощника . Служба VoiceInteractionSessionService используется системой для создания сеанса VoiceInteractionSession и взаимодействия с ним. У неё есть только одна обязанность: начинать новые сеансы по запросу.
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { @Override public VoiceInteractionSession onNewSession(Bundle args) { return new MyVoiceInteractionSession(this); } }
Наконец, основная часть работы будет выполняться в сеансе VoiceInteractionSession . Один экземпляр сеанса может быть повторно использован для выполнения нескольких взаимодействий с пользователем. В AAOS существует вспомогательный сеанс CarVoiceInteractionSession , помогающий реализовать некоторые уникальные функции автомобильной системы.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { public InteractionSession(Context context) { super(context); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); // TODO: Unhide UI and update UI state // TODO: Start processing audio input } ... }
VoiceInteractionSession имеет большой набор методов обратного вызова, которые описаны в следующих разделах. Полный список см. в документации по VoiceInteractionSession .
Реализуйте процесс настройки/входа в систему
Настройка и вход в систему могут происходить:
- Во время подключения устройства (мастер настройки).
- При голосовом взаимодействии происходит переключение сервисов (Настройки).
- При первом запуске, когда выбрано приложение.
Подробную информацию о рекомендуемом пользовательском опыте и визуальном руководстве см. в разделе Предустановленные помощники: руководство по UX .
Настройка во время переключения голосовых услуг
Пользователь всегда может выбрать неправильно настроенный VIA. Это может произойти по следующим причинам:
- Пользователь полностью пропустил мастер настройки или пропустил шаг настройки голосового взаимодействия.
- Пользователь выбрал VIA, отличный от настроенного при подключении устройства.
В любом случае у VoiceInteractionService есть несколько способов побудить пользователя завершить настройку:
- Напоминание об уведомлении.
- Автоматический голосовой ответ при попытке пользователя воспользоваться функцией.
Примечание : Настоятельно не рекомендуется демонстрировать процесс настройки VIA без явного запроса пользователя. Это означает, что VIA следует избегать автоматического отображения содержимого на HU во время загрузки устройства, а также в результате переключения пользователя или разблокировки.
Напоминание об уведомлении
Напоминание об уведомлении — это ненавязчивый способ указать на необходимость настройки и предоставить пользователям возможность перейти к процессу настройки помощника.

Рисунок 2. Напоминание об уведомлении
Вот как будет работать этот поток:

Рисунок 3. Схема напоминания об уведомлении
Голосовой ответ
Это самый простой в реализации поток: инициируется речевое сообщение в обратном вызове VoiceInteractionSession#onShow() , объясняется пользователю, что необходимо сделать, а затем запрашивается (если настройка разрешена с учётом состояния UX Restriction), хочет ли он инициировать поток настройки. Если настройка в данный момент невозможна, объясните и эту ситуацию.
Настройка при первом использовании
Пользователь всегда может запустить VIA, который не был должным образом настроен. В таких случаях:
- Устно проинформируйте пользователя о сложившейся ситуации (например, «Для корректной работы мне нужно, чтобы вы выполнили несколько шагов…»).
- Если механизм ограничений UX позволяет (см. UX_RESTRICTIONS_NO_SETUP ), спросите пользователя, хочет ли он начать процесс настройки, а затем откройте экран настроек для VIA.
- В противном случае (например, если пользователь за рулем) оставьте уведомление, чтобы пользователь мог нажать на эту опцию, когда это будет безопасно.
Создание экранов настройки голосового взаимодействия
Экраны настройки и входа следует разрабатывать регулярно . См. рекомендации по UX и визуальному оформлению пользовательского интерфейса в статье «Предустановленные помощники: руководство по UX» .
Общие рекомендации:
- VIA должны позволять пользователям прерывать и возобновлять настройку в любое время.
- Настройка не должна быть разрешена, если действует ограничение
UX_RESTRICTIONS_NO_SETUP. Подробнее см. в разделе «Руководство по предотвращению отвлечения внимания водителя» . - Экраны настройки должны соответствовать системе дизайна каждого транспортного средства. Общая компоновка экрана, значки, цвета и другие элементы должны соответствовать остальному пользовательскому интерфейсу. Подробнее см. в разделе «Настройка» .
Реализовать экран настроек

Рисунок 4. Интеграция настроек
Экраны настроек — это стандартные действия Android. При их реализации точка входа должна быть объявлена в файле res/xml/interaction_service.xml как часть манифестов VIA (см. раздел «Манифесты» ). Раздел «Настройки» — хорошее место для продолжения настройки и входа (если пользователь не выполнил его), а также для предложения выхода или смены пользователя при необходимости. Подобно экранам настройки, описанным выше, эти экраны должны:
- Предоставьте возможность вернуться на предыдущий экран в стеке экранов (например, в настройки автомобиля).
- Не допускается во время вождения. Подробнее см. в разделе «Руководство по отвлечению внимания водителя» .
- Соответствуйте каждой системе дизайна автомобиля. Подробнее см. в разделе «Кастомизация» .
Объявите требуемые разрешения в файле манифеста
Разрешения, требуемые VIA, можно разделить на три категории:
- Разрешения на подпись системы. Эти разрешения предоставляются только предустановленным APK-файлам, подписанным системой. Пользователи не могут предоставлять эти разрешения, их могут предоставлять только OEM-производители при сборке своих образов системы. Подробнее о получении разрешений на подпись см. в разделе «Предоставление привилегированных системных разрешений» .
- Опасные разрешения. Эти разрешения пользователь должен предоставить через диалоговое окно «Контроллер разрешений». OEM-производители могут предварительно предоставить некоторые из этих разрешений службе VoiceInteractionService по умолчанию. Но, учитывая, что это значение по умолчанию может меняться от устройства к устройству, приложения должны иметь возможность запрашивать эти разрешения при необходимости.
- Другие разрешения. Это все остальные разрешения, не требующие вмешательства пользователя. Эти разрешения предоставляются системой автоматически.
Учитывая вышеизложенное, в следующем разделе мы рассмотрим только запрос опасных разрешений. Разрешения следует запрашивать только на экранах входа или настроек.
Если у приложения нет необходимых разрешений для работы, рекомендуется использовать голосовое сообщение для объяснения ситуации пользователю и уведомление, чтобы предоставить пользователю возможность вернуться к экранам настроек VIA. Подробнее см. в разделе 1. Напоминание об уведомлении .
Запрос разрешений как часть экрана настроек
Опасные разрешения запрашиваются с помощью обычного метода ActivityCompat#requestPermission() (или эквивалентного). Подробную информацию о запросе разрешений см. в разделе Запрос разрешений приложения .

Рисунок 5. Запрос разрешений
Разрешение прослушивателя уведомлений
Для реализации потока TTR необходимо назначить VIA прослушивателем уведомлений. Это не разрешение как таковое, а конфигурация, позволяющая системе отправлять уведомления зарегистрированным прослушивателям. Чтобы узнать, был ли у VIA доступ к этой информации, приложения могут:
- (Необязательно) Проверьте наличие прослушивателей уведомлений заранее, используя
CarAssistUtils#assistantIsNotificationListener(). Это можно сделать, например, во время настройки. - (Обязательно) Отреагировать на обработку
CarVoiceInteractionSession#onShow()с помощью действияVOICE_ACTION_HANDLE_EXCEPTIONи исключенияEXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING.
Если этот доступ не предоставлен заранее, VIA должен направить пользователя в раздел «Доступ к уведомлениям» в настройках автомобиля, используя комбинацию речевых сообщений и уведомлений. Для открытия соответствующего раздела приложения настроек можно использовать следующий код:
private void requestNotificationListenerAccess() {
Intent intent = new Intent(Settings
.ACTION_NOTIFICATION_LISTENER_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
startActivity(intent);
}Реализовать пользовательский интерфейс голосовой пластины
Когда сеанс VoiceInteractionSession получает обратный вызов onShow() , он может отобразить пользовательский интерфейс голосовой панели. Рекомендации по визуальному оформлению и пользовательскому опыту по реализации голосовой панели см. в статье «Предзагруженные помощники: руководство по пользовательскому опыту» .

Рисунок 6. Отображение голосовой пластины
Существует два варианта реализации этого пользовательского интерфейса:
- Переопределить
VoiceInteractionSession#onCreateContentView() - Запустить действие с помощью
VoiceInteractionSession#startAssistantActivity()
Использовать onCreateContentView()
Это способ отображения голосовой панели по умолчанию. Базовый класс VoiceInteractionSession создаёт окно и управляет его жизненным циклом, пока активен голосовой сеанс. Приложения должны переопределять VoiceInteractionSession#onCreateContentView() и возвращать представление, прикреплённое к этому окну, сразу после создания сеанса. Изначально это представление должно быть невидимым. При запуске голосового взаимодействия это представление должно быть видимым с помощью VoiceInteractionSession#onShow() , а затем снова невидимым с помощью VoiceInteractionSession#onHide() .
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { private View mVoicePlate; … @Override public View onCreateContentView() { mVoicePlate = inflater.inflate(R.layout.voice_plate, null); … } @Override protected void onShow(String action, Bundle args, int showFlags) { // TODO: Update UI state to "listening" mVoicePlate.setVisibility(View.VISIBLE); } @Override public void onHide() { mVoicePlate.setVisibility(View.GONE); } … }
При использовании этого метода вам может потребоваться настроить VoiceInteractionSession#onComputeInsets() для учета скрытых областей вашего пользовательского интерфейса.
Используйте startAssistantActivity()
В этом случае VoiceInteractionSession делегирует обработку пользовательского интерфейса голосовой пластины обычной активности. При использовании этого параметра реализация VoiceInteractionSession должна отключить создание окна содержимого по умолчанию (см. раздел Использование onCreateContentView() ) в обратном вызове onPrepareShow() . В VoiceInteractionSession#onShow() сеанс запустит активность голосовой пластины с помощью VoiceInteractionSession#startAssistantActivity() . Этот метод инициирует пользовательский интерфейс с соответствующими настройками окна и флагами активности.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { … @Override public void onPrepareShow(Bundle args, int showFlags) { super.onPrepareShow(args, showFlags); setUiEnabled(false); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); Intent intent = new Intent(getContext(), VoicePlateActivity.class); intent.putExtra(VoicePlateActivity.EXTRA_ACTION, action); intent.putExtra(VoicePlateActivity.EXTRA_ARGS, args); startAssistantActivity(intent); } … }
Для поддержания связи между этой активностью и VoiceInteractionSession может потребоваться набор внутренних намерений или привязка к сервису. Например, при вызове VoiceInteractionSession#onHide() сеанс должен иметь возможность передать этот запрос активности.
Важно. В автомобильной отрасли во время вождения могут отображаться только специально аннотированные действия или действия, перечисленные в «разрешённом списке» UXR. Это также относится к действиям, запускаемым с помощью VoiceInteractionSession#startAssistantActivity() . Не забудьте либо аннотировать своё действие с помощью <meta-data android:name="distractionOptimized" android:value="true"/> , либо включить это действие в ключ systemActivityWhitelist файла /packages/services/Car/service/res/values/config.xml . Подробнее см. в разделе «Руководство по предотвращению отвлечения внимания водителя» .
Реализовать распознавание голоса
В этом разделе вы узнаете, как реализовать распознавание голоса посредством обнаружения и распознавания ключевых слов. Ключевое слово — это слово-триггер, используемое для начала нового запроса или действия с помощью голоса. Например, «OK Google» или «Hey Google».
DSP-обнаружение ключевых слов
Android предоставляет доступ к постоянно работающему детектору горячих слов на уровне DSP с помощью AlwaysOnHotwordDetector . Это способ реализовать обнаружение горячих слов с низкой загрузкой процессора. Использование этой функции разделено на две части:
- Создание экземпляра
AlwaysOnHotwordDetector. - Регистрация звуковой модели обнаружения ключевых слов.
Реализация VoiceInteractionService позволяет создать детектор ключевых слов с помощью VoiceInteractionService#createAlwaysOnHotwordDetector() , передавая ключевую фразу и локаль, которые необходимо использовать для обнаружения. В результате приложение получает обратный вызов onAvailabilityChanged() с одним из следующих возможных значений:
-
STATE_HARDWARE_UNAVAILABLE. DSP-функция на устройстве недоступна. В этом случае используется программное определение ключевых слов. -
STATE_HARDWARE_UNSUPPORTED. Поддержка DSP в целом недоступна, но DSP не поддерживает заданную комбинацию ключевой фразы и локали. Приложение может использовать функцию определения ключевых слов в программном обеспечении . -
STATE_HARDWARE_ENROLLED. Распознавание горячих слов готово и может быть запущено вызовом методаstartRecognition(). -
STATE_HARDWARE_UNENROLLED. Звуковая модель для запрошенной ключевой фразы недоступна, но регистрация возможна.
Регистрация звуковых моделей распознавания голосовых команд осуществляется с помощью IVoiceInteractionManagerService#updateKeyphraseSoundModel() . В системе одновременно может быть зарегистрировано несколько моделей, но только одна из них будет связана с AlwaysOnHotwordDetector . Распознавание голосовых команд DSP может быть доступно не на всех устройствах. Разработчикам VIA следует проверять возможности оборудования с помощью метода getDspModuleProperties() . Пример кода, демонстрирующий регистрацию звуковых моделей, см. в разделе VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java . Информация о параллельном распознавании голосовых команд приведена в разделе «Параллельный захват» .
Программное обеспечение для обнаружения ключевых слов
Как указано выше, распознавание команд DSP может быть доступно не на всех устройствах (например, эмулятор Android не поддерживает эмуляцию DSP). В этом случае единственным вариантом является программное распознавание голоса. Чтобы избежать помех в работе других приложений, которым может потребоваться доступ к микрофону, устройства VIA должны получать доступ к аудиовходу с помощью:
- Для захвата звука необходимо использовать MediaRecorder.AudioSource.HOTWORD .
- Удерживайте разрешение
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD.
Обе эти константы являются @hide и доступны только для пакетных приложений.
Управление аудиовводом и распознаванием голоса
Аудиоввод будет реализован с помощью класса MediaRecorder . Подробнее об использовании этого API см. в разделе «Обзор MediaRecorder» . Предполагается, что службы голосового взаимодействия также будут реализациями класса RecognitionService . Любое приложение в системе, требующее распознавания голоса, использует разрешение для доступа к этой возможности. Для распознавания голоса и доступа к микрофону интерфейсы VIA должны иметь разрешение android.permission.RECORD_AUDIO . Приложения, обращающиеся к реализации RecognitionService также должны иметь это разрешение.
До Android 10 доступ к микрофону предоставлялся только одному приложению одновременно (за исключением функции распознавания голосовых команд, см. выше). Начиная с Android 10, доступ к микрофону можно предоставить совместно. Подробнее см. в разделе «Совместное использование аудиовхода» .
Доступ к аудиовыходу
Когда VIA будет готов предоставить устные ответы, важно следовать следующему набору рекомендаций:
- При запросе аудиофокуса или управлении аудиовыходом приложение должно использовать
AudioAttributes#USAGE_ASSISTANTиAudioAttributes#CONTENT_TYPE_SPEECHв качестве аудиоатрибутов. - Во время распознавания речи аудиофокус необходимо запросить с помощью
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE. Имейте в виду, что некоторые медиаприложения могут некорректно реагировать на медиакоманды (см. Выполнение медиакоманд ), пока их аудиофокус недоступен.