En esta página, se describe cómo completar comandos con la interacción por voz.
Cómo completar comandos multimedia
Los comandos relacionados con los medios se pueden dividir en tres grupos diferentes:
- Fuentes de medios 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 multimedia externas
Las fuentes de medios externas se definen como apps para Android que admiten las APIs de MediaSessionCompat y MediaBrowseCompat (consulta Cómo compilar apps de medios 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 de música instaladas en el sistema, debe cumplir con los siguientes requisitos:
- Debe instalarse como firmado por el sistema (consulta los lineamientos de desarrollo de aplicaciones multimedia para AAOS y el código de ejemplo
PackageValidator). - Tener el permiso
android.permission.MEDIA_CONTENT_CONTROLcon privilegios del sistema (consulta Cómo otorgar permisos con privilegios del sistema)
Además de MediaBrowserCompat y MediaControllerCompat, AAOS proporciona lo siguiente:
CarMediaServiceproporciona 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 anteriormente después de que se apaga y se vuelve a encender el automóvil.car-media-commonproporciona métodos convenientes para enumerar, conectar e interactuar con apps de medios.
A continuación, se proporcionan lineamientos específicos para la implementación de comandos comunes de interacción por voz.
Obtén una lista de las fuentes de medios instaladas
Las fuentes de medios se pueden detectar con PackageManager y filtrar los servicios que coincidan con MediaBrowserService.SERVICE_INTERFACE.
En algunos automóviles, es posible que haya implementaciones especiales del servicio de exploración de contenido multimedia que se deben excluir. A continuación, se muestra 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 medios 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.
Conéctate a la fuente de contenido multimedia que se está reproduciendo
CarMediaService proporciona métodos para obtener la fuente multimedia seleccionada actualmente y cuando cambia esta fuente multimedia. Estos cambios pueden ocurrir porque el usuario interactuó directamente con la IU o porque usó los botones de hardware del automóvil. Por otro lado, la biblioteca car-media-common ofrece formas convenientes de conectarse a una fuente de medios determinada. A continuación, se incluye un fragmento simplificado sobre cómo conectarse a la app de medios 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; } … }
Controlar los comandos de fuentes de medios locales (radio, reproductor de CD, Bluetooth, USB)
Las fuentes de medios 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 sus comandos multimedia.
Manija de la radio
El filtro de intents ACTION_PLAY_BROADCASTRADIO permite identificar el servicio Radio MediaBrowseService. Se espera que sigan los controles de reproducción y la estructura de exploración de contenido multimedia que se describen en Implementa 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 sigan el protocolo definido, y brinda compatibilidad con las apps que consumen su árbol de exploración (por ejemplo, los VIA).
Cómo controlar la entrada auxiliar, el audio de CD y el contenido multimedia USB
No hay una implementación predeterminada de estas fuentes de medios como parte del AOSP. El enfoque sugerido es el siguiente:
- Hacer que los OEM implementen servicios de medios 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 responderían en las acciones de intents definidas en Intents generales de Play.
- Estos servicios expondrían un árbol de exploración según los lineamientos que se describen en Otros tipos de fuentes.
Controla el Bluetooth
El contenido multimedia de Bluetooth se expone a través del perfil de Bluetooth AVRCP. Para facilitar el acceso a esta funcionalidad, AAOS incluye una implementación de MediaBrowserService y MediaSession que abstrae los detalles de comunicación (consulta packages/apps/Bluetooth).
La estructura de árbol del navegador de medios respectivo se define en la clase BrowseTree. Los comandos de control de reproducción se pueden entregar de manera similar a cualquier otra app, usando su implementación de MediaSession.
Cómo controlar 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, para ello, debe implementar las APIs de MediaBrowse y MediaSession. Consulta Cómo compilar apps de música para vehículos. Si se implementan estas APIs, una app de control por voz podría (entre otras cosas):
- Participa sin problemas en la selección de la fuente de medios
- Reanudarse 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 estándar de botones de medios de hardware
Cómo completar comandos de navegación
No existe 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 las apps. Antes de iniciar un intent en cualquier app (incluida Google Maps), verifica que el intent se pueda resolver (consulta Intents de Google Maps). Esto brinda la oportunidad de informar al usuario en caso de que la app de destino no esté disponible.
Cómo completar comandos de vehículos
El acceso a las propiedades del vehículo para lectura y escritura se proporciona a través de CarPropertyManager.
Los tipos de propiedades de 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, lo mejor es consultar directamente hardware/interfaces/automotive/vehicle/2.0/types.hal.
La enumeración VehicleProperty definida allí contiene propiedades estándar y específicas del proveedor, tipos de datos, modo de cambio, unidades y definición de acceso de lectura/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 del vehículo
A continuación, se muestra un ejemplo de 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 del vehículo
A continuación, se muestra un ejemplo de cómo encender y apagar el aire acondicionado delantero.
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"));
}
…
}Cumple con los comandos de comunicación
Cómo controlar los comandos de mensajería
Las VIA deben controlar los mensajes entrantes según el flujo de "tocar para leer" que se describe en Asistente por voz: Tocar para leer, 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 automóvil o por Bluetooth.
Cómo controlar los comandos de llamadas
De manera similar, las VIA pueden usar TelephonyManager para hacer llamadas telefónicas y llamar al número de correo de voz del usuario. En estos casos, los VIA interactuarán directamente con la pila de telefonía o con la app de Car Dialer. En cualquier caso, la app de Car Dialer debe ser la que muestre la IU relacionada con las llamadas de voz al usuario.
Ejecutar otros comandos
Para obtener una lista de otros posibles puntos de integración entre la VIA y el sistema, consulta la lista de intentos de Android conocidos. Muchos comandos del usuario se pueden resolver del lado del servidor (por ejemplo, leer los correos electrónicos y los eventos del calendario de los usuarios) y no requieren ninguna interacción con el sistema más allá de la interacción por voz en sí.
Acciones envolventes (muestran contenido visual)
Cuando mejora las acciones o la comprensión del usuario, una VIA puede proporcionar contenido visual complementario en la pantalla del automóvil. Para minimizar la distracción del conductor, mantén el contenido simple, breve y práctico. Para obtener detalles sobre los lineamientos de IU/UX en acciones inmersivas, consulta Asistentes precargados: Orientación sobre la UX.
Para permitir 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 Car UI para la mayoría de los elementos de la IU. Para obtener más información, consulta Personalización.