Desarrollo de apps

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

  1. Crea un esqueleto de VIA.
  2. (Opcional) Implementa un flujo de configuración o acceso.
  3. (Opcional) Implementa una pantalla de configuración.
  4. Declara los permisos necesarios en el archivo de manifiesto.
  5. Implementa una IU de placa de voz.
  6. Implementa el reconocimiento de voz (debe incluir la implementación de la API de RecognitionService).
  7. Implementa la expresión (opcionalmente, puedes implementar la API de TextToSpeech).
  8. Implementa la entrega de comandos. Consulta este contenido en Cómo entregar 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:

  • Las VIAs deben exponer un servicio que extienda VoiceInteractionService, con un filtro de intents 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/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 todas las VIAs 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 la siguiente pieza de 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:

Lifecycles

Figura 1: Lifecycles

Como se indicó anteriormente, VoiceInteractionService es el punto de entrada a una VIA. Las principales responsabilidades de este servicio son las siguientes:

  • Inicializar cualquier proceso que deba mantenerse en ejecución mientras esta VIA esté activa (por ejemplo, la detección de palabras clave)
  • Informar las acciones de voz admitidas (consulta Tocar para leer del Asistente)
  • Iniciar sesiones de interacción por voz desde la pantalla de bloqueo (keyguard)

En su forma más sencilla, una implementación de VoiceInteractionService se vería de la siguiente manera:

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 controlar la función Tocar para leer del Asistente. El sistema usa un VoiceInteractionSessionService para crear un VoiceInteractionSession y para interactuar con él. Solo tiene una responsabilidad: iniciar sesiones nuevas cuando se solicitan.

public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService {
    @Override
    public VoiceInteractionSession onNewSession(Bundle args) {
        return new MyVoiceInteractionSession(this);
    }
}

Por último, en VoiceInteractionSession 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 objeto auxiliar CarVoiceInteractionSession que ayuda a implementar algunas de las funcionalidades únicas de Automotive.

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 en los siguientes casos:

  • Durante la integración del dispositivo (Asistente de configuración)
  • Durante el intercambio de servicios de interacción por voz (Configuración)
  • En el primer inicio cuando se selecciona la app

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 intercambio de servicios de voz

Siempre es posible que el usuario seleccione una VIA que no se haya configurado correctamente. Esto puede ocurrir por alguno de los siguientes motivos:

  • El usuario omitió por completo el Asistente de configuración o el paso de configuración de la 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 notificación
  • Respuesta de voz automática 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 las VIAs deben evitar mostrar contenido automáticamente en la HU durante el arranque del dispositivo o como resultado de un cambio o desbloqueo del usuario.

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 una indicación visual para navegar al 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 notificaciones

Figura 3: Flujo de recordatorio de notificación

Respuesta de voz

Este es el flujo más simple para implementar, iniciar una expresión en una devolución de llamada VoiceInteractionSession#onShow(), explicarle al usuario lo que se debe hacer y, luego, preguntarle (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, también explica esta situación.

Configuración en el primer uso

Siempre es posible que el usuario active una VIA que no se haya configurado correctamente. En esos casos:

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

Crea 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: Guía de UX.

Lineamientos generales:

  • Las VIAs 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 Lineamientos sobre la 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 íconos, los colores y otros aspectos deben ser coherentes con el resto de la IU. Consulta Personalización para obtener más información.

Implementa una pantalla de configuración

Integración de la configuración

Figura 4: Integración de la configuración

Las pantallas de configuración son actividades normales de Android. Si se implementan, su punto de entrada debe declararse en el 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 hacer lo siguiente:

  • Proporcionar la opción de salir 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 Lineamientos sobre la distracción del conductor.
  • Coincidir con cada sistema de diseño del vehículo (para obtener más información, consulta Personalización)

Declara los permisos necesarios en el archivo de manifiesto

Los permisos que requiere una VIA se pueden dividir en tres categorías:

  • Permisos de firma del sistema : Son permisos que solo se otorgan a los APKs preinstalados y firmados por el sistema. Los usuarios no pueden otorgar estos permisos, solo los OEMs pueden otorgarlos cuando compilan sus imágenes del sistema. Para obtener más información sobre cómo obtener permisos de firma, consulta Cómo otorgar permisos con privilegios del sistema.
  • Permisos peligrosos : Son permisos que un usuario debe otorgar con el diálogo PermissionsController. Los OEMs pueden otorgar previamente algunos de estos permisos al VoiceInteractionService predeterminado. Sin embargo, dado que este valor predeterminado puede cambiar de un dispositivo a otro, las apps deben poder solicitar estos permisos cuando sea necesario.
  • Otros permisos : 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 centra solo en solicitar permisos peligrosos. Los permisos solo se deben solicitar mientras el usuario se encuentre en las pantallas de acceso o configuración.

