Korzystanie z biblioteki aplikacji Android for Cars

Biblioteka aplikacji Androida na samochody umożliwia przeniesienie do samochodu aplikacji nawigacyjnych, zawierających punkty POI, związanych z internetem rzeczy (IoT) lub pogodowych. Dostarcza on zestaw szablonów zaprojektowanych z myślą o spełnianiu standardów dotyczących rozpraszania uwagi kierowcy i uwzględnia szczegóły takie jak różnorodność czynników związanych z ekranem samochodu i rodzajami danych wejściowych.

Ten przewodnik zawiera omówienie najważniejszych funkcji i koncepcji biblioteki oraz przeprowadzi Cię przez proces konfigurowania podstawowej aplikacji.

Zanim zaczniesz

  1. Zapoznaj się ze stronami Projektowanie z myślą o kierowcach, które zawierają informacje o bibliotece aplikacji samochodowych.
  2. Zapoznaj się z kluczowymi terminami i pojęciami w następnej sekcji.
  3. Zapoznaj się z interfejsem systemu Android Autowzornictwem systemu operacyjnego Android Automotive.
  4. Zapoznaj się z informacjami o wersji.
  5. Sprawdź próbki.

Kluczowe terminy i pojęcia

Modele i szablony
Interfejs użytkownika jest reprezentowany przez graf obiektów modelu, które można układać na różne sposoby, zgodnie z szablonem, do którego należą. Szablony to podzbiór modeli, które mogą pełnić rolę węzła głównego w tych wykresach. Modele zawierają informacje, które mają być wyświetlane użytkownikowi w formie tekstu i obrazów, a także atrybuty służące do konfigurowania aspektów wizualnych tych informacji, np. kolorów tekstu lub rozmiarów obrazów. Host przekształca modele w widoki zaprojektowane tak, aby spełniały standardy dotyczące rozpraszania uwagi kierowcy, i dba o szczegóły, takie jak różnorodność czynników związanych z ekranem samochodu i modalności wejściowe.
Zorganizuj
Host to komponent backendu, który implementuje funkcje oferowane przez interfejsy API biblioteki, dzięki czemu aplikacja może działać w samochodzie. Zakres obowiązków hosta obejmuje odkrywanie aplikacji i zarządzanie jej cyklem życia, przekształcanie modeli w widoki oraz powiadamianie aplikacji o interakcjach użytkownika. Na urządzeniach mobilnych ten host jest implementowany przez Androida Auto. W systemie operacyjnym Android Automotive ten host jest instalowany jako aplikacja systemowa.
Ograniczenia dotyczące szablonów
Różne szablony wymuszają ograniczenia w treściach modeli. Na przykład szablony list mają ograniczenia dotyczące liczby elementów, które można wyświetlić użytkownikowi. Szablony mają też ograniczenia dotyczące sposobu, w jaki można je łączyć, aby tworzyć przepływ zadania. Na przykład aplikacja może umieścić na stosie ekranów maksymalnie 5 szablonów. Więcej informacji znajdziesz w sekcji Ograniczenia dotyczące szablonów.
Screen
Screen to klasa udostępniana przez bibliotekę, którą aplikacje implementują w celu zarządzania interfejsem użytkownika. Screen ma cykl życia i zapewnia aplikacji mechanizm wysyłania szablonu do wyświetlenia, gdy ekran jest widoczny. Instancje Screen można też umieszczać na Screenstosie i zdejmować z niego, co zapewnia zgodność z ograniczeniami przepływu szablonu.
CarAppService
CarAppService to abstrakcyjna klasa Service, którą aplikacja musi zaimplementować i wyeksportować, aby host mógł ją wykryć i nią zarządzać. CarAppService aplikacji odpowiada za weryfikację, czy połączenie z hostem jest zaufane, za pomocą funkcji createHostValidator, a następnie za udostępnianie instancji Session dla każdego połączenia za pomocą funkcji onCreateSession.
Session

Session to klasa abstrakcyjna, którą aplikacja musi zaimplementować i zwrócić za pomocą funkcji CarAppService.onCreateSession. Jest to punkt wejścia do wyświetlania informacji na ekranie samochodu. Ma cykl życia, który informuje o bieżącym stanie aplikacji na ekranie samochodu, np. o tym, czy jest widoczna, czy ukryta.

Gdy rozpoczyna się Session, np. podczas pierwszego uruchomienia aplikacji, host wysyła żądanie wyświetlenia początkowego Screen za pomocą metody onCreateScreen.

Instalowanie biblioteki aplikacji samochodowych

Instrukcje dodawania biblioteki do aplikacji znajdziesz na stronie z informacjami o wersji biblioteki Jetpack.

Konfigurowanie plików manifestu aplikacji

Zanim utworzysz aplikację samochodową, skonfiguruj pliki manifestu aplikacji w ten sposób:

Deklarowanie usługi CarAppService

Host łączy się z Twoją aplikacją za pomocą implementacji CarAppService. Deklarujesz tę usługę w pliku manifestu, aby umożliwić hostowi wykrywanie aplikacji i łączenie się z nią.

Musisz też zadeklarować kategorię aplikacji w elemencie <category> filtra intencji aplikacji. Listę obsługiwanych kategorii aplikacji znajdziesz w przypadku wartości dozwolonych w tym elemencie.

Ten fragment kodu pokazuje, jak zadeklarować usługę aplikacji samochodowej dla aplikacji z punktami zainteresowania w pliku manifestu:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService"/>
        <category android:name="androidx.car.app.category.POI"/>
      </intent-filter>
    </service>

    ...
<application>

Obsługiwane kategorie aplikacji

Zadeklaruj kategorię aplikacji, dodając co najmniej jedną z tych wartości kategorii w filtrze intencji podczas deklarowania CarAppService zgodnie z opisem w poprzedniej sekcji:

Szczegółowe opisy poszczególnych kategorii i kryteria, które muszą spełniać aplikacje, aby do nich należeć, znajdziesz w artykule Jakość aplikacji samochodowych na Androida.

Określanie nazwy i ikony aplikacji

