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 obligatorios 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 (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 uno de los pasos mencionados anteriormente.

Crea un esqueleto de VIA

Manifiestos

Se detecta que una app tiene 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 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 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, 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 el siguiente metadato:

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 mencionó antes, VoiceInteractionService es el punto de entrada a una VIA. Las principales responsabilidades de este servicio son las siguientes:

  • Inicializa cualquier proceso que deba seguir ejecutándose mientras este VIA sea el activo. Por ejemplo, la detección de palabras clave.
  • Informa las acciones de voz admitidas (consulta Tap-to-Read del Asistente de voz).
  • Iniciar sesiones de interacción por voz desde la pantalla de bloqueo (keyguard)

En su forma más simple, 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 de voz. El sistema usa un VoiceInteractionSessionService para crear un VoiceInteractionSession y, luego, interactuar con él. Solo tiene una responsabilidad: iniciar sesiones nuevas cuando se solicita.

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

Por último, en un objeto VoiceInteractionSession se realizaría la mayor parte del trabajo. Es posible que se reutilice 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 funcionalidades únicas para automóviles.

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 orientación visual, consulta Asistentes precargados: orientación sobre la UX.

Configuración durante el cambio de servicio de voz

El usuario siempre puede seleccionar una VIA que no se haya configurado correctamente. Esto puede deberse a lo siguiente:

  • 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ó un VIA diferente del que se configuró durante la incorporación del dispositivo.

En cualquier caso, un objeto 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 los VIA deben evitar mostrar contenido automáticamente en la 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 proporcionar a los usuarios una posibilidad para 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 notificaciones

Figura 3: Flujo de recordatorio de notificaciones

Respuesta de voz

Este es el flujo más simple de implementar, ya que inicia una expresión en una devolución de llamada de VoiceInteractionSession#onShow(), le explica al usuario lo que debe hacer y, luego, le pregunta (si se permite la configuración según el estado de restricción de UX) si quiere iniciar el flujo de configuración. Si la configuración no es posible en ese momento, también debes explicar esta situación.

Configuración en el primer uso

El usuario siempre puede activar una VIA que no se haya configurado correctamente. En esos casos, haz lo siguiente:

  1. Informa verbalmente al usuario sobre esta situación (por ejemplo, "Para funcionar 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 quiere iniciar el proceso de configuración y, luego, abre la pantalla de configuración del VIA.
  3. De lo contrario (por ejemplo, si el usuario está conduciendo), deja una notificación para que 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: Orientación sobre la 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 está vigente la restricción UX_RESTRICTIONS_NO_SETUP. Para obtener más información, consulta los 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 detalles.

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 regulares de Android. Si se implementa, su punto de entrada se debe declarar en res/xml/interaction_service.xml como parte de los manifiestos de la 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 cerrar sesión 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 para salir y volver a la pantalla anterior en la pila de pantallas (por ejemplo, a Configuración del automóvil).
  • No se permite mientras conduces. Para obtener más información, consulta los Lineamientos sobre la distracción del conductor.
  • Une cada sistema de diseño de vehículos. Para obtener más información, consulta Personalización.

Declara los permisos requeridos en el archivo de manifiesto

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

  • Permisos de firma del sistema. Estos son permisos que solo se otorgan a los APKs preinstalados y firmados por el sistema. Los usuarios no pueden otorgar estos permisos, solo los OEM pueden hacerlo 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 los permisos que un usuario debe otorgar con el diálogo de PermissionsController. Los OEM 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 enfoca solo en solicitar permisos peligrosos. Los permisos solo se deben solicitar mientras el usuario se encuentra 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 explicarle la situación al usuario y una notificación para proporcionar una opción 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 agente de escucha de notificaciones

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

Si no se otorga este acceso previamente, el VIA debe dirigir al usuario a la sección Acceso a notificaciones de la Configuración del auto, con una combinación de expresiones y notificaciones. El siguiente código se puede usar para abrir la sección correspondiente 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 placas de voz,consulta Preloaded Assistants: UX Guidance.

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()

Cómo usar 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 dure una sesión de voz. Las apps deben anular VoiceInteractionSession#onCreateContentView() y devolver una vista que se adjunte a esa ventana en cuanto se cree la sesión. Inicialmente, esta vista debería ser invisible. Cuando comienza 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 uses 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 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(). A las 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 los parámetros 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 la comunicación entre esta actividad y el VoiceInteractionSession, es posible que se requiera un conjunto de intents internos o una 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 las actividades anotadas especialmente o las que se incluyen 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 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 búsqueda 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 a nivel del DSP a través de AlwaysOnHotwordDetector. manera de implementar la detección de palabras clave con un uso bajo de la CPU. El uso de esta funcionalidad se divide en dos partes:

  • Es la creación de instancias de un AlwaysOnHotwordDetector.
  • Es la 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 activadoras 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 devolución de llamada onAvailabilityChanged() con uno de los siguientes valores posibles:

  • STATE_HARDWARE_UNAVAILABLE. La capacidad del DSP no está disponible en el dispositivo. En este caso, se usa la detección de palabras clave por software.
  • STATE_HARDWARE_UNSUPPORTED. La asistencia de DSP no está disponible en general, pero DSP no admite la combinación de palabras clave y configuración regional determinada. La app puede optar por usar la Detección de palabras clave por 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 disponible para la frase clave solicitada, pero es posible la inscripción.

Puedes inscribir modelos de sonido de detección de palabras clave con IVoiceInteractionManagerService#updateKeyphraseSoundModel(). Se pueden registrar varios modelos en el sistema en un momento determinado, pero solo uno se asocia con un AlwaysOnHotwordDetector. Es posible que la detección de palabras clave del 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 del 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 podrían necesitar acceso al micrófono, las VIA deben acceder a la entrada de audio de la siguiente manera:

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

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 implementaciones de la clase RecognitionService. Todas las apps del sistema que requieren reconocimiento de voz usan el objeto para acceder a esta capacidad. Para realizar el reconocimiento de voz y tener acceso al micrófono, las VIA deben tener 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, 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.

Acceder a la salida de audio

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