Realizowanie poleceń

Na tej stronie dowiesz się, jak wykonywać polecenia za pomocą interakcji głosowej.

Wykonywanie 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 na serwerze (np. muzyka przesyłana strumieniowo przez VIA);
  • lokalnych źródeł multimediów (np. radia samochodowego);

Obsługa poleceń zewnętrznego źródła multimediów

Zewnętrzne źródła multimediów to aplikacje na Androida, które obsługują interfejsy MediaSessionCompatMediaBrowseCompat API (szczegółowe wyjaśnienie korzystania z tych interfejsów API znajdziesz w artykule Tworzenie aplikacji multimedialnych na samochody).

Ważne: aby aplikacja asystenta mogła połączyć się z MediaBrowseService wszystkich zainstalowanych aplikacji multimedialnych w systemie, musi:

  1. musi być zainstalowana jako aplikacja podpisana przez system (wytyczne dotyczące tworzenia aplikacji multimedialnych na AAOS i przykładowy PackageValidator kod);
  2. mieć uprawnienia systemowe android.permission.MEDIA_CONTENT_CONTROL (patrz Przyznawanie uprawnień systemowych);

Oprócz MediaBrowserCompatMediaControllerCompat AAOS udostępnia:

  • CarMediaService zawiera centralne informacje o aktualnie wybranym źródle multimediów. Jest ona również używana do wznowienia odtwarzania wcześniej odtwarzanego źródła multimediów po wyłączeniu i ponownym uruchomieniu samochodu.
  • car-media-common udostępnia wygodne metody wyświetlania listy aplikacji multimedialnych, łączenia się z nimi i korzystania z nich.

Poniżej znajdziesz wytyczne dotyczące wdrażania typowych poleceń interakcji głosowych.

Pobieranie listy zainstalowanych źródeł multimediów

Źródła multimediów można wykrywać za pomocą PackageManager i filtrować usługi pasujące 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 tej 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ładność listy, zalecamy wdrożenie instancji BroadcastReceiver w przypadku działań związanych z intencjami ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REPLACEDACTION_PACKAGE_REMOVED.

Łączenie z aktualnie odtwarzanym źródłem multimediów

CarMediaService udostępnia metody pobierania aktualnie wybranego źródła multimediów i informowania o zmianie tego źródła. Zmiany te mogą być spowodowane bezpośrednią interakcją użytkownika z interfejsem lub użyciem 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 obecnie wybraną aplikacją multimedialną:

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
    }

    
}

sterować odtwarzaniem aktualnie odtwarzanego źródła multimediów,

Po połączeniu MediaBrowserCompat możesz łatwo wysyłać do aplikacji docelowej polecenia sterowania transportem. 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ń 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 MediaSession i MediaBrowse API, 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ń multimedialnych.

Obsługa radia

Usługę Radio MediaBrowseService można zidentyfikować za pomocą filtra intencji ACTION_PLAY_BROADCASTRADIO. Powinny one korzystać z elementów sterujących odtwarzaniem i struktury przeglądania multimediów opisanych w artykule Implementowanie radia. AAOS udostępnia bibliotekęcar-broadcastradio-support zawierającą stałe i metody, które pomagają producentom OEM tworzyć implementacje MediaBrowseService dla własnych usług radiowych zgodne z określonym protokołem. Zapewnia też obsługę aplikacji korzystających z drzewa przeglądania (np. VIA).

obsługiwać wejście AUX, dźwięk z płyty CD i multimedia z USB;

W ramach AOSP nie ma domyślnej implementacji tych źródeł multimediów. Sugerowane podejście:

Obsługa Bluetootha

Treści multimedialne Bluetooth 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 (patrz packages/apps/Bluetooth).

Odpowiednia struktura drzewa przeglądarki multimediów jest zdefiniowana w klasie BrowseTree. Polecenia sterowania odtwarzaniem mogą być dostarczane podobnie jak w przypadku innych aplikacji, za pomocą implementacji MediaSession.

Obsługa poleceń dotyczących strumieniowania multimediów

Aby zaimplementować przesyłanie strumieniowe multimediów po stronie serwera, VIA musi stać się źródłem multimediów, implementując interfejsy MediaBrowse i MediaSession API. Zapoznaj się z artykułem Tworzenie aplikacji multimedialnych na samochody. Dzięki wdrożeniu 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,
  • automatycznie wznawiane po ponownym uruchomieniu samochodu;
  • zapewniać sterowanie odtwarzaniem i przeglądaniem za pomocą interfejsu Centrum multimediów,
  • Odbieranie standardowych zdarzeń przycisków multimedialnych sprzętu

Nie ma standardowego sposobu interakcji ze wszystkimi aplikacjami do nawigacji. W przypadku integracji z Mapami Google zapoznaj się z intencjami Map Google na Androida Automotive. 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ć (patrz Intencje Map Google). Dzięki temu możesz poinformować użytkownika, jeśli docelowa aplikacja jest niedostępna.

Wykonywanie poleceń dotyczących pojazdu

Dostęp do właściwości pojazdu z możliwością odczytu i zapisu jest zapewniany przez interfejs CarPropertyManager. Typy właściwości pojazdu, ich implementacja i inne szczegóły są wyjaśnione w artykule 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 z poziomu Javy, możesz użyć klasy VehiclePropertyIds i jej klas towarzyszących. Poszczególne usługi mają różne uprawnienia Androida, które kontrolują do nich dostęp. Te uprawnienia są zadeklarowane w pliku manifestu 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

Poniżej znajdziesz przykład odczytywania prędkości 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

Poniżej znajdziesz przykład włączania i wyłączania klimatyzacji 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"));
    }

    …
}

Wykonywanie poleceń komunikacyjnych

Obsługa poleceń dotyczących wiadomości

VIA musi obsługiwać wiadomości przychodzące zgodnie z procedurą „kliknij, aby przeczytać” opisaną w artykule Asystent głosowy – kliknij, aby przeczytać. Opcjonalnie może też wysyłać odpowiedzi do nadawcy wiadomości przychodzącej. Dodatkowo VIAs 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 numer poczty głosowej użytkownika. W takich przypadkach VIA będzie 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 między interfejsem VIA a systemem znajdziesz na liście znanych intencji Androida. Wiele poleceń użytkownika można rozwiązać po stronie serwera (np. odczytywanie e-maili i wydarzeń w kalendarzu użytkowników), co nie wymaga żadnych interakcji z systemem poza samą interakcją głosową.

Działania wciągające (wyświetlanie treści wizualnych)

Gdy VIA ułatwia użytkownikowi wykonanie działania lub zrozumienie czegoś, 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 zawierać konkretne działania. Szczegółowe informacje o wytycznych dotyczących interfejsu użytkownika i UX w przypadku działań immersyjnych znajdziesz w artykule Preloaded Assistants: UX Guidance (w języku angielskim).

Aby umożliwić dostosowywanie i zapewnić spójność z pozostałą częścią projektu konsoli centralnej, interfejsy VIAs powinny korzystać z komponentów biblioteki Car UI w przypadku większości elementów interfejsu. Więcej informacji znajdziesz w sekcji Dostosowywanie.