На этой странице описаны различные функции библиотеки приложений для автомобилей, которые можно использовать для реализации пошаговой навигации в приложении.
Как заявить о поддержке навигации в манифесте
В фильтре интентов CarAppService вашего навигационного приложения должна быть объявлена категория автомобильного приложения:androidx.car.app.category.NAVIGATION
<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>
Поддержка намерений навигации
Разные форматы намерений позволяют навигационным приложениям взаимодействовать с другими приложениями, например с приложениями для поиска мест и голосовыми помощниками.
Чтобы поддерживать эти форматы намерений, сначала объявите об этом, добавив фильтры намерений в манифест приложения. Расположение этих фильтров намерений зависит от платформы:
- Android Auto. В элементе манифеста
<activity>для компонентаActivity, который используется для обработки намерения, когда пользователь не работает с Android Auto. - Android Automotive OS: в элементе манифеста
<activity>дляCarAppActivity.
Затем прочитайте и обработайте намерения в обратных вызовах onCreateScreen() и onNewIntent() в реализации Session вашего приложения.
Обязательные форматы на основе намерений
Чтобы соответствовать требованиям к качеству NF-6, ваше приложение должно обрабатывать намерения навигации.
Необязательные форматы на основе намерений
Вы также можете добавить поддержку следующих форматов намерений, чтобы повысить совместимость приложения:
Как получить доступ к шаблонам навигации
Приложения для навигации могут использовать следующие шаблоны, в которых на заднем плане показывается карта, а во время активной навигации – пошаговые инструкции.
NavigationTemplate: Шаблон, в котором во время активной навигации показывается необязательное информационное сообщение и расчетное время поездки.MapWithContentTemplate: Шаблон, позволяющий приложению отрисовывать фрагменты карты с каким-либо контентом (например, списком). Обычно контент отображается как наложение поверх фрагментов карты, при этом карта видна, а ее стабильные области подстраиваются под контент.
Подробнее о том, как использовать эти шаблоны для создания интерфейса навигационного приложения, рассказывается в статье Навигационные приложения.
Чтобы получить доступ к шаблонам навигации, приложению необходимо объявить разрешение androidx.car.app.NAVIGATION_TEMPLATES в файле AndroidManifest.xml:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
...
</manifest>
Для создания карт требуется дополнительное разрешение.
Как перейти на шаблон MapWithContentTemplate
Начиная с Car App API уровня 7, элементы MapTemplate, PlaceListNavigationTemplate и RoutePreviewNavigationTemplate
считаются устаревшими. Поддержка устаревших шаблонов будет продолжена, но мы настоятельно рекомендуем перейти на MapWithContentTemplate.
Функциональность, предоставляемая этими шаблонами, можно реализовать с помощью MapWithContentTemplate. Примеры фрагментов кода приведены ниже.
MapTemplate
// MapTemplate (deprecated) val templateDeprecated = 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()
PlaceListNavigationTemplate
// PlaceListNavigationTemplate (deprecated) val templateDeprecated = 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()
RoutePreviewNavigationTemplate
// RoutePreviewNavigationTemplate (deprecated) val templateDeprecated = RoutePreviewNavigationTemplate.Builder() .setItemList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .build() ) .build() ) .setHeader(header) .setNavigateAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { /* onClick */ } .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 { /* onClick */ } .build() ) .build() ) .build() ) .setHeader(header) .build() ) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build() ) .build()
передавать метаданные навигации;
Навигационные приложения должны передавать хосту дополнительные метаданные. Хост использует эту информацию, чтобы передавать данные на стереосистему автомобиля и предотвращать конфликты между навигационными приложениями при использовании общих ресурсов.
Метаданные навигации предоставляются через автомобильный сервис NavigationManager, доступный из CarContext:
val navigationManager = carContext.getCarService(NavigationManager::class.java)
Как начать, завершить или остановить навигацию
Чтобы управлять несколькими навигационными приложениями, уведомлениями о маршрутах и данными приборной панели, хост должен знать текущее состояние навигации. Когда пользователь начинает навигацию, вызовите функцию NavigationManager.navigationStarted.
Аналогично, когда навигация заканчивается, например когда пользователь прибывает в пункт назначения или отменяет навигацию, вызовите NavigationManager.navigationEnded.
Вызывайте функцию NavigationManager.navigationEnded
только после того, как пользователь завершит навигацию. Например, если вам нужно пересчитать маршрут в середине поездки, используйте Trip.Builder.setLoading(true).
Иногда организатору нужно, чтобы приложение прекратило навигацию и звонки
onStopNavigation в объекте NavigationManagerCallback, предоставленном вашим приложением через NavigationManager.setNavigationManagerCallback.
После этого приложение должно перестать выводить информацию о следующем повороте на приборную панель, отправлять уведомления о навигации и голосовые подсказки.
Как изменить информацию о поездке
Во время активной навигации вызовите метод NavigationManager.updateTrip.
Информация, полученная в результате этого вызова, может использоваться приборной панелью и проекционным дисплеем автомобиля. В зависимости от автомобиля пользователю может быть показана не вся информация.
Например, в Desktop Head Unit (DHU) показывается Step, добавленный в Trip, но не показывается информация Destination.
Рисование на приборной панели
Чтобы обеспечить пользователям максимально удобный интерфейс, вы можете не ограничиваться показом основных метаданных на приборной панели автомобиля. Начиная с версии 6 Car App API, навигационные приложения могут отображать собственный контент непосредственно на приборной панели (в поддерживаемых автомобилях) со следующими ограничениями:
- API для кластерного дисплея не поддерживает элементы управления вводом
- Рекомендации по обеспечению качества приложений для автомобилей
NF-9: На приборной панели должны показываться только фрагменты карты. На этих элементах можно показывать активный маршрут навигации. - Кластерный API поддерживает только использование элемента
NavigationTemplate- В отличие от основного экрана, на кластерном экране могут показываться не все элементы интерфейса, например пошаговые инструкции, карточки с расчетным временем прибытия и действия.
NavigationTemplateЕдинственный элемент интерфейса, который всегда отображается, – это фрагменты карты.
- В отличие от основного экрана, на кластерном экране могут показываться не все элементы интерфейса, например пошаговые инструкции, карточки с расчетным временем прибытия и действия.
Как заявить о поддержке кластеров
Чтобы сообщить хост-приложению, что ваше приложение поддерживает отрисовку на кластерных дисплеях, добавьте элемент 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() } }
Нет никаких гарантий, что кластер будет показан и когда именно это произойдет. Кроме того, может случиться так, что кластер Session будет единственным Session (например, пользователь переключил основной экран на другое приложение, пока ваше приложение активно строит маршрут). Согласно стандартному соглашению, приложение получает контроль над кластерным дисплеем только после вызова NavigationManager::navigationStarted. Однако приложение может получить доступ к приборной панели, когда активная навигация не выполняется, или не получить его вовсе. В таких случаях ваше приложение должно отображать фрагменты карт в режиме ожидания.
Хост создает отдельные экземпляры связывателя и CarContext для каждого объекта Session. Это означает, что при использовании таких методов, как ScreenManager::push или Screen::invalidate, изменения будут применены только к Session, из которого они вызываются. Приложения должны создавать собственные каналы связи между этими экземплярами, если требуется межпроцессное взаимодействие (например, с помощью широковещательных передач, общего объекта-одиночки или чего-то ещё).Session
Поддержка тестирования кластеров
Вы можете протестировать реализацию как в Android Auto, так и в Android Automotive OS. В Android Auto это делается путем настройки головного устройства для эмуляции дополнительного дисплея приборной панели. В Android Automotive OS системные образы для уровня API 30 и выше имитируют дисплей приборной панели.
Как добавить текст или значок в TravelEstimate
Чтобы добавить к расчетному времени поездки текст, значок или и то и другое, используйте методы setTripIcon или setTripText класса TravelEstimate.Builder. С помощью элемента NavigationTemplate
можно задать текст и значки, которые будут показываться рядом с расчетным временем прибытия, оставшимся временем и расстоянием или вместо них. Для этого используется элемент TravelEstimate.
В приведенном ниже фрагменте кода используются переменные setTripIcon и setTripText, чтобы настроить расчет времени поездки:
TravelEstimate.Builder( Distance.create(350.0, Distance.UNIT_METERS), arrivalTimeAtDestination ) .setTripIcon( CarIcon.Builder( IconCompat.createWithResource(carContext, R.drawable.ic_garage) ).build() ) .setTripText(CarText.create("Custom Text")) .build()
предоставлять пошаговые уведомления;
Предоставлять пошаговые инструкции по навигации с помощью часто обновляемых уведомлений. Чтобы уведомление считалось навигационным и отображалось на экране автомобиля, его конструктор должен:
- Отметьте уведомление как текущее с помощью метода
NotificationCompat.Builder.setOngoing. - Установите для категории уведомления значение
Notification.CATEGORY_NAVIGATION. - Дополните уведомление с помощью тега
CarAppExtender.
Уведомление о навигации отображается в виджете в нижней части экрана автомобиля. Если уровень важности уведомления установлен на IMPORTANCE_HIGH, оно также будет показываться в виде всплывающего уведомления.
Если важность не задана с помощью метода CarAppExtender.Builder.setImportance, используется важность канала уведомлений.
Приложение может задать PendingIntent в CarAppExtender, который отправляется в приложение, когда пользователь нажимает на значок или виджет боковой панели.
Если функция
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) ), PendingIntent.FLAG_IMMUTABLE ) ) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build() ) .build()
Регулярно обновляйте уведомление о пошаговых инструкциях, чтобы показывать актуальную информацию о расстоянии, а также обновлять виджет на панели. Уведомление должно показываться только как уведомление о навигации.
Вы можете управлять поведением уведомлений, задавая их важность с помощью параметра CarAppExtender.Builder.setImportance. Если задать важность IMPORTANCE_HIGH, будет показано уведомление о нежелательном контенте. Если задать другое значение, обновится только виджет на боковой панели.
Как обновить контент шаблона PlaceListNavigationTemplate
Водители могут обновлять контент одним нажатием кнопки при просмотре списков мест, созданных с помощью PlaceListNavigationTemplate.
Чтобы включить обновление списка, реализуйте метод onContentRefreshRequested интерфейса OnContentRefreshListener и используйте PlaceListNavigationTemplate.Builder.setOnContentRefreshListener, чтобы задать прослушиватель в шаблоне.
В следующем фрагменте кода показано, как задать прослушиватель для шаблона:
PlaceListNavigationTemplate.Builder() .setOnContentRefreshListener { // Execute any desired logic // Then call invalidate() so onGetTemplate() is called again screen.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, в нем должна быть реализована функция обратного вызова 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.
Если водитель взаимодействует с экраном и покидает NavigationTemplate, то после показа Alert оно не переносится в другой шаблон.
Оно остается в исходном NavigationTemplate, пока не истечет время ожидания Alert, пользователь не выполнит действие или приложение не закроет Alert.
Как создать оповещение
Чтобы создать экземпляр Alert, используйте Alert.Builder.
Alert.Builder( 1, // alertId CarText.create("Hello"), // title 5000 // durationMillis ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create("Subtitle")) .setIcon(CarIcon.APP_ICON) .setCallback(alertCallback) .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.
carContext.getCarService(AppManager::class.java).showAlert(alert)
- Если вызвать
showAlertсAlert, у которогоalertIdсовпадает с идентификаторомAlert, уже показанного на экране, ничего не произойдет.Alertне обновляется. Чтобы обновитьAlert, его нужно создать заново с новымalertId. - Если вызвать функцию
showAlertс параметромAlert, который отличается отalertIdAlert, уже отображаемого на экране, то предупреждение с параметромAlertбудет закрыто.
Как закрыть оповещение
Alert автоматически закрывается по истечении времени ожидания или при взаимодействии с водителем. Вы также можете закрыть Alert вручную, например если информация в нем устарела. Чтобы закрыть Alert, вызовите метод dismissAlert, передав ему alertId Alert.
carContext.getCarService(AppManager::class.java).dismissAlert(alert.id)
Если вызвать dismissAlert с alertId, который не совпадает с уже показанным Alert, ничего не произойдет. Исключение не возникает.