Si la app no tiene los permisos necesarios para funcionar, el flujo recomendado es usar una expresión de voz para explicar la situación al usuario y una notificación para proporcionar una indicación visual que el usuario pueda usar para volver a las pantallas de configuración de la VIA. Para obtener más información, consulta 1. Recordatorio de notificación.

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.

Solicita permisos

Figura 5: Solicita permisos

Permiso de objeto de escucha de notificaciones

Para implementar el flujo de TTR, las VIAs 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 la VIA a esta información, las apps pueden hacer lo siguiente:

Si este acceso no se otorga previamente, la VIA debe dirigir al usuario a la sección Acceso a las notificaciones de la configuración del vehículo, usando una combinación de expresiones y notificaciones. Se puede 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 onShow(), puede presentar una IU de placa de voz. Para obtener lineamientos visuales y de UX sobre la implementación de la placa de voz,consulta Asistentes precargados: Guía de UX.

Cómo mostrar la placa de voz

Figura 6: Cómo mostrar la placa de voz

Existen dos opciones para implementar esta IU:

  • Anular VoiceInteractionSession#onCreateContentView()
  • 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 una sesión de voz esté activa. Las apps deben anular VoiceInteractionSession#onCreateContentView() y mostrar una vista adjunta a esa ventana en cuanto se cree la sesión. Esta vista debe ser invisible inicialmente. Cuando se inicia una interacción por 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);
    }
    
}

Cuando usas este método, es posible que desees ajustar 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 VoiceInteractionSession implementación debe inhabilitar la creación de su ventana de contenido predeterminada (consulta Cómo usar onCreateContentView()) en la devolución de llamada onPrepareShow(). En VoiceInteractionSession#onShow(), la sesión iniciará la actividad de la placa de voz con VoiceInteractionSession#startAssistantActivity(). Este método inicia la IU 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 la VoiceInteractionSession, es posible que se requiera un conjunto de intents internos o vinculación de servicios. 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 mientras se conduce las actividades con anotaciones especiales o las actividades que aparecen en la "lista de entidades permitidas" de UXR. Esto también se aplica a las actividades que se inician con VoiceInteractionSession#startAssistantActivity(). Recuerda anotar tu actividad con <meta-data android:name="distractionOptimized" android:value="true"/> o incluir esta actividad en la clave systemActivityWhitelist del archivo /packages/services/Car/service/res/values/config.xml. Para obtener más información, consulta Lineamientos sobre la distracción 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 clave es una palabra de activación que se usa para iniciar una nueva consulta o acción por voz. Por ejemplo, "OK Google" o "Hey Google".

Detección de palabras clave de DSP

Android proporciona acceso a un detector de palabras clave siempre activo en el nivel de DSP a través de AlwaysOnHotwordDetector. forma de implementar la detección de palabras clave con CPU baja. 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 clave con VoiceInteractionService#createAlwaysOnHotwordDetector(), pasando una frase clave y una configuración regional que deseen usar para la detección. Como resultado, la app recibe una onAvailabilityChanged() devolución de llamada con uno de los siguientes valores posibles:

  • STATE_HARDWARE_UNAVAILABLE: La capacidad 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 DSP no admite la combinación de frase clave y configuración regional determinada. La app puede optar por usar la detección de palabras clave de software.
  • STATE_HARDWARE_ENROLLED: La detección de palabras clave está lista y se puede iniciar llamando al método startRecognition().
  • STATE_HARDWARE_UNENROLLED: No hay un modelo de sonido disponible 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 uno 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 obtener un código de muestra que muestre cómo inscribir modelos de sonido, consulta VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java. Consulta Captura simultánea en relación con 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 de software es la única alternativa. Para evitar interferir con otras apps que podrían necesitar acceso al micrófono, las VIAs deben acceder a la entrada de audio con lo siguiente:

Ambas constantes son @hide y solo están disponibles para las apps agrupadas.

Administra 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. También se espera que los servicios de interacción por voz sean RecognitionService implementaciones de clase. Cualquier app del sistema que requiera reconocimiento de voz usa para acceder a esta capacidad. Para realizar el reconocimiento de voz y tener acceso al micrófono, las VIAs deben tener android.permission.RECORD_AUDIO. También se espera que las apps que acceden a una implementación de RecognitionService tengan este permiso.

Antes de Android 10, el acceso al micrófono se otorgaba a una sola 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.

Accede a la salida de audio

Cuando la VIA esté lista para proporcionar respuestas verbales, es importante seguir este siguiente conjunto de lineamientos: