Desarrollo de aplicaciones

Para implementar una aplicación de interacción de voz (VIA), complete estos pasos:

  1. Cree un esqueleto VIA.
  2. ( opcional ) Implemente un flujo de configuración/inicio de sesión.
  3. ( opcional ) Implementar una pantalla de Configuración.
  4. Declare los permisos necesarios en el archivo de manifiesto.
  5. Implemente una interfaz de usuario de placa de voz.
  6. Implementar el reconocimiento de voz (debe incluir la implementación de la API de RecognitionService).
  7. Implemente la expresión (opcionalmente, puede implementar la API TextToSpeech).
  8. Implementar cumplimiento de comandos. Ver este contenido en Cumplimiento de Comandos .

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:

  • Los VIA deben exponer un servicio que amplíe 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 para contener lo siguiente:

    res/xml/interacción_servicio.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 debe incluir lo siguiente en su 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 un VIA. Las principales responsabilidades de este servicio son:

  • Inicialice cualquier proceso que deba seguir ejecutándose mientras este VIA sea el activo. Por ejemplo, detección de palabras activas.
  • Informa acciones de voz admitidas (ver Asistente de voz Tocar para leer ).
  • Inicie sesiones de interacción de voz desde la pantalla de bloqueo (protector de 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 VoiceInteractionSessionService para crear e interactuar con VoiceInteractionSession . Solo 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 realizaría la mayor parte del trabajo. Una única instancia de sesión puede reutilizarse para completar múltiples interacciones de usuario. En AAOS, existe un asistente CarVoiceInteractionSession , 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 explicarán en las siguientes secciones. consulte la documentación de VoiceInterationSession 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).
  • En el primer lanzamiento cuando se selecciona la aplicación.

Para obtener detalles sobre la experiencia de usuario recomendada y la orientación visual, consulte Asistentes precargados: orientación de UX .

Configuración durante el cambio de servicio de voz

Siempre es posible que el usuario seleccione un VIA que no haya sido configurado correctamente. Esto puede suceder porque:

  • El usuario omitió el Asistente de configuración por completo o el usuario omitió el paso de configuración de la interacción de voz.
  • El usuario seleccionó un VIA diferente al configurado durante la incorporación del dispositivo.

En cualquier caso, 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 utilizarla.

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 la HU durante el arranque del dispositivo o como resultado de un cambio o desbloqueo del usuario.

1. Recordatorio de notificación

Un recordatorio de notificación es una forma no intrusiva de indicar la necesidad de configuración y de proporcionar a los usuarios la posibilidad de navegar en 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

Note : según las reglas de restricción de UX (consulte las Pautas de distracción del conductor ), es posible que no se permitan los flujos de configuración mientras se conduce. Cuando el usuario hace clic en la notificación y no se permite la configuración, la aplicación debe usar un enunciado para explicar la situación al usuario en lugar de intentar mostrar una interfaz de usuario que se bloqueará de inmediato.

2. Respuesta de voz

Este es el flujo más simple de implementar, iniciando una expresión en una devolución de llamada de VoiceInteractionSession#onShow() , explicando al usuario lo que debe hacerse y luego preguntándole (si la configuración está permitida 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 un VIA que no se haya configurado 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 ), pregúntele 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.

Creación de 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 experiencia de usuario para el desarrollo de la interfaz de usuario en Asistentes precargados: guía de experiencia de usuario.

Reglas generales:

  • Los VIA deberían permitir a los usuarios interrumpir y reanudar la configuración en cualquier momento.
  • No se debe permitir la instalación si la restricción UX_RESTRICTIONS_NO_SETUP está en vigor. Para obtener más información, consulte las Pautas de 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. Ver Personalización para más detalles.

Implementar una pantalla de configuración

Integración de configuraciones

Figura 4. Integración de configuraciones

Las pantallas de configuración son actividades regulares de Android. Si se implementa, su punto de entrada debe declararse en res/xml/interaction_service.xml como parte de los manifiestos de VIA (consulte Manifiestos ). La sección Configuración es un buen lugar para continuar con la configuración e iniciar sesión (si el usuario no la completó) u ofrecer una opción para cerrar sesión o cambiar de usuario si es necesario. De forma 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 las Pautas de 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 un 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 las imágenes de su sistema. Para obtener más información sobre cómo obtener permisos de firma, consulte Otorgar permisos privilegiados del sistema .
  • Permisos peligrosos. Estos son permisos que un usuario debe otorgar mediante el cuadro de diálogo PermissionsController. Los OEM pueden otorgar 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 serán otorgados automáticamente por el sistema.

Dado lo anterior, la siguiente sección se centrará únicamente en solicitar permisos peligrosos. Los permisos solo deben solicitarse mientras el usuario está en las pantallas de inicio de sesión o configuración.