Musisz podać nazwę i ikonę aplikacji, których host może używać do reprezentowania Twojej aplikacji w interfejsie systemu.

Możesz określić nazwę i ikonę aplikacji, które będą ją reprezentować, za pomocą atrybutów labelicon elementu CarAppService:

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

Jeśli etykieta lub ikona nie są zadeklarowane w elemencie <service>, host wraca do wartości określonych w elemencie <application>.

Ustawianie motywu niestandardowego

Aby ustawić niestandardowy motyw aplikacji samochodowej, dodaj element <meta-data> do pliku manifestu w ten sposób:

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Następnie zadeklaruj zasób stylu, aby ustawić te atrybuty niestandardowego motywu aplikacji samochodowej:

<resources>
  <style name="MyCarAppTheme">
    <item name="carColorPrimary">@layout/my_primary_car_color</item>
    <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item>
    <item name="carColorSecondary">@layout/my_secondary_car_color</item>
    <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item>
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Poziom interfejsu Car App API

Biblioteka aplikacji samochodowych definiuje własne poziomy interfejsu API, dzięki czemu możesz sprawdzić, które funkcje biblioteki są obsługiwane przez hosta szablonu w pojeździe. Aby pobrać najwyższy poziom interfejsu Car App API obsługiwany przez hosta, użyj metody getCarAppApiLevel().

Zadeklaruj minimalny poziom interfejsu Car App API obsługiwany przez aplikację w pliku AndroidManifest.xml:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

Więcej informacji o tym, jak zachować zgodność wsteczną i zadeklarować minimalny poziom interfejsu API wymagany do korzystania z funkcji, znajdziesz w dokumentacji adnotacji RequiresCarApi. Aby dowiedzieć się, który poziom interfejsu API jest wymagany do korzystania z określonej funkcji biblioteki aplikacji do samochodu, zapoznaj się z dokumentacją referencyjną CarAppApiLevels.

Tworzenie usługi CarAppService i sesji

Aplikacja musi rozszerzać klasę CarAppService i implementować jej metodę onCreateSession, która zwraca instancję Session odpowiadającą bieżącemu połączeniu z hostem:

Kotlin

class HelloWorldService : CarAppService() {
    ...
    override fun onCreateSession(): Session {
        return HelloWorldSession()
    }
    ...
}

Java

public final class HelloWorldService extends CarAppService {
    ...
    @Override
    @NonNull
    public Session onCreateSession() {
        return new HelloWorldSession();
    }
    ...
}

Session instancja odpowiada za zwrócenie instancji Screen do użycia przy pierwszym uruchomieniu aplikacji:

Kotlin

class HelloWorldSession : Session() {
    ...
    override fun onCreateScreen(intent: Intent): Screen {
        return HelloWorldScreen(carContext)
    }
    ...
}

Java

public final class HelloWorldSession extends Session {
    ...
    @Override
    @NonNull
    public Screen onCreateScreen(@NonNull Intent intent) {
        return new HelloWorldScreen(getCarContext());
    }
    ...
}

Aby obsługiwać sytuacje, w których aplikacja samochodowa musi się uruchamiać na ekranie innym niż ekran główny lub strona docelowa aplikacji (np. w przypadku obsługi precyzyjnych linków), możesz wstępnie wypełnić stos wsteczny ekranów za pomocą funkcji ScreenManager.push przed powrotem z funkcji onCreateScreen. Wstępne wypełnianie umożliwia użytkownikom powrót do poprzednich ekranów z pierwszego ekranu wyświetlanego przez aplikację.

Tworzenie ekranu startowego

Ekrany wyświetlane przez aplikację tworzysz, definiując klasy rozszerzające klasę Screen i implementując jej metodę onGetTemplate, która zwraca instancję Template reprezentującą stan interfejsu użytkownika do wyświetlenia na ekranie samochodu.

Ten fragment kodu pokazuje, jak zadeklarować element Screen, który używa szablonu PaneTemplate do wyświetlania prostego ciągu znaków „Hello world!”:

Kotlin

class HelloWorldScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val row = Row.Builder().setTitle("Hello world!").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

Java

public class HelloWorldScreen extends Screen {
    @NonNull
    @Override
    public Template onGetTemplate() {
        Row row = new Row.Builder().setTitle("Hello world!").build();
        Pane pane = new Pane.Builder().addRow(row).build();
        return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
    }
}

Klasa CarContext

Klasa CarContext jest podklasą ContextWrapper dostępną dla instancji Session i Screen. Zapewnia dostęp do usług samochodowych, takich jak ScreenManager do zarządzania stosem ekranów, AppManager do ogólnych funkcji związanych z aplikacjami, takich jak dostęp do obiektu Surface na potrzeby rysowania map, oraz NavigationManager używany przez aplikacje do nawigacji krok po kroku do przekazywania metadanych nawigacji i innych zdarzeń związanych z nawigacją do hosta.

Pełną listę funkcji biblioteki dostępnych dla aplikacji do nawigacji znajdziesz w artykule Dostęp do szablonów nawigacji.

CarContext oferuje też inne funkcje, takie jak wczytywanie zasobów rysowalnych za pomocą konfiguracji z ekranu samochodu, uruchamianie aplikacji w samochodzie za pomocą intencji i sygnalizowanie, czy aplikacja powinna wyświetlać mapę w ciemnym motywie.

Wdrażanie nawigacji po ekranie

Aplikacje często wyświetlają wiele różnych ekranów, z których każdy może korzystać z innych szablonów. Użytkownik może przechodzić między nimi, wchodząc w interakcję z interfejsem wyświetlanym na ekranie.

Klasa ScreenManager udostępnia stos ekranów, których można używać do wyświetlania ekranów, które można automatycznie zamykać, gdy użytkownik wybierze przycisk Wstecz na ekranie samochodu lub użyje sprzętowego przycisku Wstecz dostępnego w niektórych samochodach.

Poniższy fragment kodu pokazuje, jak dodać do szablonu wiadomości działanie „Wstecz” oraz działanie, które po wybraniu przez użytkownika wyświetla nowy ekran:

Kotlin

val template = MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener { screenManager.push(NextScreen(carContext)) }
            .build())
    .build()

Java

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(
                () -> getScreenManager().push(new NextScreen(getCarContext())))
            .build())
    .build();

Obiekt Action.BACK to standardowy Action, który automatycznie wywołuje ScreenManager.pop. To zachowanie można zastąpić za pomocą instancji OnBackPressedDispatcher dostępnej w CarContext.

Aby zapewnić bezpieczeństwo korzystania z aplikacji podczas jazdy, stos ekranów może mieć maksymalnie 5 ekranów. Więcej informacji znajdziesz w sekcji Ograniczenia szablonu.

Odświeżanie zawartości szablonu

Aplikacja może poprosić o unieważnienie treści Screen, wywołując metodę Screen.invalidate. Następnie host wywołuje metodę Screen.onGetTemplate w aplikacji, aby pobrać szablon z nową treścią.

Odświeżając Screen, warto wiedzieć, które elementy szablonu można zaktualizować, aby host nie wliczał nowego szablonu do limitu szablonów. Więcej informacji znajdziesz w sekcji Ograniczenia dotyczące szablonów.

Zalecamy, aby struktura ekranów zapewniała mapowanie 1:1 między Screen a typem szablonu zwracanym przez jego implementację onGetTemplate.

Rysowanie map

Aplikacje do nawigacji, prezentujące ciekawe miejsca i pogodowe, które korzystają z tych szablonów, mogą rysować mapy, uzyskując dostęp do Surface.

Aby korzystać z tych szablonów, aplikacja musi mieć jedno z odpowiednich uprawnień zadeklarowanych w elemencie <uses-permission> w pliku AndroidManifest.xml.

Szablon Uprawnienia do szablonu Wskazówki dotyczące kategorii
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES Nawigacja
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES LUB
androidx.car.app.MAP_TEMPLATES
Nawigacja, POI, Pogoda
MapTemplate (przestarzałe) androidx.car.app.NAVIGATION_TEMPLATES Nawigacja
PlaceListNavigationTemplate (przestarzałe) androidx.car.app.NAVIGATION_TEMPLATES Nawigacja
RoutePreviewNavigationTemplate (przestarzałe) androidx.car.app.NAVIGATION_TEMPLATES Nawigacja

Zadeklaruj uprawnienia do powierzchni

Oprócz uprawnień wymaganych w szablonie, z którego korzysta aplikacja, musi ona zadeklarować uprawnienie androidx.car.app.ACCESS_SURFACE w pliku AndroidManifest.xml, aby uzyskać dostęp do interfejsu:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
  ...
</manifest>

Dostęp do platformy

Aby uzyskać dostęp do Surface udostępnianego przez hosta, musisz zaimplementować SurfaceCallback i udostępnić tę implementację usłudze samochodowej AppManager. Bieżąca wartość Surface jest przekazywana do funkcji SurfaceCallback w parametrze SurfaceContainer wywołań zwrotnych onSurfaceAvailable()onSurfaceDestroyed().

Kotlin

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

Java

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

Określanie widocznego obszaru platformy

Host może rysować elementy interfejsu użytkownika dla szablonów na mapie. Host przekazuje obszar powierzchni, który jest gwarantowany jako niezasłonięty i w pełni widoczny dla użytkownika, wywołując metodę SurfaceCallback.onVisibleAreaChanged. Aby zminimalizować liczbę zmian, host wywołuje metodę SurfaceCallback.onStableAreaChanged z najmniejszym prostokątem, który jest zawsze widoczny na podstawie bieżącego szablonu.

Na przykład gdy aplikacja do nawigacji używa NavigationTemplatepaskiem działań u góry, pasek działań może się ukryć, gdy użytkownik przez jakiś czas nie wchodzi w interakcję z ekranem, aby zrobić więcej miejsca na mapę. W tym przypadku wywołanie zwrotne do onStableAreaChangedonVisibleAreaChanged następuje w przypadku tego samego prostokąta. Gdy pasek działań jest ukryty, w przypadku większego obszaru wywoływana jest tylko funkcja onVisibleAreaChanged. Jeśli użytkownik wejdzie w interakcję z ekranem, ponownie zostanie wywołana tylko funkcja onVisibleAreaChanged z pierwszym prostokątem.

Obsługa ciemnego motywu

Gdy host uzna, że warunki tego wymagają, aplikacje muszą ponownie narysować mapę na instancji Surface, używając odpowiednich ciemnych kolorów, zgodnie z opisem w wskazówkach dotyczących jakości aplikacji na Androida na potrzeby samochodów.

Aby zdecydować, czy narysować ciemną mapę, możesz użyć metody CarContext.isDarkMode. Gdy stan ciemnego motywu się zmieni, otrzymasz wywołanie funkcji Session.onCarConfigurationChanged.

Rysowanie map na wyświetlaczu klastra

Oprócz wyświetlania map na głównym ekranie aplikacje nawigacyjne mogą też wyświetlać mapy na ekranie deski rozdzielczej za kierownicą. Dodatkowe wskazówki znajdziesz w artykule Wyświetlanie rysunków w klastrze.

Umożliwianie użytkownikom interakcji z mapą

Korzystając z tych szablonów, możesz dodać obsługę interakcji użytkowników z rysowanymi przez Ciebie mapami, np. umożliwić im wyświetlanie różnych części mapy przez powiększanie i przesuwanie.

Szablon Interaktywność obsługiwana od poziomu interfejsu Car App API
NavigationTemplate 2
PlaceListNavigationTemplate (wycofane) 4
RoutePreviewNavigationTemplate (wycofane) 4
MapTemplate (wycofane) 5. Wprowadzenie do szablonu
MapWithContentTemplate 7. Wprowadzenie do szablonu

Wdrażanie wywołań zwrotnych interaktywności

Interfejs SurfaceCallback ma kilka metod wywołania zwrotnego, które możesz wdrożyć, aby dodać interaktywność do map utworzonych za pomocą szablonów z poprzedniej sekcji:

Interakcja SurfaceCallback metoda Obsługiwane od poziomu interfejsu Car App API
Kliknij onClick 5
Ściągnij, aby powiększyć onScale 2
Przeciąganie jednym dotknięciem onScroll 2
Przesunięcie jednym dotknięciem onFling 2
Kliknij dwukrotnie onScale (współczynnik skalowania określony przez hosta szablonu) 2
Przesunięcie obrotowe w trybie przesuwania onScroll (z czynnikiem odległości określonym przez hosta szablonu) 2

Dodawanie paska działań na mapie

Szablony te mogą zawierać pasek działań na mapie, który umożliwia wykonywanie działań związanych z mapą, takich jak powiększanie i pomniejszanie, ponowne wyśrodkowywanie, wyświetlanie kompasu i inne działania, które chcesz wyświetlać. Pasek działań na mapie może zawierać maksymalnie 4 przyciski z samymi ikonami, które można odświeżać bez wpływu na głębokość zadania. W stanie bezczynności jest ukryty, a w stanie aktywności pojawia się ponownie.

Aby otrzymywać wywołania zwrotne dotyczące interaktywności mapy, musisz dodać przycisk Action.PAN na pasku działań mapy. Gdy użytkownik naciśnie przycisk panoramowania, host przejdzie w tryb panoramowania zgodnie z opisem w następnej sekcji.

Jeśli aplikacja nie zawiera przycisku Action.PAN na pasku działań mapy, nie otrzymuje danych wejściowych użytkownika z metod SurfaceCallback, a aplikacja hosta zamyka wcześniej aktywowany tryb przesuwania.

Na ekranie dotykowym przycisk przesuwania nie jest wyświetlany.

Informacje o trybie przesuwania

W trybie przesuwania host szablonu tłumaczy dane wejściowe użytkownika z urządzeń wejściowych innych niż dotykowe, takich jak pokrętła i touchpady, na odpowiednie metody SurfaceCallback. Odpowiedz na działanie użytkownika polegające na włączeniu lub wyłączeniu trybu przesuwania za pomocą metody setPanModeListenerNavigationTemplate.Builder. Gdy użytkownik jest w trybie przesuwania, gospodarz może ukryć inne komponenty interfejsu w szablonie.

Interakcja z użytkownikiem

Aplikacja może wchodzić w interakcje z użytkownikiem w sposób podobny do aplikacji mobilnej.

Obsługa danych wejściowych użytkownika

Aplikacja może reagować na dane wejściowe użytkownika, przekazując odpowiednie odbiorniki do modeli, które je obsługują. Poniższy fragment kodu pokazuje, jak utworzyć model Action, który ustawia OnClickListener wywołujący metodę zdefiniowaną przez kod aplikacji:

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

Metoda onClickNavigate może następnie uruchomić domyślną aplikację samochodową do nawigacji za pomocą metody CarContext.startCarApp:

Kotlin

private fun onClickNavigate() {
    val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address))
    carContext.startCarApp(intent)
}

Java

private void onClickNavigate() {
    Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
    getCarContext().startCarApp(intent);
}

Więcej informacji o uruchamianiu aplikacji, w tym o formacie intencjiACTION_NAVIGATE, znajdziesz w sekcji Uruchamianie aplikacji samochodowej za pomocą intencji.

Niektóre działania, np. te, które wymagają przekierowania użytkownika do kontynuowania interakcji na urządzeniu mobilnym, są dozwolone tylko wtedy, gdy samochód jest zaparkowany. Możesz użyć ParkedOnlyOnClickListener , aby wdrożyć te działania. Jeśli samochód nie jest zaparkowany, host wyświetla użytkownikowi informację, że w tym przypadku działanie jest niedozwolone. Jeśli samochód jest zaparkowany, kod jest wykonywany normalnie. Poniższy fragment kodu pokazuje, jak użyć ParkedOnlyOnClickListener do otwierania ekranu ustawień na urządzeniu mobilnym:

Kotlin

val row = Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone))
    .build()

Java

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone))
    .build();

Wyświetlanie powiadomień

Powiadomienia wysyłane na urządzenie mobilne wyświetlają się na ekranie samochodu tylko wtedy, gdy są rozszerzone o CarAppExtender. Niektóre atrybuty powiadomień, takie jak tytuł treści, tekst, ikona i działania, można ustawić w CarAppExtender, zastępując atrybuty powiadomienia, gdy pojawiają się one na ekranie samochodu.

Poniższy fragment kodu pokazuje, jak wysłać powiadomienie na ekran samochodu, które wyświetla inny tytuł niż ten widoczny na urządzeniu mobilnym:

Kotlin

val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build()

Java

Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build();

Powiadomienia mogą wpływać na te elementy interfejsu:

  • Użytkownikowi może się wyświetlić powiadomienie typu heads-up.
  • W centrum powiadomień może pojawić się wpis, opcjonalnie z plakietką widoczną na pasku.
  • W przypadku aplikacji do nawigacji powiadomienie może być wyświetlane w widżecie kolumny, jak opisano w sekcji Powiadomienia z instrukcjami skrętu.

Możesz skonfigurować powiadomienia aplikacji tak, aby wpływały na poszczególne elementy interfejsu użytkownika, korzystając z priorytetu powiadomienia, zgodnie z opisem w CarAppExtenderdokumentacji.

Jeśli funkcja NotificationCompat.Builder.setOnlyAlertOnce zostanie wywołana z wartością true, powiadomienie o wysokim priorytecie wyświetli się jako powiadomienie HUN tylko raz.

Więcej informacji o projektowaniu powiadomień aplikacji samochodowej znajdziesz w przewodniku Google Design for Driving w sekcji Powiadomienia.

Wyświetlanie powiadomień

Aplikacja może wyświetlać komunikat w formie wyskakującego okienka za pomocą elementu CarToast, jak pokazano w tym fragmencie kodu:

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

Prośba o uprawnienia

Jeśli aplikacja potrzebuje dostępu do danych lub działań o ograniczonym dostępie, np. lokalizacji, obowiązują standardowe reguły uprawnień Androida. Aby poprosić o uprawnienia, możesz użyć metody CarContext.requestPermissions().

Zaletą korzystania z CarContext.requestPermissions() w porównaniu ze standardowymi interfejsami API Androida jest to, że nie musisz uruchamiać własnego Activity, aby utworzyć okno dialogowe z prośbą o uprawnienia. Co więcej, możesz używać tego samego kodu zarówno w Androidzie Auto, jak i w Androidzie Automotive OS, zamiast tworzyć przepływy zależne od platformy.

Stylizowanie okna z prośbą o uprawnienia w Androidzie Auto

W Androidzie Auto okno uprawnień użytkownika pojawi się na telefonie. Domyślnie za oknem nie będzie tła. Aby ustawić niestandardowe tło, zadeklaruj motyw aplikacji samochodowej w pliku AndroidManifest.xml i ustaw atrybut carPermissionActivityLayout dla motywu aplikacji samochodowej.

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Następnie ustaw atrybut carPermissionActivityLayout dla motywu aplikacji samochodowej:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Uruchamianie aplikacji samochodowej za pomocą intencji

Możesz wywołać metodę CarContext.startCarApp , aby wykonać jedną z tych czynności:

Poniższy przykład pokazuje, jak utworzyć powiadomienie z działaniem, które otwiera aplikację z ekranem zawierającym szczegóły rezerwacji miejsca parkingowego. Rozszerzasz instancję powiadomienia o intencję treści, która zawiera element PendingIntent opakowujący jawną intencję działania aplikacji:

Kotlin

val notification = notificationBuilder
    ...
    .extend(
        CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(ComponentName(context, MyNotificationReceiver::class.java)),
                    0))
            .build())

Java

Notification notification = notificationBuilder
    ...
    .extend(
        new CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    new Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(new ComponentName(context, MyNotificationReceiver.class)),
                    0))
            .build());

Aplikacja musi też deklarować BroadcastReceiver, które jest wywoływane w celu przetworzenia intencji, gdy użytkownik wybierze działanie w interfejsie powiadomień i wywoła CarContext.startCarApp z intencją zawierającą identyfikator URI danych:

Kotlin

class MyNotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val intentAction = intent.action
        if (ACTION_VIEW_PARKING_RESERVATION == intentAction) {
            CarContext.startCarApp(
                intent,
                Intent(Intent.ACTION_VIEW)
                    .setComponent(ComponentName(context, MyCarAppService::class.java))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)))
        }
    }
}

Java

public class MyNotificationReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction();
        if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
            CarContext.startCarApp(
                intent,
                new Intent(Intent.ACTION_VIEW)
                    .setComponent(new ComponentName(context, MyCarAppService.class))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
        }
    }
}

Na koniec metoda Session.onNewIntent w aplikacji obsługuje ten zamiar, umieszczając ekran rezerwacji parkingu na stosie, jeśli nie jest on jeszcze na górze:

Kotlin

override fun onNewIntent(intent: Intent) {
    val screenManager = carContext.getCarService(ScreenManager::class.java)
    val uri = intent.data
    if (uri != null
        && MY_URI_SCHEME == uri.scheme
        && MY_URI_HOST == uri.schemeSpecificPart
        && ACTION_VIEW_PARKING_RESERVATION == uri.fragment
    ) {
        val top = screenManager.top
        if (top !is ParkingReservationScreen) {
            screenManager.push(ParkingReservationScreen(carContext))
        }
    }
}

Java

@Override
public void onNewIntent(@NonNull Intent intent) {
    ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
    Uri uri = intent.getData();
    if (uri != null
        && MY_URI_SCHEME.equals(uri.getScheme())
        && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
        && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())
    ) {
        Screen top = screenManager.getTop();
        if (!(top instanceof ParkingReservationScreen)) {
            screenManager.push(new ParkingReservationScreen(getCarContext()));
        }
    }
}

Więcej informacji o obsłudze powiadomień w aplikacji samochodowej znajdziesz w sekcji Wyświetlanie powiadomień.

Ograniczenia dotyczące szablonów

Host ogranicza liczbę szablonów wyświetlanych dla danego zadania do maksymalnie 5, z których ostatni musi być jednym z tych typów:

Pamiętaj, że ten limit dotyczy liczby szablonów, a nie liczby instancji w stosie.Screen Jeśli na przykład aplikacja wyśle 2 szablony na ekranie A, a następnie przejdzie do ekranu B, może teraz wysłać 3 kolejne szablony. Jeśli każdy ekran jest skonstruowany tak, aby wysyłać pojedynczy szablon, aplikacja może umieścić na stosie ScreenManager 5 instancji ekranu.

Istnieją jednak wyjątki od tych ograniczeń: odświeżanie szablonów oraz operacje przywracania i resetowania.

Odświeżanie szablonów

Niektóre aktualizacje treści nie są wliczane do limitu szablonów. Ogólnie rzecz biorąc, jeśli aplikacja przesyła nowy szablon tego samego typu, który zawiera te same główne treści co poprzedni szablon, nie jest on wliczany do limitu. Na przykład zmiana stanu przełącznika w wierszu w ListTemplate nie jest wliczana do limitu. Więcej informacji o tym, jakie rodzaje aktualizacji treści można uznać za odświeżenie, znajdziesz w dokumentacji poszczególnych szablonów.

Operacje cofania

Aby włączyć podrzędne przepływy w ramach zadania, host wykrywa, kiedy aplikacja wycofuje się z Screen stosu ScreenManager, i aktualizuje pozostały limit na podstawie liczby szablonów, o którą aplikacja się cofa.

Jeśli na przykład aplikacja wyśle 2 szablony, gdy jest na ekranie A, a potem przejdzie na ekran B i wyśle kolejne 2 szablony, będzie jej przysługiwał jeszcze 1 szablon. Jeśli aplikacja wróci do ekranu A, host zresetuje limit do 3, ponieważ aplikacja cofnęła się o 2 szablony.

Pamiętaj, że podczas powrotu do ekranu aplikacja musi wysłać szablon tego samego typu co ostatni szablon wysłany przez ten ekran. Wysłanie szablonu innego typu spowoduje błąd. Jeśli jednak typ pozostanie taki sam podczas operacji powrotu, aplikacja może swobodnie modyfikować zawartość szablonu bez wpływu na limit.

Operacje resetowania

