Korzystanie z biblioteki aplikacji Android for Cars

Biblioteka aplikacji Android do samochodu pozwala przenieść do samochodu aplikacje do nawigacji, ciekawych miejsc i internetu rzeczy (IOT). Aby to zrobić, udostępnia zestaw szablonów, które spełniają standardy dotyczące rozpraszania uwagi kierowcy i dbają o szczegóły, takie jak różne czynniki na ekranie samochodu i metody wprowadzania.

Ten przewodnik zawiera omówienie głównych funkcji i koncepcji biblioteki oraz przeprowadzi Cię przez proces konfigurowania podstawowej aplikacji.

Zanim zaczniesz

  1. Przejrzyj strony Design for Automotive dotyczące biblioteki aplikacji samochodowych.
  2. Zapoznaj się z kluczowymi terminami i pojęciami w sekcji poniżej.
  3. Zapoznaj się z interfejsem systemu Android Auto i projektem systemu operacyjnego Android Automotive.
  4. Zapoznaj się z informacjami o wersji.
  5. Przejrzyj Sample.

Najważniejsze terminy i koncepcje

Modele i szablony
Interfejs przedstawia wykres przedstawiający obiekty modelu, które można ułożyć razem na różne sposoby zgodnie z szablonem, do którego należą. Szablony są podzbiorem modeli, które mogą stanowić podstawę na tych wykresach. Modele zawierają informacje, które mają być wyświetlane użytkownikowi w postaci tekstu i obrazów, a także atrybuty do konfigurowania różnych aspektów wizualnych tych informacji, takich jak kolory tekstu czy rozmiary obrazów. Gospodarz konwertuje modele na widoki, które spełniają standardy dotyczące rozpraszania uwagi kierowcy, i dba o szczegóły, takie jak różnorodność elementów na ekranie samochodu i metody wprowadzania.
Host
Host to komponent backendu, który implementuje funkcje oferowane przez interfejsy API biblioteki, dzięki czemu aplikacja może działać w samochodzie. Odpowiedzialność hosta obejmuje znalezienie aplikacji i zarządzanie jej cyklem życia, po przekształcanie modeli w wyświetlenia i powiadamianie aplikacji o interakcjach użytkowników. Na urządzeniach mobilnych host ten jest wdrażany przez Androida Auto. W systemie operacyjnym Android Automotive ten host jest instalowany jako aplikacja systemowa.
Ograniczenia szablonów
Różne szablony narzucają ograniczenia dotyczące treści modeli. Na przykład w przypadku szablonów list obowiązują ograniczenia liczby elementów, które można wyświetlić użytkownikowi. Szablony mają też ograniczenia w sposobie łączenia, aby stanowiły przepływ zadania. Na przykład aplikacja może przekazać maksymalnie 5 szablonów na stos ekranów. Więcej informacji znajdziesz w artykule Ograniczenia szablonów.
Screen
Screen to klasa udostępniana przez bibliotekę implementowaną przez aplikacje w celu zarządzania interfejsem prezentowanym użytkownikowi. Screen ma cykl życia i umożliwia aplikacji wysyłanie szablonu do wyświetlenia, gdy ekran jest widoczny. Instancje Screen można również wypychać do stosu Screen, a następnie wyskakiwać z niego. Dzięki temu są one zgodne z ograniczeniami przepływu szablonu.
CarAppService
CarAppService to abstrakcyjna klasa Service, którą aplikacja musi wdrożyć i wyeksportować, aby mogła zostać wykryta i zarządzana przez hosta. Działanie CarAppService aplikacji odpowiada za sprawdzanie, czy połączenie z hostem jest wiarygodne za pomocą createHostValidator, a następnie za udostępnianie instancji Session dla każdego połączenia przez onCreateSession.
Session

Session jest klasą abstrakcyjną, którą aplikacja musi wdrożyć i zwrócić za pomocą CarAppService.onCreateSession. Służy jako punkt wejścia do wyświetlania informacji na ekranie samochodu. Mają cykl życia, który informuje o aktualnym stanie aplikacji na ekranie samochodu (np. o tym, czy jest widoczna lub ukryta).

Po uruchomieniu obiektu Session, na przykład przy pierwszym uruchomieniu aplikacji, host wysyła żądanie wyświetlenia początkowego Screen za pomocą metody onCreateScreen.

Instalowanie biblioteki aplikacji w samochodzie

Instrukcje dodawania biblioteki do aplikacji znajdziesz na stronie wersji biblioteki Jetpack.

Skonfiguruj pliki manifestu aplikacji

Zanim utworzysz aplikację samochodu, skonfiguruj jej pliki manifestu w ten sposób.

Deklarowanie usługi CarAppService

Host łączy się z aplikacją przez implementację CarAppService. Aby host mógł wykryć Twoją aplikację i się z nią połączyć, deklarujesz tę usługę w pliku manifestu.

Kategorię aplikacji musisz też zadeklarować w elemencie <category> filtra intencji. Przejrzyj listę obsługiwanych kategorii aplikacji, aby znaleźć wartości dozwolone w przypadku tego elementu.

Ten fragment kodu pokazuje, jak w pliku manifestu zadeklarować usługę aplikacji samochodowej dla aplikacji będącej miejscem zainteresowania:

<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 1 z tych wartości kategorii w filtrze intencji podczas zadeklarowania właściwości CarAppService w sposób opisany w poprzedniej sekcji:

Szczegółowe opisy poszczególnych kategorii i kryteriów, jakie muszą spełniać aplikacje, znajdziesz w artykule Jakość aplikacji na Androida dla samochodów.

Podaj nazwę i ikonę aplikacji

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

Możesz określić nazwę i ikonę aplikacji, które reprezentują Twoją aplikację, za pomocą atrybutów label i icon w CarAppService:

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

Jeśli w elemencie <service> nie jest zadeklarowana etykieta lub ikona, host wraca do wartości określonych dla elementu <application>.

Ustawianie motywu niestandardowego

Aby ustawić motyw niestandardowy dla aplikacji samochodu, dodaj element <meta-data> w 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 API aplikacji samochodu

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

Zadeklaruj w pliku AndroidManifest.xml minimalny poziom interfejsu API aplikacji samochodowej obsługiwany przez Twoją aplikację:

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

Szczegółowe informacje o zapewnianiu zgodności wstecznej i zadeklarowaniu minimalnego poziomu interfejsu API wymaganego do używania funkcji znajdziesz w dokumentacji adnotacji RequiresCarApi. Definicję poziomu interfejsu API, który jest wymagany do korzystania z określonej funkcji biblioteki aplikacji w samochodzie, znajdziesz w dokumentacji referencyjnej CarAppApiLevels.

Tworzenie usługi CarAppService i sesji

Twoja aplikacja musi rozszerzyć klasę CarAppService i wdrożyć swoją 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();
    }
    ...
}

Instancja Session odpowiada za zwrócenie instancji Screen w celu 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 samochodu musi zaczynać się od ekranu, który nie jest ekranem głównym ani ekranem docelowym aplikacji (np. obsługiwać precyzyjne linki), możesz wstępnie utworzyć tylny stos ekranów za pomocą polecenia ScreenManager.push, zanim wrócisz z onCreateScreen. Wstępne wypełnianie umożliwia użytkownikom powrót do poprzednich ekranów z pierwszego ekranu wyświetlanego aplikacji.

Tworzenie ekranu startowego

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

Ten fragment kodu pokazuje, jak zadeklarować obiekt 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 to podklasa ContextWrapper dostępna dla instancji Session i Screen. Zapewnia dostęp do usług samochodowych, takich jak ScreenManager do zarządzania stosem ekranu oraz AppManager do ogólnych funkcji związanych z aplikacjami, takich jak dostęp do obiektu Surface w celu rysowania map, oraz do NavigationManager używanych przez aplikacje nawigacyjne zakręt po zakręcie do przekazywania metadanych nawigacji i innych metadanych hosta.

Pełną listę funkcji bibliotek dostępnych w aplikacjach do nawigacji znajdziesz w artykule Dostęp do szablonów nawigacji.

CarContext oferuje też inne funkcje, np. umożliwia ładowanie zasobów rysowalnych za pomocą konfiguracji na ekranie samochodu, uruchamianie aplikacji w samochodzie za pomocą intencji i sygnalizowanie, czy aplikacja powinna wyświetlać mapę w ciemnym motywie.

Wdróż nawigację po ekranie

Aplikacje często wyświetlają kilka różnych ekranów, z których każdy może korzystać z innych szablonów, z których użytkownik może korzystać podczas korzystania z wyświetlanego na ekranie interfejsu.

Klasa ScreenManager udostępnia stos ekranów, za pomocą których można przesuwać ekrany, które mogą być automatycznie wyskakiwane, gdy użytkownik wybierze przycisk Wstecz na ekranie samochodu lub użyje fizycznego przycisku Wstecz dostępnego w niektórych samochodach.

Ten fragment kodu pokazuje, jak dodać działanie wsteczne do szablonu wiadomości oraz działanie powodujące wyświetlenie nowego ekranu po wybraniu przez użytkownika:

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 obiekt Action, który automatycznie wywołuje ScreenManager.pop. To zachowanie można zastąpić przy użyciu instancji OnBackPressedDispatcher dostępnej z interfejsu CarContext.

Aby można było bezpiecznie używać aplikacji podczas jazdy, stos ekranów może mieć maksymalnie 5 ekranów. Więcej informacji znajdziesz w sekcji Ograniczenia dotyczące szablonów.

Odświeżanie zawartości szablonu

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

Podczas odświeżania szablonu Screen ważne jest, aby znać konkretną zawartość, którą można zaktualizować, aby host nie wliczał nowego szablonu do limitu szablonu. Więcej informacji znajdziesz w sekcji Ograniczenia dotyczące szablonów.

Zalecamy zbudowanie struktury ekranów w taki sposób, aby zapewnić mapowanie 1:1 między elementem Screen a typem szablonu zwracanego przez implementację onGetTemplate.

Rysuj mapy

Aplikacje do nawigacji i ciekawych miejsc, które korzystają z tych szablonów, mogą rysować mapy przy użyciu Surface:

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, Ważne miejsca
MapTemplate (wycofane) androidx.car.app.NAVIGATION_TEMPLATES Nawigacja
PlaceListNavigationTemplate (wycofane) androidx.car.app.NAVIGATION_TEMPLATES Nawigacja
RoutePreviewNavigationTemplate (wycofane) androidx.car.app.NAVIGATION_TEMPLATES Nawigacja

Deklarowanie uprawnień do korzystania z powierzchni

Aby uzyskać dostęp do platformy, oprócz uprawnień wymaganych do korzystania z szablonu, z którego korzysta Twoja aplikacja, aplikacja musi zadeklarować uprawnienie androidx.car.app.ACCESS_SURFACE w pliku AndroidManifest.xml:

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

Dostęp do powierzchni

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

Kotlin

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

Java

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

Poznaj widoczny obszar powierzchni

Host może rysować elementy interfejsu dla tych szablonów na górze mapy. Host komunikuje przy użyciu metody SurfaceCallback.onVisibleAreaChanged obszar powierzchni, który ma być nieprzesłonięty i w pełni widoczny dla użytkownika. Dodatkowo, aby zminimalizować liczbę zmian, host wywołuje metodę SurfaceCallback.onStableAreaChanged z najmniejszym prostokątem, który jest zawsze widoczny na podstawie bieżącego szablonu.

Jeśli na przykład aplikacja do nawigacji używa NavigationTemplate z paskiem 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 występuje wywołanie zwrotne do onStableAreaChanged i onVisibleAreaChanged z tym samym prostokątem. Gdy pasek działań jest ukryty, wywoływana jest tylko funkcja onVisibleAreaChanged z większym obszarem. Jeśli użytkownik wejdzie w interakcję z ekranem, z pierwszym prostokątem zostanie wywołana tylko funkcja onVisibleAreaChanged.

Obsługa ciemnego motywu

Aplikacje muszą ponownie narysować mapę na instancji Surface w odpowiednich ciemnych kolorach, gdy gospodarz określi odpowiednie warunki, zgodnie z opisem w artykule Jakość aplikacji na Androida dla samochodów.

Aby zdecydować, czy chcesz narysować ciemną mapę, możesz użyć metody CarContext.isDarkMode. Po każdej zmianie stanu ciemnego motywu otrzymasz wywołanie Session.onCarConfigurationChanged.

Pozwól użytkownikom korzystać z mapy

Gdy korzystasz z poniższych szablonów, możesz umożliwić użytkownikom interakcję z rysowanymi mapami, np. umożliwić im wyświetlanie różnych części mapy przez powiększanie i przesuwanie.

Szablon Obsługa interaktywności od poziomu interfejsu Car App API
NavigationTemplate 2
PlaceListNavigationTemplate (wycofane) 4
RoutePreviewNavigationTemplate (wycofane) 4
MapTemplate (wycofane) 5 (wprowadzenie szablonu)
MapWithContentTemplate 7 (wprowadzenie szablonu)

Wdrażanie interaktywnych wywołań zwrotnych

Interfejs SurfaceCallback zawiera kilka metod wywołań zwrotnych, 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
Dotknięcie onClick 5
Zmiana powiększania ekranu przez rozsunięcie lub ściągnięcie palców onScale 2
Przeciąganie jednym dotknięciem onScroll 2
Przesuwanie jednym kliknięciem onFling 2
Kliknij dwukrotnie onScale (ze współczynnikiem skali określanym przez hosta szablonu) 2
Posuw obrotowy w trybie przesuwania onScroll (ze współczynnikiem odległości określanym przez hosta szablonu) 2

Dodaj pasek działań na mapie

Szablony te mogą zawierać pasek działań związanych z mapą, np. powiększanie i pomniejszanie, wyśrodkowanie, wyświetlanie kompasu i inne wybrane działania. Pasek działań na mapie może zawierać maksymalnie 4 przyciski z ikonami, które można odświeżać bez wpływu na szczegółowość zadań. Ukrywa się ona w stanie bezczynności i pojawia się ponownie w stanie aktywności.

Aby otrzymywać interaktywne wywołania zwrotne mapy, musisz dodać przycisk Action.PAN na pasku działań mapy. Gdy użytkownik naciśnie przycisk przesuwania, host przejdzie w tryb przesuwania, jak opisano w następnej sekcji.

Jeśli Twoja aplikacja pominie przycisk Action.PAN na pasku działań mapy, nie otrzyma danych wejściowych użytkownika za pomocą metod SurfaceCallback, a host wyjdzie z wcześniej aktywowanego trybu przesuwania.

Na ekranie dotykowym przycisk przesuwania nie jest wyświetlany.

Informacje o trybie przesuwania

W trybie przesuwania host szablonu przekształca dane wejściowe użytkownika z niedotykowych urządzeń wejściowych, takich jak kontrolery obrotowe i touchpady, na odpowiednie metody SurfaceCallback. Zareaguj na działanie użytkownika przejścia do trybu przesuwania lub jego opuszczenia za pomocą metody setPanModeListener w NavigationTemplate.Builder. Gdy użytkownik jest w trybie przesuwania, host może ukryć w szablonie inne komponenty UI.

Interakcja z użytkownikiem

Aplikacja może wchodzić w interakcję z użytkownikiem za pomocą wzorców podobnych do aplikacji mobilnej.

Obsługa danych wejściowych użytkownika

Aplikacja może reagować na dane wejściowe użytkownika, przekazując odpowiednie detektory do modeli, które je obsługują. Ten fragment kodu pokazuje, jak utworzyć model Action, który ustawia obiekt OnClickListener wywołujący metodę zdefiniowaną w kodzie 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ę samochodu do nawigacji, używając 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 intencji ACTION_NAVIGATE, znajdziesz w sekcji Uruchamianie aplikacji samochodowej z intencją.

Niektóre działania, np. te, które wymagają nakłaniania użytkownika do kontynuowania interakcji na urządzeniu mobilnym, są dozwolone tylko wtedy, gdy jest zaparkowany samochód. Te działania możesz wdrożyć za pomocą narzędzia ParkedOnlyOnClickListener. Jeśli samochód nie jest zaparkowany, host wyświetla użytkownikowi komunikat, że w tym przypadku określone działanie jest niedozwolone. Gdy samochód jest zaparkowany, kod działa normalnie. Ten fragment kodu pokazuje, jak za pomocą funkcji ParkedOnlyOnClickListener otworzyć ekran 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 zostały rozszerzone o CarAppExtender. Niektóre atrybuty powiadomień, takie jak tytuł treści, tekst, ikona i działania, można skonfigurować w CarAppExtender. Spowoduje to zastąpienie atrybutów powiadomienia wyświetlanych na ekranie samochodu.

Z tego fragmentu kodu dowiesz się, jak wysłać powiadomienie na ekran samochodu, który wyświetla inny tytuł niż 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 części interfejsu użytkownika:

  • Użytkownikowi może zostać wyświetlone powiadomienie HUD.
  • W centrum powiadomień można dodać wpis, opcjonalnie z widoczną na szynie plakietką.
  • W przypadku aplikacji nawigacyjnych powiadomienie może się wyświetlać w widżecie kolei zgodnie z opisem w sekcji Powiadomienia zakręt po zakręcie.

Korzystając z priorytetu powiadomienia, możesz skonfigurować, jak powiadomienia aplikacji wpływają na każdy z tych elementów interfejsu, zgodnie z opisem w dokumentacji CarAppExtender.

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

Aby uzyskać więcej informacji o projektowaniu powiadomień w aplikacji w samochodzie, zapoznaj się z przewodnikiem Google Design for Automotive na temat powiadomień.

Wyświetlaj powiadomienia

Aplikacja może wyświetlać toast za pomocą parametru CarToast, tak jak w tym fragmencie:

Kotlin

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

Java

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

Poproś o uprawnienia

Jeśli aplikacja wymaga dostępu do danych lub działań podlegających ograniczeniom – np. do lokalizacji – obowiązują standardowe reguły uprawnień Androida. Aby poprosić o przyznanie uprawnień, możesz użyć metody CarContext.requestPermissions().

Zaletą używania CarContext.requestPermissions() w przeciwieństwie do standardowych interfejsów API Androida jest to, że nie trzeba uruchamiać własnego interfejsu Activity, aby utworzyć okno uprawnień. Zamiast tworzyć procesy zależne od platformy, możesz użyć tego samego kodu zarówno w Androidzie Auto, jak i w systemie operacyjnym Android Automotive.

Określanie stylu okna uprawnień w Androidzie Auto

W Androidzie Auto na telefonie pojawi się okno uprawnień użytkownika. Domyślnie okno dialogowe nie jest umieszczone w żadnym tle. 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 z zamiarem

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

  • Aby zadzwonić, otwórz aplikację telefonu.
  • Rozpocznij szczegółową nawigację do lokalizacji w domyślnej aplikacji do nawigacji.
  • Uruchom własną aplikację z intencją.

Poniższy przykład pokazuje, jak utworzyć powiadomienie z działaniem powodującym otwarcie aplikacji na ekranie ze szczegółami rezerwacji parkingu. Rozszerzasz instancję powiadomienia o intencję dotyczącą treści, która zawiera element PendingIntent opakowujący wyraźną intencję do działania w 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ż zadeklarować obiekt BroadcastReceiver wywoływany w celu przetworzenia intencji, gdy użytkownik wybierze działanie w interfejsie powiadomień i wywoła działanie 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)));
        }
    }
}

Metoda Session.onNewIntent w Twojej aplikacji obsługuje tę intencję, przesuwając ekran rezerwacji parkingu na stos, jeśli jeszcze nie znajduje się 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ń z aplikacji samochodowej znajdziesz w sekcji Wyświetlanie powiadomień.

Ograniczenia 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 Screen w stosie. Jeśli na przykład aplikacja wyśle 2 szablony na ekranie A, a potem wypchnie ekran B, może teraz wysłać 3 kolejne. Z kolei, jeśli każdy ekran jest tak skonfigurowany, aby wysyłać 1 szablon, aplikacja może przekazać 5 wystąpień ekranu do stosu ScreenManager.

Istnieją szczególne przypadki z tymi ograniczeniami: odświeżanie szablonu, cofanie i resetowanie.

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 przekazuje nowy szablon tego samego typu i zawierający tę samą główną zawartość co poprzedni, nowy szablon nie jest wliczany do limitu. Na przykład aktualizacja stanu przełączania wiersza w ListTemplate nie jest wliczana do limitu. Aby dowiedzieć się, jakie typy aktualizacji treści można uznać za odświeżenie, zapoznaj się z dokumentacją poszczególnych szablonów.

Operacje wsteczne

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

Jeśli np. aplikacja wyśle 2 szablony na ekranie A, a potem wypchnie ekran B i wyśle 2 kolejne szablony, aplikacji pozostanie 1 limit. Jeśli aplikacja wróci na ekran A, host zresetuje limit do 3, ponieważ aplikacja została cofnięta o 2 szablony.

Pamiętaj, że po powrocie na ekran aplikacja musi wysłać szablon tego samego typu co ostatnio wysłane z tego ekranu. Wysłanie innego typu powoduje błąd. Jeśli jednak podczas operacji cofania typ pozostanie taki sam, aplikacja może swobodnie modyfikować zawartość szablonu bez ograniczania limitu.

Zresetuj operacje

Niektóre szablony mają specjalną semantykę, która wskazuje zakończenie danego zadania. Na przykład NavigationTemplate to widok, który powinien pozostać na ekranie i odświeżać się za pomocą nowych, szczegółowych instrukcji dla użytkownika. Po dotarciu do jednego z tych szablonów host zresetuje limit szablonów, traktując ten szablon tak, jakby był to pierwszy krok nowego zadania. Dzięki temu aplikacja będzie mogła rozpocząć nowe zadanie. Aby sprawdzić, które z nich uruchamiają resetowanie na hoście, zapoznaj się z dokumentacją poszczególnych szablonów.

Jeśli host otrzyma intencję uruchomienia aplikacji z poziomu powiadomienia lub programu uruchamiającego, limit również zostanie zresetowany. Ten mechanizm umożliwia aplikacji rozpoczęcie nowego przepływu zadań z poziomu powiadomień i działa nawet wtedy, gdy aplikacja jest już powiązana i znajduje się 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 samochodu z intencją dowiesz się, jak uruchamiać aplikację po kliknięciu powiadomienia.

Interfejs Connection API

Aby sprawdzić, czy aplikacja działa w systemie operacyjnym Android Auto lub Android Automotive, możesz pobrać informacje o połączeniu w czasie działania za pomocą interfejsu API CarConnection.

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

Kotlin

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

Java

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

Można w nim 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();
}

Interfejs API ograniczeń

W poszczególnych samochodach użytkownik może wyświetlać różną liczbę instancji Item naraz. Użyj narzędzia ConstraintManager, aby sprawdzić limit treści w czasie działania i ustawić odpowiednią liczbę elementów w szablonach.

Zacznij od pobrania ConstraintManager z CarContext:

Kotlin

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

Java

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

Następnie możesz przesłać zapytanie do pobranego obiektu ConstraintManager, aby sprawdzić odpowiedni limit treści. Aby na przykład uzyskać liczbę elementów, które mogą być wyświetlane w siatce, wywołaj funkcję getContentLimit za pomocą polecenia CONTENT_LIMIT_TYPE_GRID:

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

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

Dodaj proces logowania

