Asistente de voz Tocar para leer

Android Automotive considera que la voz es un componente crucial para las interacciones seguras al conducir y una de las formas más seguras para que los usuarios interactúen con el sistema operativo Android Automotive mientras conducen. Como resultado, ampliamos las API del asistente de voz de Android (incluida VoiceInteractionSession ) para permitir que los asistentes de voz realicen tareas para los usuarios que pueden ser difíciles de realizar mientras conducen.

Tap-to-Read permite a los asistentes de voz leer y responder mensajes de texto en nombre del usuario, cuando el usuario interactúa con las notificaciones de mensajes. Para proporcionar esta funcionalidad, puede integrar un asistente de voz con CarVoiceInteractionSession .

En Automotriz, las notificaciones publicadas en el Centro de notificaciones identificadas como INBOX o INBOX_IN_GROUP (por ejemplo, mensajes SMS) incluyen un botón Reproducir . El usuario puede hacer clic en Reproducir para que el asistente de voz seleccionado lea la notificación en voz alta y, opcionalmente, responda por voz.

Notificación de tocar para leer

Figura 1. Notificación Tocar para leer con el botón Reproducir.

Integrar con CarVoiceInteractionSession

Las siguientes secciones describen cómo integrar un asistente de voz con CarVoiceInteractionSession .

Admite interacciones de voz

Las aplicaciones que brindan servicios de interacción de voz en automóviles deben integrarse con las interacciones de voz existentes de Android. Para obtener más información, consulte Asistente de Google para Android (con la excepción de VoiceInteractionSession ). Si bien todos los elementos de la API de interacción de voz siguen siendo los mismos que los implementados en dispositivos móviles, CarVoiceInteractionSession (descrito en Implementar CarVoiceInteractionSession ) reemplaza VoiceInteractionSession . Para obtener más información, consulte estas páginas:

Implementar CarVoiceInteractionSession

CarVoiceInteractionSession expone API que puede usar para permitir que los asistentes de voz lean mensajes de texto en voz alta y luego respondan a esos mensajes en nombre del usuario.

La diferencia clave entre las clases CarVoiceInteractionSession y VoiceInteractionSession es que CarVoiceInteractionSession pasa la acción en onShow para que el asistente de voz pueda detectar el contexto de la solicitud del usuario tan pronto como CarVoiceInteractionSession inicia una sesión. Los parámetros de onShow para cada clase se enumeran en la siguiente tabla:

CocheVozInteracciónSesión Sesión de interacción de voz
onShow toma estos tres parámetros:
  • args
  • showFlags
  • actions
onShow toma estos dos parámetros:
  • args
  • showFlags

Cambios en Android 10

A partir de Android 10, la plataforma llama a VoiceInteractionService.onGetSupportedVoiceActions para detectar qué acciones son compatibles. El asistente de voz anula e implementa VoiceInteractionService.onGetSupportedVoiceActions , como se muestra en el siguiente ejemplo:

public class MyInteractionService extends VoiceInteractionService {
    private static final List SUPPORTED_VOICE_ACTIONS = Arrays.asList(
        CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION);

    @Override
    public Set onGetSupportedVoiceActions(@NonNull Set voiceActions) {
       Set result = new HashSet<>(voiceActions);
       result.retainAll(SUPPORTED_VOICE_ACTIONS);
       return result;
   }
}

Las acciones válidas se describen en la siguiente tabla. Para obtener detalles sobre cada acción, consulte Diagramas de secuencia .

Acción Carga útil esperada Acción de interacción de voz esperada
VOICE_ACTION_READ_NOTIFICATION Lea los mensajes en voz alta al usuario y luego active la intención Marcar como lectura pendiente cuando los mensajes se lean correctamente. Opcionalmente, solicite una respuesta al usuario.
VOICE_ACTION_REPLY_NOTIFICATION Parcelable con llave.
KEY_NOTIFICATION que se asigna a StatusBarNotification .
Requiere android.permission.BIND_NOTIFICATION_LISTENER_SERVICE .
Solicite al usuario que indique el mensaje de respuesta, ingrese el mensaje de respuesta en RemoteInputReply de la intención pendiente y luego active la intención pendiente.
VOICE_ACTION_HANDLE_EXCEPTION Cuerda con llave.
KEY_EXCEPTION que se asigna a ExceptionValue (descrito en Valores de excepción ).
KEY_FALLBACK_ASSISTANT_ENABLED que se asigna a un valor booleano. Si el valor es true , se ha deshabilitado el asistente alternativo que puede manejar la solicitud del usuario.
La acción esperada que se debe tomar para la excepción se define en la documentación de la excepción.

