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.
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:
|
onShow toma estos dos parámetros:
|
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 ListSUPPORTED_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
:
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:
Figura 3: Diagrama de secuencias para VOICE_ACTION_REPLY_NOTIFICATION.
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; }