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
- Zapoznaj się ze stronami Projektowanie z myślą o kierowcach, które zawierają informacje o bibliotece aplikacji samochodowych.
- Omówienie kategorii aplikacji nawigacyjnych i innych aplikacji związanych z prowadzeniem pojazdów
- Omówienie tworzenia aplikacji za pomocą szablonów
- Podstawowe elementy, które obejmują szablony i komponenty szablonów.
- Przykładowe ścieżki demonstrujące typowe wzorce UX
- Wymagania dotyczące aplikacji opartej na szablonie
- Zapoznaj się z kluczowymi terminami i pojęciami w następnej sekcji.
- Zapoznaj się z interfejsem systemu Android Auto i wzornictwem systemu operacyjnego Android Automotive.
- Zapoznaj się z informacjami o wersji.
- 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. InstancjeScreen
można też umieszczać naScreen
stosie i zdejmować z niego, co zapewnia zgodność z ograniczeniami przepływu szablonu.CarAppService
CarAppService
to abstrakcyjna klasaService
, 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ą funkcjicreateHostValidator
, a następnie za udostępnianie instancjiSession
dla każdego połączenia za pomocą funkcjionCreateSession
.Session
Session
to klasa abstrakcyjna, którą aplikacja musi zaimplementować i zwrócić za pomocą funkcjiCarAppService.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ątkowegoScreen
za pomocą metodyonCreateScreen
.
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:
androidx.car.app.category.NAVIGATION
: aplikacja, która podaje wskazówki nawigacyjne zakręt po zakręcie. Zobacz Tworzenie aplikacji nawigacyjnych na samochody.androidx.car.app.category.POI
: aplikacja, która udostępnia funkcje związane ze znajdowaniem interesujących miejsc, takich jak miejsca parkingowe, stacje ładowania i stacje benzynowe. Zobacz Tworzenie aplikacji z ciekawymi miejscami na potrzeby samochodów.androidx.car.app.category.IOT
: aplikacja, która umożliwia użytkownikom podejmowanie odpowiednich działań na połączonych urządzeniach z poziomu samochodu. Zobacz tworzenie aplikacji internetu rzeczy na potrzeby samochodów.androidx.car.app.category.WEATHER
: aplikacja, która wyświetla użytkownikom istotne informacje o pogodzie w ich bieżącej lokalizacji lub na trasie. Zobacz Tworzenie aplikacji pogodowych na samochody.androidx.car.app.category.MEDIA
: aplikacja, która umożliwia użytkownikom przeglądanie i odtwarzanie muzyki, radia, audiobooków i innych treści audio w samochodzie. Zobacz Tworzenie aplikacji multimedialnych opartych na szablonach na potrzeby samochodów.androidx.car.app.category.MESSAGING
: aplikacja, która umożliwia użytkownikom komunikowanie się za pomocą krótkich wiadomości tekstowych. Zobacz Tworzenie szablonowych wiadomości na Androida Auto.androidx.car.app.category.CALLING
: aplikacja, która umożliwia użytkownikom komunikowanie się za pomocą połączeń głosowych. Zobacz Tworzenie funkcji połączeń dla Androida Auto.
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 label
i icon
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()
i 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 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 wywołanie zwrotne do onStableAreaChanged
i onVisibleAreaChanged
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 setPanModeListener
w NavigationTemplate.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 CarAppExtender
dokumentacji.
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:
- Otwórz aplikację Telefon, aby zadzwonić.
- Uruchom szczegółową nawigację do lokalizacji za pomocą domyślnej aplikacji do nawigacji samochodowej.
- Uruchom własną aplikację za pomocą intencji.
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:
NavigationTemplate
PaneTemplate
MessageTemplate
MediaPlaybackTemplate
SignInTemplate
LongMessageTemplate
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 ConstraintManager
z CarContext
:
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 SignInTemplate
i LongMessageTemplate
, 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 Google i jednego 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 |
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 Czujniki i Konfiguracja 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 Session
i Screen
implementują interfejs LifecycleOwner
. Gdy użytkownik wchodzi w interakcję z aplikacją, wywoływane są wywołania zwrotne cyklu życia obiektów Session
i Screen
, jak pokazano na poniższych diagramach.
Cykle życia usługi CarAppService i sesji

Session
cykl życiaSzczegółowe informacje znajdziesz w dokumentacji metody Session.getLifecycle
.
Cykl życia ekranu

Screen
cykl życiaSzczegółowe informacje znajdziesz w dokumentacji metody Screen.getLifecycle
.
Nagrywanie z mikrofonu w samochodzie
Za pomocą interfejsów API CarAppService
i CarAudioRecord
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
Screen
i
Template
.
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.
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.