Niektóre szablony mają specjalną semantykę, która oznacza koniec zadania. Na przykład NavigationTemplate to widok, który ma pozostać na ekranie i być odświeżany nowymi instrukcjami skrętu, z których użytkownik może korzystać. Gdy osiągnie jeden z tych szablonów, host zresetuje limit szablonu, traktując go tak, jakby był pierwszym krokiem nowego zadania. Umożliwia to aplikacji rozpoczęcie nowego zadania. W dokumentacji poszczególnych szablonów znajdziesz informacje o tym, które z nich powodują zresetowanie hosta.

Jeśli gospodarz otrzyma intencję uruchomienia aplikacji z działania powiadomienia lub z programu uruchamiającego, limit zostanie zresetowany. Ten mechanizm umożliwia aplikacji rozpoczęcie nowego przepływu zadań z poziomu powiadomień, nawet jeśli jest ona już powiązana i działa na pierwszym planie.

Więcej informacji o wyświetlaniu powiadomień z aplikacji na ekranie samochodu znajdziesz w sekcji Wyświetlanie powiadomień. W sekcji Uruchamianie aplikacji samochodowej za pomocą intencji znajdziesz informacje o tym, jak uruchamiać aplikację z poziomu działania powiadomienia.

Connection API

Aby sprawdzić, czy aplikacja działa na Androidzie Auto czy Androidzie Automotive OS, użyj interfejsu CarConnection API, aby pobrać informacje o połączeniu w czasie działania.

Na przykład w Session aplikacji samochodowej zainicjuj CarConnection i zasubskrybuj aktualizacje LiveData:

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

W obserwatorze możesz reagować na zmiany stanu połączenia:

Kotlin

fun onConnectionStateUpdated(connectionState: Int) {
  val message = when(connectionState) {
    CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit"
    CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS"
    CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto"
    else -> "Unknown car connection type"
  }
  CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show()
}

Java

private void onConnectionStateUpdated(int connectionState) {
  String message;
  switch(connectionState) {
    case CarConnection.CONNECTION_TYPE_NOT_CONNECTED:
      message = "Not connected to a head unit";
      break;
    case CarConnection.CONNECTION_TYPE_NATIVE:
      message = "Connected to Android Automotive OS";
      break;
    case CarConnection.CONNECTION_TYPE_PROJECTION:
      message = "Connected to Android Auto";
      break;
    default:
      message = "Unknown car connection type";
      break;
  }
  CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show();
}

Constraints API

Różne samochody mogą wyświetlać użytkownikowi jednocześnie różną liczbę instancji Item. Użyj funkcji ConstraintManager do sprawdzania limitu treści w czasie działania i ustaw odpowiednią liczbę elementów w szablonach.

Zacznij od pobrania ConstraintManagerCarContext:

Kotlin

val manager = carContext.getCarService(ConstraintManager::class.java)

Java

ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);

Następnie możesz wysłać zapytanie do pobranego obiektu ConstraintManager, aby uzyskać odpowiedni limit treści. Aby na przykład uzyskać liczbę elementów, które można wyświetlić w siatce, wywołaj funkcję getContentLimit z parametrem CONTENT_LIMIT_TYPE_GRID:

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);

Dodawanie procesu logowania

Jeśli Twoja aplikacja wymaga zalogowania się użytkownika, możesz użyć szablonów, takich jak SignInTemplateLongMessageTemplate, z interfejsem Car App API na poziomie 2 lub wyższym, aby obsługiwać logowanie się w aplikacji na jednostce głównej samochodu.

Aby utworzyć SignInTemplate, zdefiniuj SignInMethod. Biblioteka aplikacji na samochody obsługuje obecnie te metody logowania:

  • InputSignInMethod do logowania się za pomocą nazwy użytkownika i hasła.
  • PinSignInMethod w przypadku logowania za pomocą kodu PIN, gdy użytkownik łączy konto z telefonu za pomocą kodu PIN wyświetlanego na jednostce głównej.
  • ProviderSignInMethod w przypadku logowania u dostawcy, np. logowania przez Googlejednego dotknięcia.
  • QRCodeSignInMethod logowanie się za pomocą kodu QR, w którym użytkownik skanuje kod QR, aby dokończyć logowanie na telefonie. Jest to dostępne w przypadku interfejsu Car API na poziomie 4 lub wyższym.

Aby na przykład wdrożyć szablon, który zbiera hasło użytkownika, zacznij od utworzenia InputCallback do przetwarzania i weryfikowania danych wejściowych użytkownika:

Kotlin

val callback = object : InputCallback {
    override fun onInputSubmitted(text: String) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    override fun onInputTextChanged(text: String) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
}

Java

InputCallback callback = new InputCallback() {
    @Override
    public void onInputSubmitted(@NonNull String text) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    @Override
    public void onInputTextChanged(@NonNull String text) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
};

W przypadku InputSignInMethod Builder wymagany jest InputCallback.

Kotlin

val passwordInput = InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build()

Java

InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build();

Na koniec użyj nowego kodu InputSignInMethod, aby utworzyć kod SignInTemplate.

Kotlin

SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build()

Java

new SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build();

Używanie AccountManager

Aplikacje na system operacyjny Android Automotive, które wymagają uwierzytelniania, muszą korzystać z usługi AccountManager z tych powodów:

  • Lepsze wrażenia użytkowników i łatwiejsze zarządzanie kontami: użytkownicy mogą łatwo zarządzać wszystkimi kontami w menu kont w ustawieniach systemu, w tym logować się i wylogowywać.
  • Funkcje dla „gości”: ponieważ samochody są urządzeniami współdzielonymi, producenci OEM mogą włączyć w pojeździe funkcje dla gości, w których nie można dodawać kont.

Dodawanie wariantów ciągu tekstowego

Na ekranach samochodowych o różnych rozmiarach może się wyświetlać różna ilość tekstu. W przypadku interfejsu Car App API na poziomie 2 i wyższym możesz określić wiele wariantów ciągu tekstowego, aby jak najlepiej dopasować go do ekranu. Aby sprawdzić, gdzie akceptowane są warianty tekstu, poszukaj szablonów i komponentów, które przyjmują wartość CarText.

Warianty ciągów tekstowych możesz dodać do elementu CarText za pomocą metody CarText.Builder.addVariant():

Kotlin

val itemTitle = CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build()

Java

CarText itemTitle = new CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build();

Możesz wtedy użyć tego CarText, np. jako głównego tekstu GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

Dodaj ciągi znaków w kolejności od najbardziej do najmniej preferowanych, np. od najdłuższego do najkrótszego. Host wybiera ciąg znaków o odpowiedniej długości w zależności od ilości miejsca dostępnego na ekranie samochodu.

Dodawanie ikon CarIcon w wierszach

Możesz dodawać ikony w tekście, aby zwiększyć atrakcyjność wizualną aplikacji za pomocą elementu CarIconSpan. Więcej informacji o tworzeniu tych zakresów znajdziesz w dokumentacji dotyczącej CarIconSpan.create. Więcej informacji o stylizowaniu tekstu za pomocą zakresów znajdziesz w artykule Spantastic text styling with Spans (w języku angielskim).

Kotlin

  
val rating = SpannableString("Rating: 4.5 stars")
rating.setSpan(
    CarIconSpan.create(
        // Create a CarIcon with an image of four and a half stars
        CarIcon.Builder(...).build(),
        // Align the CarIcon to the baseline of the text
        CarIconSpan.ALIGN_BASELINE
    ),
    // The start index of the span (index of the character '4')
    8,
    // The end index of the span (index of the last 's' in "stars")
    16,
    Spanned.SPAN_INCLUSIVE_INCLUSIVE
)

val row = Row.Builder()
    ...
    .addText(rating)
    .build()
  
  

Java

  
SpannableString rating = new SpannableString("Rating: 4.5 stars");
rating.setSpan(
        CarIconSpan.create(
                // Create a CarIcon with an image of four and a half stars
                new CarIcon.Builder(...).build(),
                // Align the CarIcon to the baseline of the text
                CarIconSpan.ALIGN_BASELINE
        ),
        // The start index of the span (index of the character '4')
        8,
        // The end index of the span (index of the last 's' in "stars")
        16,
        Spanned.SPAN_INCLUSIVE_INCLUSIVE
);
Row row = new Row.Builder()
        ...
        .addText(rating)
        .build();
  
  

Interfejsy API sprzętu samochodowego

Od poziomu 3 interfejsu Car App API biblioteka Car App zawiera interfejsy API, których możesz używać do uzyskiwania dostępu do właściwości i czujników pojazdu.

Wymagania

Aby używać interfejsów API z Androidem Auto, zacznij od dodania zależności od androidx.car.app:app-projected do pliku build.gradle modułu Androida Auto. W przypadku systemu operacyjnego Android Automotive dodaj zależność od androidx.car.app:app-automotive do pliku build.gradle modułu Android Automotive.

Dodatkowo w pliku AndroidManifest.xml musisz zadeklarować odpowiednie uprawnienia potrzebne do wysyłania żądań dotyczących danych samochodu, których chcesz używać. Pamiętaj, że te uprawnienia muszą też zostać Ci przyznane przez użytkownika. Możesz używać tego samego kodu zarówno w Androidzie Auto, jak i w Androidzie Automotive OS, zamiast tworzyć przepływy zależne od platformy. Wymagane uprawnienia są jednak inne.

CarInfo

W tej tabeli opisujemy właściwości udostępniane przez interfejsy API CarInfo oraz uprawnienia, o które musisz poprosić, aby ich używać:

Metody Właściwości Uprawnienia Androida Auto Uprawnienia w systemie operacyjnym Android Automotive Obsługiwane od poziomu interfejsu Car App API
fetchModel Marka, model, rok android.car.permission.CAR_INFO 3
fetchEnergyProfile Typy złączy EV, rodzaje paliw com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

Te dane są dostępne tylko w niektórych pojazdach z systemem operacyjnym Android Automotive z interfejsem API w wersji 30 lub nowszej.

Wymiary zewnętrzne Nie dotyczy android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
Stan karty płatności, typ karty płatności 3
addEnergyLevelListener
removeEnergyLevelListener
Poziom baterii, poziom paliwa, niski poziom paliwa, pozostały zasięg com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_ENERGY,
android.car.permission.CAR_ENERGY_PORTS,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addSpeedListener
removeSpeedListener
Rzeczywista prędkość, prędkość wyświetlana (na wyświetlaczu w samochodzie) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener

Ostrzeżenie: metoda getOdometerMeters klasy Mileage ma nieprawidłową nazwę i zwraca kilometry, a nie metry.

Odległość z drogomierza com.google.android.gms.permission.CAR_MILEAGE Te dane nie są dostępne w systemie operacyjnym Android Automotive dla aplikacji zainstalowanych ze Sklepu Play. 3

Aby na przykład uzyskać pozostały zakres, utwórz instancję obiektu CarInfo, a następnie utwórz i zarejestruj obiekt OnCarDataAvailableListener:

Kotlin

val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo

val listener = OnCarDataAvailableListener<EnergyLevel> { data ->
    if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) {
      val rangeRemaining = data.rangeRemainingMeters.value
    } else {
      // Handle error
    }
  }

carInfo.addEnergyLevelListener(carContext.mainExecutor, listener)

// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener)

Java

CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo();

OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
  if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) {
    float rangeRemaining = data.getRangeRemainingMeters().getValue();
  } else {
    // Handle error
  }
};

carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener);

// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener);

Nie zakładaj, że dane z samochodu są dostępne przez cały czas. Jeśli pojawi się błąd, sprawdź stan żądanej wartości, aby lepiej zrozumieć, dlaczego nie udało się pobrać żądanych danych. Pełną definicję klasy CarInfo znajdziesz w dokumentacji.

CarSensors

Klasa CarSensors umożliwia dostęp do akcelerometru, żyroskopu, kompasu i danych o lokalizacji pojazdu. Dostępność tych wartości może zależeć od producenta OEM. Format danych z akcelerometru, żyroskopu i kompasu jest taki sam jak w przypadku interfejsu SensorManager API. Aby na przykład sprawdzić kierunek pojazdu:

Kotlin

val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors

val listener = OnCarDataAvailableListener<Compass> { data ->
    if (data.orientations.status == CarValue.STATUS_SUCCESS) {
      val orientation = data.orientations.value
    } else {
      // Data not available, handle error
    }
  }

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener)

// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener)

Java

CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors();

OnCarDataAvailableListener<Compass> listener = (data) -> {
  if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) {
    List<Float> orientations = data.getOrientations().getValue();
  } else {
    // Data not available, handle error
  }
};

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(),
    listener);

// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener);

Aby uzyskać dostęp do danych o lokalizacji z samochodu, musisz też zadeklarować uprawnienie android.permission.ACCESS_FINE_LOCATION i poprosić o nie.

Testowanie

Aby symulować dane z czujników podczas testowania w Androidzie Auto, zapoznaj się z sekcjami CzujnikiKonfiguracja czujników w przewodniku po jednostce głównej na komputer. Aby symulować dane z czujników podczas testowania w systemie operacyjnym Android Automotive, zapoznaj się z sekcją Emulowanie stanu sprzętu w przewodniku po emulatorze systemu operacyjnego Android Automotive.

Cykle życia CarAppService, sesji i ekranu

Klasy SessionScreen implementują interfejs LifecycleOwner. Gdy użytkownik wchodzi w interakcję z aplikacją, wywoływane są wywołania zwrotne cyklu życia obiektów SessionScreen, jak pokazano na poniższych diagramach.

Cykle życia usługi CarAppService i sesji

Rysunek 1. Sessioncykl życia
.

Szczegółowe informacje znajdziesz w dokumentacji metody Session.getLifecycle.

Cykl życia ekranu

Rysunek 2. Screencykl życia
.

Szczegółowe informacje znajdziesz w dokumentacji metody Screen.getLifecycle.

Nagrywanie z mikrofonu w samochodzie

Za pomocą interfejsów API CarAppServiceCarAudioRecord możesz przyznać aplikacji dostęp do mikrofonu w samochodzie użytkownika. Użytkownicy muszą przyznać aplikacji uprawnienia dostępu do mikrofonu w samochodzie. Aplikacja może nagrywać i przetwarzać dane wejściowe użytkownika w swoim obrębie.

Uprawnienia do nagrywania

Zanim zaczniesz nagrywać dźwięk, musisz najpierw zadeklarować uprawnienia do nagrywania w AndroidManifest.xml i poprosić użytkownika o ich przyznanie.

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

Musisz poprosić o uprawnienia do nagrywania w czasie działania. Więcej informacji o tym, jak poprosić o uprawnienia w aplikacji samochodowej, znajdziesz w sekcji Prośba o uprawnienia.

Nagrywanie dźwięku

Gdy użytkownik zezwoli na nagrywanie, możesz nagrać dźwięk i przetworzyć nagranie.

Kotlin

val carAudioRecord = CarAudioRecord.create(carContext)
        carAudioRecord.startRecording()

        val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE)
        while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording()
 

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        carAudioRecord.startRecording();

        byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE];
        while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording();
 

Aktywność audio

Podczas nagrywania z mikrofonu w samochodzie najpierw uzyskaj fokus audio, aby zatrzymać odtwarzanie multimediów. Jeśli utracisz fokus dźwięku, zatrzymaj nagrywanie.

Oto przykład uzyskiwania fokusu audio:

Kotlin

 
val carAudioRecord = CarAudioRecord.create(carContext)
        
        // Take audio focus so that user's media is not recorded
        val audioAttributes = AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            // Use the most appropriate usage type for your use case
            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
            .build()
        
        val audioFocusRequest =
            AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                .setAudioAttributes(audioAttributes)
                .setOnAudioFocusChangeListener { state: Int ->
                    if (state == AudioManager.AUDIOFOCUS_LOSS) {
                        // Stop recording if audio focus is lost
                        carAudioRecord.stopRecording()
                    }
                }
                .build()
        
        if (carContext.getSystemService(AudioManager::class.java)
                .requestAudioFocus(audioFocusRequest)
            != AudioManager.AUDIOFOCUS_REQUEST_GRANTED
        ) {
            // Don't record if the focus isn't granted
            return
        }
        
        carAudioRecord.startRecording()
        // Process the audio and abandon the AudioFocusRequest when done

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        // Take audio focus so that user's media is not recorded
        AudioAttributes audioAttributes =
                new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                        // Use the most appropriate usage type for your use case
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
                        .build();

        AudioFocusRequest audioFocusRequest =
                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                        .setAudioAttributes(audioAttributes)
                        .setOnAudioFocusChangeListener(state -> {
                            if (state == AudioManager.AUDIOFOCUS_LOSS) {
                                // Stop recording if audio focus is lost
                                carAudioRecord.stopRecording();
                            }
                        })
                        .build();

        if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest)
                != AUDIOFOCUS_REQUEST_GRANTED) {
            // Don't record if the focus isn't granted
            return;
        }

        carAudioRecord.startRecording();
        // Process the audio and abandon the AudioFocusRequest when done
 

Testing Library

Biblioteka testowa Androida Auto udostępnia klasy pomocnicze, których możesz używać do weryfikowania działania aplikacji w środowisku testowym. Na przykład polecenie SessionController umożliwia symulowanie połączenia z hostem i sprawdzanie, czy utworzono i zwrócono prawidłowe wartości ScreenTemplate.

Przykłady użycia znajdziesz w sekcji Przykłady.

Zgłaszanie problemów z biblioteką aplikacji Android for Cars

Jeśli znajdziesz problem z biblioteką, zgłoś go za pomocą Google Issue Tracker. Pamiętaj, aby podać wszystkie wymagane informacje w szablonie problemu.

Tworzenie nowego problemu

Zanim zgłosisz nowy problem, sprawdź, czy nie ma go w informacjach o wersji biblioteki lub na liście problemów. Możesz subskrybować problemy i głosować na nie, klikając gwiazdkę przy danym problemie w narzędziu do śledzenia. Więcej informacji znajdziesz w artykule Subskrybowanie problemu.