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

Чтобы реализовать приложение голосового взаимодействия (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 является активным. Например, обнаружение горячих слов.
  • Отчеты поддерживают голосовые действия (см. «Голосовой помощник для чтения »).
  • Запускайте сеансы голосового взаимодействия с экрана блокировки (клавиатуры).

В своей простейшей форме реализация 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 .

Реализация процесса настройки/входа в систему

Настройка и вход в систему могут происходить:

  • Во время подключения устройства (Мастер настройки).
  • При переключении услуг голосового взаимодействия (Настройки).
  • При первом запуске, когда приложение выбрано.

Подробные сведения о рекомендуемом пользовательском интерфейсе и визуальных руководствах см. в разделе «Предварительно загруженные помощники: руководство по пользовательскому интерфейсу» .

Настройка во время переключения голосовых услуг

Пользователь всегда может выбрать VIA, который не был настроен должным образом. Это может произойти потому, что:

  • Пользователь полностью пропустил мастер установки или пропустил этап настройки голосового взаимодействия.
  • Пользователь выбрал VIA, отличный от того, который был настроен во время подключения устройства.

В любом случае у VoiceInteractionService есть несколько способов побудить пользователя завершить настройку:

  • Уведомление-напоминание.
  • Автоматический голосовой ответ, когда пользователь пытается его использовать.

Примечание . Настоятельно не рекомендуется представлять процесс установки VIA без явного запроса пользователя. Это означает, что VIA следует избегать автоматического отображения контента на HU во время загрузки устройства или в результате переключения или разблокировки пользователя.

Напоминание об уведомлении

Напоминание об уведомлении — это ненавязчивый способ указать на необходимость настройки и предоставить пользователям возможность перейти к процессу настройки помощника.

Напоминание об уведомлении

Рисунок 2. Напоминание об уведомлении

Вот как будет работать этот поток:

Последовательность напоминаний об уведомлениях

Рисунок 3. Схема напоминания об уведомлении

Голосовой ответ

Это самый простой для реализации поток: инициирование произнесения в обратном вызове VoiceInteractionSession#onShow() , объяснение пользователю, что нужно сделать, а затем запрос его (если настройка разрешена с учетом состояния ограничения UX), хотят ли они инициировать поток установки. Если установка в данный момент невозможна, объясните и эту ситуацию.

Настройка при первом использовании

Пользователь всегда может запустить VIA, который не был настроен должным образом. В таких случаях:

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

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

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

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

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

Реализация экрана настроек

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

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

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

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

Объявите необходимые разрешения в файле манифеста.

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

  • Разрешения на подпись системы. Эти разрешения предоставляются только предустановленным APK-файлам, подписанным системой. Пользователи не могут предоставлять эти разрешения, только OEM-производители могут предоставлять их при создании образов своих систем. Дополнительные сведения о получении разрешений на подпись см. в разделе Предоставление системных разрешений .
  • Опасные разрешения. Это разрешения, которые пользователь должен предоставить с помощью диалогового окна PermissionsController. 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() , он может представить пользовательский интерфейс голосовой панели. Рекомендации по визуальным и 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() сеанс должен иметь возможность передать этот запрос действию.

Важный. В автомобильной отрасли во время вождения могут отображаться только специально аннотированные действия или действия, перечисленные в «разрешенном списке» UXR. Это также относится к действиям, начатым с помощью VoiceInteractionSession#startAssistantActivity() . Не забудьте либо аннотировать свою активность с помощью <meta-data android:name="distractionOptimized" android:value="true"/> либо включить эту активность в ключ systemActivityWhitelist /packages/services/Car/service/res/values/config.xml файл /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 должна получить доступ к аудиовходу, используя:

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

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

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

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

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

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