Si la aplicación no tiene los permisos necesarios para operar, el flujo recomendado es usar una declaración de voz para explicar la situación al usuario y una notificación para proporcionar una posibilidad que el usuario puede usar para navegar de regreso 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

Los permisos peligrosos se solicitan mediante el método normal ActivityCompat#requestPermission() (o equivalente). Para obtener detalles sobre cómo solicitar permisos, consulte Solicitar permisos de aplicaciones .

Solicitar permisos

Figura 5. Solicitud de permisos

Permiso de escucha de notificaciones

Para implementar el flujo de TTR, los VIA deben designarse como detectores de notificaciones. Este no es un permiso en sí, sino una configuración que permite que el sistema envíe notificaciones a los oyentes registrados. Para saber si el VIA tuvo acceso a esta información, las aplicaciones pueden:

Si este acceso no se otorga previamente, el VIA debe dirigir al usuario a la sección Acceso a notificaciones de Configuración del automóvil, mediante una combinación de declaraciones 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 experiencia de usuario sobre la implementación de placas de voz, consulte Asistentes precargados: orientación de experiencia de usuario .

Visualización de 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()

Usando onCreateContentView()

Esta es la forma predeterminada de presentar una placa de voz. La clase base VoiceInteractionSession creará una ventana y administrará su ciclo de vida mientras la sesión de voz esté activa. Las aplicaciones deben anular VoiceInteractionSession#onCreateContentView() y devolver una vista que se adjuntará a esa ventana tan pronto como se cree la sesión. Esta vista debería ser inicialmente 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 usar este método, es posible que desee ajustar VoiceInteractionSession#onComputeInsets() para tener en cuenta las regiones ocultas de su interfaz de usuario.

Uso startAssistantActivity()

En este caso, VoiceInteractionSession delegará 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 de VoiceInteractionSession debe deshabilitar la creación de su ventana de contenido predeterminada (consulte Usar onCreateContentView() ) en la devolución de llamada onPrepareShow() . En VoiceInteractionSession#onShow() , la sesión iniciaría la actividad de la placa de voz mediante 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 intentos internos o enlace de servicio. Por ejemplo, cuando se VoiceInteractionSession#onHide() , la sesión debe poder pasar esta solicitud a la actividad.

Importante. En Automotive, solo las actividades especialmente anotadas o las actividades enumeradas en la "lista permitida" de UXR se pueden mostrar mientras se conduce. 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 de distracción del conductor .

Implementar reconocimiento de voz

En esta sección, aprenderá a implementar el reconocimiento de voz a través de la detección y el reconocimiento de palabras activas. 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 de DSP mediante la clase AlwaysOnHotwordDetector . Esto proporciona una manera conveniente de implementar la detección de palabras activas con poca CPU. El uso de esta funcionalidad se divide en dos partes:

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 recibirá 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 utilizará la detección de palabras activas de 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 la detección de palabras activas de software .
  • STATE_HARDWARE_ENROLLED . La detección de palabras activas está lista y se puede iniciar llamando al método startRecognition() .
  • STATE_HARDWARE_UNENROLLED . No hay disponible un modelo de sonido 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 en un momento dado, pero solo un modelo se asociará con un AlwaysOnHotwordDetector . Es posible que la detección de palabras activas de DSP no esté disponible en todos los dispositivos. Los desarrolladores de VIA deben comprobar las capacidades del hardware mediante el método getDspModuleProperties() . Para obtener un código de muestra que muestre cómo inscribir modelos de sonido, consulte VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java . Consulte Captura simultánea sobre el reconocimiento de palabras activas simultáneas.

Detección de palabras activas de software

Como se indicó anteriormente, es posible que la detección de palabras activas 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 software de reconocimiento de voz 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 empaquetadas.

Administrar la entrada de audio y el reconocimiento de voz

La entrada de audio se implementaría mediante la API de MediaRecorder . Para obtener más información sobre cómo usar esta API, consulte la descripción general de MediaRecorder . También se espera que los servicios de interacción de voz sean implementaciones de RecognitionService . Cualquier aplicación en el sistema que requiera reconocimiento de voz utilizará la API SpeechRecognizer para acceder a esta capacidad. Para realizar el reconocimiento de voz y tener acceso al micrófono, los VIA deben tener android.permission.RECORD_AUDIO . Se espera que las aplicaciones que acceden a una implementación de RecognitionService también tengan este permiso.

Antes de Android 10, el acceso al micrófono se otorgaba solo a una aplicación a la vez (con la excepción de la detección de palabras activas, consulte más 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 .

Acceso a la salida de audio

Cuando el VIA esté listo para proporcionar respuestas verbales, es importante seguir el siguiente conjunto de pautas: