Stopover and Legs

This commit is contained in:
Dimitris
2026-03-31 17:06:55 +02:00
parent 60b842d883
commit 6838ad09c4
35 changed files with 542 additions and 280 deletions
@@ -5,7 +5,6 @@ import android.location.LocationManager
import androidx.car.app.navigation.model.Maneuver
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.kouros.data.R
import com.kouros.navigation.data.Constants.homeHohenwaldeck
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.tomtom.TomTomRepository
@@ -182,6 +182,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
init {
lifecycle.addObserver(lifecycleObserver)
repository.routingEngineFlow.asLiveData().observe(this, Observer {
onRoutingEngineStateUpdated(it)
routingEngine = it
})
@@ -199,13 +200,15 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
* Creates appropriate repository based on user selection.
*/
fun onRoutingEngineStateUpdated(routeEngine: Int) {
navigationViewModel = when (routeEngine) {
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
else -> NavigationViewModel(TomTomRepository())
if (!::navigationViewModel.isInitialized || routeEngine != routingEngine) {
navigationViewModel = when (routeEngine) {
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
else -> NavigationViewModel(TomTomRepository())
}
observerManager = NavigationObserverManager(navigationViewModel, this)
observerManager.attachAllObservers(this)
}
observerManager = NavigationObserverManager(navigationViewModel, this)
observerManager.attachAllObservers(this)
}
/**
@@ -578,6 +581,8 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
* Called when user starts navigation
*/
override fun startNavigation() {
Log.d(TAG, "startNavigation")
surfaceRenderer.navigation = true
surfaceRenderer.viewStyle = ViewStyle.VIEW
navigationManager.navigationStarted()
navigationManagerStarted = true
@@ -597,6 +602,8 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
* Called when user exits navigation or arrives at destination.
*/
override fun stopNavigation() {
Log.d(TAG, "stopNavigation")
surfaceRenderer.navigation = false
routeModel.stopNavigation()
navigationManager.navigationEnded()
if (autoDriveEnabled) {
@@ -609,6 +616,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
navigationScreen.navigationType = NavigationType.VIEW
if (notificationActive)
notificationManager.stopNotificationService()
Log.d(TAG, "end stopNavigation")
}
override fun updateTrip(trip: Trip) {
@@ -723,9 +731,20 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
* Loads a route to the specified place and sets it as the destination.
*/
override fun navigateToPlace(place: Place) {
Log.d(TAG, "navigateToPlace ${place.street}")
var prevDestination = Place()
if (surfaceRenderer.navigation) {
prevDestination = routeModel.navState.destination
stopNavigation()
}
val preview = place.route
navigationViewModel.previewRoute.value = ""
val location = location(place.longitude, place.latitude)
val location = if (place.stopOver && prevDestination.latitude != 0.0) {
listOf(location(place.longitude, place.latitude), location(prevDestination.longitude, prevDestination.latitude))
} else {
listOf(location(place.longitude, place.latitude))
}
navigationViewModel.saveRecent(carContext, place)
routeModel.navState = routeModel.navState.copy(destination = place)
if (preview.isEmpty()) {
@@ -813,7 +832,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
navigationViewModel.loadRoute(
carContext,
location,
destination,
listOf(destination),
surfaceRenderer.carOrientation
)
}
@@ -10,17 +10,12 @@ import androidx.car.app.AppManager
import androidx.car.app.CarContext
import androidx.car.app.SurfaceCallback
import androidx.car.app.SurfaceContainer
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
@@ -54,7 +49,6 @@ import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.style.BaseStyle
import org.maplibre.spatialk.geojson.Position
import java.time.Duration
import java.time.LocalDateTime
@@ -123,6 +117,9 @@ class SurfaceRenderer(
// Current view mode (navigation, preview, etc.)
var viewStyle = ViewStyle.VIEW
// Flag to indicate if in navigation mode
var navigation = false
// Center location for route preview
lateinit var centerLocation: Location
@@ -457,7 +454,7 @@ class SurfaceRenderer(
with(routeModel) {
routeData.value = curRoute.routeGeoJson
centerLocation = curRoute.centerLocation
previewDistance = curRoute.summary.distance
previewDistance = curLeg.summary.distance
}
tilt = 0.0
updateCameraPosition(
@@ -473,8 +470,10 @@ class SurfaceRenderer(
* Calculates appropriate zoom
*/
fun setStandardView() {
if (!navigation) {
setRouteData("")
}
viewStyle = ViewStyle.VIEW
setRouteData("")
val zoom = calculateZoom(0.0)
tilt = calculateTilt(zoom, tilt)
updateCameraPosition(
@@ -367,7 +367,7 @@ fun NavigationImage(
x = topLeftX ,
y = topLeftY,
),
color = if (darkMode) navigationColor else Color.White,
color = if (darkMode) NavigationColorLight else Color.White,
cornerRadius = CornerRadius(x = 10f, y = 10f),
)
drawText(
@@ -20,14 +20,13 @@ import androidx.car.app.model.ForegroundCarColorSpan
import androidx.car.app.navigation.model.Lane
import androidx.car.app.navigation.model.LaneDirection
import androidx.car.app.navigation.model.Maneuver
import androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
import androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
import androidx.car.app.navigation.model.Step
import androidx.car.app.navigation.model.TravelEstimate
import androidx.core.graphics.drawable.IconCompat
import com.kouros.data.R
import com.kouros.navigation.car.screen.createCarIcon
import com.kouros.navigation.data.StepData
import com.kouros.navigation.data.route.ManeuverType
import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.utils.formattedDistance
import java.time.Duration
@@ -47,17 +46,15 @@ class RouteCarModel : RouteModel() {
val maneuver = Maneuver.Builder(stepData.currentManeuverType)
.setIcon(createCarIcon(carContext, stepData.icon))
if (stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
|| stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
if (stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW.ordinal
|| stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW.ordinal
) {
maneuver.setRoundaboutExitNumber(stepData.exitNumber)
}
val step =
Step.Builder(currentStepCueWithImage)
if (navState.destination.street != null) {
step.setRoad(navState.destination.street!!)
}
step.setRoad(navState.destination.street!!)
if (stepData.lane.isNotEmpty()) {
val lanesAdded = addLanes(carContext, step, stepData)
if (lanesAdded) {
@@ -77,8 +74,8 @@ class RouteCarModel : RouteModel() {
createString(stepData.instruction)
val maneuver = Maneuver.Builder(stepData.currentManeuverType)
.setIcon(createCarIcon(carContext, stepData.icon))
if (stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
|| stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
if (stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW.ordinal
|| stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW.ordinal
) {
maneuver.setRoundaboutExitNumber(stepData.exitNumber)
}
@@ -27,8 +27,8 @@ class Simulation {
) {
if (routeModel.navState.route.isRouteValid()) {
if (BuildConfig.DEBUG) {
gpxSimulation(routeModel, lifecycleScope, updateLocation)
//currentSimulation(routeModel, lifecycleScope, updateLocation)
//gpxSimulation(routeModel, lifecycleScope, updateLocation)
currentSimulation(routeModel, lifecycleScope, updateLocation)
} else {
currentSimulation(routeModel, lifecycleScope, updateLocation)
}
@@ -149,7 +149,7 @@ class CategoryScreen(
navigationViewModel.loadRoute(
carContext,
currentLocation = surfaceRenderer.lastLocation,
location(it.lon, it.lat),
listOf(location(it.lon, it.lat)),
surfaceRenderer.carOrientation
)
setResult(
@@ -197,7 +197,7 @@ class CategoryScreen(
@DrawableRes iconRes: Int,
scale: Int
): Action {
return createAction(carContext, iconRes, FLAG_IS_PERSISTENT, {
return createAction(carContext, iconRes, FLAG_IS_PERSISTENT, {
surfaceRenderer.handleScale(scale)
})
}
@@ -117,18 +117,18 @@ open class NavigationScreen(
)
}, { settingsAction() })
return when (navigationType) {
NavigationType.NAVIGATION -> navigationTemplate(actionStripBuilder)
NavigationType.RECENT -> navigationRecentPlacesTemplate()
NavigationType.REROUTE -> navigationRerouteTemplate(actionStripBuilder)
NavigationType.ARRIVAL -> navigationEndTemplate(actionStripBuilder)
else -> navigationViewTemplate(actionStripBuilder)
NavigationType.NAVIGATION -> navigation(actionStripBuilder)
NavigationType.RECENT -> navigationRecentPlaces()
NavigationType.REROUTE -> navigationReroute(actionStripBuilder)
NavigationType.ARRIVAL -> navigationEnd(actionStripBuilder)
else -> navigationView(actionStripBuilder)
}
}
/**
* Creates and returns a NavigationTemplate for the active navigation state.
*/
private fun navigationTemplate(actionStripBuilder: ActionStrip.Builder): Template {
private fun navigation(actionStripBuilder: ActionStrip.Builder): Template {
actionStripBuilder.addAction(
createAction(
carContext,
@@ -150,7 +150,7 @@ open class NavigationScreen(
carContext = carContext, R.drawable.ic_pan_24,
0,
onClickAction = {
surfaceRenderer.viewStyle = ViewStyle.VIEW
surfaceRenderer.setStandardView()
invalidate()
}
)
@@ -163,7 +163,7 @@ open class NavigationScreen(
/**
* Creates and returns a template for the default view state.
*/
private fun navigationViewTemplate(actionStripBuilder: ActionStrip.Builder): Template {
private fun navigationView(actionStripBuilder: ActionStrip.Builder): Template {
return NavigationTemplate.Builder()
.setBackgroundColor(backGroundColor)
.setActionStrip(actionStripBuilder.build())
@@ -187,24 +187,24 @@ open class NavigationScreen(
/**
* Creates and returns a template for the arrival.
*/
private fun navigationEndTemplate(actionStripBuilder: ActionStrip.Builder): Template {
private fun navigationEnd(actionStripBuilder: ActionStrip.Builder): Template {
arrivalTimer?.cancel()
arrivalTimer = object : CountDownTimer(8000, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
// routeModel.navState = routeModel.navState.copy(arrived = false)
// routeModel.navState = routeModel.navState.copy(arrived = false)
navigationType = NavigationType.VIEW
invalidate()
}
}
arrivalTimer?.start()
return navigationArrivedTemplate(actionStripBuilder)
return navigationArrived(actionStripBuilder)
}
/**
* Creates and returns a NavigationTemplate specifically for when the destination is reached.
*/
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
fun navigationArrived(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
var street = ""
if (destinations.first().address != null) {
street = destinations.first().address.toString()
@@ -226,7 +226,7 @@ open class NavigationScreen(
)
.build()
)
// .setBackgroundColor(routeModel.backGroundColor())
.setBackgroundColor(backGroundColor)
.setActionStrip(actionStripBuilder.build())
.setMapActionStrip(
mapActionStrip(
@@ -247,10 +247,10 @@ open class NavigationScreen(
/**
* Creates and returns a template showing recent places or destinations.
*/
fun navigationRecentPlacesTemplate(): Template {
fun navigationRecentPlaces(): Template {
if (!tripSuggestion || recentPlaces.isEmpty()) {
navigationType = NavigationType.VIEW
return navigationViewTemplate(
return navigationView(
createActionStripBuilder(
{
createAction(
@@ -263,7 +263,7 @@ open class NavigationScreen(
)
}
val listBuilder = ItemList.Builder()
recentPlaces.filter { it.category == Constants.RECENT && it.distance > 300F }.forEach {
recentPlaces.filter { it.category == Constants.RECENT && it.distance > 300F }.forEach {
val row = Row.Builder()
.setTitle(it.name)
.addAction(
@@ -316,7 +316,7 @@ open class NavigationScreen(
/**
* Creates and returns a template for when the route is being recalculated.
*/
fun navigationRerouteTemplate(actionStripBuilder: ActionStrip.Builder): Template {
fun navigationReroute(actionStripBuilder: ActionStrip.Builder): Template {
return NavigationTemplate.Builder()
.setNavigationInfo(RoutingInfo.Builder().setLoading(true).build())
.setActionStrip(actionStripBuilder.build())
@@ -486,7 +486,7 @@ open class NavigationScreen(
navigationViewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
destination,
listOf(destination),
surfaceRenderer.carOrientation
)
}
@@ -5,7 +5,6 @@ import android.text.Spannable
import android.text.SpannableString
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.OnScreenResultListener
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.CarIcon
@@ -28,7 +27,6 @@ import com.kouros.navigation.data.Constants.CONTACTS
import com.kouros.navigation.data.Constants.FAVORITES
import com.kouros.navigation.data.Constants.RECENT
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.utils.getSettingsRepository
@@ -66,41 +64,12 @@ class PlaceListScreen(
val itemListBuilder = ItemList.Builder()
.setNoItemsMessage(carContext.getString(R.string.no_places))
recentPlaces.filter { it.category == category }.forEach {
val street = if (it.street != null) {
it.street
} else {
""
}
val street = it.street
val row = Row.Builder()
.setImage(contactIcon(null, it.category))
.setTitle("$street ${it.city}")
.setOnClickListener {
place = Place(
0,
it.name,
it.category,
it.latitude,
it.longitude,
it.postalCode,
it.city,
it.street,
// avatar = null
)
screenManager
.pushForResult(
RoutePreviewScreen(
carContext,
RoutePreviewType.MULTI_ROUTE,
surfaceRenderer,
place,
navigationViewModel,
)
) { obj: Any? ->
if (obj != null) {
setResult(obj)
finish()
}
}
clickOnPlace(it)
}
if (category != CONTACTS) {
row.addText(SpannableString(" ").apply {
@@ -138,6 +107,69 @@ class PlaceListScreen(
.build()
}
/**
* Creates an Action to navigate to a specific place.
*/
private fun clickOnPlace(it: Place) {
place = Place(
0,
it.name,
it.category,
it.latitude,
it.longitude,
it.postalCode,
it.city,
it.street,
// avatar = null
)
if (surfaceRenderer.navigation) {
startStopOverScreen(place)
} else {
starPreviewScreen(place)
}
}
/**
* Starts preview screen for a specific place.
*/
private fun starPreviewScreen(place: Place) {
screenManager
.pushForResult(
RoutePreviewScreen(
carContext,
RoutePreviewType.MULTI_ROUTE,
surfaceRenderer,
place,
navigationViewModel,
)
) { obj: Any? ->
if (obj != null) {
setResult(obj)
finish()
}
}
}
/**
* Starts preview screen for a specific place.
*/
private fun startStopOverScreen(place: Place) {
screenManager
.pushForResult(
StopOverScreen(
carContext,
surfaceRenderer,
navigationViewModel,
place,
)
) { obj: Any? ->
if (obj != null) {
setResult(obj)
finish()
}
}
}
/**
* Creates an Action to delete a place.
*/
@@ -71,6 +71,8 @@ class RoutePreviewScreen(
var loading = true
var showAlternativeRoute = true
private val backPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
}
@@ -82,6 +84,10 @@ class RoutePreviewScreen(
routeModel.startNavigation(route)
surfaceRenderer.setPreviewRouteData(routeModel)
loading = false
if (routeModel.route.routes.size == 1) {
routeType = RoutePreviewType.SINGLE_ROUTE
showAlternativeRoute = false
}
invalidate()
}
}
@@ -139,15 +145,12 @@ class RoutePreviewScreen(
}
}
val street = if (destination.street.isEmpty()) {
val street = destination.street.ifEmpty {
carContext.getString((R.string.route_preview))
} else {
destination.street
}
val header = Header.Builder()
.setStartHeaderAction(Action.BACK)
.setTitle(street)
if (routeType == RoutePreviewType.SINGLE_ROUTE) {
header.addEndHeaderAction(
favoriteAction()
@@ -174,16 +177,6 @@ class RoutePreviewScreen(
.build()
listContent.build()
} else {
val navigateActionIcon: CarIcon = CarIcon.Builder(
IconCompat.createWithResource(
carContext, R.drawable.navigation_48px
)
).build()
val selectRouteIcon: CarIcon = CarIcon.Builder(
IconCompat.createWithResource(
carContext, R.drawable.alt_route_48px
)
).build()
val navigateAction =
createAction(carContext, R.drawable.navigation_48px, FLAG_DEFAULT,{
onNavigate(routeModel.navState.currentRouteIndex)
@@ -195,7 +188,10 @@ class RoutePreviewScreen(
val listContent = MessageTemplate.Builder(message)
.setHeader(header.build())
.addAction(navigateAction)
.addAction(selectRouteAction)
if (showAlternativeRoute) {
listContent.addAction(selectRouteAction)
}
if (loading) {
listContent.setLoading(true)
}
@@ -211,7 +207,7 @@ class RoutePreviewScreen(
} )).build()
)
if (routeType == RoutePreviewType.MULTI_ROUTE && !routeSelected) {
if (routeModel.route.routes.size > 1 && routeType == RoutePreviewType.MULTI_ROUTE && !routeSelected) {
template.setActionStrip(createActionStrip {
createAction(
carContext, R.drawable.navigation_48px,
@@ -301,7 +297,7 @@ class RoutePreviewScreen(
}
private fun createRow(route: Routes, index: Int): Row {
val navigateAction = createAction(carContext, R.drawable.navigation_48px ) {
val navigateAction = createAction(carContext, R.drawable.navigation_48px, FLAG_DEFAULT ) {
this.onNavigate(index)
}
val routeText = createRouteText(route)
@@ -0,0 +1,68 @@
package com.kouros.navigation.car.screen
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.Action.FLAG_DEFAULT
import androidx.car.app.model.Action.FLAG_IS_PERSISTENT
import androidx.car.app.model.CarText
import androidx.car.app.model.Header
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Template
import androidx.car.app.navigation.model.MapController
import androidx.car.app.navigation.model.MapWithContentTemplate
import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.model.NavigationViewModel
class StopOverScreen(
private val carContext: CarContext,
private val surfaceRenderer: SurfaceRenderer,
private val navigationViewModel: NavigationViewModel,
private val place: Place,
) : Screen(carContext) {
override fun onGetTemplate(): MapWithContentTemplate {
val cancelAction =
createAction(carContext, R.drawable.ic_close_white_24dp, FLAG_IS_PERSISTENT,{
finish()
})
val header = Header.Builder()
.setStartHeaderAction(Action.BACK)
.addEndHeaderAction(cancelAction)
.setTitle(place.street)
val message = CarText.Builder("Neue Fahrt oder Zwischenstopp einfügen")
.build()
val navigateAction = Action.Builder()
.setIcon(createCarIcon(carContext, R.drawable.navigation_48px))
.setFlags(FLAG_DEFAULT)
.setOnClickListener {
setResult(place)
finish()
}
.build()
val selectRouteAction = Action.Builder()
.setIcon(createCarIcon(carContext, R.drawable.alt_route_48px))
.setFlags(FLAG_IS_PERSISTENT)
.setOnClickListener {
place.stopOver = true
setResult(place)
finish()
}
.build()
val listContent = MessageTemplate.Builder(message)
.setHeader(header.build())
.addAction(navigateAction)
.addAction(selectRouteAction)
val template = MapWithContentTemplate.Builder()
.setContentTemplate(listContent.build())
return template.build()
}
}
@@ -33,6 +33,8 @@ class NavigationSettings(
private var carLocationToggleState = false
private var alternativeRoutesToggleState = false
val settingsViewModel = getSettingsViewModel(carContext)
init {
@@ -41,6 +43,7 @@ class NavigationSettings(
settingsViewModel.avoidMotorway.first()
settingsViewModel.avoidFerry.first()
settingsViewModel.carLocation.first()
settingsViewModel.alternativeRoutes.first()
}
}
@@ -49,6 +52,7 @@ class NavigationSettings(
tollWayToggleState = settingsViewModel.avoidTollway.value
ferryToggleState = settingsViewModel.avoidFerry.value
carLocationToggleState = settingsViewModel.carLocation.value
alternativeRoutesToggleState = settingsViewModel.alternativeRoutes.value
val listBuilder = ItemList.Builder()
@@ -89,6 +93,14 @@ class NavigationSettings(
carLocationToggleState = !carLocationToggleState
}.setChecked(carLocationToggleState).build()
// Alternative routes
val alternativeRoutesToggle: Toggle =
Toggle.Builder { checked: Boolean ->
settingsViewModel.onAlternativeRoutes(checked)
alternativeRoutesToggleState = !alternativeRoutesToggleState
}.setChecked(alternativeRoutesToggleState).build()
listBuilder.addItem(
buildRowForTemplate(
R.string.use_car_location,
@@ -97,6 +109,14 @@ class NavigationSettings(
)
)
listBuilder.addItem(
buildRowForTemplate(
R.string.alternative_routes,
alternativeRoutesToggle,
createCarIcon(carContext,R.drawable.alt_route_48px)
)
)
listBuilder.addItem(
buildRowForScreenTemplate(
RoutingSettings(carContext, navigationViewModel),
@@ -109,6 +129,7 @@ class NavigationSettings(
R.string.tomtom_api_key
)
)
return ListTemplate.Builder()
.setSingleList(listBuilder.build())
.setHeader(