Asistente de voz Tocar para leer

Android Automotive considera que la voz es un componente crucial en la habilitación de interacciones seguras en el manejo y una de las formas más seguras para que los usuarios interactúen con el sistema operativo Android Automotive mientras conducen. Como resultado, las API existentes de Android Voice Assistant (incluida VoiceInteractionSession ) se han ampliado 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 de ese usuario, cuando el usuario interactúa con las notificaciones de mensajes. Para proporcionar esta funcionalidad, puede integrar un asistente de voz con CarVoiceInteractionSession .

Notificación de tocar para leer

En Automotive, las notificaciones publicadas en el Centro de notificaciones identificadas como notificaciones INBOX o INBOX_IN_GROUP (por ejemplo, mensajes SMS), incluirán un botón de acción Reproducir. Este botón permite al usuario hacer que la notificación sea leída en voz alta por el VIA seleccionado y opcionalmente responder por voz.

Figura 1. Notificación de tocar para leer

Integrar con CarVoiceInteractionSession

1. Admite VoiceInteractions

Las aplicaciones que brindan servicios de interacción de voz del automóvil deben integrarse con las interacciones de voz de Android existentes (con la excepción de VoiceInteractionSession ). Si bien todos los demás componentes de Voice Interaction API siguen siendo los mismos que se implementaron en los dispositivos móviles, CarVoiceInteractionSession (que se describe a continuación) reemplaza VoiceInteractionSession . Para obtener más información, consulte estos artículos:

2. Implementar CarVoiceInteractionSession

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

Al comparar CarVoiceInteractionSession y VoiceInteractionSession , la diferencia clave es que CarVoiceInteractionSession transfiere la acción en onShow para que el asistente de voz pueda detectar el contexto de la solicitud del usuario tan pronto como CarVoiceInteractionSession inicie una sesión.

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 a continuación:

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 los diagramas de secuencia a continuación.

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 pendiente Marcar como leído 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 una 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 un ExceptionValue (descrito a continuación).
KEY_FALLBACK_ASSISTANT_ENABLED que se asigna a un valor booleano. Si el valor es verdadero, el Asistente de respaldo que puede manejar la solicitud del usuario se ha deshabilitado.
La acción esperada que se tomará para la excepción se definirá en la documentación de la excepción.

ExceptionValue

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.

3. Solicitar permiso de escucha de notificaciones

Si el asistente de voz predeterminado no tiene permisos de oyente de notificaciones, el asistente de respaldo de la plataforma (si está habilitado por el fabricante del automóvil) puede leer el mensaje en voz alta antes de que se notifique al asistente de voz para que solicite el permiso. Para determinar si FallbackAssistant está habilitado y, por lo tanto, leyó 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 en cuanto a la cantidad de veces que se solicita este permiso. Al hacerlo, se respeta al usuario que no desea otorgar este permiso al asistente de voz y prefiere que el FallbackAssistant lea en voz alta los mensajes de texto. Solicitar permiso a este usuario cada vez que presiona Reproducir en la notificación del mensaje podría generar una experiencia negativa para el usuario. La plataforma no impone límites de velocidad en nombre 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 brinda instrucciones sobre cómo otorgar el permiso. Hacerlo ayuda (y recuerda) al usuario a otorgar el permiso cuando sea más seguro.

4. Trabajar con StatusBarNotification

StatusBarNotifications que se transmiten con las acciones de voz Leer y Responder siempre se encuentran en una notificación de mensajería compatible con el automóvil, como se describe en Notificar mensajes a los usuarios . Si bien es posible que algunas notificaciones no tengan la intención de respuesta pendiente, todas tienen la intención de marcar como leído pendiente.

Para agilizar las interacciones con las notificaciones, use 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 lea el mensaje, el asistente de voz debe activar la intención Marcar como leído.

5. Satisfacer las condiciones previas de tocar para leer

Solo se notifica a 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 a continuación, se recomienda la aplicación de límites de tasa en las solicitudes de permiso.

VOZ_ACCIÓN_RESPUESTA_NOTIFICACIÓN

Figura 3. Diagrama de secuencia para VOICE_ACTION_REPLY_NOTIFICATION

VOICE_ACTION_HANDLE_EXCEPTION

Figura 4. Diagrama de secuencia para VOICE_ACTION_HANDLE_EXCEPTION

Consejos y trucos

Lectura del nombre de una aplicación

Si su asistente de voz desea leer en voz alta el nombre de la aplicación de mensajería durante la lectura del mensaje (es decir, "Sam de Hangouts dijo..."), debe crear una función como la que se muestra a continuación para asegurarse de leer 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 applications, so check this
    // field for a system application 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;
}