Desarrollo de apps

Para implementar una aplicación de interacción por voz (VIA), sigue 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 oración (de manera opcional, puedes implementar la API de TextToSpeech).
  8. 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ó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 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:

Lifecycles

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.

Recordatorio de notificaciones

Figura 2: Recordatorio de notificaciones

Este flujo funcionaría de la siguiente manera:

Flujo de recordatorios de notificaciones

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:

  1. Informa al usuario verbalmente 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 (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.

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

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 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.

Solicita permisos

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:

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.

Cómo mostrar la placa de voz

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

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: