Auf dieser Seite wird beschrieben, wie Sie Befehle mit Sprachinteraktion ausführen.
Erfüllen Sie Medienbefehle
Medienbezogene Befehle können in drei verschiedene Gruppen unterteilt werden:
- Externe Medienquellen (z. B. Spotify, installiert in AAOS).
- Backend-Medienquellen (z. B. über VIA gestreamte Musik).
- Lokale Medienquellen (z. B. Autoradio).
Behandeln Sie Befehle für externe Medienquellen
Externe Medienquellen werden als Android-Apps definiert, die die APIs MediaSessionCompat
und MediaBrowseCompat
unterstützen (eine ausführliche Erläuterung zur Verwendung dieser APIs finden Sie unter Erstellen von Medien-Apps für Autos ).
Wichtig: Damit eine Assistenten-App eine Verbindung zum MediaBrowseService
aller im System installierten Medien-Apps herstellen kann, muss sie:
- Muss systemsigniert installiert werden (siehe Richtlinien zur Medienanwendungsentwicklung für AAOS und den Beispiel-
PackageValidator
Code). - Halten Sie die systemprivilegierte Berechtigung
android.permission.MEDIA_CONTENT_CONTROL
(siehe Erteilen systemprivilegierter Berechtigungen ).
Zusätzlich zu MediaBrowserCompat
und MediaControllerCompat
bietet AAOS Folgendes:
-
CarMediaService
stellt zentralisierte Informationen zur aktuell ausgewählten Medienquelle bereit. Dies wird auch verwendet, um eine zuvor wiedergegebene Medienquelle nach dem Herunterfahren und Neustarten des Fahrzeugs fortzusetzen. -
car-media-common
bietet praktische Methoden zum Auflisten, Verbinden und Interagieren mit Medien-Apps.
Nachfolgend finden Sie Richtlinien speziell für die Implementierung allgemeiner Sprachinteraktionsbefehle.
Rufen Sie eine Liste der installierten Medienquellen ab
Medienquellen können mithilfe von PackageManager
erkannt werden und nach Diensten gefiltert werden, die dem MediaBrowserService.SERVICE_INTERFACE
entsprechen. In einigen Autos gibt es möglicherweise spezielle Implementierungen von Medienbrowserdiensten, die ausgeschlossen werden sollten. Hier ist ein Beispiel für diese Logik:
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; }
Beachten Sie, dass Medienquellen jederzeit installiert oder deinstalliert werden können. Um eine genaue Liste zu verwalten, wird empfohlen, eine BroadcastReceiver
Instanz für die Intent-Aktionen ACTION_PACKAGE_ADDED
, ACTION_PACKAGE_CHANGED
, ACTION_PACKAGE_REPLACED
und ACTION_PACKAGE_REMOVED
zu implementieren.
Stellen Sie eine Verbindung zur aktuell wiedergegebenen Medienquelle her
CarMediaService
stellt Methoden bereit, um die aktuell ausgewählte Medienquelle abzurufen und wenn sich diese Medienquelle ändert. Diese Änderungen können auftreten, weil der Benutzer direkt mit der Benutzeroberfläche interagiert oder Hardwaretasten im Auto verwendet. Andererseits bietet die Car-Media-Common-Bibliothek bequeme Möglichkeiten, eine Verbindung zu einer bestimmten Medienquelle herzustellen. Hier ist ein vereinfachter Ausschnitt zum Herstellen einer Verbindung mit der aktuell ausgewählten Medien-App:
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 } … }
Steuern Sie die Wiedergabe der aktuell wiedergegebenen Medienquelle
Mit einem verbundenen MediaBrowserCompat
ist es einfach , Transportsteuerungsbefehle an die Ziel-App zu senden. Hier ist ein vereinfachtes Beispiel:
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; } … }
Behandeln Sie lokale Medienquellenbefehle (Radio, CD-Player, Bluetooth, USB).
Lokale Medienquellen stellen ihre Funktionalität dem System über dieselben MediaSession- und MediaBrowse-APIs zur Verfügung, die oben beschrieben wurden. Um den Besonderheiten jedes Hardwaretyps gerecht zu werden, verwenden diese MediaBrowse-Dienste bestimmte Konventionen, um ihre Informationen und Medienbefehle zu organisieren.
Griffradio
Radio MediaBrowseService kann durch den Absichtsfilter ACTION_PLAY_BROADCASTRADIO
identifiziert werden. Von ihnen wird erwartet, dass sie den Wiedergabesteuerungen und der Mediensuchstruktur folgen, die unter „Radio implementieren“ beschrieben sind. AAOS bietet die Bibliothek car-broadcastradio-support
mit Konstanten und Methoden, die OEMs bei der Erstellung von MediaBrowseService-Implementierungen für ihre eigenen Radiodienste unterstützen, die dem definierten Protokoll folgen, und bietet Unterstützung für Apps, die ihren Suchbaum nutzen (z. B. VIAs).
Behandelt Aux-Eingang, CD-Audio und USB-Medien
Es gibt keine Standardimplementierung dieser Medienquellen als Teil von AOSP. Der vorgeschlagene Ansatz ist:
- Lassen Sie OEMs Mediendienste für jeden von ihnen implementieren. Einzelheiten finden Sie unter Erstellen von Medien-Apps für Autos .
- Diese MediaBrowseService-Implementierungen würden in den unter „Allgemeine Wiedergabeabsichten“ definierten Absichtsaktionen identifiziert und darauf reagiert.
- Diese Dienste würden einen Suchbaum gemäß den unter „Andere Quelltypen“ beschriebenen Richtlinien verfügbar machen.
Behandeln Sie Bluetooth
Bluetooth-Medieninhalte werden über das AVRCP-Bluetooth-Profil verfügbar gemacht. Um den Zugriff auf diese Funktionalität zu erleichtern, enthält AAOS eine MediaBrowserService- und MediaSession-Implementierung, die die Kommunikationsdetails abstrahiert (siehe Pakete/Apps/Bluetooth ).
Die jeweilige Baumstruktur des Medienbrowsers wird in der Klasse BrowseTree definiert. Befehle zur Wiedergabesteuerung können mithilfe der MediaSession-Implementierung ähnlich wie bei jeder anderen App bereitgestellt werden.
Behandeln Sie Streaming-Medienbefehle
Um serverseitiges Medienstreaming zu implementieren, muss die VIA selbst zu einer Medienquelle werden und MediaBrowse und MediaSession API implementieren. Siehe Erstellen von Medien-Apps für Autos . Durch die Implementierung dieser APIs wäre eine Sprachsteuerungs-App (unter anderem) in der Lage:
- Beteiligen Sie sich nahtlos an der Auswahl der Medienquelle
- Wird nach dem Neustart des Fahrzeugs automatisch fortgesetzt
- Bieten Sie Wiedergabe- und Browsing-Steuerung über die Media Center-Benutzeroberfläche
- Empfangen Sie Standard-Hardware-Medientastenereignisse
Erfüllen Sie Navigationsbefehle
Es gibt keine standardisierte Art der Interaktion mit allen Navigations-Apps. Informationen zur Integration mit Google Maps finden Sie unter Google Maps für Android Automotive Intents . Für Integrationen mit anderen Apps wenden Sie sich direkt an die App-Entwickler. Bevor Sie eine Absicht für eine App (einschließlich Google Maps) starten, überprüfen Sie, ob die Absicht aufgelöst werden kann (siehe Absichtsanfragen ). Dadurch besteht die Möglichkeit, den Benutzer zu informieren, falls die Ziel-App nicht verfügbar ist.
Fahrzeugbefehle ausführen
Der Lese- und Schreibzugriff auf Fahrzeugeigenschaften erfolgt über CarPropertyManager . Fahrzeugeigenschaftentypen, ihre Implementierung und andere Details werden in Eigenschaftskonfigurationen erläutert. Eine genaue Beschreibung der von Android unterstützten Eigenschaften finden Sie am besten direkt unter hardware/interfaces/automotive/vehicle/2.0/types.hal
. Die dort definierte VehicleProperty- Enumeration enthält sowohl Standard- als auch herstellerspezifische Eigenschaften, Datentypen, Änderungsmodus, Einheiten und Lese-/Schreibzugriffsdefinition.
Um von Java aus auf dieselben Konstanten zuzugreifen, können Sie VehiclePropertyIds und die dazugehörigen Klassen verwenden. Verschiedene Eigenschaften verfügen über unterschiedliche Android-Berechtigungen, die ihren Zugriff steuern. Diese Berechtigungen werden im CarService-Manifest deklariert und die Zuordnung zwischen Eigenschaften und Berechtigungen wird im Javadoc VehiclePropertyIds beschrieben und in PropertyHalServiceIds erzwungen.
Lesen Sie eine Fahrzeugeigenschaft
Das folgende Beispiel zeigt, wie die Fahrzeuggeschwindigkeit abgelesen werden kann:
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); } ... }
Legen Sie eine Fahrzeugeigenschaft fest
Das folgende Beispiel zeigt, wie die vordere Klimaanlage ein- und ausgeschaltet wird.
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")); } … }
Kommunikationsbefehle ausführen
Behandeln Sie Messaging-Befehle
VIAs müssen eingehende Nachrichten nach dem im Sprachassistenten „Tap-to-Read“ beschriebenen „Tap-to-Read “-Ablauf verarbeiten, der optional das Zurücksenden von Antworten an den Absender der eingehenden Nachricht übernehmen kann. Darüber hinaus können VIAs SmsManager
(Teil des android.telephony
Pakets) verwenden, um SMS-Nachrichten direkt aus dem Auto oder über Bluetooth zu verfassen und zu versenden.
Behandeln Sie Anrufbefehle
Auf ähnliche Weise können VIAs TelephonyManager
verwenden, um Telefonanrufe zu tätigen und die Voicemail-Nummer des Benutzers anzurufen. In diesen Fällen interagieren VIAs direkt mit dem Telefonie-Stack oder mit der Car Dialer-App. In jedem Fall sollte die Car Dialer-App diejenige sein, die dem Benutzer eine Benutzeroberfläche für Sprachanrufe anzeigt.
Erfülle andere Befehle
Eine Liste weiterer möglicher Integrationspunkte zwischen VIA und dem System finden Sie in der Liste bekannter Android-Intents . Viele Benutzerbefehle können serverseitig aufgelöst werden (z. B. das Lesen von Benutzer-E-Mails und Kalenderereignissen) und erfordern außer der Sprachinteraktion selbst keine Interaktionen mit dem System.
Immersive Aktionen (visuelle Inhalte anzeigen)
Wo es die Aktionen oder das Verständnis des Benutzers verbessert, kann ein VIA zusätzliche visuelle Inhalte auf dem Autobildschirm bereitstellen. Halten Sie solche Inhalte einfach, kurz und umsetzbar, um die Ablenkung des Fahrers zu minimieren. Einzelheiten zu UI/UX-Richtlinien für immersive Aktionen finden Sie unter Vorinstallierte Assistenten: UX-Anleitung .
Um eine individuelle Anpassung und Konsistenz mit dem restlichen Design der Head Unit (HU) zu ermöglichen, sollten VIAs für die meisten UI-Elemente Komponenten der Car UI Library verwenden. Einzelheiten finden Sie unter Anpassung .