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

Чтобы реализовать приложение голосового взаимодействия (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() необходима для обработки функции 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 и визуальному оформлению пользовательского интерфейса в статье «Предустановленные помощники: руководство по UX» .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если этот доступ не предоставлен заранее, 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 должны получать доступ к аудиовходу с помощью:

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

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

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

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

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

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