Desarrollo de aplicaciones

Para implementar una Aplicación de Interacción de Voz (VIA), debe completar estos pasos:

  1. Crea un esqueleto VIA.
  2. ( opcional ) Implemente un flujo de configuración/inicio de sesión.
  3. ( opcional ) Implemente una pantalla de Configuración.
  4. Declare los permisos necesarios en el archivo de manifiesto.
  5. Implementar una interfaz de usuario de placa de voz.
  6. Implementar reconocimiento de voz (debe incluir la implementación de la API RecognitionService).
  7. Implemente la expresión (opcionalmente, puede implementar la API TextToSpeech).
  8. 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ón VoiceInteractionService.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:

Ciclos de vida

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.

Recordatorio de notificación

Figura 2. Recordatorio de notificación

Así es como funcionaría este flujo:

Flujo de recordatorio de notificación

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:

  1. Informar verbalmente al usuario sobre esta situación (por ejemplo, "Para que funcione correctamente, necesito que completes algunos pasos...").
  2. 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.
  3. 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

Integración 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 .

Solicitar permisos

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:

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 .

Mostrando la placa de voz

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étodo startRecognition() .
  • 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:

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: