Exécuter les commandes

Cette page décrit comment exécuter des commandes avec une interaction vocale.

Exécuter les commandes multimédias

Les commandes liées aux médias peuvent être divisées en trois groupes différents :

  • Sources multimédias externes (telles que Spotify installé dans AAOS).
  • Sources multimédias back-end (telles que la musique diffusée via le VIA).
  • Sources médiatiques locales (telles que l'autoradio).

Gérer les commandes de sources multimédias externes

Les sources multimédias externes sont définies comme des applications Android prenant en charge les API MediaSessionCompat et MediaBrowseCompat (reportez-vous à Créer des applications multimédias pour les voitures pour une explication détaillée sur l'utilisation de ces API).

Important : Pour qu'une application assistante se connecte au MediaBrowseService de toutes les applications multimédias installées dans le système, elle doit :

  1. Être installé avec une signature système (voir les directives de développement d'applications multimédias pour AAOS et l'exemple de code PackageValidator ).
  2. Détenez l’autorisation privilégiée du système android.permission.MEDIA_CONTENT_CONTROL (voir Accorder des autorisations privilégiées au système ).

En plus de MediaBrowserCompat et MediaControllerCompat , AAOS fournit les éléments suivants :

  • CarMediaService fournit des informations centralisées sur la source multimédia actuellement sélectionnée. Ceci est également utilisé pour reprendre une source multimédia en cours de lecture après l'arrêt-redémarrage de la voiture.
  • car-media-common fournit des méthodes pratiques pour répertorier, se connecter et interagir avec les applications multimédias.

Vous trouverez ci-dessous des lignes directrices spécifiques à la mise en œuvre de commandes d'interaction vocale courantes.

Obtenez une liste des sources multimédias installées

Les sources multimédias peuvent être détectées à l'aide PackageManager et en filtrant les services correspondant à MediaBrowserService.SERVICE_INTERFACE . Dans certaines voitures, il peut y avoir des implémentations spéciales de services de navigateur multimédia, qui doivent être exclues. Voici un exemple de cette logique :

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;
}

Sachez que les sources multimédias peuvent être installées ou désinstallées à tout moment. Afin de conserver une liste précise, il est recommandé d'implémenter une instance BroadcastReceiver pour les actions d'intention ACTION_PACKAGE_ADDED , ACTION_PACKAGE_CHANGED , ACTION_PACKAGE_REPLACED et ACTION_PACKAGE_REMOVED .

Se connecter à la source multimédia en cours de lecture

CarMediaService fournit des méthodes pour obtenir la source multimédia actuellement sélectionnée et le moment où cette source multimédia change. Ces changements peuvent se produire parce que l'utilisateur a interagi directement avec l'interface utilisateur ou en raison de l'utilisation de boutons matériels dans la voiture. D'un autre côté, la bibliothèque car-media-common offre des moyens pratiques de se connecter à une source multimédia donnée. Voici un extrait simplifié expliquant comment se connecter à l'application multimédia actuellement sélectionnée :

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
    }

    …
}

Contrôler la lecture de la source multimédia en cours de lecture

Avec un MediaBrowserCompat connecté, il est facile d'envoyer des commandes de contrôle de transport à l'application cible. Voici un exemple simplifié :

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;
    }

    …
}

Gérer les commandes des sources multimédias locales (radio, lecteur CD, Bluetooth, USB)

Les sources multimédias locales exposent leurs fonctionnalités au système à l'aide des mêmes API MediaSession et MediaBrowse détaillées ci-dessus. Pour s'adapter aux particularités de chaque type de matériel, ces services MediaBrowse utilisent des conventions spécifiques pour organiser leurs informations et leurs commandes multimédias.

Poignée radio

Radio MediaBrowseService peut être identifié par le filtre d'intention ACTION_PLAY_BROADCASTRADIO . Ils doivent suivre les commandes de lecture et la structure de navigation multimédia décrites dans Implement radio . AAOS propose la bibliothèque car-broadcastradio-support contenant des constantes et des méthodes pour aider les OEM à créer des implémentations MediaBrowseService pour leurs propres services radio qui suivent le protocole défini, et prend en charge les applications consommant leur arborescence de navigation (par exemple, les VIA).

Gérer l'entrée auxiliaire, le CD audio et le support USB

Il n’y a pas d’implémentation par défaut de ces sources multimédias dans le cadre de l’AOSP. L’approche suggérée est la suivante :

Gérer le Bluetooth

Le contenu multimédia Bluetooth est exposé via le profil Bluetooth AVRCP. Afin de faciliter l'accès à cette fonctionnalité, AAOS inclut une implémentation MediaBrowserService et MediaSession qui extrait les détails de la communication (voir packages/apps/Bluetooth ).

