Asistente de voz con función Presiona para leer

Android Automotive considera que la voz es un componente fundamental para las interacciones seguras durante la conducción y una de las formas más seguras en que los usuarios pueden interactuar con el SO Android Automotive mientras conducen. Como resultado, expandimos las APIs de los asistentes 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.

Presiona para leer permite que los asistentes de voz lean y respondan mensajes de texto en nombre del usuario cuando este interactúa con las notificaciones de los mensajes. Para proporcionar esta funcionalidad, puedes integrar un asistente de voz con CarVoiceInteractionSession.

En Automotive, 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, de manera opcional, responder con la voz.

Notificación de Tocar para leer

Figura 1: Notificación de presionar para leer con el botón Reproducir.

Cómo realizar la integración con CarVoiceInteractionSession

En las siguientes secciones, se describe cómo integrar un asistente de voz con CarVoiceInteractionSession.

Cómo admitir interacciones de voz

Las apps que proporcionan servicios de interacción por voz en el vehículo deben integrar las interacciones por voz de Android existentes. Para obtener más información, consulta Asistente de Google para Android (con la excepción de VoiceInteractionSession). Si bien todos los elementos de la API de interacción por voz siguen siendo los mismos que se implementan en dispositivos móviles, CarVoiceInteractionSession (que se describe en Implementa CarVoiceInteractionSession) reemplaza a VoiceInteractionSession. Para obtener más información, consulta las siguientes páginas:

Implementa CarVoiceInteractionSession

CarVoiceInteractionSession expone APIs que puedes usar para permitir que los asistentes de voz lean mensajes de texto en voz alta y, luego, respondan 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 en cuanto CarVoiceInteractionSession inicia una sesión. Los parámetros de onShow para cada clase se enumeran en la siguiente tabla:

CarVoiceInteractionSession VoiceInteractionSession
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 y 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, consulta Diagramas de secuencias.

Acción Carga útil esperada Acción de interacción por voz esperada
VOICE_ACTION_READ_NOTIFICATION Lee los mensajes en voz alta para el usuario y, luego, vuelve a activar el intent pendiente de Mark as Read cuando los mensajes se lean correctamente. De manera opcional, solicita una respuesta al usuario.
VOICE_ACTION_REPLY_NOTIFICATION Es particionable con clave.
KEY_NOTIFICATION que se asigna a StatusBarNotification.
Requiere android.permission.BIND_NOTIFICATION_LISTENER_SERVICE.
Pídele al usuario que indique el mensaje de respuesta, ingrésalo en el RemoteInputReply del intent pendiente y, luego, activa el intent pendiente.
VOICE_ACTION_HANDLE_EXCEPTION Cadena con clave.
KEY_EXCEPTION que se asigna a ExceptionValue (se describe en Valores de excepción).
KEY_FALLBACK_ASSISTANT_ENABLED que se asigna a un valor booleano. Si el valor es true, se inhabilitó el asistente de resguardo que puede controlar la solicitud del usuario.
La acción esperada que se debe realizar para la excepción se define en la documentación de la excepción.

Valores de excepción

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

Cómo solicitar permiso de objeto de escucha de notificaciones

Si el asistente de voz predeterminado no tiene el permiso del objeto de escucha de notificaciones, es posible que FallbackAssistant de la plataforma (si el fabricante del automóvil lo habilitó) lea 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 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 lógica de limitación de frecuencia para la cantidad de veces que se solicita este permiso. De esta manera, se respeta al usuario que no quiere otorgarle este permiso al asistente de voz y prefiere que FallbackAssistant lea los mensajes de texto en voz alta. Solicitar permiso al usuario cada vez que presiona Reproducir en una notificación de mensaje puede ser una experiencia negativa para el usuario. La plataforma no impone límites de frecuencia en nombre del asistente de voz.

Cuando se solicita el permiso del objeto de escucha de notificaciones, el asistente de voz debe usar CarUxRestrictionsManager para determinar si un usuario está estacionado o conduciendo. Si el usuario está conduciendo, el asistente de voz muestra una notificación que proporciona instrucciones para otorgar el permiso. De esta manera, se ayuda (y recuerda) al usuario que otorgue el permiso cuando sea más seguro.

Cómo trabajar con StatusBarNotification

El objeto StatusBarNotification que se pasa con las acciones de voz Leer y responder siempre está en una notificación de mensajería compatible con vehículos, como se describe en Cómo notificar a los usuarios sobre los mensajes. Si bien es posible que algunas notificaciones no tengan el intent de respuesta pendiente, todas tienen intents de marcado como leído pendiente.

Para optimizar las interacciones con las notificaciones, usa NotificationPayloadHandler, que proporciona métodos para extraer mensajes de la notificación y escribir los mensajes de respuesta en el intent pendiente adecuado de la notificación. Después de que el asistente de voz lea el mensaje, debe activar el intent Mark as Read.

Cumple con 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 del objeto de escucha de notificaciones.

Diagramas de secuencias

En estas figuras, se muestran los flujos lógicos de CarVoiceInteractionSession actions:

VOICE_ACTION_READ_NOTIFICATION

Figura 2: Diagrama de secuencias para VOICE_ACTION_READ_NOTIFICATION.

En el caso de la Figura 3, se recomienda que la app aplique límites de frecuencia a las solicitudes de permisos:

VOICE_ACTION_REPLY_NOTIFICATION

Figura 3: Diagrama de secuencias para VOICE_ACTION_REPLY_NOTIFICATION.

VOICE_ACTION_HANDLE_EXCEPTION

Figura 4: Diagrama de secuencias para VOICE_ACTION_HANDLE_EXCEPTION.

Lee el nombre de la app

Si deseas que el asistente de voz lea el nombre de la app de mensajería en voz alta durante la lectura de los mensajes (por ejemplo, "Sam de Hangouts dijo…"), crea una función como la que se muestra en el siguiente ejemplo de código para asegurarte de que el asistente lea 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;
}