На этой странице подробно описаны различные функции библиотеки автомобильных приложений, которые вы можете использовать для реализации функций вашего приложения для пошаговой навигации.
Объявите поддержку навигации в своем манифесте.
Ваше навигационное приложение должно объявить категорию автомобильного приложения androidx.car.app.category.NAVIGATION
в фильтре намерений своего CarAppService
:
<application>
...
<service
...
android:name=".MyNavigationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
</service>
...
</application>
Поддержка навигационных целей
Чтобы поддерживать намерения навигации в вашем приложении, в том числе те, которые поступают от Google Assistant с помощью голосового запроса, вашему приложению необходимо обрабатывать намерение CarContext.ACTION_NAVIGATE
в своих Session.onCreateScreen
и Session.onNewIntent
.
Подробную информацию о формате намерения см. в документации по CarContext.startCarApp
.
Доступ к шаблонам навигации
Навигационные приложения могут получить доступ к следующим шаблонам, которые отображают поверхность на фоне карты и, во время активной навигации, пошаговые инструкции.
-
NavigationTemplate
: также отображает дополнительное информационное сообщение и оценку поездки во время активной навигации. -
MapWithContentTemplate
: шаблон, который позволяет приложению отображать фрагменты карты с каким-либо содержимым (например, списком). Содержимое обычно отображается как наложение поверх фрагментов карты, при этом видимые и стабильные области карты адаптируются к содержимому.
Дополнительные сведения о том, как разработать пользовательский интерфейс вашего навигационного приложения с помощью этих шаблонов, см. в разделе «Навигационные приложения» .
Чтобы получить доступ к шаблонам навигации, вашему приложению необходимо объявить разрешение androidx.car.app.NAVIGATION_TEMPLATES
в файле AndroidManifest.xml
:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
...
</manifest>
Для рисования карт требуется дополнительное разрешение.
Перейдите на MapWithContentTemplate.
Начиная с уровня API автомобильного приложения 7, MapTemplate
, PlaceListNavigationTemplate
и RoutePreviewNavigationTemplate
устарели. Устаревшие шаблоны будут по-прежнему поддерживаться, но настоятельно рекомендуется перейти на MapWithContentTemplate
.
Функциональность, предоставляемая этими шаблонами, может быть реализована с помощью MapWithContentTemplate
. Примеры см. в следующих фрагментах:
КартаШаблон
Котлин
// MapTemplate (deprecated) val template = MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(mapController) .build()
Ява
// MapTemplate (deprecated) MapTemplate template = new MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) build()) .setActionStrip(actionStrip) .setMapController(mapController) .build();
PlaceListNavigationШаблон
Котлин
// PlaceListNavigationTemplate (deprecated) val template = PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build()
Ява
// PlaceListNavigationTemplate (deprecated) PlaceListNavigationTemplate template = new PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(new MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build();
МаршрутПредварительный просмотрНавигацияШаблон
Котлин
// RoutePreviewNavigationTemplate (deprecated) val template = RoutePreviewNavigationTemplate.Builder() .setItemList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .build()) .build()) .setHeader(header) .setNavigateAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { ... } .build()) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .addAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { ... } .build()) .build()) .build()) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build()
Ява
// RoutePreviewNavigationTemplate (deprecated) RoutePreviewNavigationTemplate template = new RoutePreviewNavigationTemplate.Builder() .setItemList(new ItemList.Builder() .addItem(new Row.Builder() .setTitle(title)) .build()) .build()) .setHeader(header) .setNavigateAction(new Action.Builder() .setTitle(actionTitle) .setOnClickListener(() -> { ... }) .build()) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build(); // MapWithContentTemplate MapWithContentTemplate template = new MapWithContentTemplate.Builder() .setContentTemplate(new ListTemplate.Builder() .setSingleList(new ItemList.Builder() .addItem(new Row.Builder() .setTitle(title)) .addAction(new Action.Builder() .setTitle(actionTitle) .setOnClickListener(() -> { ... }) .build()) .build()) .build())) .setHeader(header) .build()) .setActionStrip(actionStrip) .setMapController(new MapController.Builder() .setMapActionStrip(mapActionStrip) .build()) .build();
Передача навигационных метаданных
Навигационные приложения должны передавать дополнительные метаданные навигации с хостом. Хост использует эту информацию для предоставления информации головному устройству автомобиля и для предотвращения конфликтов навигационных приложений из-за общих ресурсов.
Метаданные навигации предоставляются через автомобильный сервис NavigationManager
, доступный из CarContext
:
Котлин
val navigationManager = carContext.getCarService(NavigationManager::class.java)
Ява
NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
Запуск, завершение и остановка навигации
Чтобы хост мог управлять несколькими навигационными приложениями, уведомлениями о маршрутах и данными кластера транспортных средств, ему необходимо знать текущее состояние навигации. Когда пользователь начинает навигацию, вызовите NavigationManager.navigationStarted
. Аналогичным образом, когда навигация заканчивается — например, когда пользователь достигает пункта назначения или пользователь отменяет навигацию — вызовите NavigationManager.navigationEnded
.
Вызывайте NavigationManager.navigationEnded
только тогда, когда пользователь завершает навигацию. Например, если вам нужно пересчитать маршрут в середине поездки, используйте вместо этого Trip.Builder.setLoading(true)
.
Иногда узлу требуется приложение, чтобы остановить навигацию, и он вызывает onStopNavigation
в объекте NavigationManagerCallback
, предоставленном вашим приложением через NavigationManager.setNavigationManagerCallback
. После этого приложение должно прекратить выдачу информации о следующем повороте на кластерном дисплее, навигационных уведомлениях и голосовых подсказках.
Обновить информацию о поездке
Во время активной навигации вызовите NavigationManager.updateTrip
. Информация, предоставленная в этом вызове, может использоваться приборной панелью автомобиля и проекционными дисплеями. В зависимости от конкретного транспортного средства, которым управляете, пользователю отображается не вся информация. Например, на настольном головном устройстве (DHU) отображается Step
добавленный к Trip
, но не отображается информация Destination
.
Рисование на дисплее кластера
Чтобы обеспечить максимальное погружение в работу пользователя, возможно, вам захочется выйти за рамки отображения основных метаданных на дисплее приборной панели автомобиля. Начиная с уровня 6 Car App API, навигационные приложения имеют возможность отображать собственный контент непосредственно на дисплее кластера (в поддерживаемых автомобилях) со следующими ограничениями:
- API отображения кластера не поддерживает элементы управления вводом.
- На дисплее кластера должны отображаться только фрагменты карты. На этих плитках опционально может отображаться активная навигация по маршруту.
- API отображения кластера поддерживает только использование
NavigationTemplate
- В отличие от основных дисплеев, кластерные дисплеи могут не всегда отображать все элементы пользовательского интерфейса
NavigationTemplate
, такие как пошаговые инструкции, карточки ETA и действия. Плитки карты — единственный постоянно отображаемый элемент пользовательского интерфейса.
- В отличие от основных дисплеев, кластерные дисплеи могут не всегда отображать все элементы пользовательского интерфейса
Объявить поддержку кластера
Чтобы сообщить главному приложению, что ваше приложение поддерживает рендеринг на дисплеях кластера, вы должны добавить элемент androidx.car.app.category.FEATURE_CLUSTER
<category>
в <intent-filter>
вашего CarAppService
, как показано в следующем фрагменте:
<application> ... <service ... android:name=".MyNavigationCarAppService" android:exported="true"> <intent-filter> <action android:name="androidx.car.app.CarAppService" /> <category android:name="androidx.car.app.category.NAVIGATION"/> <category android:name="androidx.car.app.category.FEATURE_CLUSTER"/> </intent-filter> </service> ... </application>
Жизненный цикл и управление состоянием
Начиная с уровня API 6, жизненный цикл автомобильного приложения остается прежним, но теперь CarAppService::onCreateSession
принимает параметр типа SessionInfo
, который предоставляет дополнительную информацию о создаваемом Session
(а именно, тип отображения и набор поддерживаемых шаблонов).
Приложения имеют возможность либо использовать один и тот же класс Session
для обработки кластера и основного дисплея, либо создавать Sessions
для конкретного дисплея для настройки поведения на каждом дисплее (как показано в следующем фрагменте кода).
Котлин
override fun onCreateSession(sessionInfo: SessionInfo): Session { return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) { ClusterSession() } else { MainDisplaySession() } }
Ява
@Override @NonNull public Session onCreateSession(@NonNull SessionInfo sessionInfo) { if (sessionInfo.getDisplayType() == SessionInfo.DISPLAY_TYPE_CLUSTER) { return new ClusterSession(); } else { return new MainDisplaySession(); } }
Нет никаких гарантий относительно того, когда и будет ли предоставлено отображение кластера, а также возможно, что Session
кластера будет единственным Session
(например, пользователь переключил основной дисплей на другое приложение, пока ваше приложение активно перемещается). «Стандартное» соглашение заключается в том, что приложение получает контроль над отображением кластера только после вызова NavigationManager::navigationStarted
. Однако приложению может быть предоставлено отображение кластера при отсутствии активной навигации или вообще не предоставлено отображение кластера. Ваше приложение должно обрабатывать эти сценарии, отображая состояние простоя вашего приложения на плитках карты.
Хост создает отдельные экземпляры связывателя и CarContext
для каждого Session
. Это означает, что при использовании таких методов, как ScreenManager::push
или Screen::invalidate
, затрагивается только тот Session
, из которого они вызваны. Приложения должны создавать свои собственные каналы связи между этими экземплярами, если требуется Session
связь (например, с помощью широковещательных рассылок , общего синглтона или чего-то еще).
Тестирование поддержки кластера
Вы можете протестировать свою реализацию как на Android Auto, так и на ОС Android Automotive. В Android Auto это делается путем настройки головного устройства настольного компьютера на эмуляцию вторичного кластерного дисплея . Для Android Automotive OS общие образы системы для уровня API 30 и выше имитируют отображение кластера.
Настройте TravelEstimate с помощью текста или значка
Чтобы настроить оценку поездки с помощью текста, значка или того и другого, используйте методы setTripIcon
или setTripText
класса TravelEstimate.Builder
. NavigationTemplate
использует TravelEstimate
для установки текста и значков рядом или вместо расчетного времени прибытия, оставшегося времени и оставшегося расстояния.
В следующем фрагменте кода используются setTripIcon
и setTripText
для настройки оценки поездки:
Котлин
TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build()
Ява
new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build();
Предоставляйте пошаговые уведомления
Предоставьте пошаговые инструкции по навигации (TBT), используя часто обновляемое навигационное уведомление. Чтобы ваше уведомление рассматривалось как навигационное уведомление на экране автомобиля, разработчик вашего уведомления должен выполнить следующие действия:
- Отметьте уведомление как продолжающееся с помощью метода
NotificationCompat.Builder.setOngoing
. - Установите категорию уведомления
Notification.CATEGORY_NAVIGATION
. - Расширьте уведомление с помощью
CarAppExtender
.
Уведомление о навигации отображается в виджете железной дороги в нижней части экрана автомобиля. Если уровень важности уведомления установлен на IMPORTANCE_HIGH
, оно также отображается как хедз-ап-уведомление (HUN). Если важность не установлена с помощью метода CarAppExtender.Builder.setImportance
, используется важность канала уведомления .
Приложение может установить PendingIntent
в CarAppExtender
, который отправляется в приложение, когда пользователь нажимает на HUN или виджет железной дороги.
Если NotificationCompat.Builder.setOnlyAlertOnce
вызывается со значением true
, уведомление высокой важности выдает предупреждение только один раз в HUN.
В следующем фрагменте показано, как создать уведомление о навигации:
Котлин
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), Intent(ACTION_OPEN_APP).setComponent( ComponentName(context, MyNotificationReceiver::class.java)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build()
Ява
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( new CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), new Intent(ACTION_OPEN_APP).setComponent( new ComponentName(context, MyNotificationReceiver.class)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build();
Регулярно обновляйте уведомление TBT при изменении расстояния, что приводит к обновлению виджета железной дороги, и отображайте уведомление только как HUN. Вы можете контролировать поведение HUN, устанавливая важность уведомления с помощью CarAppExtender.Builder.setImportance
. Установка важности на IMPORTANCE_HIGH
отображает HUN. Установка любого другого значения только обновит виджет железной дороги.
Обновить содержимое PlaceListNavigationTemplate.
Вы можете позволить водителям обновлять содержимое одним нажатием кнопки при просмотре списков мест, созданных с помощью PlaceListNavigationTemplate
. Чтобы включить обновление списка, реализуйте метод onContentRefreshRequested
интерфейса OnContentRefreshListener
и используйте PlaceListNavigationTemplate.Builder.setOnContentRefreshListener
, чтобы установить прослушиватель в шаблоне.
В следующем фрагменте показано, как настроить прослушиватель в шаблоне:
Котлин
PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate() } .build()
Ява
new PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener(() -> { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate(); }) .build();
Кнопка обновления отображается в заголовке PlaceListNavigationTemplate
только в том случае, если прослушиватель имеет значение.
Когда пользователь нажимает кнопку обновления, вызывается метод onContentRefreshRequested
вашей реализации OnContentRefreshListener
. В onContentRefreshRequested
вызовите метод Screen.invalidate
. Затем хост вызывает метод Screen.onGetTemplate
вашего приложения, чтобы получить шаблон с обновленным содержимым. Дополнительные сведения об обновлении шаблонов см. в разделе Обновление содержимого шаблона . Если следующий шаблон, возвращаемый onGetTemplate
имеет тот же тип, он считается обновлением и не учитывается в квоте шаблона.
Предоставить аудио-руководство
Чтобы воспроизводить навигационные указания через динамики автомобиля, ваше приложение должно запросить аудиофокус . В рамках вашего AudioFocusRequest
установите использование как AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
. Кроме того, установите усиление фокуса как AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Имитировать навигацию
Чтобы проверить функциональность навигации вашего приложения при отправке его в Google Play Store, ваше приложение должно реализовать обратный вызов NavigationManagerCallback.onAutoDriveEnabled
. При вызове этого обратного вызова ваше приложение должно имитировать навигацию к выбранному пункту назначения, когда пользователь начинает навигацию. Ваше приложение может выйти из этого режима всякий раз, когда жизненный цикл текущего Session
достигает состояния Lifecycle.Event.ON_DESTROY
.
Вы можете проверить, что ваша реализация onAutoDriveEnabled
вызывается, выполнив следующую команду из командной строки:
adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE
Это показано в следующем примере:
adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE
Автомобильное навигационное приложение по умолчанию
В Android Auto автомобильное навигационное приложение по умолчанию соответствует последнему навигационному приложению, которое запускал пользователь. Приложение по умолчанию получает намерения навигации, когда пользователь вызывает команды навигации через Ассистента или когда другое приложение отправляет намерение начать навигацию.
Отображать контекстные навигационные оповещения
Alert
отображает важную информацию для водителя с дополнительными действиями, не выходя из контекста экрана навигации. Чтобы обеспечить водителю максимальное удобство, Alert
работает внутри NavigationTemplate
, чтобы не блокировать маршрут навигации и свести к минимуму отвлечение водителя.
Alert
доступно только внутри NavigationTemplate
. Чтобы уведомить пользователя за пределами NavigationTemplate
, рассмотрите возможность использования хедз-ап-уведомления (HUN), как описано в разделе Отображение уведомлений .
Например, используйте Alert
, чтобы:
- Сообщите водителю об обновлении, относящемся к текущей навигации, например об изменении условий дорожного движения.
- Попросите водителя сообщить вам обновленную информацию, связанную с текущей навигацией, например, наличие ловушки скорости.
- Предложите предстоящую задачу и спросите, принимает ли ее водитель, например, готов ли водитель подобрать кого-нибудь по дороге.
В своей базовой форме Alert
состоит из заголовка и продолжительности Alert
. Время продолжительности представлено индикатором выполнения. При желании вы можете добавить подзаголовок, значок и до двух объектов Action
.
После отображения Alert
оно не переносится на другой шаблон, если взаимодействие с драйвером приводит к выходу NavigationTemplate
. Он остается в исходном NavigationTemplate
до тех пор, пока не истечет время Alert
, пользователь не выполнит действие или приложение не отклонит Alert
.
Создать оповещение
Используйте Alert.Builder
для создания экземпляра Alert
:
Котлин
Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build()
Ява
new Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build();
Если вы хотите прослушивать отмену или закрытие Alert
, создайте реализацию интерфейса AlertCallback
. Пути вызова AlertCallback
:
Если время
Alert
истекло, хост вызывает методAlertCallback.onCancel
со значениемAlertCallback.REASON_TIMEOUT
. Затем он вызывает методAlertCallback.onDismiss
.Если драйвер нажимает одну из кнопок действий, хост вызывает
Action.OnClickListener
, а затем вызываетAlertCallback.onDismiss
.Если
Alert
не поддерживается, хост вызываетAlertCallback.onCancel
со значениемAlertCallback.REASON_NOT_SUPPORTED
. Хост не вызываетAlertCallback.onDismiss
, посколькуAlert
не было показано.
Настройка продолжительности оповещения
Выберите продолжительность Alert
, соответствующую потребностям вашего приложения. Рекомендуемая продолжительность навигационного Alert
— 10 секунд. Дополнительную информацию см. в разделе «Навигационные оповещения» .
Показать оповещение
Чтобы отобразить Alert
, вызовите метод AppManager.showAlert
, доступный через CarContext
вашего приложения.
// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
- Вызов
showAlert
сAlert
,alertId
которого совпадает с идентификаторомAlert
, отображаемого в данный момент, ничего не дает.Alert
не обновляется. Чтобы обновитьAlert
, вы должны воссоздать его с новымalertId
. - Вызов
showAlert
сAlert
, который имеет идентификаторalertId
, отличный от отображаемого в данный моментAlert
, отклоняет отображаемое в данный моментAlert
.
Отклонить оповещение
Хотя Alert
автоматически закрывается из-за тайм-аута или взаимодействия с драйвером, вы также можете закрыть Alert
вручную, например, если его информация устарела. Чтобы закрыть Alert
, вызовите метод dismissAlert
с alertId
Alert
.
// Dismiss the same alert
carContext.getCarService(AppManager.class).dismissAlert(alert.getId())
Вызов dismissAlert
с alertId
, который не соответствует отображаемому в данный момент Alert
ничего не дает. Это не вызывает исключения.