Valores de excepción

EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING indica al asistente de voz que le falta el permiso Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE y que debe obtener este permiso del usuario.

Solicitar permiso de escucha de notificaciones

Si el asistente de voz predeterminado no tiene el permiso de escucha de notificaciones, el FallbackAssistant de la plataforma (si lo habilita el fabricante del automóvil) podría leer el mensaje en voz alta antes de que se le notifique al asistente de voz que solicite el permiso. Para determinar si FallbackAssistant está habilitado y ha leído el mensaje, el asistente de voz debe verificar el valor booleano KEY_FALLBACK_ASSISTANT_ENABLED en la carga útil.

La plataforma recomienda que el asistente de voz agregue una lógica de limitación de velocidad para la cantidad de veces que se solicita este permiso. Al hacerlo, se respeta al usuario que no desea otorgarle este permiso al asistente de voz y prefiere que FallbackAssistant lea los mensajes de texto en voz alta. Solicitar permiso a un usuario cada vez que presiona Reproducir en una notificación de mensaje puede ser una experiencia de usuario negativa. La plataforma no impone límites de tarifas por parte del asistente de voz.

Al solicitar el permiso de escucha de notificaciones, el asistente de voz debe usar CarUxRestrictionsManager para determinar si un usuario está estacionado o está conduciendo. Si el usuario está conduciendo, el asistente de voz muestra una notificación que proporciona instrucciones sobre cómo otorgar el permiso. Hacerlo ayuda (y recuerda) al usuario a otorgar el permiso cuando sea más seguro.

Trabajar con StatusBarNotification

StatusBarNotification pasada con las acciones de voz Leer y Responder siempre está en una notificación de mensajería compatible con el automóvil, como se describe en Notificar a los usuarios de los mensajes . Si bien es posible que algunas notificaciones no tengan la intención de Responder pendiente, todas tienen intenciones pendientes de Marcar como leída.

Para optimizar las interacciones con las notificaciones, utilice NotificationPayloadHandler , que proporciona métodos para extraer mensajes de la notificación y escribir los mensajes de respuesta en la intención pendiente adecuada de la notificación. Después de que el asistente de voz lee el mensaje, debe activar la intención Marcar como leído.

Satisfacer las condiciones previas de Tocar para leer

Solo se notifica VoiceInteractionSession del asistente de voz predeterminado cuando un usuario activa la acción de voz para leer y responder mensajes. Como se mencionó anteriormente, este asistente de voz predeterminado también debe tener el permiso de escucha de notificaciones.

Diagramas de secuencia

Estas figuras muestran los flujos lógicos de CarVoiceInteractionSession actions :

VOICE_ACTION_READ_NOTIFICATION

Figura 2. Diagrama de secuencia para VOICE_ACTION_READ_NOTIFICATION.

En el caso de la Figura 3, se recomienda la aplicación de límites de tarifas en las solicitudes de permiso:

VOICE_ACTION_REPLY_NOTIFICATION

Figura 3. Diagrama de secuencia para VOICE_ACTION_REPLY_NOTIFICATION.

VOICE_ACTION_HANDLE_EXCEPTION

Figura 4. Diagrama de secuencia para VOICE_ACTION_HANDLE_EXCEPTION.

Leer el nombre de la aplicación

Si desea que su asistente de voz lea el nombre de la aplicación de mensajería en voz alta durante la lectura del mensaje (por ejemplo, "Sam de Hangouts dijo..."), cree una función como la que se muestra en el siguiente ejemplo de código para asegurarse de que el asistente esté leyendo el nombre correcto:

@Nullable
String getMessageApplicationName(Context context, StatusBarNotification statusBarNotification) {
    ApplicationInfo info = getApplicationInfo(context, statusBarNotification.getPackageName());
    if (info == null) return null;

    Notification notification = statusBarNotification.getNotification();

    // Sometimes system packages will post on behalf of other apps, so check this
    // field for a system app notification.
    if (isSystemApp(info)
            && notification.extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
        return notification.extras.getString(Notification.EXTRA_SUBSTITUTE_APP_NAME);
    } else {
        PackageManager pm = context.getPackageManager();
        return String.valueOf(pm.getApplicationLabel(info));
    }
}

@Nullable
ApplicationInfo getApplicationInfo(Context context, String packageName) {
    final PackageManager pm = context.getPackageManager();
    ApplicationInfo info;
    try {
        info = pm.getApplicationInfo(packageName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
    return info;
}

boolean isSystemApp(ApplicationInfo info) {
    return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}