En esta página, se describe cómo entregar comandos con interacción por voz.
Cómo entregar comandos multimedia
Los comandos relacionados con el contenido multimedia se pueden dividir en tres grupos diferentes:
- Fuentes de contenido multimedia externas (como Spotify instalado en AAOS)
- Fuentes de contenido multimedia de backend (como la música que se transmite a través de la VIA)
- Fuentes de contenido multimedia locales (como la radio del automóvil)
Controla los comandos de fuentes de contenido multimedia externas
Las fuentes de contenido multimedia externas se definen como apps para Android que admiten las APIs de MediaSessionCompat
y MediaBrowseCompat
(consulta Cómo compilar apps multimedia para automóviles para obtener una explicación detallada sobre el uso de estas APIs).
Importante: Para que una app de asistente se conecte al MediaBrowseService
de todas las apps multimedia instaladas en el sistema, debe cumplir con los siguientes requisitos:
- Se debe instalar como firmado por el sistema (consulta los lineamientos de desarrollo de aplicaciones multimedia para AAOS y el código de muestra
PackageValidator
). - Tener el permiso con privilegios del sistema
android.permission.MEDIA_CONTENT_CONTROL
(consulta Otorga permisos con privilegios del sistema)
Además de MediaBrowserCompat
y MediaControllerCompat
, AAOS proporciona lo siguiente:
CarMediaService
proporciona información centralizada sobre la fuente de contenido multimedia seleccionada actualmente. También se usa para reanudar una fuente de contenido multimedia que se estaba reproduciendo antes de apagar y reiniciar el vehículo.car-media-common
proporciona métodos convenientes para enumerar, conectar e interactuar con apps multimedia.
A continuación, se proporcionan lineamientos específicos para la implementación de comandos de interacción por voz comunes.
Obtén una lista de las fuentes de contenido multimedia instaladas
Las fuentes de contenido multimedia se pueden detectar con PackageManager
y filtrar los servicios que coincidan con MediaBrowserService.SERVICE_INTERFACE
.
En algunos vehículos, es posible que haya implementaciones especiales de servicios de navegador multimedia, que se deben excluir. Este es un ejemplo de esta lógica:
private Map<String, MediaSource> getAvailableMediaSources() { List<String> customMediaServices = Arrays.asList(mContext.getResources() .getStringArray(R.array.custom_media_packages)); List<ResolveInfo> mediaServices = mPackageManager.queryIntentServices( new Intent(MediaBrowserService.SERVICE_INTERFACE), PackageManager.GET_RESOLVED_FILTER); Map<String, MediaSource> result = new HashMap<>(); for (ResolveInfo info : mediaServices) { String packageName = info.serviceInfo.packageName; if (customMediaServices.contains(packageName)) { // Custom media sources should be ignored, as they might have a // specialized handling (e.g., radio). continue; } String className = info.serviceInfo.name; ComponentName componentName = new ComponentName(packageName, className); MediaSource source = MediaSource.create(mContext, componentName); result.put(source.getDisplayName().toString().toLowerCase(), source); } return result; }
Ten en cuenta que las fuentes de contenido multimedia se pueden instalar o desinstalar en cualquier momento. Para mantener una lista precisa, se recomienda implementar una instancia de BroadcastReceiver
para las acciones de intent ACTION_PACKAGE_ADDED
, ACTION_PACKAGE_CHANGED
, ACTION_PACKAGE_REPLACED
y ACTION_PACKAGE_REMOVED
.
Cómo conectarse a la fuente de contenido multimedia que se está reproduciendo
CarMediaService
proporciona métodos para obtener la fuente multimedia seleccionada actualmente y cuándo cambia. Estos cambios pueden ocurrir porque el usuario interactuó directamente con la IU o debido al uso de botones de hardware en el automóvil. Por otro lado, la biblioteca car-media-common ofrece formas convenientes de conectarse a una fuente de contenido multimedia determinada. A continuación, se muestra un fragmento simplificado para conectarse a la app de música seleccionada actualmente:
public class MediaActuator implements MediaBrowserConnector.onConnectedBrowserChanged { private final Car mCar; private CarMediaManager mCarMediaManager; private MediaBrowserConnector mBrowserConnector; … public void initialize(Context context) { mCar = Car.createCar(context); mBrowserConnector = new MediaBrowserConnector(context, this); mCarMediaManager = (CarMediaManager) mCar.getCarManager(Car.CAR_MEDIA_SERVICE); mBrowserConnector.connectTo(mCarMediaManager.getMediaSource()); … } @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { // TODO: Handle connected/disconnected browser } … }
Controla la reproducción de la fuente de contenido multimedia que se está reproduciendo
Con un MediaBrowserCompat
conectado, es fácil enviar comandos de control de transporte a la app de destino. Este es un ejemplo simplificado:
public class MediaActuator … { … private MediaControllerCompat mMediaController; @Override public void onConnectedBrowserChanged( @Nullable MediaBrowserCompat browser) { if (browser != null && browser.isConnected()) { mMediaController = new MediaControllerCompat(mContext, browser.getSessionToken()); } else { mMediaController = null; } } private boolean playSongOnCurrentSource(String song) { if (mMediaController == null) { // No source selected. return false; } MediaControllerCompat.TransportControls controls = mMediaController.getTransportControls(); PlaybackStateCompat state = controller.getPlaybackState(); if (state == null || ((state.getActions() & PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH) == 0)) { // Source can't play from search return false; } controls.playFromSearch(query, null); return true; } … }
Controla los comandos de la fuente de contenido multimedia local (radio, reproductor de CD, Bluetooth y USB)
Las fuentes de contenido multimedia locales exponen su funcionalidad al sistema con las mismas APIs de MediaSession y MediaBrowse que se detallaron anteriormente. Para adaptarse a las particularidades de cada tipo de hardware, estos servicios de MediaBrowse usan convenciones específicas para organizar su información y comandos multimedia.
Controla la radio
El filtro de intents ACTION_PLAY_BROADCASTRADIO
puede identificar MediaBrowserService de radio. Se espera que sigan los controles de reproducción y la estructura de navegación de contenido multimedia que se describen en Cómo implementar la radio. AAOS ofrece la biblioteca car-broadcastradio-support
que contiene constantes y métodos para ayudar a los OEM a crear implementaciones de MediaBrowseService para sus propios servicios de radio que siguen el protocolo definido y proporciona compatibilidad con apps que consumen su árbol de navegación (por ejemplo, VIA).
Controla la entrada auxiliar, el audio de CD y el contenido multimedia USB
No hay una implementación predeterminada de estas fuentes multimedia como parte de AOSP. El enfoque sugerido es el siguiente:
- Hacer que los OEM implementen servicios multimedia para cada uno de ellos Para obtener más información, consulta Cómo compilar apps de música para vehículos.
- Estas implementaciones de MediaBrowseService se identificarían y se responderían en las acciones de intent definidas en Intents de reproducción generales.
- Estos servicios expondrán un árbol de navegación según los lineamientos descritos en Otros tipos de fuentes.
Controla Bluetooth
El contenido multimedia Bluetooth se expone a través del perfil Bluetooth AVRCP. Para facilitar el acceso a esta funcionalidad, el AAOS incluye una implementación de MediaBrowserService y MediaSession que abstrae los detalles de la comunicación (consulta packages/apps/Bluetooth).
La estructura de árbol del navegador multimedia correspondiente se define en la clase BrowseTree. Los comandos de control de reproducción se pueden entregar de manera similar a cualquier otra app mediante su implementación de MediaSession.
Controla los comandos de transmisión de contenido multimedia
Para implementar la transmisión de contenido multimedia del servidor, el VIA debe convertirse en una fuente de contenido multimedia y, luego, implementar las APIs de MediaBrowse y MediaSession. Consulta Cómo compilar apps de música para vehículos. Con la implementación de estas APIs, una app de control por voz podría hacer lo siguiente (entre otras cosas):
- Participar sin problemas en la selección de fuentes de contenido multimedia
- Se reanudará automáticamente después de reiniciar el automóvil.
- Proporciona control de reproducción y navegación con la IU de Media Center
- Cómo recibir eventos de botones de medios de hardware estándar
Cómo entregar comandos de navegación
No hay una forma estandarizada de interactuar con todas las apps de navegación. Para las integraciones con Google Maps, consulta Intents de Google Maps para Android Automotive. Para las integraciones con otras apps, comunícate directamente con los desarrolladores de la app. Antes de iniciar un intent en cualquier app (incluida Google Maps), verifica que se pueda resolver (consulta Solicitudes de intent). Esto crea la oportunidad de informar al usuario en caso de que la app de destino no esté disponible.
Cómo entregar comandos del vehículo
El acceso a las propiedades del vehículo para la lectura y la escritura se proporciona a través de CarPropertyManager.
Los tipos de propiedades de los vehículos, su implementación y otros detalles se explican en Configuraciones de propiedades. Para obtener una descripción precisa de las propiedades que admite Android, es mejor consultar directamente hardware/interfaces/automotive/vehicle/2.0/types.hal
.
La enumeración VehicleProperty que se define allí contiene propiedades estándar y específicas del proveedor, tipos de datos, modo de cambio, unidades y definición de acceso de lectura y escritura.
Para acceder a estas mismas constantes desde Java, puedes usar VehiclePropertyIds y sus clases complementarias. Las diferentes propiedades tienen diferentes permisos de Android que controlan su acceso. Estos permisos se declaran en el manifiesto de CarService, y la asignación entre propiedades y permisos se describe en el Javadoc de VehiclePropertyIds y se aplica en PropertyHalServiceIds.
Cómo leer una propiedad de vehículo
El siguiente es un ejemplo que muestra cómo leer la velocidad del vehículo:
public class CarActuator ... { private final Car mCar; private final CarPropertyManager mCarPropertyManager; private final TextToSpeech mTTS; /** Global VHAL area id */ public static final int GLOBAL_AREA_ID = 0; public CarActuator(Context context, TextToSpeech tts) { mCar = Car.createCar(context); mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE); mTTS = tts; ... } @Nullable private void getSpeedInMetersPerSecond() { if (!mCarPropertyManager.isPropertyAvailable(VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID)) { mTTS.speak("I'm sorry, but I can't read the speed of this vehicle"); return; } // Data type and unit can be found in // automotive/vehicle/2.0/types.hal float speedInMps = mCarPropertyManager.getFloatProperty( VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID); int speedInMph = (int)(speedInMetersPerSecond * 2.23694f); mTTS.speak(String.format("Sure. Your current speed is %d miles " + "per hour", speedInUserUnit); } ... }
Cómo establecer una propiedad de vehículo
En el siguiente ejemplo, se muestra cómo encender y apagar el aire acondicionado frontal.
public class CarActuator … { … private void changeFrontAC(boolean turnOn) { List<CarPropertyConfig> configs = mCarPropertyManager .getPropertyList(new ArraySet<>(Arrays.asList( VehiclePropertyIds.HVAC_AC_ON))); if (configs == null || configs.size() != 1) { mTTS.speak("I'm sorry, but I can't control the AC of your vehicle"); return; } // Find the front area Ids for the AC property. int[] areaIds = configs.get(0).getAreaIds(); List<Integer> areasToChange = new ArrayList<>(); for (int areaId : areaIds) { if ((areaId & (VehicleAreaSeat.SEAT_ROW_1_CENTER | VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_RIGHT)) == 0) { continue; } boolean isACInAreaAlreadyOn = mCarPropertyManager .getBooleanProperty(VehiclePropertyIds.HVAC_AC_ON, areaId); if ((!isACInAreaAlreadyOn && turnOn) || (isACInAreaAlreadyOn && !turnOn)) { areasToChange.add(areaId); } } if (areasToChange.isEmpty()) { mTTS.speak(String.format("The AC is already %s", turnOn ? "on" : "off")); return; } for (int areaId : areasToChange) { mCarPropertyManager.setBooleanProperty( VehiclePropertyIds.HVAC_AC_ON, areaId, turnOn); } mTTS.speak(String.format("Okay, I'm turning your front AC %s", turnOn ? "on" : "off")); } … }
Ejecuta comandos de comunicación
Cómo controlar los comandos de mensajería
Los VIA deben controlar los mensajes entrantes siguiendo el flujo "Presiona para leer" que se describe en Presiona para leer con el Asistente de voz, que puede controlar de forma opcional el envío de respuestas al remitente del mensaje entrante.
Además, los VIA pueden usar SmsManager
(parte del paquete android.telephony
) para redactar y enviar mensajes SMS directamente desde el vehículo o a través de Bluetooth.
Cómo controlar los comandos de llamada
De manera similar, las VIAs pueden usar TelephonyManager
para realizar llamadas telefónicas y llamar al número de buzón de voz del usuario. En estos casos, los IVAs interactuarán con la pila de telefonía directamente o con la app de Car Dialer. En cualquier caso, la app de Car Dialer debería ser la que le muestre al usuario la IU relacionada con las llamadas de voz.
Cómo completar otros comandos
Para obtener una lista de otros posibles puntos de integración entre el VIA y el sistema, consulta la lista de intents de Android conocidos. Muchos comandos del usuario se pueden resolver del servidor (por ejemplo, leer los correos electrónicos y los eventos de calendario de los usuarios) y no requieren ninguna interacción con el sistema, excepto la interacción por voz.
Acciones envolventes (mostrar contenido visual)
Cuando mejora la comprensión o las acciones del usuario, un VIA puede proporcionar contenido visual complementario en la pantalla del vehículo. Para minimizar la distracción del conductor, mantén ese contenido simple, breve y práctico. Para obtener detalles sobre los lineamientos de IU/UX sobre acciones envolventes, consulta Asistentes precargados: Orientación sobre UX.
Para habilitar la personalización y la coherencia con el resto del diseño de la unidad principal (HU), los VIA deben usar componentes de la biblioteca de la IU del automóvil para la mayoría de los elementos de la IU. Para obtener más información, consulta Personalización.