Para implementar una Aplicación de Interacción de Voz (VIA), debe completar estos pasos:
- Crea un esqueleto VIA.
- ( opcional ) Implemente un flujo de configuración/inicio de sesión.
- ( opcional ) Implemente una pantalla de Configuración.
- Declare los permisos necesarios en el archivo de manifiesto.
- Implementar una interfaz de usuario de placa de voz.
- Implementar reconocimiento de voz (debe incluir la implementación de la API RecognitionService).
- Implemente la expresión (opcionalmente, puede implementar la API TextToSpeech).
- Implementar el cumplimiento de comandos. Vea este contenido en Cumpliendo órdenes .
Las siguientes secciones describen cómo completar cada paso mencionado anteriormente.
Crear un esqueleto VIA
Manifiestos
Una aplicación se detecta como una con interacción de voz cuando se incluye lo siguiente en el manifiesto:
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>
En este ejemplo:
- Las VIA deben exponer un servicio que extienda
VoiceInteractionService
, con un filtro de intención para la acciónVoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService")
. - Este servicio debe tener el permiso de firma del sistema
BIND_VOICE_INTERACTION
. - Este servicio debe incluir un archivo de metadatos
android.voice_interaction
que contenga lo siguiente: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" />
Para obtener detalles sobre cada campo, consulte R.styleable#VoiceInteractionService
. Dado que todos los VIA también son servicios de reconocimiento de voz, también debes incluir lo siguiente en tu manifiesto:
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>
Los servicios de reconocimiento de voz también requieren los siguientes metadatos:
res/xml/recognition_service.xml
<recognition-service xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.example.MyRecognizerSettingsActivity" />
VoiceInteractionService, VoiceInteractionSessionService y VoiceInteractionSession
El siguiente diagrama muestra el ciclo de vida de cada una de estas entidades:
Figura 1. Ciclos de vida
Como se indicó anteriormente, VoiceInteractionService
es el punto de entrada a una VIA. Las principales responsabilidades de este servicio son:
- Inicialice cualquier proceso que deba mantenerse en ejecución mientras esta VIA sea la activa. Por ejemplo, detección de palabras activas.
- Informa las acciones de voz admitidas (consulte Tocar para leer del asistente de voz ).
- Inicie sesiones de interacción de voz desde la pantalla de bloqueo (protección del teclado).
En su forma más simple, una implementación de VoiceInteractionService se vería así:
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; } ... }
Se requiere la implementación de VoiceInteractionService#onGetSupportedVoiceActions()
para manejar Voice Assistant Tap-to-Read . El sistema utiliza un VoiceInteractionSessionService para crear e interactuar con un VoiceInteractionSession . Sólo tiene una responsabilidad, iniciar nuevas sesiones cuando se le solicite.
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { @Override public VoiceInteractionSession onNewSession(Bundle args) { return new MyVoiceInteractionSession(this); } }
Finalmente, una VoiceInteractionSession es donde se haría la mayor parte del trabajo. Se puede reutilizar una única instancia de sesión para completar múltiples interacciones de usuarios. En AAOS, existe una CarVoiceInteractionSession
auxiliar que ayuda a implementar algunas de las funcionalidades únicas del automóvil.
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
tiene un gran conjunto de métodos de devolución de llamada que se explican en las siguientes secciones. consulte la documentación de VoiceInteractionSession
para obtener una lista completa.
Implementar un flujo de configuración/inicio de sesión
La configuración y el inicio de sesión pueden ocurrir:
- Durante la incorporación del dispositivo (Asistente de configuración).
- Durante el intercambio de servicios de interacción de voz (Configuración).
- Al iniciarse por primera vez cuando se selecciona la aplicación.
Para obtener detalles sobre la experiencia de usuario recomendada y la guía visual, consulte Asistentes precargados: Guía de UX .
Configuración durante el intercambio de servicios de voz
Siempre es posible que el usuario seleccione una VIA que no haya sido configurada correctamente. Esto puede suceder porque:
- El usuario se saltó el asistente de configuración por completo o el usuario se saltó el paso de configuración de la interacción de voz.
- El usuario seleccionó una VIA diferente a la configurada durante la incorporación del dispositivo.
En cualquier caso, un VoiceInteractionService
tiene varias formas de animar al usuario a completar la configuración:
- Recordatorio de notificación.
- Respuesta de voz automática cuando el usuario intenta utilizarlo.
Nota : Se desaconseja encarecidamente presentar un flujo de configuración de VIA sin una solicitud explícita del usuario. Esto significa que los VIA deben evitar mostrar contenido automáticamente en el HU durante el inicio del dispositivo o como resultado de un cambio o desbloqueo de usuario.
Recordatorio de notificación
Un recordatorio de notificación es una forma no intrusiva de indicar la necesidad de configuración y de brindar a los usuarios la posibilidad de navegar por el flujo de configuración del asistente.
Figura 2. Recordatorio de notificación
Así es como funcionaría este flujo:
Figura 3. Flujo de recordatorio de notificación
respuesta de voz
Este es el flujo más simple de implementar, iniciando una expresión en una devolución de llamada VoiceInteractionSession#onShow()
, explicando al usuario lo que se debe hacer y luego preguntándole (si se permite la configuración dado el estado de restricción de UX) si desea iniciar el flujo de configuración. Si la configuración no es posible en ese momento, explique también esta situación.
Configuración en el primer uso
Siempre es posible que el usuario active una VIA que no haya sido configurada correctamente. En esos casos:
- Informar verbalmente al usuario sobre esta situación (por ejemplo, "Para que funcione correctamente, necesito que completes algunos pasos...").
- Si el motor de restricciones de UX lo permite (consulte UX_RESTRICTIONS_NO_SETUP ), pregunte al usuario si desea iniciar el proceso de configuración y luego abra la pantalla Configuración para VIA.
- De lo contrario (por ejemplo, si el usuario está conduciendo), deje una notificación para que el usuario haga clic en la opción cuando sea seguro hacerlo.
Cree pantallas de configuración de interacción de voz
Las pantallas de configuración e inicio de sesión deben desarrollarse como actividades regulares. Consulte las pautas visuales y de UX para el desarrollo de la UI en Asistentes precargados: Guía de UX .
Reglas generales:
- Los VIA deberían permitir a los usuarios interrumpir y reanudar la configuración en cualquier momento.
- No se debe permitir la configuración si la restricción
UX_RESTRICTIONS_NO_SETUP
está vigente. Para obtener más información, consulte Pautas sobre distracción del conductor . - Las pantallas de configuración deben coincidir con el sistema de diseño de cada vehículo. El diseño general de la pantalla, los iconos, los colores y otros aspectos deben ser coherentes con el resto de la interfaz de usuario. Consulte Personalización para obtener más detalles.
Implementar una pantalla de configuración
Figura 4. Integración de configuraciones
Las pantallas de configuración son actividades habituales de Android. Si se implementa, su punto de entrada debe declararse en res/xml/interaction_service.xml
como parte de los manifiestos VIA (consulte Manifiestos ). La sección Configuración es un buen lugar para continuar con la configuración y el inicio de sesión (si el usuario no lo completó) u ofrecer una opción de cierre de sesión o cambio de usuario si es necesario. De manera similar a las pantallas de configuración descritas anteriormente, estas pantallas deberían:
- Proporcione la opción de volver a la pantalla anterior en la pila de pantallas (por ejemplo, a Configuración del automóvil).
- No se permitirá mientras se conduce. Para obtener más información, consulte Pautas sobre distracción del conductor .
- Haga coincidir cada sistema de diseño de vehículos. Para obtener más información, consulte Personalización .
Declarar los permisos requeridos en el archivo de manifiesto
Los permisos requeridos por una VIA se pueden dividir en tres categorías:
- Permisos de firma del sistema. Estos son permisos que solo se otorgan a APK preinstalados y firmados por el sistema. Los usuarios no pueden otorgar estos permisos, solo los OEM pueden otorgarlos al crear sus imágenes del sistema. Para obtener más información sobre cómo obtener permisos de firma, consulte Conceder permisos con privilegios del sistema .
- Permisos peligrosos. Estos son permisos que un usuario debe otorgar mediante el cuadro de diálogo PermissionsController. Los OEM pueden conceder previamente algunos de estos permisos al VoiceInteractionService predeterminado. Pero dado que este valor predeterminado puede cambiar de un dispositivo a otro, las aplicaciones deberían poder solicitar estos permisos cuando sea necesario.
- Otros permisos. Estos son todos los demás permisos que no requieren la intervención del usuario. Estos permisos son otorgados automáticamente por el sistema.
Dado lo anterior, el siguiente apartado se centra únicamente en solicitar permisos peligrosos. Los permisos solo deben solicitarse mientras el usuario se encuentra en las pantallas de inicio de sesión o configuración.
Si la aplicación no tiene los permisos necesarios para funcionar, el flujo recomendado es utilizar una expresión de voz para explicar la situación al usuario y una notificación para proporcionar una posibilidad que el usuario puede utilizar para volver a las pantallas de configuración de VIA. . Para obtener más información, consulte 1. Recordatorio de notificación .
Solicitar permisos como parte de la pantalla de configuración
Se solicitan permisos peligrosos utilizando el método regular ActivityCompat#requestPermission()
(o equivalente). Para obtener detalles sobre cómo solicitar permisos, consulte Solicitar permisos de aplicaciones .
Figura 5. Solicitar permisos
Permiso de escucha de notificaciones
Para implementar el flujo TTR, las VIA deben designarse como escuchas de notificaciones. Este no es un permiso per se, sino una configuración que permite al sistema enviar notificaciones a los oyentes registrados. Para saber si VIA tuvo acceso a esta información, las aplicaciones pueden:
- (Opcional) Verifique si hay oyentes de notificaciones con anticipación usando
CarAssistUtils#assistantIsNotificationListener()
. Esto podría hacerse, por ejemplo, durante el flujo de configuración. - (Obligatorio) Reaccione al manejo de
CarVoiceInteractionSession#onShow()
con la acciónVOICE_ACTION_HANDLE_EXCEPTION
y la excepciónEXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING
.
Si este acceso no está otorgado previamente, VIA debe dirigir al usuario a la sección Acceso a notificaciones de Configuración del automóvil, utilizando una combinación de expresiones y notificaciones. El siguiente código se puede utilizar para abrir la sección correspondiente de la aplicación de configuración:
private void requestNotificationListenerAccess() { Intent intent = new Intent(Settings .ACTION_NOTIFICATION_LISTENER_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); startActivity(intent); }
Implementar una interfaz de usuario de placa de voz
Cuando una VoiceInteractionSession
recibe una devolución de llamada onShow()
, puede presentar una interfaz de usuario de placa de voz. Para obtener pautas visuales y de UX sobre la implementación de la placa de voz, consulte Asistentes precargados: orientación de UX .
Figura 6. Visualización de la placa de voz
Hay dos opciones sobre cómo implementar esta interfaz de usuario:
- Anular
VoiceInteractionSession#onCreateContentView()
- Inicie una actividad usando
VoiceInteractionSession#startAssistantActivity()
Utilice onCreateContentView()
Esta es la forma predeterminada de presentar un plato de voz. La clase base VoiceInteractionSession
crea una ventana y administra su ciclo de vida mientras la sesión de voz esté activa. Las aplicaciones deben anular VoiceInteractionSession#onCreateContentView()
y devolver una vista adjunta a esa ventana tan pronto como se crea la sesión. Inicialmente esta vista debería ser invisible. Cuando comienza una interacción de voz, esta vista debe hacerse visible en VoiceInteractionSession#onShow()
y luego volver a ser invisible en 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); } … }
Al utilizar este método, es posible que desees ajustar VoiceInteractionSession#onComputeInsets()
para tener en cuenta las regiones oscurecidas de tu interfaz de usuario.
Utilice startAssistantActivity()
En este caso, VoiceInteractionSession
delega el manejo de la interfaz de usuario de la placa de voz a una actividad normal. Cuando se usa esta opción, una implementación VoiceInteractionSession
debe deshabilitar la creación de su ventana de contenido predeterminada (consulte Uso de onCreateContentView() ) en la devolución de llamada onPrepareShow()
. En VoiceInteractionSession#onShow()
, la sesión iniciaría la actividad de la placa de voz usando VoiceInteractionSession#startAssistantActivity()
. Este método inicia la interfaz de usuario con la configuración de ventana y los indicadores de actividad adecuados.
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); } … }
Para mantener una comunicación entre esta actividad y VoiceInteractionSession
, es posible que se requiera un conjunto de Intents internos o un enlace de servicio. Por ejemplo, cuando se invoca VoiceInteractionSession#onHide()
, la sesión debe poder pasar esta solicitud a la actividad.
Importante. En Automoción, solo se pueden mostrar mientras se conduce actividades especialmente anotadas o actividades enumeradas en la "lista permitida" de UXR. Esto también se aplica a las actividades iniciadas con VoiceInteractionSession#startAssistantActivity()
. Recuerde anotar su actividad con <meta-data android:name="distractionOptimized" android:value="true"/>
o incluir esta actividad en la clave systemActivityWhitelist
de /packages/services/Car/service/res/values/config.xml
archivo /packages/services/Car/service/res/values/config.xml
. Para obtener más información, consulte Pautas sobre distracción del conductor .
Implementar reconocimiento de voz
En esta sección, aprenderá cómo implementar el reconocimiento de voz mediante la detección y el reconocimiento de palabras clave. Una palabra activa es una palabra desencadenante que se utiliza para iniciar una nueva consulta o acción por voz. Por ejemplo, "OK Google" o "Hola Google".
Detección de palabras activas DSP
Android proporciona acceso a un detector de palabras activas siempre activo en el nivel DSP mediante AlwaysOnHotwordDetector
. forma de implementar la detección de palabras activas con poca CPU. El uso de esta funcionalidad se divide en dos partes:
- Creación de instancias de un
AlwaysOnHotwordDetector
. - Inscripción de un modelo de sonido de detección de palabras clave.
La implementación de VoiceInteractionService puede crear un detector de palabras activas utilizando VoiceInteractionService#createAlwaysOnHotwordDetector()
, pasando una frase clave y una configuración regional que deseen usar para la detección. Como resultado, la aplicación recibe una devolución de llamada onAvailabilityChanged()
con uno de los siguientes valores posibles:
-
STATE_HARDWARE_UNAVAILABLE
. La capacidad DSP no está disponible en el dispositivo. En este caso, se utiliza la detección de palabras clave por software. -
STATE_HARDWARE_UNSUPPORTED
. La compatibilidad con DSP no está disponible en general, pero DSP no admite una determinada combinación de frase clave y configuración regional. La aplicación puede optar por utilizar el software de detección de palabras activas . -
STATE_HARDWARE_ENROLLED
. La detección de palabras calientes está lista y se puede iniciar llamando al métodostartRecognition()
. -
STATE_HARDWARE_UNENROLLED
. No hay disponible un modelo sonoro para la frase clave solicitada, pero es posible realizar la inscripción.
La inscripción de modelos de sonido de detección de palabras activas se puede realizar mediante IVoiceInteractionManagerService#updateKeyphraseSoundModel()
. Se pueden registrar varios modelos en el sistema a la vez, pero solo un modelo está asociado con AlwaysOnHotwordDetector
. Es posible que la detección de palabras activas DSP no esté disponible en todos los dispositivos. Los desarrolladores de VIA deben verificar las capacidades del hardware utilizando el método getDspModuleProperties()
. Para obtener un código de muestra que muestra cómo registrar modelos de sonido, consulte VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
. Consulte Captura simultánea sobre el reconocimiento simultáneo de palabras activas.
Software de detección de palabras activas
Como se indicó anteriormente, es posible que la detección de palabras activas DSP no esté disponible en todos los dispositivos (por ejemplo, el emulador de Android no proporciona emulación DSP). En este caso, el reconocimiento de voz por software es la única alternativa. Para evitar interferir con otras aplicaciones que puedan necesitar acceso al micrófono, los VIA deben acceder a la entrada de audio mediante:
- La captura de audio debe utilizar MediaRecorder.AudioSource.HOTWORD .
- Mantenga el permiso
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
.
Ambas constantes son @hide
y están disponibles solo para aplicaciones incluidas.
Administrar la entrada de audio y el reconocimiento de voz.
La entrada de audio se implementaría utilizando la clase MediaRecorder . Para obtener más información sobre cómo utilizar esta API, consulte la descripción general de MediaRecorder . También se espera que los servicios de interacción de voz sean implementaciones de la clase RecognitionService
. Cualquier aplicación del sistema que requiera reconocimiento de voz utiliza para acceder a esta capacidad. Para realizar reconocimiento de voz y tener acceso al micrófono, los VIA deben mantener android.permission.RECORD_AUDIO
. Se espera que las aplicaciones que acceden a una implementación RecognitionService
también tengan este permiso.
Antes de Android 10, el acceso al micrófono solo se otorgaba a una aplicación a la vez (con la excepción de la detección de palabras activas, ver arriba). A partir de Android 10, se puede compartir el acceso al micrófono. Para obtener más información, consulte Compartir entrada de audio .
Acceder a la salida de audio
Cuando el VIA esté listo para proporcionar respuestas verbales, es importante seguir el siguiente conjunto de pautas:
- Al solicitar el enfoque de audio o administrar la salida de audio, la aplicación debe usar
AudioAttributes#USAGE_ASSISTANT
yAudioAttributes#CONTENT_TYPE_SPEECH
como atributos de audio. - Durante el reconocimiento de voz, se debe solicitar el enfoque de audio con
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
. Tenga en cuenta que es posible que algunas aplicaciones multimedia no reaccionen correctamente a los comandos multimedia (consulte Cumplimiento de comandos multimedia ) mientras se elimina el foco de audio.