Разработка приложений

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

  1. Создайте скелет VIA.
  2. ( необязательно ) Реализуйте процесс настройки/входа в систему.
  3. ( необязательно ) Реализуйте экран настроек.
  4. Укажите необходимые разрешения в файле манифеста.
  5. Реализовать пользовательский интерфейс голосовой панели.
  6. Реализовать распознавание голоса (необходимо включить реализацию API RecognitionService).
  7. Реализовать речевое воспроизведение (при желании можно реализовать API TextToSpeech).
  8. Реализовать выполнение команд. См. этот контент в разделе Выполнение команд .

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

Создать скелет 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() необходима для обработки Voice Assistant 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, который не был правильно настроен. В таких случаях:

  1. Устно проинформируйте пользователя о данной ситуации (например, «Для корректной работы мне нужно, чтобы вы выполнили несколько шагов…»).
  2. Если механизм ограничений UX позволяет (см. UX_RESTRICTIONS_NO_SETUP ), спросите пользователя, хочет ли он начать процесс настройки, а затем откройте экран настроек для VIA.
  3. В противном случае (например, если пользователь за рулем) оставьте уведомление, чтобы пользователь мог нажать на эту опцию, когда это будет безопасно.

Создание экранов настройки голосового взаимодействия

Экраны настройки и входа должны разрабатываться как регулярные действия . См. UX и визуальные рекомендации по разработке пользовательского интерфейса в разделе Preloaded Assistants: UX Guidance .

Общие рекомендации:

  • VIA должны позволять пользователям прерывать и возобновлять настройку в любое время.
  • Настройка не должна быть разрешена, если действует ограничение UX_RESTRICTIONS_NO_SETUP . Подробности см. в разделе Рекомендации по отвлечению внимания водителя .
  • Экраны настройки должны соответствовать системе дизайна для каждого транспортного средства. Общая компоновка экрана, значки, цвета и другие аспекты должны соответствовать остальной части пользовательского интерфейса. Подробности см. в разделе Настройка .

Реализовать экран настроек

Интеграция настроек

Рисунок 4. Интеграция настроек

Экраны настроек — это обычные действия Android. Если они реализованы, их точка входа должна быть объявлена ​​в res/xml/interaction_service.xml как часть манифестов VIA (см. Manifests ). Раздел настроек — хорошее место для продолжения настройки и входа (если пользователь не завершил его) или предложения выхода или переключения пользователя при необходимости. Подобно экранам настройки, описанным выше, эти экраны должны:

  • Предоставьте возможность вернуться на предыдущий экран в стеке экранов (например, в настройки автомобиля).
  • Не допускается во время вождения. Подробности см. в разделе «Руководство по отвлечению внимания водителя» .
  • Сопоставьте каждую систему дизайна транспортного средства. Подробности см. в разделе Настройка .

Объявите требуемые разрешения в файле манифеста

Разрешения, требуемые VIA, можно разделить на три категории:

  • Разрешения подписи системы. Это разрешения, предоставляемые только предварительно установленным, подписанным системой APK. Пользователи не могут предоставлять эти разрешения, их могут предоставлять только OEM-производители при сборке своих образов системы. Для получения дополнительной информации о получении разрешений подписи см. Предоставление разрешений системной привилегии .
  • Опасные разрешения. Это разрешения, которые пользователь должен предоставить с помощью диалогового окна PermissionsController. OEM-производители могут предварительно предоставить некоторые из этих разрешений VoiceInteractionService по умолчанию. Но учитывая, что это значение по умолчанию может меняться от устройства к устройству, приложения должны иметь возможность запрашивать эти разрешения при необходимости.
  • Другие разрешения. Это все другие разрешения, не требующие вмешательства пользователя. Эти разрешения автоматически предоставляются системой.

Учитывая вышеизложенное, следующий раздел фокусируется только на запросе опасных разрешений. Разрешения следует запрашивать только тогда, когда пользователь находится на экранах входа или настройки.

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

Запрос разрешений как часть экрана настроек

Опасные разрешения запрашиваются с помощью обычного метода ActivityCompat#requestPermission() (или эквивалентного). Подробнее о том, как запрашивать разрешения, см. в разделе Запрос разрешений приложения .

Запросить разрешения

Рисунок 5. Запрос разрешений

Разрешение прослушивателя уведомлений

Для реализации потока TTR VIA должны быть назначены в качестве прослушивателя уведомлений. Это не разрешение само по себе, а конфигурация, которая позволяет системе отправлять уведомления зарегистрированным слушателям. Чтобы узнать, был ли предоставлен VIA доступ к этой информации, приложения могут:

Если этот доступ не был предварительно предоставлен, VIA должен направить пользователя в раздел Notification Access в Car Settings, используя комбинацию высказываний и уведомлений. Следующий код можно использовать для открытия соответствующего раздела приложения настроек:

private void requestNotificationListenerAccess() {
    Intent intent = new Intent(Settings
        .ACTION_NOTIFICATION_LISTENER_SETTINGS);
    intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
    startActivity(intent);
}

Реализовать пользовательский интерфейс голосовой пластины

Когда VoiceInteractionSession получает обратный вызов onShow() , он может представить пользовательский интерфейс голосовой пластины. Для визуальных и UX-руководств по реализации голосовой пластины см. Предварительно загруженные помощники: UX-руководство .

Отображение голосовой пластины

Рисунок 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() , сеанс должен иметь возможность передать этот запрос активности.

Важно. В Automotive только специально аннотированные действия или действия, перечисленные в «разрешенном списке» 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 не поддерживает данную комбинацию ключевой фразы и локали. Приложение может использовать Software Hotword Detection .
  • 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 должны получать доступ к аудиовходу с помощью:

Обе эти константы являются @hide и доступны только для связанных приложений.

Управление аудиовходом и распознаванием голоса

Аудиовход будет реализован с использованием класса MediaRecorder . Для получения дополнительной информации об использовании этого API см. Обзор MediaRecorder . Ожидается, что службы голосового взаимодействия также будут реализациями класса RecognitionService . Любое приложение в системе, которому требуется распознавание голоса, использует для доступа к этой возможности. Чтобы распознавать голос и иметь доступ к микрофону, VIA должны иметь android.permission.RECORD_AUDIO . Ожидается, что приложения, получающие доступ к реализации RecognitionService , также будут иметь это разрешение.

До Android 10 доступ к микрофону предоставлялся только одному приложению за раз (за исключением обнаружения горячих слов, см. выше). Начиная с Android 10, доступ к микрофону может быть общим. Для получения дополнительной информации см. Общий доступ к аудиовходу .

Доступ к аудиовыходу

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