Biblioteka aplikacji Android for Cars umożliwia korzystanie z aplikacji do nawigacji, informacji o miejscach i Internetu rzeczy w samochodzie. Udostępnia on zestaw szablonów zaprojektowanych zgodnie ze standardami dotyczącymi rozpraszania uwagi kierowcy i zapewniający obsługę takich szczegółów, jak różne czynniki ekranu samochodowego i metody wprowadzania danych.
Z tego przewodnika dowiesz się, jak skonfigurować podstawową aplikację. Znajdziesz w nim omówienie najważniejszych funkcji i koncepcji biblioteki.
.Zanim zaczniesz
- Przejrzyj strony Projektowanie z myślą o prowadzeniu pojazdu dotyczące Biblioteki aplikacji samochodowych.
- Omówienie kategorii aplikacji do nawigacji i innych aplikacji związanych z prowadzeniem pojazdu
- Omówienie tworzenia aplikacji za pomocą szablonów
- Elementy składowe obejmujące szablony i elementy szablonu.
- Przykładowe przepływy pokazujące typowe wzorce UX
- Wymagania dotyczące aplikacji opartych na szablonach
- Zapoznaj się z kluczowymi terminami i pojęciami w tej sekcji.
- Zapoznaj się z interfejsem użytkownika systemu Android Auto i projektowaniem systemu operacyjnego Android Automotive.
- Zapoznaj się z informacjami o wersji.
- Przejrzyj przykłady.
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 w ramach możliwości danego szablonu. Szablony to podzbiór modeli, które mogą pełnić funkcję wierzchołka w tych grafach. Modele zawierają informacje, które mają być wyświetlane użytkownikowi w postaci tekstu i obrazów, a także atrybuty umożliwiające konfigurowanie aspektów wizualnych tych informacji, np. kolorów tekstu lub rozmiarów obrazów. Host konwertuje modele na widoki, które spełniają standardy dotyczące rozpraszania uwagi kierowcy, i dba o szczegóły, takie jak różnorodność czynników ekranu samochodowego i modalności wprowadzania danych.
- Zorganizuj
- Host to komponent backendu, który implementuje funkcje oferowane przez interfejsy API biblioteki, aby aplikacja mogła działać w samochodzie. Obowiązki hosta obejmują odkrywanie Twojej aplikacji i zarządzanie jej cyklem życia, a także przekształcanie modeli w wyświetlenia i powiadamianie aplikacji o interakcjach użytkowników. Na urządzeniach mobilnych host jest implementowany przez Androida. W systemie operacyjnym Android Automotive host jest instalowany jako aplikacja systemowa.
- Ograniczenia dotyczące szablonów
- Różne szablony nakładają ograniczenia na zawartość modeli. Na przykład szablony list mają limity dotyczące liczby elementów, które mogą być wyświetlane użytkownikowi. Szablony mają też ograniczenia dotyczące sposobu ich łączenia w ramach procesu wykonania zadania. Na przykład aplikacja może przesłać do stosu 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ą, aby zarządzać interfejsem użytkownika.Screen
ma cykl życia i zapewnia aplikacji mechanizm do wysyłania szablonu do wyświetlenia, gdy ekran jest widoczny.Screen
instancje mogą być również przesuwane do i zScreen
komputera, co zapewnia ich zgodność z ograniczeniami przepływu szablonu.CarAppService
CarAppService
to abstrakcyjna klasaService
, którą Twoja aplikacja musi zaimplementować i wyeksportować, aby była wykrywana i zarządzana przez hosta. KomponentCarAppService
Twojej aplikacji odpowiada za weryfikację, czy połączenie z hostem jest bezpieczne, korzystając z elementucreateHostValidator
, a następnie udostępnia instancjeSession
dla każdego połączenia za pomocą elementuonCreateSession
.Session
Session
to abstrakcyjna klasa, którą aplikacja musi zaimplementować i zwrócić za pomocąCarAppService.onCreateSession
. Jest punktem wejścia do wyświetlania informacji na ekranie samochodu. Ma cykl życia, który informuje o bieżącym stanie aplikacji na ekranie samochodu, np. czy jest widoczna, czy ukryta.Gdy
Session
jest uruchamiany, np. podczas pierwszego uruchamiania aplikacji, host prosi o wyświetlenie początkowegoScreen
za pomocą metodyonCreateScreen
.
Instalowanie biblioteki aplikacji samochodowych
Instrukcje dodawania biblioteki do aplikacji znajdziesz na stronie wersji biblioteki Jetpack.
Konfigurowanie plików manifestu aplikacji
Zanim utworzysz aplikację samochodową, skonfiguruj pliki manifestu aplikacji w ten sposób:
Zadeklaruj CarAppService
Host łączy się z Twoją aplikacją przez implementację CarAppService
. W pliku manifestu deklarujesz tę usługę, aby umożliwić jej hostowi znalezienie i połączenie z Twoją aplikacją.
Musisz też zadeklarować kategorię aplikacji w elemencie <category>
filtra intencji aplikacji. Aby sprawdzić, jakie wartości są dozwolone w przypadku tego elementu, zobacz listę obsługiwanych kategorii aplikacji.
Ten fragment kodu pokazuje, jak w pliku manifestu zadeklarować usługę aplikacji samochodowej dla aplikacji typu punkt 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 deklarowania CarAppService
zgodnie z opisem w poprzedniej sekcji:
androidx.car.app.category.NAVIGATION
: aplikacja, która wyświetla wskazówki dojazdu zakręt po zakręcie. Zobacz Tworzenie aplikacji nawigacyjnych dla samochodów.androidx.car.app.category.POI
: aplikacja, która umożliwia znajdowanie punktów zainteresowania, takich jak miejsca parkingowe, stacje ładowania i stacje paliw. Zobacz Tworzenie aplikacji z ciekawymi miejscami dla samochodów.androidx.car.app.category.IOT
: aplikacja, która umożliwia użytkownikom wykonywanie odpowiednich działań na połączonych urządzeniach z poziomu samochodu. Zobacz artykuł Tworzenie aplikacji IoT dla samochodów.androidx.car.app.category.WEATHER
: aplikacja, która umożliwia użytkownikom wyświetlanie informacji o pogodzie związanych z ich bieżącą lokalizacją lub trasą. Zobacz Tworzenie aplikacji pogodowych na potrzeby samochodów.
Szczegółowe opisy poszczególnych kategorii i kryteriów, które muszą spełniać aplikacje, aby należały do danej kategorii, 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 aplikacji w interfejsie systemu.
Możesz określić nazwę i ikonę aplikacji, które będą reprezentować Twoją aplikację, za pomocą atrybutów label
i icon
: 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 używa wartości określonych w elemencie <application>
.
Ustawianie motywu niestandardowego
Aby ustawić niestandardowy motyw dla 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 dla 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 Car
Biblioteka aplikacji samochodowych definiuje własne poziomy interfejsu API, aby można było sprawdzić, które funkcje biblioteki są obsługiwane przez hosta szablonu w samochodzie.
Aby pobrać najwyższy poziom interfejsu API obsługiwany przez hosta, użyj metody getCarAppApiLevel()
.
Zadeklaruj minimalny poziom interfejsu API aplikacji na potrzeby samochodu, który jest obsługiwany przez Twoją aplikację w pliku AndroidManifest.xml
:
<manifest ...>
<application ...>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"/>
</application>
</manifest>
W dokumentacji dotyczącej adnotacji RequiresCarApi
znajdziesz szczegółowe informacje o zachowaniu zgodności wstecznej i deklarowaniu minimalnego poziomu interfejsu API wymaganego do korzystania z funkcji. Aby dowiedzieć się, jaki poziom interfejsu API jest wymagany do korzystania z określonej funkcji biblioteki aplikacji samochodowych, 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(); } ... }
Instancja Session
odpowiada za zwrócenie instancji Screen
, która ma być używana podczas pierwszego uruchamiania 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łużyć scenariusze, w których aplikacja samochodowa musi się uruchamiać z ekranu innego niż ekran główny lub lądowania, np. w przypadku obsługi precyzyjnych linków, możesz wstępnie ustawić stos ekranów za pomocą metody ScreenManager.push
przed powrotem z onCreateScreen
.
Wstępna selekcja umożliwia użytkownikom przechodzenie 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 wdrażając 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
, do której mają dostęp instancje Session
i Screen
. Zapewnia dostęp do usług samochodowych, takich jak ScreenManager
do zarządzania strukturą ekranu, AppManager
do ogólnych funkcji związanych z aplikacją, takich jak dostęp do obiektu Surface
w celu rysowania map, oraz NavigationManager
używany przez aplikacje nawigacji z nawigacją krok po kroku do przesyłania metadanych nawigacji i innych zdarzeń związanych z nawigacją do hosta.
Pełną listę funkcji biblioteki dostępnych w przypadku aplikacji do nawigacji znajdziesz w artykule Dostęp do szablonów nawigacji.
CarContext
oferuje też inne funkcje, takie jak możliwość wczytywania zasobów do wyświetlania za pomocą konfiguracji z ekranu samochodu, uruchamianie aplikacji w samochodzie za pomocą intencji oraz sygnalizowanie, czy aplikacja ma wyświetlać mapę w ciemnym motywie.
Implementowanie nawigacji po ekranie
Aplikacje często zawierają wiele różnych ekranów, z których każdy może używać różnych szablonów, po których użytkownik może się poruszać, korzystając z interfejsu wyświetlanego na ekranie.
Klasa ScreenManager
udostępnia zestaw ekranów, które możesz przesuwać, aby wyświetlać ekrany, które mogą być automatycznie wyświetlane, gdy użytkownik kliknie przycisk Wstecz na ekranie samochodu lub użyje przycisku Wstecz na sprzęcie (dostępnego w niektórych samochodach).
Ten fragment kodu pokazuje, jak dodać do szablonu wiadomości działanie wstecz, a także działanie, które powoduje 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 obiekt ScreenManager.pop
.
To zachowanie można zastąpić, używając instancji OnBackPressedDispatcher
dostępnej w CarContext
.
Aby zapewnić bezpieczne korzystanie z aplikacji podczas jazdy, liczba ekranów w grupie może wynosić maksymalnie 5. Więcej informacji znajdziesz w sekcji Ograniczenia szablonów.
Odświeżanie zawartości szablonu
Twoja aplikacja może poprosić o unieważnienie treści w elementach Screen
, wywołując metodę Screen.invalidate
.
Następnie host wywołuje metodę Screen.onGetTemplate
aplikacji, aby pobrać szablon z nowymi treściami.
Podczas odświeżania Screen
ważne jest, aby wiedzieć, które treści w szablonie można zaktualizować, aby host nie liczył nowego szablonu w ramach limitu.
Więcej informacji znajdziesz w sekcji Ograniczenia dotyczące szablonów.
Zalecamy sformatowanie ekranów tak, aby Screen
było jednoznacznie powiązane z typem szablonu zwracanego przez implementację onGetTemplate
.
Rysowanie map
Aplikacje do nawigacji i ciekawych miejsc korzystające z tych szablonów mogą wyświetlać mapy, uzyskując dostęp do Surface
:
Szablon | Uprawnienia 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 (wycofane) |
androidx.car.app.NAVIGATION_TEMPLATES |
Nawigacja |
PlaceListNavigationTemplate (wycofane) |
androidx.car.app.NAVIGATION_TEMPLATES |
Nawigacja |
RoutePreviewNavigationTemplate (wycofane) |
androidx.car.app.NAVIGATION_TEMPLATES |
Nawigacja |
Zadeklaruj uprawnienie do wyświetlania
Aby uzyskać dostęp do powierzchni, oprócz uprawnień wymaganych przez szablon, w pliku AndroidManifest.xml
aplikacji musisz zadeklarować uprawnienie androidx.car.app.ACCESS_SURFACE
:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
...
</manifest>
Dostęp do powierzchni
Aby uzyskać dostęp do Surface
, który udostępnia host, musisz wdrożyć SurfaceCallback
i udostępnić go usłudze AppManager
car. Bieżąca wartość Surface
jest przekazywana do funkcji SurfaceCallback
w parametrze SurfaceContainer
funkcji onSurfaceAvailable()
i onSurfaceDestroyed()
.
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
Widoczny obszar powierzchni
Gospodarz może rysować elementy interfejsu dla szablonów na mapie. Host przekazuje informacje o obszarze powierzchni, który jest niezasłonięty i w pełni widoczny dla użytkownika, wywołując metodę SurfaceCallback.onVisibleAreaChanged
. Aby zminimalizować liczbę zmian, gospodarz 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 korzysta z elementu NavigationTemplate
z paskiem akcji u góry ekranu, pasek akcji 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 jest wywołanie funkcji 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, ponownie zostanie wywołana tylko funkcja onVisibleAreaChanged
z pierwszym prostokątem.
Obsługa ciemnego motywu
Aplikacje muszą ponownie narysować mapę na instancji Surface
z odpowiednimi ciemnymi kolorami, gdy host uzna, że warunki tego wymagają, zgodnie z opisem w artykule o jakości aplikacji na Androida w samochodach.
Aby zdecydować, czy chcesz narysować ciemną mapę, możesz użyć metody CarContext.isDarkMode
. Gdy zmieni się stan ciemnego motywu, otrzymasz połączenie od Session.onCarConfigurationChanged
.
Umożliwienie użytkownikom interakcji z mapą
Korzystając z podanych niżej szablonów, możesz umożliwić użytkownikom interakcję z rysowanymi przez Ciebie mapami, na przykład umożliwiając im wyświetlanie różnych części mapy przez powiększanie i przesuwanie.
Szablon | Interakcja obsługiwana od poziomu interfejsu API aplikacji na potrzeby samochodów |
---|---|
NavigationTemplate |
2 |
PlaceListNavigationTemplate (wycofane) |
4 |
RoutePreviewNavigationTemplate (wycofane) |
4 |
MapTemplate (wycofane) |
5 (wprowadzenie do szablonu) |
MapWithContentTemplate |
7 (wprowadzenie do szablonu) |
Implementowanie wywołań zwrotnych interakcji
Interfejs SurfaceCallback
udostępnia kilka metod wywołania, które możesz zastosować, aby dodać interaktywność do map utworzonych za pomocą szablonów z poprzedniej sekcji:
Interakcja | SurfaceCallback metoda |
Obsługiwane od poziomu interfejsu API Car App |
---|---|---|
Kliknij | onClick |
5 |
Ściągnij, aby powiększyć | onScale |
2 |
Przeciąganie jednym palcem | onScroll |
2 |
Przesuwanie jednym dotknięciem | onFling |
2 |
Kliknij dwukrotnie | onScale (z współczynnikiem skalowania określonym przez hosta szablonu) |
2 |
Przesuwanie za pomocą kółka w trybie przesuwania | onScroll (z współczynnikiem odległości określonym przez hosta szablonu) |
2 |
Dodawanie paska działań na mapie
Te szablony mogą zawierać pasek działań związanych z mapą, np. powiększanie i pomniejszanie, ponowne wyśrodkowywanie, wyświetlanie kompasu i inne działania, które wybierzesz do wyświetlenia. Pasek czynności na mapie może zawierać maksymalnie 4 przyciski z ikonami, które można odświeżać bez wpływu na głębokość zadania. Ukrywa się w stanie bezczynności i pojawia się ponownie w stanie aktywnym.
Aby otrzymywać wywołania zwrotne interakcji z mapą, musisz dodać przycisk Action.PAN
do paska działań na mapie. Gdy użytkownik naciśnie przycisk panoramowania, gospodarz przejdzie do trybu panoramowania, jak opisano w sekcji poniżej.
Jeśli Twoja aplikacja pomija przycisk Action.PAN
na pasku działań na mapie, nie otrzymuje danych wejściowych od użytkownika z metod SurfaceCallback
, a gospodarz zamyka wcześniej aktywowany tryb panoramowania.
Na ekranie dotykowym przycisk przesuwania nie jest wyświetlany.
Tryb przesuwania
W trybie przesuwania host szablonu przekształca dane wejściowe użytkownika z urządzeń bezdotykowych, takich jak sterowniki obrotowe i touchpady, w odpowiednie metody SurfaceCallback
. Odpowiedź na działanie użytkownika polegające na włączeniu lub wyłączeniu trybu panoramowania za pomocą metody setPanModeListener
w komponencie NavigationTemplate.Builder
. Gospodarz może ukryć inne elementy interfejsu w szablonie, gdy użytkownik jest w trybie panoramowania.
Interakcje z użytkownikiem
Aplikacja może wchodzić w interakcję z użytkownikiem, korzystając z wzorów podobnych do tych stosowanych w aplikacjach mobilnych.
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ą. Ten fragment kodu pokazuje, jak utworzyć model Action
, który ustawia zmienną OnClickListener
wywołującą 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ę nawigacyjną 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 intencjonalnego wywołania ACTION_NAVIGATE
, znajdziesz w sekcji Uruchamianie aplikacji samochodowej za pomocą intencjonalnego wywołania.
Niektóre działania, takie jak te, które wymagają od użytkownika kontynuowania interakcji na urządzeniach mobilnych, są dozwolone tylko wtedy, gdy samochód jest zaparkowany.
Aby wdrożyć te działania, możesz użyć interfejsu ParkedOnlyOnClickListener
. 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. Ten fragment kodu pokazuje, jak użyć ParkedOnlyOnClickListener
do otwarcia 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 powiadomienia, takie jak tytuł treści, tekst, ikona i działania, można ustawić w CarAppExtender
, zastępując atrybuty powiadomienia, gdy są wyświetlane na ekranie samochodu.
Poniższy fragment kodu pokazuje, jak wysłać powiadomienie na ekran samochodu, które będzie miało 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 elementy interfejsu użytkownika:
- Użytkownik może zobaczyć powiadomienie o zbliżającym się terminie.
- Może zostać dodany wpis w centrum powiadomień, opcjonalnie z plakietką widoczną na pasku.
- W przypadku aplikacji do nawigacji powiadomienie może być wyświetlane w widżecie w kolejce zgodnie z opisem w artykule Powiadomienia o nawigacji.
Możesz wybrać sposób konfigurowania powiadomień z aplikacji, aby wpływać na te elementy interfejsu użytkownika, korzystając z priorytetu powiadomienia zgodnie z opisem w dokumentacjiCarAppExtender
.
Jeśli funkcja NotificationCompat.Builder.setOnlyAlertOnce
jest wywoływana z wartością true
, powiadomienie o wysokim priorytecie jest wyświetlane jako powiadomienie o wysokim priorytecie tylko raz.
Więcej informacji o projektowaniu powiadomień aplikacji samochodowej znajdziesz w przewodniku Google Design for Driving dotyczącym powiadomień.
Wyświetlanie komunikatów
Aplikacja może wyświetlać komunikat za pomocą interfejsu CarToast
, jak 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 ograniczonych danych lub działań, takich jak lokalizacja, obowiązują standardowe zasady dotyczące uprawnień Androida. Aby poprosić o uprawnienia, możesz użyć metody CarContext.requestPermissions()
.
Zaletą korzystania z interfejsu CarContext.requestPermissions()
w porównaniu z używaniem standardowych interfejsów API Androida jest to, że nie musisz uruchamiać własnego interfejsu Activity
, aby utworzyć okno z uprawnieniami. Co więcej, możesz używać tego samego kodu zarówno w Android Auto, jak i w systemie operacyjnym Android Automotive, zamiast tworzyć przepływy zależne od platformy.
Styl okna z prośbą o uprawnienia w Androidzie Auto
W Android Auto na telefonie wyświetli się okno z uprawnieniami dla użytkownika.
Domyślnie tło nie będzie widoczne za oknem dialogowym. Aby ustawić niestandardowe tło, w pliku AndroidManifest.xml
zadeklaruj motyw aplikacji samochodowej 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:
- Aby zadzwonić, otwórz aplikację Telefon.
- Uruchom domyślną aplikację nawigacyjną, aby uzyskać szczegółowe wskazówki dojazdu do danej lokalizacji.
- Uruchom własną aplikację za pomocą intencji.
Ten przykład pokazuje, jak utworzyć powiadomienie z działaniem, które otwiera aplikację z ekranem z informacjami o rezerwacji miejsca parkingowego.
Rozszerzasz wystąpienie powiadomienia o zawartą w nim intencję treści PendingIntent
, która otacza 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ż zadeklarować obiekt BroadcastReceiver
, który jest wywoływany do przetwarzania inencji, gdy użytkownik wybierze działanie w interfejsie powiadomienia i wywołuje CarContext.startCarApp
z intencją zawierającą 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, przesuwając ekran rezerwacji miejsca parkingowego na stos, jeśli nie jest on już 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ługiwaniu powiadomień z aplikacji samochodowej znajdziesz w sekcji Wyświetlanie powiadomień.
Ograniczenia dotyczące szablonów
Host ogranicza liczbę szablonów wyświetlanych w przypadku danego zadania do maksymalnie 5 elementów, 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 zestawie. Jeśli na przykład aplikacja wysyła 2 szablony na ekranie A, a potem przechodzi na ekran B, może wysłać jeszcze 3 szablony. Jeśli natomiast każdy ekran jest sformatowany tak, aby wysyłać pojedynczy szablon, aplikacja może przesłać 5 ekranów do stosu ScreenManager
.
Istnieją specjalne przypadki tych ograniczeń: odświeżanie szablonów oraz operacje przywracania i resetowania.
Odświeżanie szablonów
Niektóre aktualizacje treści nie wliczają się 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, nowy szablon nie jest wliczany do limitu. Na przykład zaktualizowanie stanu przełącznika wiersza w ListTemplate
nie powoduje przekroczenia 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 wstecz
Aby umożliwić podprocesy w ramach zadania, host wykrywa, kiedy aplikacja wyciąga Screen
ze stosu ScreenManager
i aktualizuje pozostały limit na podstawie liczby szablonów, o które aplikacja się cofa.
Jeśli na przykład aplikacja wysyła 2 szablony na ekranie A, a potem wyświetla ekran B i wysyła jeszcze 2 szablony, pozostaje jej 1 kwota. Jeśli aplikacja wróci do ekranu A, gospodarz zresetuje limit do 3, ponieważ aplikacja cofnęła się o 2 wzorce.
Pamiętaj, że gdy aplikacja wraca do ekranu, musi wysłać szablon tego samego typu, co ostatnio wysłany przez ten ekran. Wysyłanie innego typu szablonu powoduje błąd. Dopóki jednak typ pozostaje taki sam podczas operacji wstecznej, aplikacja może swobodnie modyfikować zawartość szablonu bez wpływu na limit.
Resetowanie operacji
Niektóre szablony mają specjalną semantykę, która oznacza koniec zadania. Na przykład widok NavigationTemplate
powinien pozostać na ekranie i być odświeżany o nowe szczegółowe wskazówki dla użytkownika. Gdy dotrze do jednego z tych szablonów, host resetuje limit szablonów, traktując ten szablon jak pierwszy krok nowego zadania. Pozwala to aplikacji rozpocząć nowe zadanie.
W dokumentacji poszczególnych szablonów znajdziesz informacje o tym, które z nich powodują resetowanie hosta.
Jeśli gospodarz otrzyma intencję uruchomienia aplikacji z działania powiadomienia lub z menu, limit zostanie zresetowany. Ten mechanizm umożliwia aplikacji rozpoczęcie nowego procesu z powiadomień, nawet jeśli aplikacja jest już zwią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 samochodowej za pomocą intencji znajdziesz informacje o tym, jak uruchomić aplikację za pomocą działania powiadomienia.
Connection API
Aby określić, czy aplikacja działa na Androidzie Auto, czy na systemie operacyjnym Android Automotive, możesz użyć interfejsu CarConnection
API, aby pobrać informacje o połączeniu w czasie działania.
Na przykład w aplikacji samochodowej Session
zainicjuj CarConnection
i zasubskrybuj aktualizacje LiveData
:
Kotlin
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)
Java
new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);
W obserwowanym środowisku 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 różne liczby instancji Item
. Użyj funkcji ConstraintManager
, aby sprawdzić limit treści w czasie działania, i ustaw odpowiednią liczbę elementów w swoich szablonach.
Na początek pobierz 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 zwróconego obiektu ConstraintManager
, aby uzyskać odpowiedni limit treści. Aby na przykład uzyskać liczbę elementów, które można wyświetlić w siatkowaniu, wywołaj funkcję getContentLimit
z parametrami 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 umożliwia użytkownikom logowanie się, możesz użyć szablonów takich jak SignInTemplate
i LongMessageTemplate
z interfejsem API Car App na poziomie 2 lub wyższym, aby obsłużyć logowanie w aplikacji na jednostce głównej samochodu.
Aby utworzyć SignInTemplate
, zdefiniuj SignInMethod
. Biblioteka aplikacji samochodowych obsługuje obecnie te metody logowania:
InputSignInMethod
do logowania się przy użyciu 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
dla logowania się przez dostawcę, takiego jak Zaloguj się przez Google i Jedno dotknięcie.QRCodeSignInMethod
dla logowania się przy użyciu kodu QR, w którym użytkownik skanuje kod QR, aby dokończyć logowanie na telefonie. Ta funkcja jest dostępna w interfejsie Car API na poziomie 4 lub nowszym.
Aby na przykład wdrożyć szablon, który zbiera hasło użytkownika, zacznij od utworzenia InputCallback
, aby przetworzyć i sprawdzić dane wejściowe 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
wymagane jest pole 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 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();
Korzystanie z usługi AccountManager
Aplikacje na system operacyjny Android Automotive, które wymagają uwierzytelniania, muszą używać interfejsu AccountManager z tych powodów:
- Lepsze wrażenia użytkowników i łatwe zarządzanie kontami: użytkownicy mogą łatwo zarządzać wszystkimi swoimi kontami w menu kont w ustawieniach systemu, w tym logować się i wylogowywać.
- Funkcje „gościa”: ponieważ samochody są urządzeniami współdzielonymi, producenci OEM mogą włączyć funkcje „gościa” w samochodzie, w których nie można dodawać kont.
Dodawanie wariantów ciągu tekstowego
Różne rozmiary ekranów w samochodach mogą wyświetlać różne ilości tekstu. Za pomocą interfejsu Car App API poziomu 2 lub nowszego możesz określić kilka 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 mają wartość CarText
.
Aby dodać warianty ciągu tekstowego do CarText
, użyj 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 CarText
, na przykład jako głównego tekstu GridItem
.
Kotlin
GridItem.Builder() .addTitle(itemTitle) ... .build()
Java
new GridItem.Builder() .addTitle(itemTitle) ... build();
Dodawaj ciągi znaków w kolejności od najbardziej do najmniej preferowanych, np. od najdłuższego do najkrótszego. Gospodarz wybiera ciąg znaków o odpowiedniej długości w zależności od ilości miejsca na ekranie samochodu.
Dodawanie wbudowanych ikon samochodów do wierszy
Aby wzbogacić wizualnie swoją aplikację, możesz dodać ikony w tekstach za pomocą CarIconSpan
.
Więcej informacji o tworzeniu tych zakresów znajdziesz w dokumentacji CarIconSpan.create
. Aby dowiedzieć się więcej o stylizacji tekstu za pomocą elementów, przeczytaj artykuł Spantastic: stylizacja tekstu za pomocą elementów.
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
Począwszy od interfejsu Car App API 3, biblioteka Car App API zawiera interfejsy API, których możesz używać do uzyskiwania dostępu do właściwości pojazdu i czujników.
Wymagania
Aby używać interfejsów API z Androidem Auto, zacznij od dodania zależności androidx.car.app:app-projected
do pliku build.gradle
w module Androida Auto. W przypadku systemu operacyjnego Android Automotive dodaj zależność od pliku androidx.car.app:app-automotive
do pliku build.gradle
w module systemu operacyjnego Android Automotive.
Dodatkowo w pliku AndroidManifest.xml
musisz zadeklarować odpowiednie uprawnienia, które są potrzebne do wysłania żądania danych samochodu, których chcesz używać. Pamiętaj, że użytkownik musi Ci przyznać te uprawnienia. Zamiast tworzyć przepływy zależne od platformy, możesz użyć tego samego kodu zarówno w Androidzie Auto, jak i w systemie operacyjnym Android Automotive. Wymagane uprawnienia są jednak inne.
CarInfo
W tej tabeli opisano właściwości udostępniane przez interfejsy API CarInfo
oraz uprawnienia, o które musisz poprosić, aby z nich korzystać:
Metody | Właściwości | Uprawnienia Androida Auto | Uprawnienia systemu operacyjnego Android Automotive | Obsługiwane od poziomu interfejsu API Car App |
---|---|---|---|---|
fetchModel |
Marka, model, rok | android.car.permission.CAR_INFO |
3 | |
fetchEnergyProfile |
Typy złączy EV, rodzaje paliwa | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
3 |
fetchExteriorDimensions
Te dane są dostępne tylko na niektórych pojazdach z systemem operacyjnym Android Automotive na poziomie API 30 lub wyższym. |
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 |
Szybkość rzeczywista, wyświetlana prędkość (wyświetlana na wyświetlaczu tablicy rozdzielczej pojazdu) | com.google.android.gms.permission.CAR_SPEED |
android.car.permission.CAR_SPEED ,android.car.permission.READ_CAR_DISPLAY_UNITS |
3 |
addMileageListener
removeMileageListener |
Odległość z drogomierza | com.google.android.gms.permission.CAR_MILEAGE |
Te dane nie są dostępne w przypadku aplikacji zainstalowanych ze Sklepu Play w systemie operacyjnym Android Automotive. | 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 wartości, o którą Ci chodzi, aby dowiedzieć się, dlaczego nie udało się pobrać żądanych danych. Pełną definicję klasy CarInfo
znajdziesz w dokumentacji referencyjnej.
CarSensors
Klasa CarSensors
zapewnia dostęp do akcelerometru, żyroskopu, kompasu i danych o lokalizacji pojazdu. Dostępność tych wartości może zależeć od 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ć i poprosić o dostęp do android.permission.ACCESS_FINE_LOCATION
.
Testowanie
Aby symulować dane czujników podczas testowania w Androidzie Auto, zapoznaj się z sekcjami Czujniki i Konfiguracja czujników w przewodniku po konsoli na komputerze. Aby symulować dane czujników podczas testowania w systemie operacyjnym Android Automotive, zapoznaj się z sekcją Symulowanie stanu sprzętu w przewodniku dotyczącym emulatora systemu operacyjnego Android Automotive.
Cykl życia usługi CarAppService, sesji i ekranu
Klasy Session
i Screen
implementują interfejs LifecycleOwner
. Gdy użytkownik korzysta z aplikacji, wywoływane są metody obsługi zdarzeń cyklu życia obiektów Session
i Screen
, jak pokazano na diagramach poniżej.
Cykl życia usługi CarAppService i sesji
Szczegółowe informacje znajdziesz w dokumentacji metody Session.getLifecycle
.
Cykl życia ekranu
Szczegółowe informacje znajdziesz w dokumentacji metody Screen.getLifecycle
.
Nagrywanie z mikrofonu w samochodzie
Za pomocą interfejsu CarAppService
i interfejsu CarAudioRecord
możesz przyznać aplikacji dostęp do mikrofonu w samochodzie użytkownika. Użytkownicy muszą zezwolić aplikacji na dostęp do mikrofonu samochodowego. Aplikacja może rejestrować i przetwarzać dane wprowadzane przez użytkownika.
Zezwolenie na nagrywanie
Zanim nagrasz dźwięk, musisz najpierw zadeklarować uprawnienie do nagrywania w swojej aplikacji (AndroidManifest.xml
) i poprosić użytkownika o jego przyznanie.
<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 uprawnienia w aplikacji samochodowej, znajdziesz w sekcji Poproś o uprawnienia.
Nagrywanie dźwięku
Gdy użytkownik udzieli zgody 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 za pomocą mikrofonu w samochodzie najpierw uzyskaj fokus dźwiękowy, aby zatrzymać wszystkie trwające multimedia. Jeśli stracisz fokus dźwięku, zatrzymaj nagrywanie.
Oto przykład sposobu uzyskania skupienia na dźwięku:
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 testów Androida dla samochodów udostępnia dodatkowe klasy, których możesz używać do sprawdzania zachowania aplikacji w środowisku testowym.
Na przykład funkcja
SessionController
pozwala na symulowanie połączenia z hostem i sprawdzanie, czy zostały utworzone i zwrócone prawidłowe wartości Screen
i Template
.
Przykłady użycia znajdziesz w pliku Samples.
Zgłaszanie problemów z biblioteką aplikacji Android for Cars
Jeśli znajdziesz problem z biblioteką, zgłoś go za pomocą śledzika problemów Google. Pamiętaj, aby podać wszystkie wymagane informacje w szablonie zgłoszenia.
Zanim zgłosisz nowy problem, sprawdź, czy nie jest on wymieniony w notatkach do wersji biblioteki lub na liście problemów. Możesz subskrybować problemy i głosować na nie, klikając gwiazdkę obok problemu w śledzeniu. Więcej informacji znajdziesz w artykule Subskrybowanie problemu.