Para implementar una aplicación de interacción por voz (VIA), sigue estos pasos:
- Crea un esqueleto de VIA.
- (opcional) Implementa un flujo de configuración o acceso.
- (opcional) Implementa una pantalla de configuración.
- Declara los permisos necesarios en el archivo de manifiesto.
- Implementa una IU de placa de voz.
- Implementa el reconocimiento de voz (debe incluir la implementación de la API de RecognitionService).
- Implementa la oración (de manera opcional, puedes implementar la API de TextToSpeech).
- Implementa la entrega de comandos. Consulta este contenido en Cómo completar comandos.
En las siguientes secciones, se describe cómo completar cada paso mencionado anteriormente.
Crea un esqueleto de VIA
Manifiestos
Una app se detecta como una con interacción por 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:
- Los VIA deben exponer un servicio que extienda
VoiceInteractionService
, con un filtro de intents 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
para contener 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, consulta 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
En el siguiente diagrama, se muestra el ciclo de vida de cada una de estas entidades:
Figura 1: Lifecycles
Como se indicó antes, VoiceInteractionService
es el punto de entrada a un VIA. Las principales responsabilidades de este servicio son las siguientes:
- Inicializa todos los procesos que se deben mantener en ejecución mientras esta VIA sea la activa. Por ejemplo, la detección de palabras activas.
- Informa las acciones de voz compatibles (consulta Presiona para leer con Asistente de voz).
- Iniciar sesiones de interacción por voz desde la pantalla de bloqueo (protector de pantalla)
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; } ... }
La implementación de VoiceInteractionService#onGetSupportedVoiceActions()
es obligatoria para controlar la función Presiona para leer con Asistente de voz.
El sistema usa un VoiceInteractionSessionService para crear una VoiceInteractionSession e interactuar con ella. Solo tiene una responsabilidad, que es iniciar sesiones nuevas cuando se solicite.
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { @Override public VoiceInteractionSession onNewSession(Bundle args) { return new MyVoiceInteractionSession(this); } }
Por último, en una VoiceInteractionSession es donde se realizaría la mayor parte del trabajo. Se puede volver a usar una sola instancia de sesión para completar varias interacciones del usuario. En AAOS, existe un CarVoiceInteractionSession
auxiliar que ayuda a implementar algunas de las funciones únicas de la industria automotriz.
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. Consulta la documentación de VoiceInteractionSession
para obtener una lista completa.
Implementa un flujo de configuración o acceso
La configuración y el acceso pueden ocurrir de las siguientes maneras:
- Durante la integración del dispositivo (asistente de configuración).
- Durante el cambio de servicio de interacción por voz (Configuración).
- Cuando se selecciona la app por primera vez.
Para obtener detalles sobre la experiencia del usuario recomendada y la guía visual, consulta Asistentes precargados: Guía de UX.
Configuración durante el cambio de servicio de voz
El usuario siempre puede seleccionar un VIA que no se configuró correctamente. Esto puede deberse a lo siguiente:
- El usuario omitió el asistente de configuración por completo o omitió el paso de configuración de interacción por voz.
- El usuario seleccionó una VIA diferente de la que se configuró durante la integración del dispositivo.
En cualquier caso, un VoiceInteractionService
tiene varias formas de alentar al usuario a completar la configuración:
- Recordatorio de notificaciones
- Respuesta automática por voz cuando el usuario intenta usarla.
Nota: No se recomienda 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 la HU durante el inicio del dispositivo o como resultado de un cambio de usuario o desbloqueo.
Recordatorio de notificaciones
Un recordatorio de notificación es una forma no invasiva de indicar la necesidad de la configuración y de proporcionarles a los usuarios una indicación visual para navegar por el flujo de configuración del asistente.
Figura 2: Recordatorio de notificaciones
Este flujo funcionaría de la siguiente manera:
Figura 3: Flujo de recordatorios de notificaciones
Respuesta de voz
Este es el flujo más simple de implementar, ya que inicia una oración en una devolución de llamada de VoiceInteractionSession#onShow()
, le explica al usuario lo que debe hacer y, luego, le pregunta (si la configuración está permitida según el estado de restricción de la UX) si quiere iniciar el flujo de configuración. Si no es posible realizar la configuración en ese momento, explícale esta
situación.
Configuración durante el primer uso
El usuario siempre puede activar un VIA que no se configuró correctamente. En esos casos, haz lo siguiente:
- Informa al usuario verbalmente sobre esta situación (por ejemplo, "Para que funcione correctamente, necesito que completes algunos pasos … ").
- Si el motor de restricciones de UX lo permite (consulta UX_RESTRICTIONS_NO_SETUP), pregúntale al usuario si desea iniciar el proceso de configuración y, luego, abre la pantalla de configuración de la VIA.
- De lo contrario (por ejemplo, si el usuario está conduciendo), deja una notificación para que el usuario haga clic en la opción cuando sea seguro hacerlo.
Cómo compilar pantallas de configuración de interacción por voz
Las pantallas de configuración y acceso deben desarrollarse como actividades normales. Consulta los lineamientos visuales y de UX para el desarrollo de la IU en Asistentes precargados: Orientación sobre UX.
Lineamientos generales:
- Los VIA deben permitir que los usuarios interrumpan y reanuden 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, consulta los Lineamientos sobre distracciones 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 íconos, los colores y otros aspectos deben ser coherentes con el resto de la IU. Consulta Personalización para obtener más detalles.
Cómo implementar una pantalla de configuración
Figura 4: Integración de la configuración
Las pantallas de configuración son actividades normales de Android. Si se implementa, su punto de entrada se debe declarar en res/xml/interaction_service.xml
como parte de los manifiestos de VIA (consulta Manifiestos).
La sección Configuración es un buen lugar para continuar con la configuración y el acceso (si el usuario no lo completó) o para ofrecer una opción de salir o cambiar de usuario si es necesario. Al igual que las pantallas de configuración descritas anteriormente, estas pantallas deben cumplir con los siguientes requisitos:
- Proporciona la opción de volver a la pantalla anterior en la pila de pantallas (por ejemplo, a la configuración del vehículo).
- No se permite mientras se conduce. Para obtener más información, consulta los Lineamientos sobre distracciones del conductor.
- Une cada sistema de diseño de vehículo. Para obtener más información, consulta Personalización.
Declara los permisos necesarios en el archivo de manifiesto
Los permisos que requiere un VIA se pueden dividir en tres categorías:
- Permisos de firma del sistema Estos son permisos que solo se otorgan a los APKs preinstalados firmados por el sistema. Los usuarios no pueden otorgar estos permisos, solo los OEMs pueden hacerlo cuando compilan sus imágenes del sistema. Para obtener más información sobre cómo obtener permisos de firma, consulta Otorga permisos con privilegios del sistema.
- Permisos peligrosos. Estos son los permisos que un usuario debe otorgar con el diálogo PermissionsController. Los OEMs pueden otorgar algunos de estos permisos de forma previa a VoiceInteractionService predeterminado. Sin embargo, dado que este valor predeterminado puede cambiar de un dispositivo a otro, las apps 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. El sistema otorga estos permisos automáticamente.
Teniendo en cuenta lo anterior, la siguiente sección se enfoca solo en solicitar permisos peligrosos. Los permisos solo deben solicitarse mientras el usuario está en las pantallas de acceso o configuración.
Si la app no tiene los permisos necesarios para funcionar, el flujo recomendado es usar una frase de voz para explicarle la situación al usuario y una notificación para proporcionar una indicación que el usuario pueda usar para volver a las pantallas de configuración de VIA. Para obtener más información, consulta 1. Recordatorio de notificaciones.
Solicita permisos como parte de la pantalla de configuración
Los permisos peligrosos se solicitan con el método ActivityCompat#requestPermission()
normal (o equivalente). Para obtener detalles sobre cómo solicitar permisos, consulta Cómo solicitar permisos de la app.
Figura 5: Solicita permisos
Permiso del objeto de escucha de notificaciones
Para implementar el flujo de TTR, los VIA deben designarse como un objeto de escucha de notificaciones. Esto no es un permiso en sí, sino una configuración que permite que el sistema envíe notificaciones a los objetos de escucha registrados. Para saber si se le otorgó acceso a esta información al VIA, las apps pueden hacer lo siguiente:
- (Opcional) Usa
CarAssistUtils#assistantIsNotificationListener()
para verificar si hay objetos de escucha de notificaciones con anticipación. Esto se puede hacer, por ejemplo, durante el flujo de configuración. - (Obligatorio) Reaccionar al manejo de
CarVoiceInteractionSession#onShow()
con la acciónVOICE_ACTION_HANDLE_EXCEPTION
y la excepciónEXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING
Si no se otorga este acceso de antemano, el VIA debe dirigir al usuario a la sección Acceso a notificaciones de la configuración del vehículo con una combinación de oraciones y notificaciones. Puedes usar el siguiente código para abrir la sección adecuada de la app de configuración:
private void requestNotificationListenerAccess() { Intent intent = new Intent(Settings .ACTION_NOTIFICATION_LISTENER_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); startActivity(intent); }
Implementa una IU de placa de voz
Cuando un VoiceInteractionSession
recibe una devolución de llamada de onShow()
, puede presentar una IU de placa de voz. Para obtener lineamientos visuales y de UX sobre la implementación de placas de voz,consulta Asistentes precargados: Guía de UX.
Figura 6: Cómo mostrar la placa de voz
Existen dos opciones para implementar esta IU:
- Anula
VoiceInteractionSession#onCreateContentView()
- Cómo iniciar una actividad con
VoiceInteractionSession#startAssistantActivity()
Usa onCreateContentView()
Esta es la forma predeterminada de presentar una placa de voz. La clase base VoiceInteractionSession
crea una ventana y administra su ciclo de vida mientras esté activa una sesión de voz. Las apps deben anular VoiceInteractionSession#onCreateContentView()
y mostrar una vista adjunta a esa ventana en cuanto se crea la sesión. Esta vista debería ser invisible en un principio. Cuando comienza una interacción por voz, esta vista debe hacerse visible en VoiceInteractionSession#onShow()
y, luego, volver a hacerse 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); } … }
Cuando uses este método, te recomendamos que ajustes VoiceInteractionSession#onComputeInsets()
para tener en cuenta las regiones ocultas de tu IU.
Usa startAssistantActivity()
En este caso, VoiceInteractionSession
delega el control de la IU de la placa de voz a una actividad normal. Cuando se usa esta opción, una implementación de VoiceInteractionSession
debe inhabilitar la creación de su ventana de contenido predeterminada (consulta Cómo usar onCreateContentView()) en la devolución de llamada de onPrepareShow()
. En VoiceInteractionSession#onShow()
, la sesión iniciaría la actividad de la placa de voz con VoiceInteractionSession#startAssistantActivity()
. Este método inicia la IU con la configuración de ventana y las marcas de actividad adecuadas.
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 una vinculación de servicio. Por ejemplo, cuando se invoca VoiceInteractionSession#onHide()
, la sesión debe poder pasar esta solicitud a la actividad.
Importante. En Automotive, solo se pueden mostrar actividades con anotaciones especiales o actividades que figuran en la "lista de entidades permitidas" de UXR mientras se conduce. Esto también se aplica a las actividades iniciadas con VoiceInteractionSession#startAssistantActivity()
. Recuerda anotar tu actividad con <meta-data
android:name="distractionOptimized" android:value="true"/>
o incluirla en la clave systemActivityWhitelist
del archivo /packages/services/Car/service/res/values/config.xml
. Para obtener más información, consulta las Pautas sobre distracciones del conductor.
Implementa el reconocimiento de voz
En esta sección, aprenderás a implementar el reconocimiento de voz a través de la detección y el reconocimiento de palabras clave. Una palabra activa es una palabra activadora que se usa para iniciar una nueva consulta o acción por voz. Por ejemplo, "Hey Google" o "Ok Google".
Detección de palabras clave de la DSP
Android proporciona acceso a un detector de palabras clave siempre activo a nivel de la DSP a través de AlwaysOnHotwordDetector
.
manera de implementar la detección de palabras clave con una CPU baja. El uso de esta funcionalidad se divide en dos partes:
- Creación de una instancia de
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 clave con VoiceInteractionService#createAlwaysOnHotwordDetector()
y pasar una frase clave y una configuración regional que se desee usar para la detección. Como resultado, la app recibe una devolución de llamada onAvailabilityChanged()
con uno de los siguientes valores posibles:
STATE_HARDWARE_UNAVAILABLE
. La función de DSP no está disponible en el dispositivo. En este caso, se usa la detección de palabras clave de software.STATE_HARDWARE_UNSUPPORTED
. La compatibilidad con DSP no está disponible en general, pero la DSP no admite una combinación determinada de frase clave y configuración regional. La app puede optar por usar la detección de palabras clave de software.STATE_HARDWARE_ENROLLED
. La detección de palabras activas está lista y se puede iniciar llamando al métodostartRecognition()
.STATE_HARDWARE_UNENROLLED
. No hay un modelo de sonido para la frase clave solicitada, pero es posible la inscripción.
La inscripción de modelos de sonido de detección de palabras clave se puede realizar con IVoiceInteractionManagerService#updateKeyphraseSoundModel()
.
Se pueden registrar varios modelos en el sistema en un momento determinado, pero solo un
modelo está asociado con un AlwaysOnHotwordDetector
.
Es posible que la detección de palabras clave de DSP no esté disponible en todos los dispositivos. Los desarrolladores de VIA deben verificar las capacidades de hardware con el método getDspModuleProperties()
. Para ver un código de muestra que muestra cómo inscribir modelos de sonido, consulta VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
.
Consulta Captura simultánea para obtener información sobre el reconocimiento simultáneo de palabras clave.
Detección de palabras clave de software
Como se indicó anteriormente, es posible que la detección de palabras clave de DSP no esté disponible en todos los dispositivos (por ejemplo, el emulador de Android no proporciona emulación de DSP). En este caso, el reconocimiento de voz por software es la única alternativa. Para evitar interferir con otras apps que puedan necesitar acceso al micrófono, los VIA deben acceder a la entrada de audio de la siguiente manera:
- La captura de audio debe usar MediaRecorder.AudioSource.HOTWORD.
- Mantén el permiso
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
.
Ambas constantes son @hide
y solo están disponibles para las apps empaquetadas.
Cómo administrar la entrada de audio y el reconocimiento de voz
La entrada de audio se implementaría con la clase MediaRecorder.
Para obtener más información sobre cómo usar esta API, consulta la descripción general de MediaRecorder. Se espera que los servicios de interacción por voz también sean implementaciones de clase RecognitionService
. Cualquier app del sistema que requiera reconocimiento de voz usa el servicio para acceder a esta función. Para realizar el reconocimiento de voz y tener acceso al micrófono, los VIA deben mantener android.permission.RECORD_AUDIO
.
Se espera que las apps que acceden a una implementación de RecognitionService
también tengan este permiso.
Antes de Android 10, solo se otorgaba acceso al micrófono a una app a la vez (con la excepción de la detección de palabras clave, consulta más arriba). A partir de Android 10, se puede compartir el acceso al micrófono. Para obtener más información, consulta Cómo compartir entradas de audio.
Cómo acceder a la salida de audio
Cuando el VIA esté listo para proporcionar respuestas verbales, es importante seguir estos lineamientos:
- Cuando se solicita el foco de audio o se administra la salida de audio, la app debe usar
AudioAttributes#USAGE_ASSISTANT
yAudioAttributes#CONTENT_TYPE_SPEECH
como atributos de audio. - Durante el reconocimiento de voz, se debe solicitar el foco de audio con
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
. Ten en cuenta que es posible que algunas apps multimedia no reaccionen correctamente a los comandos multimedia (consulta Cómo entregar comandos multimedia) mientras se quita su enfoque de audio.