Cumplir comandos

Esta página describe cómo cumplir comandos con interacción de voz.

Cumplir comandos multimedia

El comando relacionado con los medios se puede dividir en tres grupos diferentes:

  • Fuentes de medios externos (como Spotify instalado en AAOS).
  • Fuentes de medios backend (como música transmitida a través de VIA).
  • Fuentes de medios locales (como radios de automóviles).

Manejar comandos de fuentes de medios externos

Las fuentes de medios externos se definen como aplicaciones de Android que admiten las API MediaSessionCompat y MediaBrowseCompat (consulte Crear aplicaciones multimedia para automóviles para obtener una explicación detallada sobre el uso de estas API).

Importante: Para que una aplicación de asistente se conecte al MediaBrowseService de todas las aplicaciones multimedia instaladas en el sistema, debe:

  1. Estar instalado como firmado por el sistema (consulte las pautas de desarrollo de aplicaciones multimedia para AAOS y el código de muestra PackageValidator ).
  2. Mantenga android.permission.MEDIA_CONTENT_CONTROL permiso con privilegios del sistema (consulte Conceder permisos con privilegios del sistema ).

Además de MediaBrowserCompat y MediaControllerCompat , AAOS proporciona lo siguiente:

  • CarMediaService proporciona información centralizada sobre la fuente de medios seleccionada actualmente. Esto también se utiliza para reanudar una fuente multimedia que se estaba reproduciendo anteriormente después de apagar y reiniciar el automóvil.
  • car-media-common proporciona métodos convenientes para enumerar, conectarse e interactuar con aplicaciones multimedia.

A continuación se proporcionan pautas específicas para la implementación de comandos de interacción de voz comunes.

Obtenga una lista de fuentes de medios instaladas

Las fuentes de medios se pueden detectar utilizando PackageManager y filtrando servicios que coincidan con MediaBrowserService.SERVICE_INTERFACE . En algunos automóviles puede haber algunas implementaciones de servicios de navegador de medios especiales, que deberían excluirse. 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;
}

Tenga en cuenta que las fuentes multimedia pueden instalarse o desinstalarse en cualquier momento. Para mantener una lista precisa, se recomienda implementar una instancia BroadcastReceiver para las acciones de intención ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACED y ACTION_PACKAGE_REMOVED .

Conéctese a la fuente multimedia que se está reproduciendo actualmente

CarMediaService proporciona métodos para obtener la fuente de medios seleccionada actualmente y cuándo cambia esta fuente de medios. Estos cambios podrían ocurrir porque el usuario interactuó directamente con la interfaz de usuario 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 medios determinada. Aquí hay un fragmento simplificado sobre cómo conectarse a la aplicación multimedia actualmente seleccionada:

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
    }

    …
}

Controlar la reproducción de la fuente multimedia que se está reproduciendo actualmente

Con un MediaBrowserCompat conectado, es fácil enviar comandos de control de transporte a la aplicación de destino. Aquí hay 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;
    }

    …
}

Manejar comandos de fuentes de medios locales (radio, reproductor de CD, Bluetooth, USB)

Las fuentes de medios locales exponen su funcionalidad al sistema utilizando las mismas API MediaSession y MediaBrowse detalladas anteriormente. Para adaptarse a las particularidades de cada tipo de hardware, estos servicios MediaBrowse utilizan convenciones específicas para organizar su información y comandos multimedia.

manejar la radio

Radio MediaBrowseService se puede identificar mediante el filtro de intención ACTION_PLAY_BROADCASTRADIO . Se espera que sigan los controles de reproducción y la estructura de exploración de medios que se describen en 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 brinda soporte para aplicaciones que consumen su árbol de navegación (por ejemplo, VIA).

Maneja entrada auxiliar, audio CD y medios USB

No existe una implementación predeterminada de estas fuentes de medios como parte de AOSP. El enfoque sugerido es:

Manejar Bluetooth

