Na tej stronie opisujemy, jak realizować polecenia za pomocą interakcji głosowej.
Realizowanie poleceń dotyczących multimediów
Polecenia związane z multimediami można podzielić na 3 różne grupy:
- zewnętrzne źródła multimediów (np. Spotify zainstalowane w AAOS);
- źródła multimediów w backendzie (np. muzyka przesyłana strumieniowo przez VIA);
- lokalne źródła multimediów (np. radio samochodowe).
Obsługa poleceń dotyczących zewnętrznych źródeł multimediów
Zewnętrzne źródła multimediów są definiowane jako aplikacje na Androida, które obsługują interfejsy API MediaSessionCompat
i MediaBrowseCompat (szczegółowe wyjaśnienie dotyczące korzystania z tych interfejsów API znajdziesz w artykule Tworzenie aplikacji do multimediów dla
samochodów).
Ważne: aby aplikacja asystenta mogła połączyć się z MediaBrowseService wszystkich zainstalowanych w systemie aplikacji do multimediów, musi:
- być zainstalowana jako aplikacja podpisana przez system (zapoznaj się z wytycznymi dotyczącymi tworzenia aplikacji do multimediów dla AAOS i przykładowym kodem
PackageValidator); - mieć uprawnienie systemowe
android.permission.MEDIA_CONTENT_CONTROL(zapoznaj się z artykułem Przyznawanie uprawnień systemowych).
Oprócz MediaBrowserCompat
i MediaControllerCompat,
AAOS udostępnia te elementy:
CarMediaServicezawiera scentralizowane informacje o aktualnie wybranym źródle multimediów. Jest też używany do wznawiania wcześniej odtwarzanego źródła multimediów po wyłączeniu i ponownym uruchomieniu samochodu.car-media-commonudostępnia wygodne metody do wyświetlania listy aplikacji do multimediów, łączenia się z nimi i interakcji z nimi.
Poniżej znajdziesz wytyczne dotyczące implementacji typowych poleceń interakcji głosowej.
Pobieranie listy zainstalowanych źródeł multimediów
Źródła multimediów można wykrywać za pomocą PackageManager,
i filtrowania usług pasujących do MediaBrowserService.SERVICE_INTERFACE.
W niektórych samochodach mogą występować specjalne implementacje usługi przeglądarki multimediów, które należy wykluczyć. Oto przykład takiej logiki:
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; }
Pamiętaj, że źródła multimediów można w każdej chwili zainstalować lub odinstalować. Aby zachować dokładną listę, zalecamy zaimplementowanie BroadcastReceiverinstancji dla działań intencji ACTION_PACKAGE_ADDED,
ACTION_PACKAGE_CHANGED,
ACTION_PACKAGE_REPLACED,
i ACTION_PACKAGE_REMOVED.
Łączenie się z aktualnie odtwarzanym źródłem multimediów
CarMediaService
udostępnia metody do pobierania aktualnie wybranego źródła multimediów i informacji o jego zmianie. Zmiany te mogą nastąpić, gdy użytkownik bezpośrednio korzysta z interfejsu lub używa przycisków sprzętowych w samochodzie. Z drugiej strony biblioteka car-media-common oferuje wygodne sposoby łączenia się z danym źródłem multimediów. Oto uproszczony fragment kodu pokazujący, jak połączyć się z aktualnie wybraną aplikacją do multimediów:
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 } … }
Sterowanie odtwarzaniem aktualnie odtwarzanego źródła multimediów
Po połączeniu z MediaBrowserCompat
można łatwo wysyłać polecenia sterowania transportem
do aplikacji docelowej. Oto uproszczony
przykład:
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; } … }
Obsługa poleceń dotyczących lokalnych źródeł multimediów (radio, odtwarzacz CD, Bluetooth, USB)
Lokalne źródła multimediów udostępniają swoje funkcje systemowi za pomocą tych samych interfejsów API MediaSession i MediaBrowse, które zostały opisane powyżej. Aby uwzględnić specyfikę każdego typu sprzętu, te usługi MediaBrowse używają określonych konwencji do organizowania informacji i poleceń dotyczących multimediów.
Obsługa radia
Usługę MediaBrowse radia można zidentyfikować za pomocą filtra intencji ACTION_PLAY_BROADCASTRADIO. Oczekuje się, że będą one zgodne z elementami sterującymi odtwarzaniem i strukturą przeglądania multimediów
opisanymi w artykule Implementowanie radia. AAOS udostępnia bibliotekę zawierającą stałe i metody, które pomagają producentom OEM tworzyć implementacje MediaBrowseService dla własnych usług radiowych zgodnych z określonym protokołem. Zapewnia też obsługę aplikacji korzystających z drzewa przeglądania (np. VIA).car-broadcastradio-support
Obsługa wejścia pomocniczego, dźwięku z płyty CD i multimediów z USB
W AOSP nie ma domyślnej implementacji tych źródeł multimediów. Sugerujemy te rozwiązania:
- Producenci OEM powinni zaimplementować usługi multimedialne dla każdego z tych źródeł. Więcej informacji znajdziesz w artykule Tworzenie aplikacji do multimediów dla samochodów.
- Te implementacje MediaBrowseService będą identyfikowane i obsługiwane w działaniach intencji zdefiniowanych w sekcji Ogólne intencje odtwarzania.
- Te usługi będą udostępniać drzewo przeglądania zgodnie z wytycznymi opisanymi w sekcji Inne typy źródeł.
Obsługa Bluetootha
Treści multimedialne Bluetootha są udostępniane za pomocą profilu Bluetooth AVRCP. Aby ułatwić dostęp do tej funkcji, AAOS zawiera implementację MediaBrowserService i MediaSession, która abstrahuje szczegóły komunikacji (zobacz packages/apps/Bluetooth).
Odpowiednia struktura drzewa przeglądarki multimediów jest zdefiniowana w klasie BrowseTree. Polecenia sterowania odtwarzaniem można dostarczać podobnie jak w przypadku innych aplikacji, używając implementacji MediaSession.
Obsługa poleceń dotyczących strumieniowania multimediów
Aby zaimplementować strumieniowanie multimediów po stronie serwera, VIA musi stać się źródłem multimediów, implementując interfejsy API MediaBrowse i MediaSession. Zapoznaj się z artykułem Tworzenie aplikacji do multimediów dla samochodów. Dzięki implementacji tych interfejsów API aplikacja do sterowania głosowego będzie mogła (między innymi):
- bezproblemowo uczestniczyć w wyborze źródła multimediów;
- być automatycznie wznawiana po ponownym uruchomieniu samochodu;
- zapewniać sterowanie odtwarzaniem i przeglądaniem za pomocą interfejsu Centrum multimediów;
- otrzymywać standardowe zdarzenia z przycisków sprzętowych multimediów.
Realizowanie poleceń dotyczących nawigacji
Nie ma standardowego sposobu interakcji ze wszystkimi aplikacjami do nawigacji. Informacje o integracji z Mapami Google znajdziesz w artykule Google Intencje Map Google na Androida. W przypadku integracji z innymi aplikacjami skontaktuj się bezpośrednio z ich deweloperami. Przed uruchomieniem intencji w dowolnej aplikacji (w tym w Mapach Google) sprawdź, czy można ją rozwiązać (zobacz Intencje Map Google). Dzięki temu możesz poinformować użytkownika, jeśli aplikacja docelowa jest niedostępna.
Realizowanie poleceń dotyczących pojazdu
Dostęp do właściwości pojazdu (zarówno do odczytu, jak i zapisu) jest zapewniany przez
CarPropertyManager.
Typy właściwości pojazdu, ich implementacja i inne szczegóły są opisane
w Konfiguracje
właściwości. Aby uzyskać dokładny opis właściwości obsługiwanych
przez Androida, najlepiej zapoznać się bezpośrednio z hardware/interfaces/automotive/vehicle/2.0/types.hal.
Zdefiniowany tam wyliczenie VehicleProperty
zawiera zarówno standardowe, jak i specyficzne dla dostawcy właściwości, typy danych, tryb zmiany, jednostki oraz definicję dostępu do odczytu i zapisu.
Aby uzyskać dostęp do tych samych stałych w Javie, możesz użyć VehiclePropertyIds i jego klas towarzyszących. Różne właściwości mają różne uprawnienia Androida, które kontrolują do nich dostęp. Te uprawnienia są zadeklarowane w manifeście CarService, a mapowanie między właściwościami i uprawnieniami jest opisane w dokumentacji Javadoc VehiclePropertyIds i wymuszane w PropertyHalServiceIds.
Odczytywanie właściwości pojazdu
Oto przykład pokazujący, jak odczytać prędkość pojazdu:
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); } ... }
Ustawianie właściwości pojazdu
Oto przykład pokazujący, jak włączyć i wyłączyć klimatyzację z przodu:
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"));
}
…
}Realizowanie poleceń dotyczących komunikacji
Obsługa poleceń dotyczących wiadomości
VIA muszą obsługiwać wiadomości przychodzące zgodnie z przepływem "kliknij, aby przeczytać" opisanym
w artykule Asystent głosowy
„Kliknij, aby przeczytać”. Opcjonalnie mogą też obsługiwać wysyłanie odpowiedzi do nadawcy wiadomości przychodzącej.
Dodatkowo VIA mogą używać SmsManager
(część pakietu android.telephony
) do tworzenia i wysyłania SMS-ów bezpośrednio z samochodu lub przez Bluetooth.
Obsługa poleceń dotyczących połączeń
Podobnie VIA mogą używać TelephonyManager
do wykonywania połączeń telefonicznych i dzwonienia na pocztę głosową użytkownika. W takich przypadkach VIA będą wchodzić w interakcję bezpośrednio ze stosem telefonicznym lub z aplikacją Car Dialer. W każdym przypadku to aplikacja Car Dialer powinna wyświetlać użytkownikowi interfejs związany z połączeniami głosowymi.
Realizowanie innych poleceń
Listę innych możliwych punktów integracji VIA z systemem znajdziesz w artykule Znane intencje Androida. Wiele poleceń użytkownika można rozwiązać po stronie serwera (np. odczytywanie e-maili i wydarzeń z kalendarza użytkowników) i nie wymagają one żadnych interakcji z systemem poza samą interakcją głosową.
Działania immersyjne (wyświetlanie treści wizualnych)
Jeśli to ułatwia użytkownikowi wykonywanie działań lub zrozumienie, VIA może wyświetlać dodatkowe treści wizualne na ekranie samochodu. Aby nie rozpraszać uwagi kierowcy, takie treści powinny być proste, krótkie i umożliwiać podjęcie działania. Szczegółowe informacje o wytycznych dotyczących interfejsu i UX w przypadku działań immersyjnych znajdziesz w artykule Wstępnie zainstalowani asystenci: wskazówki dotyczące UX.
Aby umożliwić dostosowanie i zachować spójność z resztą projektu jednostki głównej, VIA powinny używać komponentów biblioteki Car UI w przypadku większości elementów interfejsu. Więcej informacji znajdziesz w sekcji Dostosowywanie.