La structure arborescente respective du navigateur multimédia est définie dans la classe BrowseTree . Les commandes de contrôle de lecture peuvent être transmises de la même manière à n'importe quelle autre application, en utilisant son implémentation MediaSession.

Gérer les commandes multimédias en streaming

Pour mettre en œuvre le streaming multimédia côté serveur, le VIA doit devenir lui-même une source multimédia, en implémentant les API MediaBrowse et MediaSession. Reportez-vous à Créer des applications multimédias pour les voitures . En implémentant ces API, une application de commande vocale serait capable (entre autres) :

  • Participez en toute transparence à la sélection des sources média
  • Être automatiquement repris après le redémarrage de la voiture
  • Fournir un contrôle de lecture et de navigation à l'aide de l'interface utilisateur Media Center
  • Recevoir les événements des boutons multimédias matériels standard

Il n’existe pas de manière standardisée d’interagir avec toutes les applications de navigation. Pour les intégrations avec Google Maps, consultez Google Maps pour Android Automotive Intents . Pour les intégrations avec d'autres applications, contactez directement les développeurs d'applications. Avant de lancer une intention vers une application (y compris Google Maps), vérifiez que l'intention peut être résolue (voir Demandes d'intention ). Cela crée la possibilité d'informer l'utilisateur si l'application cible n'est pas disponible.

Exécuter les commandes du véhicule

L'accès aux propriétés du véhicule en lecture et en écriture est fourni via CarPropertyManager . Les types de propriétés du véhicule, sa mise en œuvre et d'autres détails sont expliqués dans Configurations des propriétés . Pour une description précise des propriétés prises en charge par Android, il est préférable de se référer directement à hardware/interfaces/automotive/vehicle/2.0/types.hal . L'énumération VehicleProperty définie ici contient à la fois des propriétés standard et spécifiques au fournisseur, des types de données, un mode de modification, des unités et une définition d'accès en lecture/écriture.

Pour accéder à ces mêmes constantes depuis Java, vous pouvez utiliser VehiclePropertyIds et ses classes compagnons. Différentes propriétés disposent de différentes autorisations Android contrôlant leur accès. Ces autorisations sont déclarées dans le manifeste CarService et le mappage entre les propriétés et les autorisations décrit dans le Javadoc VehiclePropertyIds et appliqué dans PropertyHalServiceIds .

Lire la propriété d'un véhicule

Voici un exemple montrant comment lire la vitesse du véhicule :

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);
    }

    ...
}

Définir une propriété de véhicule

Ce qui suit est un exemple montrant comment allumer et éteindre le courant alternatif avant.

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"));
    }

    …
}

Exécuter les commandes de communication

Gérer les commandes de messagerie

Les VIA doivent gérer les messages entrants en suivant le flux « appuyer pour lire » décrit dans Assistant vocal Tap-to-Read , qui peut éventuellement gérer le renvoi des réponses à l'expéditeur du message entrant. De plus, les VIA peuvent utiliser SmsManager (qui fait partie du package android.telephony ) pour composer et envoyer des messages SMS directement depuis la voiture ou via Bluetooth.

Gérer les commandes d'appel

De la même manière, les VIA peuvent utiliser TelephonyManager pour passer des appels téléphoniques et appeler le numéro de messagerie vocale de l'utilisateur. Dans ces cas, les VIA interagiront directement avec la pile téléphonique ou avec l’application Car Dialer. Dans tous les cas, l'application Car Dialer doit être celle qui affiche l'interface utilisateur liée aux appels vocaux à l'utilisateur.

Exécuter d'autres commandes

Pour une liste d'autres points d'intégration possibles entre le VIA et le système, consultez la liste des intentions Android bien connues. De nombreuses commandes utilisateur peuvent être résolues côté serveur (par exemple, lire les e-mails des utilisateurs et les événements du calendrier) et ne nécessitent aucune interaction avec le système autre que l'interaction vocale elle-même.

Actions immersives (afficher du contenu visuel)

Lorsqu'il améliore les actions ou la compréhension de l'utilisateur, un VIA peut fournir un contenu visuel supplémentaire sur l'écran de la voiture. Pour minimiser la distraction du conducteur, gardez ce contenu simple, bref et exploitable. Pour plus de détails sur les directives UI/UX sur les actions immersives, consultez Assistants préchargés : conseils UX .

Pour permettre la personnalisation et la cohérence avec le reste de la conception de l'unité principale (HU), les VIA doivent utiliser les composants de la bibliothèque d'interface utilisateur de voiture pour la plupart des éléments de l'interface utilisateur. Pour plus de détails, voir Personnalisation .