Jeśli Twoja aplikacja umożliwia logowanie się użytkownikom, możesz użyć szablonów takich jak SignInTemplate i LongMessageTemplate z interfejsem Car App API na poziomie 2 lub wyższym, aby obsługiwać logowanie w aplikacji na konsoli głównej samochodu.

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

  • InputSignInMethod do logowania według nazwy użytkownika i hasła.
  • PinSignInMethod w przypadku logowania się za pomocą kodu PIN, gdy użytkownik łączy swoje konto z telefonu za pomocą kodu PIN wyświetlanego na radioodtwarzaczu.
  • ProviderSignInMethod w przypadku logowania przez dostawcę, na przykład logowania przez Google czy jednokrotnego kliknięcia.
  • QRCodeSignInMethod w przypadku logowania za pomocą kodu QR – użytkownik skanuje ten kod, aby dokończyć logowanie za pomocą telefonu. Ta funkcja jest dostępna w przypadku interfejsu Car API na poziomie 4 lub wyższym.

Aby na przykład wdrożyć szablon, który zbiera hasła użytkownika, zacznij od utworzenia obiektu 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.
    }
};

InputSignInMethod Builder wymaga podania wartości 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 urządzenia InputSignInMethod, aby utworzyć 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żyj usługi AccountManager

Aplikacje na system operacyjny Android Automotive, które korzystają z uwierzytelniania, muszą używać usługi AccountManager z tych powodów:

  • Lepsze wrażenia użytkowników i łatwiejsze zarządzanie kontem: użytkownicy mogą łatwo zarządzać wszystkimi swoimi kontami za pomocą menu kont w ustawieniach systemu, w tym funkcji logowania i wylogowywania się.
  • Usługi dla gości: samochody to współdzielone urządzenia, dlatego producenci OEM mogą włączyć obsługę w pojeździe gościnnym, bo nie można dodawać kont.

Dodaj warianty ciągu tekstowego

Ilość tekstu może się różnić w zależności od rozmiaru ekranu samochodu. Interfejs Car App API na poziomie 2 lub wyższym umożliwia określenie wielu wariantów ciągu tekstowego, które najlepiej pasują do ekranu. Aby dowiedzieć się, gdzie akceptowane są warianty tekstu, poszukaj szablonów i komponentów, które przyjmują identyfikator CarText.

Do elementu CarText możesz dodać warianty ciągu tekstowego, używając 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();

Następnie możesz użyć tego elementu CarText 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 największej do najmniej preferowanej wartości, np. od najdłuższego do najkrótszego. Gospodarz dobiera odpowiednią długość ciągu znaków w zależności od ilości miejsca dostępnego na ekranie samochodu.

Dodaj wbudowane CarIcon dla wierszy

Aby zwiększyć atrakcyjność aplikacji, możesz dodać ikony w tekście za pomocą narzędzia CarIconSpan. Więcej informacji o tworzeniu tych spanów znajdziesz w dokumentacji CarIconSpan.create. Aby dowiedzieć się, jak działa stylizowanie tekstu ze spanami, przeczytaj artykuł o tworzeniu stylów tekstu Spanss.

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 do sprzętu samochodowego

Począwszy od interfejsu Car App API poziomu 3, biblioteka aplikacji samochodowych zawiera interfejsy API umożliwiające dostęp do właściwości i czujników pojazdu.

Wymagania

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

Dodatkowo w pliku AndroidManifest.xml musisz zadeklarować odpowiednie uprawnienia niezbędne do przesłania prośby o dane o samochodzie, których chcesz używać. Pamiętaj, że te uprawnienia też musi Ci przyznać użytkownik. Zamiast tworzyć procesy zależne od platformy, możesz użyć tego samego kodu zarówno w Androidzie Auto, jak i w systemie operacyjnym Android Automotive. Potrzebne są jednak różne uprawnienia.

Informacje o samochodzie

W tej tabeli opisano właściwości udostępniane przez interfejsy API CarInfo oraz uprawnienia, które musisz uzyskać, aby z nich korzystać:

Metody Właściwości Uprawnienia Androida Auto Uprawnienia systemu operacyjnego 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, paliwa 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łatnej, typ karty płatnej 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
Prędkość ogólna, wyświetlanie (widoczne 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
Drogomierz com.google.android.gms.permission.CAR_MILEAGE Te dane nie są dostępne w systemie operacyjnym Android Automotive w przypadku 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 wystąpi błąd, sprawdź stan żądanej wartości, by lepiej zrozumieć, dlaczego nie udało się pobrać żądanych danych. Pełną definicję klasy CarInfo znajdziesz w dokumentacji referencyjnej.

Czujniki samochodów

Klasa CarSensors zapewnia dostęp do danych z akcelerometru, żyroskopu, kompasu i 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 API SensorManager. Na przykład aby sprawdzić nagłówek 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 samochodu, musisz też zadeklarować uprawnienie android.permission.ACCESS_FINE_LOCATION i poprosić o takie uprawnienia.

Testowanie

Aby przeprowadzić symulację danych z czujników podczas testowania w Androidzie Auto, zapoznaj się z sekcjami Czujniki i Konfiguracja czujnika w przewodniku po module centrali komputerowej. Aby symulować dane z czujników podczas testowania w systemie operacyjnym Android Automotive, zapoznaj się z sekcją Emuluj stan sprzętu w przewodniku po emulatorze systemu operacyjnego Android Automotive.

Cykle życia usług CarAppService, sesji i ekranu

Klasy Session i Screen implementują interfejs LifecycleOwner. Gdy użytkownik wchodzi w interakcję z aplikacją, wywołania zwrotne cyklu życia obiektów Session i Screen są wywoływane zgodnie z opisem na diagramach poniżej.

Cykle życia sesji CarAppService i sesji

Rysunek 1 Cykl życia Session.

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

Cykl życia ekranu

Rysunek 2. Cykl życia Screen.

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

Nagrywaj z mikrofonu w samochodzie

Za pomocą CarAppService i interfejsu API CarAudioRecord aplikacji możesz przyznać jej dostęp do mikrofonu samochodowego użytkownika. Użytkownicy muszą zezwolić aplikacji na dostęp do mikrofonu w samochodzie. Aplikacja może rejestrować i przetwarzać dane wejściowe użytkownika.

Uprawnienia do nagrywania

Przed rozpoczęciem nagrywania dźwięku musisz zadeklarować uprawnienia do nagrywania w usłudze AndroidManifest.xml i poprosić użytkownika, aby je udzielił.

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

Musisz poprosić o uprawnienia do nagrywania w czasie działania. Szczegółowe informacje o tym, jak poprosić o zezwolenie w aplikacji samochodu, znajdziesz w sekcji Prośba o uprawnienia.

Nagrywanie dźwięku

Gdy użytkownik zezwoli na nagrywanie, możesz zacząć nagrywać dźwięk i przetwarzać 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

W przypadku nagrywania z mikrofonu w samochodzie najpierw ustaw fokus audio, aby zatrzymać odtwarzanie multimediów. Jeśli utracisz aktywność audio, zatrzymaj nagrywanie.

Oto przykład uzyskania aktywności 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
 

Biblioteka testów

Biblioteka testowania Androida do samochodu zawiera klasy pomocnicze, których możesz używać do weryfikowania działania aplikacji w środowisku testowym. SessionController pozwala na przykład symulować połączenie z hostem oraz sprawdzić, czy zostały utworzone i zwrócone prawidłowe wartości Screen i Template.

Przykłady użycia znajdziesz w przykładach.

Zgłaszanie problemu z biblioteką aplikacji Android do samochodu

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

Tworzenie nowego numeru

Zanim zgłosisz nowy problem, sprawdź, czy znajduje się on w informacjach o wersji biblioteki lub na liście problemów. Możesz zasubskrybować kanał i głosować na problemy, klikając gwiazdkę przy problemie w trackerze. Więcej informacji znajdziesz w artykule Subskrybowanie problemu.