Для внедрения приложения голосового взаимодействия (VIA) необходимо выполнить следующие шаги:
- Создайте каркас VIA.
- ( необязательно ) Реализуйте процесс настройки/входа в систему.
- ( необязательно ) Реализуйте экран настроек.
- Укажите необходимые права доступа в файле манифеста.
- Реализуйте пользовательский интерфейс голосовой панели.
- Реализуйте распознавание голоса (обязательно с реализацией API RecognitionService).
- Реализуйте функцию озвучивания (при желании можно реализовать API преобразования текста в речь).
- Реализуйте выполнение команд. См. этот раздел в подразделе «Выполнение команд» .
В следующих разделах описано, как выполнить каждый из упомянутых выше шагов.
Создайте каркас 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 является активным. Например, распознавание ключевых слов.
- В отчетах поддерживаются голосовые действия (см. Функция «Нажмите для чтения» голосового помощника ).
- Запуск сеансов голосового взаимодействия с экрана блокировки (блокировки клавиатуры).
В простейшем виде реализация 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() . Система использует 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 .
Реализуйте процесс настройки/входа в систему.
Настройка и вход в систему возможны:
- В процессе подключения устройства (мастер настройки).
- Во время переключения служб голосового взаимодействия (Настройки).
- При первом запуске, когда приложение выбрано.
Подробную информацию о рекомендуемом пользовательском интерфейсе и визуальных рекомендациях см. в разделе «Предварительно загруженные помощники: рекомендации по пользовательскому интерфейсу» .
Настройка во время переключения голосовой службы
Пользователь всегда может выбрать VIA, который не был должным образом настроен. Это может произойти по следующим причинам:
- Пользователь либо полностью пропустил мастер настройки, либо пропустил шаг настройки голосового взаимодействия.
- Пользователь выбрал VIA, отличный от того, который был настроен во время подключения устройства.
В любом случае, VoiceInteractionService предлагает несколько способов побудить пользователя завершить настройку:
- Напоминание об уведомлении.
- Автоматический голосовой ответ при попытке пользователя использовать эту функцию.
Примечание : Категорически не рекомендуется демонстрировать процесс настройки VIA без явного запроса пользователя. Это означает, что VIA следует избегать автоматического отображения контента на головном устройстве во время загрузки устройства или в результате переключения или разблокировки пользователем.
Напоминание об уведомлении
Уведомление-напоминание — это ненавязчивый способ указать на необходимость настройки и предоставить пользователям возможность перейти к процессу настройки с помощью помощника.

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

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

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

Рисунок 5. Запрос разрешений
разрешение слушателя уведомлений
Для реализации алгоритма TTR (Time-to-Read) 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() - Запустите Activity с помощью
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 может потребоваться набор внутренних Intents или привязка к сервису. Например, при вызове VoiceInteractionSession#onHide() сессия должна иметь возможность передать этот запрос активности.
Важно. В Automotive во время вождения могут отображаться только специально аннотированные действия или действия, перечисленные в «разрешенном списке» UXR. Это относится и к действиям, запускаемым с помощью VoiceInteractionSession#startAssistantActivity() . Не забудьте либо аннотировать ваше действие с помощью <meta-data android:name="distractionOptimized" android:value="true"/> , либо включить это действие в ключ systemActivityWhitelist файла /packages/services/Car/service/res/values/config.xml . Для получения дополнительной информации см. Рекомендации по отвлечению водителя .
Внедрить распознавание голоса
В этом разделе вы узнаете, как реализовать распознавание голоса с помощью обнаружения и распознавания ключевых слов. Ключевое слово — это слово-триггер, используемое для начала нового запроса или действия с помощью голоса. Например, «Окей, Google» или «Привет, 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 . Любое приложение в системе, которому требуется распознавание голоса, использует его для доступа к этой возможности. Для распознавания голоса и доступа к микрофону приложениям голосового взаимодействия необходимо иметь разрешение android.permission.RECORD_AUDIO . Ожидается, что приложения, обращающиеся к реализации RecognitionService также будут иметь это разрешение.
До Android 10 доступ к микрофону предоставлялся только одному приложению одновременно (за исключением распознавания ключевых слов, см. выше). Начиная с Android 10, доступ к микрофону можно передавать другим приложениям. Для получения дополнительной информации см. раздел «Совместное использование аудиовхода» .
Доступ к аудиовыходу
Когда специалист по визуальному осмотру готов дать устные ответы, важно следовать следующим рекомендациям:
- При запросе фокуса на аудио или управлении выводом звука приложение должно использовать
AudioAttributes#USAGE_ASSISTANTиAudioAttributes#CONTENT_TYPE_SPEECHв качестве атрибутов аудио. - Во время распознавания речи необходимо запросить фокус на аудиопоток с помощью
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE. Имейте в виду, что некоторые мультимедийные приложения могут некорректно реагировать на команды воспроизведения (см. раздел «Выполнение команд воспроизведения »), пока их фокус на аудиопотоке снят.