El contenido multimedia Bluetooth se expone a través del perfil Bluetooth AVRCP. Para facilitar el acceso a esta funcionalidad, AAOS incluye una implementación MediaBrowserService y MediaSession que abstrae los detalles de la comunicación (consulte paquetes/aplicaciones/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 aplicación, utilizando su implementación MediaSession.

Manejar comandos de transmisión de medios

Para implementar la transmisión de medios del lado del servidor, VIA debe convertirse en una fuente de medios, implementando MediaBrowse y MediaSession API. Consulte Crear aplicaciones multimedia para automóviles . Al implementar estas API, una aplicación de control por voz podría (entre otras cosas):

  • Participe sin problemas en la selección de fuentes de medios.
  • Se reanudará automáticamente después de reiniciar el automóvil.
  • Proporciona control de reproducción y navegación mediante la interfaz de usuario de Media Center.
  • Reciba eventos de botones multimedia de hardware estándar

No existe una forma estandarizada de interactuar con todas las aplicaciones de navegación. Para integraciones con Google Maps, consulte Google Maps para Android Automotive Intents . Para integraciones con otras aplicaciones, comuníquese directamente con los desarrolladores de la aplicación. Antes de iniciar un intent en cualquier aplicación (incluido Google Maps), verifique que el intent se pueda resolver (consulte Solicitudes de intent ). Esto crea la oportunidad de informar al usuario en caso de que la aplicación de destino no esté disponible.

Cumplir órdenes del vehículo

El acceso a las propiedades del vehículo tanto para lectura como para escritura se proporciona a través de CarPropertyManager . Los tipos de propiedades del vehículo, su implementación y otros detalles se explican en Configuraciones de propiedades . Para obtener una descripción precisa de las propiedades admitidas por Android, es mejor consultar directamente hardware/interfaces/automotive/vehicle/2.0/types.hal . La enumeración VehicleProperty definida allí contiene propiedades, tipos de datos, modo de cambio, unidades y definición de acceso de lectura/escritura tanto estándar como específicas del proveedor.

Para acceder a estas mismas constantes desde Java, puede utilizar VehiclePropertyIds y sus clases complementarias. Diferentes propiedades tienen diferentes permisos de Android que controlan su acceso. Estos permisos se declaran en el manifiesto CarService y la asignación entre propiedades y permisos se describe en el Javadoc VehiclePropertyIds y se aplica en PropertyHalServiceIds .

Leer la propiedad de un 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);
    }

    ...
}

Establecer una propiedad de vehículo

El siguiente es un ejemplo que 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"));
    }

    …
}

Cumplir comandos de comunicación

Manejar comandos de mensajería

Los VIA deben manejar los mensajes entrantes siguiendo el flujo de "tocar para leer" descrito en Asistente de voz Tocar para leer , que opcionalmente puede manejar 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 mediante Bluetooth.

Manejar comandos de llamada

De manera similar, los VIA pueden usar TelephonyManager para realizar llamadas telefónicas y llamar al número de correo de voz del usuario. En estos casos, los VIA interactuarán con la pila de telefonía directamente o con la aplicación Car Dialer. En cualquier caso, la aplicación Car Dialer debe ser la que muestre al usuario la interfaz de usuario relacionada con las llamadas de voz.

Cumplir otros comandos

Para obtener una lista de otros posibles puntos de integración entre VIA y el sistema, consulte la lista de intents de Android conocidos. Muchos comandos de usuario se pueden resolver en el lado del servidor (por ejemplo, leer los correos electrónicos de los usuarios y los eventos del calendario) y no requieren ninguna interacción con el sistema aparte de la interacción de voz en sí.

Acciones inmersivas (mostrar contenido visual)

Cuando mejora las acciones o la comprensión del usuario, un VIA puede proporcionar contenido visual complementario en la pantalla del automóvil. Para minimizar la distracción del conductor, mantenga dicho contenido simple, breve y práctico. Para obtener detalles sobre las pautas de UI/UX sobre acciones inmersivas, consulte Asistentes precargados: Guía de UX .

Para permitir la personalización y la coherencia con el resto del diseño de la unidad principal (HU), los VIA deben utilizar componentes de la biblioteca de UI del automóvil para la mayoría de los elementos de la UI. Para obtener más información, consulte Personalización .