From 6838ad09c421b11c2c78f30d5aa45875fba4e7cc Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 31 Mar 2026 17:06:55 +0200 Subject: [PATCH] Stopover and Legs --- .../navigation/ui/search/SearchScreen.kt | 4 +- .../navigation/ui/search/SearchSheet.kt | 4 +- .../kouros/navigation/car/RouteModelTest.kt | 1 - .../navigation/car/NavigationSession.kt | 35 ++- .../kouros/navigation/car/SurfaceRenderer.kt | 15 +- .../com/kouros/navigation/car/map/MapView.kt | 2 +- .../car/navigation/RouteCarModel.kt | 15 +- .../navigation/car/navigation/Simulation.kt | 4 +- .../navigation/car/screen/CategoryScreen.kt | 4 +- .../navigation/car/screen/NavigationScreen.kt | 36 ++-- .../navigation/car/screen/PlaceListScreen.kt | 98 ++++++--- .../car/screen/RoutePreviewScreen.kt | 30 ++- .../navigation/car/screen/StopOverScreen.kt | 68 ++++++ .../car/screen/settings/NavigationSettings.kt | 21 ++ .../java/com/kouros/navigation/data/Color.kt | 6 +- .../java/com/kouros/navigation/data/Data.kt | 1 + .../navigation/data/NavigationRepository.kt | 2 +- .../data/datastore/DataStoreManager.kt | 14 ++ .../navigation/data/osrm/OsrmRepository.kt | 4 +- .../kouros/navigation/data/osrm/OsrmRoute.kt | 2 +- .../com/kouros/navigation/data/route/Leg.kt | 4 + .../kouros/navigation/data/route/Maneuver.kt | 54 +++++ .../data/tomtom/TomTomRepository.kt | 29 ++- .../navigation/data/tomtom/TomTomRoute.kt | 204 ++++++++++-------- .../data/valhalla/ValhallaRepository.kt | 4 +- .../com/kouros/navigation/model/IconMapper.kt | 99 ++++----- .../navigation/model/NavigationViewModel.kt | 13 +- .../navigation/model/SettingsViewModel.kt | 9 + .../repository/SettingsRepository.kt | 7 + .../data/src/main/res/values-de/strings.xml | 1 + .../data/src/main/res/values-el/strings.xml | 1 + .../data/src/main/res/values-pl/strings.xml | 1 + common/data/src/main/res/values/strings.xml | 1 + .../navigation/model/RouteCalculatorTest.kt | 27 +-- .../kouros/navigation/model/RouteModelTest.kt | 2 +- 35 files changed, 542 insertions(+), 280 deletions(-) create mode 100644 common/car/src/main/java/com/kouros/navigation/car/screen/StopOverScreen.kt diff --git a/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt b/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt index 2bb1913..fd5d3af 100644 --- a/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt @@ -181,7 +181,7 @@ fun Categories( Button(onClick = { val places = viewModel.loadRecentPlace(applicationContext) val toLocation = location(places.first()!!.longitude, places.first()!!.latitude) - viewModel.loadRoute(applicationContext, location, toLocation, 0F) + viewModel.loadRoute(applicationContext, location, listOf(toLocation), 0F) closeSheet() }) { Icon( @@ -248,7 +248,7 @@ private fun SearchPlaces( viewModel.saveRecent(context, pl) val toLocation = location(place.lon.toDouble(), place.lat.toDouble()) - viewModel.loadRoute(context, location, toLocation, 0F) + viewModel.loadRoute(context, location, listOf(toLocation), 0F) closeSheet() } .fillMaxWidth() diff --git a/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt b/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt index 10cb952..e910ffe 100644 --- a/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt +++ b/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt @@ -118,7 +118,7 @@ fun Home( Button(onClick = { val places = viewModel.loadRecentPlace(applicationContext) val toLocation = location(places.first()!!.longitude, places.first()!!.latitude) - viewModel.loadRoute(applicationContext, location, toLocation, 0F) + viewModel.loadRoute(applicationContext, location, listOf(toLocation), 0F) closeSheet() }) { Icon( @@ -168,7 +168,7 @@ private fun RecentPlaces( modifier = Modifier .clickable { val toLocation = location(place.longitude, place.latitude) - viewModel.loadRoute(context, location, toLocation, 0F) + viewModel.loadRoute(context, location, listOf(toLocation), 0F) closeSheet() } .fillMaxWidth() diff --git a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt index 235c2de..ee0092a 100644 --- a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt +++ b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt @@ -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 diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt index 759edfc..3fe1e38 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt @@ -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 ) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt index 2a20ae4..1877ffd 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt @@ -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( diff --git a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt index 158f005..534f9b0 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt @@ -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( diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt index 18aba18..1ad6b0b 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt @@ -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) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt index a1f6994..c568736 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt @@ -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) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt index fb3ff9f..7e14e40 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt @@ -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) }) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt index 75d75a6..918f2e2 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt @@ -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 ) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt index 274bd74..04d1ebf 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt @@ -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. */ diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt index 71970ae..3e77566 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt @@ -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) diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/StopOverScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/StopOverScreen.kt new file mode 100644 index 0000000..e91e10d --- /dev/null +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/StopOverScreen.kt @@ -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() + } +} \ No newline at end of file diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt index de4fa1c..e729aad 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt @@ -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( diff --git a/common/data/src/main/java/com/kouros/navigation/data/Color.kt b/common/data/src/main/java/com/kouros/navigation/data/Color.kt index 7b79ca6..6bf42ca 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Color.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Color.kt @@ -2,11 +2,11 @@ package com.kouros.navigation.data import androidx.compose.ui.graphics.Color -val NavigationColorLight = Color(0xFF066462) +val NavigationColorLight = Color(0xFF17A119) -val NavigationColorDark = Color(0xFF10DED9) +val NavigationColorDark = Color(0xFF4EDE10) -val RouteColor = Color(0xFF7B06E1) +val RouteColor = Color(0xFF05692D) val SpeedColor = Color(0xFF262525) diff --git a/common/data/src/main/java/com/kouros/navigation/data/Data.kt b/common/data/src/main/java/com/kouros/navigation/data/Data.kt index 0f12b6c..0891e2b 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Data.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Data.kt @@ -46,6 +46,7 @@ data class Place( var lastDate: Long = 0, var routeIndex: Int = 0, var route: String = "", + var stopOver: Boolean = false, ) data class ContactData( diff --git a/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt b/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt index b3a8505..e8ad243 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt @@ -20,7 +20,7 @@ abstract class NavigationRepository { abstract fun getRoute( context: Context, currentLocation: Location, - destination: Location, + location: List, carOrientation: Float, searchFilter: SearchFilter ): String diff --git a/common/data/src/main/java/com/kouros/navigation/data/datastore/DataStoreManager.kt b/common/data/src/main/java/com/kouros/navigation/data/datastore/DataStoreManager.kt index 10efc41..0640d5f 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/datastore/DataStoreManager.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/datastore/DataStoreManager.kt @@ -58,6 +58,8 @@ class DataStoreManager(private val context: Context) { val ENGINE_TYPE = intPreferencesKey("EngineType") + val ALTERNATIVE_ROUTES = booleanPreferencesKey("AlternativeRoutes") + } // Read values @@ -145,6 +147,11 @@ class DataStoreManager(private val context: Context) { ?: EngineType.COMBUSTION.ordinal } + val alternativeRoutesFlow: Flow = + context.dataStore.data.map { preferences -> + preferences[PreferencesKeys.ALTERNATIVE_ROUTES] == true + } + // Save values suspend fun setShow3D(enabled: Boolean) { context.dataStore.edit { preferences -> @@ -235,4 +242,11 @@ class DataStoreManager(private val context: Context) { prefs[PreferencesKeys.ENGINE_TYPE] = mode } } + + suspend fun setAlternativeRoutes(enabled: Boolean) { + context.dataStore.edit { preferences -> + preferences[PreferencesKeys.ALTERNATIVE_ROUTES] = enabled + } + } + } diff --git a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRepository.kt b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRepository.kt index 46cce7f..8dee748 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRepository.kt @@ -13,7 +13,7 @@ class OsrmRepository : NavigationRepository() { override fun getRoute( context: Context, currentLocation: Location, - location: Location, + location: List, carOrientation: Float, searchFilter: SearchFilter ): String { @@ -28,7 +28,7 @@ class OsrmRepository : NavigationRepository() { if (searchFilter.avoidFerry) { exclude = "$exclude&exclude=ferry" } - val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true&alternatives=false" + val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.first().longitude},${location.first().latitude}?steps=true&alternatives=false" return fetchUrl(routeUrl + routeLocation + exclude, true) } diff --git a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt index c2fb4cc..9abd541 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt @@ -69,7 +69,7 @@ class OsrmRoute { steps.add(step) stepIndex += 1 } - legs.add(Leg(steps)) + legs.add(Leg(steps, summary)) } val routeGeoJson = createLineStringCollection(waypoints) val centerLocation = createCenterLocation(createLineStringCollection(waypoints)) diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt index 27dc4d2..fe6ecff 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt @@ -1,5 +1,9 @@ package com.kouros.navigation.data.route +import android.location.Location +import com.kouros.navigation.utils.location + data class Leg( var steps : List = arrayListOf(), + val summary: Summary, ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt index ea97162..f8e50bd 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt @@ -13,3 +13,57 @@ data class Maneuver( val message: String = "", val pointIndex : Int = 0, ) + +enum class ManeuverType { + TYPE_UNKNOWN, + TYPE_DEPART, + TYPE_NAME_CHANGE, + TYPE_KEEP_LEFT, + TYPE_KEEP_RIGHT, + TYPE_TURN_SLIGHT_LEFT, + TYPE_TURN_SLIGHT_RIGHT, + TYPE_TURN_NORMAL_LEFT, + TYPE_TURN_NORMAL_RIGHT, + TYPE_TURN_SHARP_LEFT, + TYPE_TURN_SHARP_RIGHT, + TYPE_U_TURN_LEFT, + TYPE_U_TURN_RIGHT, + TYPE_ON_RAMP_SLIGHT_LEFT, + TYPE_ON_RAMP_SLIGHT_RIGHT, + TYPE_ON_RAMP_NORMAL_LEFT, + TYPE_ON_RAMP_NORMAL_RIGHT, + TYPE_ON_RAMP_SHARP_LEFT, + TYPE_ON_RAMP_SHARP_RIGHT, + TYPE_ON_RAMP_U_TURN_LEFT, + TYPE_ON_RAMP_U_TURN_RIGHT, + TYPE_OFF_RAMP_SLIGHT_LEFT, + TYPE_OFF_RAMP_SLIGHT_RIGHT, + TYPE_OFF_RAMP_NORMAL_LEFT, + TYPE_OFF_RAMP_NORMAL_RIGHT, + TYPE_FORK_LEFT, + TYPE_FORK_RIGHT, + TYPE_MERGE_LEFT, + TYPE_MERGE_RIGHT, + TYPE_MERGE_SIDE_UNSPECIFIED, + TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW, + TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE, + TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW, + TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE, + TYPE_STRAIGHT, + TYPE_FERRY_BOAT, + TYPE_FERRY_TRAIN, + TYPE_DESTINATION, + TYPE_DESTINATION_STRAIGHT, + TYPE_DESTINATION_LEFT, + TYPE_DESTINATION_RIGHT, + TYPE_ROUNDABOUT_ENTER_CW, + TYPE_ROUNDABOUT_EXIT_CW, + TYPE_ROUNDABOUT_ENTER_CCW, + TYPE_ROUNDABOUT_EXIT_CCW, + TYPE_FERRY_BOAT_LEFT, + TYPE_FERRY_BOAT_RIGHT, + TYPE_FERRY_TRAIN_LEFT, + TYPE_FERRY_TRAIN_RIGHT, + TYPE_WAYPOINT_RIGHT, + TYPE_WAYPOINT_LEFT +} diff --git a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt index 56b0c43..9b1d6f9 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRepository.kt @@ -23,14 +23,14 @@ private const val tomtomFields = val useLocal = BuildConfig.DEBUG -val useLocalTraffic = BuildConfig.DEBUG +val useLocalTraffic = BuildConfig.DEBUG class TomTomRepository : NavigationRepository() { override fun getRoute( context: Context, currentLocation: Location, - location: Location, + location: List, carOrientation: Float, searchFilter: SearchFilter ): String { @@ -55,17 +55,34 @@ class TomTomRepository : NavigationRepository() { engineType = "electric" } val repository = getSettingsRepository(context) - val tomtomApiKey = runBlocking { repository.tomTomApiKeyFlow.first() } + val tomtomApiKey = runBlocking { + repository.tomTomApiKeyFlow.first() + } + val alternativeRoutes = runBlocking { + repository.alternativeRoutesFlow.first() + } + val altRoutes = if (alternativeRoutes) { + "&maxAlternatives=2" + } else { + "&maxAlternatives=0" + } val currentLocale = Locale.getDefault() val language = currentLocale.language + "-" + currentLocale.country + var loc = "" + location.forEach { + loc += if (loc.isEmpty()) { + "${it.latitude},${it.longitude}" + } else { + ":${it.latitude},${it.longitude}" + } + } val url = - routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" + + routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:$loc" + "/json?sectionType=traffic&report=effectiveSettings&routeType=eco" + "&traffic=true&avoid=unpavedRoads&travelMode=car" + "&vehicleMaxSpeed=120&vehicleCommercial=false" + "&instructionsType=text&language=$language§ionType=lanes" + - "&routeRepresentation=encodedPolyline" + - "&maxAlternatives=2" + + "&routeRepresentation=encodedPolyline$altRoutes" + "&vehicleEngineType=$engineType$filter&key=$tomtomApiKey" return fetchUrl( url, diff --git a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt index 04fcfea..3a2abeb 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt @@ -1,10 +1,13 @@ package com.kouros.navigation.data.tomtom + import com.kouros.navigation.data.Route import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.data.route.Intersection import com.kouros.navigation.data.route.Lane import com.kouros.navigation.data.route.Leg +import com.kouros.navigation.data.route.ManeuverType +import com.kouros.navigation.data.route.Routes import com.kouros.navigation.data.route.Step import com.kouros.navigation.data.route.Summary import com.kouros.navigation.utils.GeoUtils.createCenterLocation @@ -17,12 +20,12 @@ import com.kouros.navigation.data.route.Maneuver as RouteManeuver class TomTomRoute { fun mapToRoute(routeJson: TomTomResponse, builder: Route.Builder) { - val routes = mutableListOf() + val routes = mutableListOf() routeJson.routes.forEach { route -> val waypoints = mutableListOf>() + val points = mutableListOf>() val legs = mutableListOf() var stepIndex = 0 - var points = listOf>() val summary = Summary( route.summary.travelTimeInSeconds.toDouble(), route.summary.lengthInMeters.toDouble(), @@ -30,89 +33,98 @@ class TomTomRoute { route.summary.trafficLengthInMeters.toDouble() ) route.legs.forEach { leg -> - points = decodePolyline(leg.encodedPolyline, leg.encodedPolylinePrecision) - waypoints.addAll(points) + val p = decodePolyline(leg.encodedPolyline, leg.encodedPolylinePrecision) + points.addAll(p) + waypoints.addAll(p) } - var stepDistance = 0.0 - var stepDuration = 0.0 - val steps = mutableListOf() - var lastPointIndex = 0 - for (index in 1.. + var stepDistance = 0.0 + var stepDuration = 0.0 + val steps = mutableListOf() + val summary = Summary( + leg.summary.travelTimeInSeconds.toDouble(), + leg.summary.lengthInMeters.toDouble(), + leg.summary.trafficDelayInSeconds.toDouble(), + leg.summary.trafficLengthInMeters.toDouble() ) + var lastPointIndex = 0 + for (index in 1..() - route.sections?.forEach { section -> - if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex - && section.endPointIndex >= lastPointIndex - ) { - val lanes = mutableListOf() - var startIndex = 0 - var lastLane: Lane? = null - section.lanes?.forEach { itLane -> - val lane = Lane( - location = location( - waypoints[section.startPointIndex][0], - waypoints[section.startPointIndex][1] - ), - valid = itLane.directions.first() == itLane.follow, - indications = itLane.directions, - startIndex = startIndex, - endIndex = section.endPointIndex - ) - startIndex = section.startPointIndex - if (lastLane == null - || (!(lastLane.valid && lane.valid - && lastLane.indications == lane.indications)) - ) { - lanes.add(lane) + lastPointIndex = instruction.pointIndex + val intersections = mutableListOf() + route.sections?.forEach { section -> + if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex + && section.endPointIndex >= lastPointIndex + ) { + val lanes = mutableListOf() + var startIndex = 0 + var lastLane: Lane? = null + section.lanes?.forEach { itLane -> + val lane = Lane( + location = location( + waypoints[section.startPointIndex][0], + waypoints[section.startPointIndex][1] + ), + valid = itLane.directions.first() == itLane.follow, + indications = itLane.directions, + startIndex = startIndex, + endIndex = section.endPointIndex + ) + startIndex = section.startPointIndex + if (lastLane == null + || (!(lastLane.valid && lane.valid + && lastLane.indications == lane.indications)) + ) { + lanes.add(lane) + } + lastLane = lane } - lastLane = lane + intersections.add(Intersection(waypoints[startIndex], lanes)) } - intersections.add(Intersection(waypoints[startIndex], lanes)) } + stepDistance = + route.guidance.instructions[index].routeOffsetInMeters - stepDistance + stepDuration = + route.guidance.instructions[index].travelTimeInSeconds - stepDuration + val step = Step( + index = stepIndex, + street = street, + distance = stepDistance, + duration = stepDuration, + maneuver = maneuver, + intersection = intersections, + countryCode = lastInstruction.countryCode + ) + stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble() + stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble() + steps.add(step) + stepIndex += 1 } - stepDistance = - route.guidance.instructions[index].routeOffsetInMeters - stepDistance - stepDuration = - route.guidance.instructions[index].travelTimeInSeconds - stepDuration - val step = Step( - index = stepIndex, - street = street, - distance = stepDistance, - duration = stepDuration, - maneuver = maneuver, - intersection = intersections, - countryCode = lastInstruction.countryCode - ) - stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble() - stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble() - steps.add(step) - stepIndex += 1 + legs.add(Leg(steps, summary)) } - legs.add(Leg(steps)) val routeGeoJson = createLineStringCollection(waypoints) val centerLocation = createCenterLocation(createLineStringCollection(waypoints)) - val newRoute = com.kouros.navigation.data.route.Routes( + val newRoute = Routes( legs, summary, routeGeoJson, @@ -130,75 +142,79 @@ class TomTomRoute { var newType = 0 when (type) { "DEPART" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_DEPART + newType = ManeuverType.TYPE_DEPART.ordinal } "ARRIVE" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION + newType = ManeuverType.TYPE_DESTINATION.ordinal } + "ARRIVE_LEFT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_LEFT + newType = ManeuverType.TYPE_DESTINATION_LEFT.ordinal } "ARRIVE_RIGHT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_RIGHT + newType = ManeuverType.TYPE_DESTINATION_RIGHT.ordinal } "STRAIGHT", "FOLLOW" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_STRAIGHT + newType = ManeuverType.TYPE_STRAIGHT.ordinal } "KEEP_RIGHT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_RIGHT + newType = ManeuverType.TYPE_KEEP_RIGHT.ordinal } "BEAR_RIGHT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT + newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal } "BEAR_LEFT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT + newType = ManeuverType.TYPE_TURN_SLIGHT_LEFT.ordinal } "KEEP_LEFT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_LEFT + newType = ManeuverType.TYPE_KEEP_LEFT.ordinal } "TURN_LEFT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_LEFT + newType = ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal } "TURN_RIGHT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_RIGHT + newType = ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal } "SHARP_LEFT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_LEFT + newType = ManeuverType.TYPE_TURN_SHARP_LEFT.ordinal } "SHARP_RIGHT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT + newType = ManeuverType.TYPE_TURN_SHARP_RIGHT.ordinal } "ROUNDABOUT_RIGHT", "ROUNDABOUT_CROSS" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CCW + newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.ordinal } "ROUNDABOUT_LEFT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW + newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CW.ordinal } - "MAKE_UTURN" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_LEFT + "MAKE_UTURN", "TRY_MAKE_UTURN" -> { + newType = ManeuverType.TYPE_U_TURN_LEFT.ordinal } "ENTER_MOTORWAY" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_LEFT + newType = ManeuverType.TYPE_MERGE_LEFT.ordinal } "TAKE_EXIT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT + newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal + } + "WAYPOINT_RIGHT" -> { + newType = ManeuverType.TYPE_WAYPOINT_RIGHT.ordinal } } return newType diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRepository.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRepository.kt index b8e11bb..db37abc 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRepository.kt @@ -16,7 +16,7 @@ class ValhallaRepository : NavigationRepository() { override fun getRoute( context: Context, currentLocation: Location, - location: Location, + location: List, carOrientation: Float, searchFilter: SearchFilter ): String { @@ -35,7 +35,7 @@ class ValhallaRepository : NavigationRepository() { lon = currentLocation.longitude, searchFilter = exclude ), - Locations(lat = location.latitude, lon = location.longitude, searchFilter = exclude) + Locations(lat = location.first().latitude, lon = location.first().longitude, searchFilter = exclude) ) val valhallaLocation = ValhallaLocation( locations = vLocation, diff --git a/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt b/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt index 575bfec..ed109b2 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt @@ -6,15 +6,12 @@ import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.Matrix import android.graphics.Paint -import androidx.annotation.DrawableRes -import androidx.car.app.model.CarIcon import androidx.car.app.navigation.model.LaneDirection -import androidx.car.app.navigation.model.Maneuver import androidx.core.graphics.createBitmap import androidx.core.graphics.drawable.IconCompat import com.kouros.data.R import com.kouros.navigation.data.StepData -import java.util.Collections +import com.kouros.navigation.data.route.ManeuverType import java.util.Locale class IconMapper { @@ -22,62 +19,66 @@ class IconMapper { fun maneuverIcon(routeManeuverType: Int): Int { var currentTurnIcon = R.drawable.ic_turn_name_change when (routeManeuverType) { - Maneuver.TYPE_STRAIGHT -> { + ManeuverType.TYPE_STRAIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_name_change } - Maneuver.TYPE_DESTINATION, - Maneuver.TYPE_DESTINATION_RIGHT, - Maneuver.TYPE_DESTINATION_LEFT, - Maneuver.TYPE_DESTINATION_STRAIGHT + ManeuverType.TYPE_DESTINATION.ordinal, + ManeuverType.TYPE_DESTINATION_RIGHT.ordinal, + ManeuverType.TYPE_DESTINATION_LEFT.ordinal, + ManeuverType.TYPE_DESTINATION_STRAIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_destination } - Maneuver.TYPE_TURN_NORMAL_RIGHT -> { + ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_normal_right } - Maneuver.TYPE_TURN_NORMAL_LEFT -> { + ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_normal_left } - Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT -> { + ManeuverType.TYPE_OFF_RAMP_SLIGHT_RIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_slight_right } - Maneuver.TYPE_TURN_SLIGHT_RIGHT -> { + ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_slight_right } - Maneuver.TYPE_KEEP_RIGHT -> { + ManeuverType.TYPE_KEEP_RIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_name_change } - Maneuver.TYPE_KEEP_LEFT -> { + ManeuverType.TYPE_KEEP_LEFT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_name_change } - Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> { + ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.ordinal -> { currentTurnIcon = R.drawable.ic_roundabout_ccw } - Maneuver.TYPE_ROUNDABOUT_EXIT_CCW -> { + ManeuverType.TYPE_ROUNDABOUT_EXIT_CCW.ordinal -> { currentTurnIcon = R.drawable.ic_roundabout_ccw } - Maneuver.TYPE_U_TURN_LEFT -> { + ManeuverType.TYPE_U_TURN_LEFT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_u_turn_left } - Maneuver.TYPE_U_TURN_RIGHT -> { + ManeuverType.TYPE_U_TURN_RIGHT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_u_turn_right } - Maneuver.TYPE_MERGE_LEFT -> { + ManeuverType.TYPE_MERGE_LEFT.ordinal -> { currentTurnIcon = R.drawable.ic_turn_merge_symmetrical } + + ManeuverType.TYPE_WAYPOINT_RIGHT.ordinal -> { + currentTurnIcon = R.drawable.ic_turn_destination + } } return currentTurnIcon } @@ -86,8 +87,8 @@ class IconMapper { val laneDirection = when (direction.lowercase(Locale.getDefault())) { "left_straight" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_NORMAL_LEFT - Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT + ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_NORMAL_LEFT + ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT else -> LaneDirection.SHAPE_UNKNOWN } @@ -95,7 +96,7 @@ class IconMapper { "left" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_NORMAL_LEFT + ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_NORMAL_LEFT else -> LaneDirection.SHAPE_UNKNOWN } @@ -103,9 +104,9 @@ class IconMapper { "straight" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT - Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_STRAIGHT - Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_STRAIGHT + ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT + ManeuverType.TYPE_KEEP_LEFT.ordinal -> LaneDirection.SHAPE_STRAIGHT + ManeuverType.TYPE_KEEP_RIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT else -> LaneDirection.SHAPE_UNKNOWN } @@ -113,7 +114,7 @@ class IconMapper { "right" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT + ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT else -> LaneDirection.SHAPE_UNKNOWN } @@ -121,8 +122,8 @@ class IconMapper { "right_straight" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT - Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT + ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT + ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT else -> LaneDirection.SHAPE_UNKNOWN } @@ -130,8 +131,8 @@ class IconMapper { "left_slight", "slight_left" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT - Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT + ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_SLIGHT_LEFT + ManeuverType.TYPE_KEEP_LEFT.ordinal -> LaneDirection.SHAPE_SLIGHT_LEFT else -> LaneDirection.SHAPE_UNKNOWN } @@ -139,8 +140,8 @@ class IconMapper { "right_slight", "slight_right" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_SLIGHT_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT - Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_SLIGHT_RIGHT + ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT + ManeuverType.TYPE_KEEP_RIGHT.ordinal -> LaneDirection.SHAPE_SLIGHT_RIGHT else -> LaneDirection.SHAPE_UNKNOWN } @@ -212,8 +213,8 @@ class IconMapper { return when (direction) { "left_straight" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_LEFT -> "left_o_straight_x" - Maneuver.TYPE_STRAIGHT -> "left_x_straight_o" + ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> "left_o_straight_x" + ManeuverType.TYPE_STRAIGHT.ordinal -> "left_x_straight_o" else -> "left_x_straight_x" } @@ -221,29 +222,29 @@ class IconMapper { "right_straight" -> { when (stepData.currentManeuverType) { - Maneuver.TYPE_TURN_NORMAL_RIGHT -> "right_x_straight_x" - Maneuver.TYPE_STRAIGHT -> "right_x_straight_o" - Maneuver.TYPE_TURN_SLIGHT_RIGHT -> "right_o_straight_o" + ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> "right_x_straight_x" + ManeuverType.TYPE_STRAIGHT.ordinal -> "right_x_straight_o" + ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> "right_o_straight_o" else -> "right_x_straight_x" } } - "right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_o" else "${direction}_x" - "left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x" - "straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT - || stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT - || stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT + "right" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal) "${direction}_o" else "${direction}_x" + "left" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal) "${direction}_o" else "${direction}_x" + "straight" -> if (stepData.currentManeuverType == ManeuverType.TYPE_STRAIGHT.ordinal + || stepData.currentManeuverType == ManeuverType.TYPE_KEEP_LEFT.ordinal + || stepData.currentManeuverType == ManeuverType.TYPE_KEEP_RIGHT.ordinal ) "${direction}_o" else "${direction}_x" - "right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT - || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT - || stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT + "right_slight", "slight_right" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal + || stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal + || stepData.currentManeuverType == ManeuverType.TYPE_KEEP_RIGHT.ordinal ) "slight_right_o" else "slight_right_x" - "left_slight", "slight_left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT - || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT - || stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT + "left_slight", "slight_left" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_SLIGHT_LEFT.ordinal + || stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal + || stepData.currentManeuverType == ManeuverType.TYPE_KEEP_LEFT.ordinal ) "slight_left_o" else "slight_left_x" else -> { diff --git a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt index ddd519c..9747732 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt @@ -3,6 +3,7 @@ package com.kouros.navigation.model //import com.kouros.navigation.data.Preferences.boxStore import android.content.Context import android.location.Location +import android.util.Log import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList import androidx.lifecycle.MutableLiveData @@ -147,7 +148,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo fun loadRoute( context: Context, currentLocation: Location, - destination: Location, + destination: List, carOrientation: Float ) { viewModelScope.launch(Dispatchers.IO) { @@ -181,9 +182,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo ) if (data.isNotEmpty()) { val trafficData = rebuildTraffic(data) - traffic.postValue( - trafficData - ) + if (trafficData.isNotEmpty()) { + traffic.postValue( + trafficData + ) + } } } catch (e: Exception) { e.printStackTrace() @@ -234,7 +237,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo repository.getRoute( context, currentLocation, - location, + listOf(location), carOrientation, getSearchFilter(context) ) diff --git a/common/data/src/main/java/com/kouros/navigation/model/SettingsViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/SettingsViewModel.kt index 20a78cd..efc1e9d 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/SettingsViewModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/SettingsViewModel.kt @@ -102,6 +102,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel( 0 ) + val alternativeRoutes = repository.alternativeRoutesFlow.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + false + ) + fun onShow3DChanged(enabled: Boolean) { viewModelScope.launch { repository.setShow3D(enabled) } } @@ -159,4 +165,7 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel( viewModelScope.launch { repository.setEngineType(mode) } } + fun onAlternativeRoutes(enabled: Boolean) { + viewModelScope.launch { repository.setAlternativeRoutes(enabled) } + } } diff --git a/common/data/src/main/java/com/kouros/navigation/repository/SettingsRepository.kt b/common/data/src/main/java/com/kouros/navigation/repository/SettingsRepository.kt index 7469645..7518594 100644 --- a/common/data/src/main/java/com/kouros/navigation/repository/SettingsRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/repository/SettingsRepository.kt @@ -50,6 +50,9 @@ class SettingsRepository( val engineTypeFlow: Flow = dataStoreManager.engineTypeFlow + val alternativeRoutesFlow: Flow = + dataStoreManager.alternativeRoutesFlow + suspend fun setShow3D(enabled: Boolean) { dataStoreManager.setShow3D(enabled) } @@ -109,4 +112,8 @@ class SettingsRepository( suspend fun setEngineType(mode: Int) { dataStoreManager.setEngineType(mode) } + + suspend fun setAlternativeRoutes(enabled: Boolean) { + dataStoreManager.setAlternativeRoutes(enabled) + } } diff --git a/common/data/src/main/res/values-de/strings.xml b/common/data/src/main/res/values-de/strings.xml index 7673c57..7a3935d 100644 --- a/common/data/src/main/res/values-de/strings.xml +++ b/common/data/src/main/res/values-de/strings.xml @@ -71,4 +71,5 @@ Combustion Electric Engine type + Alternative routes diff --git a/common/data/src/main/res/values-el/strings.xml b/common/data/src/main/res/values-el/strings.xml index 9cb7f14..f557400 100644 --- a/common/data/src/main/res/values-el/strings.xml +++ b/common/data/src/main/res/values-el/strings.xml @@ -55,4 +55,5 @@ Combustion Electric Engine type + Alternative routes diff --git a/common/data/src/main/res/values-pl/strings.xml b/common/data/src/main/res/values-pl/strings.xml index 9338f09..f6bb504 100644 --- a/common/data/src/main/res/values-pl/strings.xml +++ b/common/data/src/main/res/values-pl/strings.xml @@ -55,4 +55,5 @@ Combustion Electric Engine type + Alternative routes diff --git a/common/data/src/main/res/values/strings.xml b/common/data/src/main/res/values/strings.xml index 17e79fb..2810404 100644 --- a/common/data/src/main/res/values/strings.xml +++ b/common/data/src/main/res/values/strings.xml @@ -58,4 +58,5 @@ Combustion Electric Engine type + Alternative routes \ No newline at end of file diff --git a/common/data/src/test/java/com/kouros/navigation/model/RouteCalculatorTest.kt b/common/data/src/test/java/com/kouros/navigation/model/RouteCalculatorTest.kt index 80e1149..a31ee93 100644 --- a/common/data/src/test/java/com/kouros/navigation/model/RouteCalculatorTest.kt +++ b/common/data/src/test/java/com/kouros/navigation/model/RouteCalculatorTest.kt @@ -55,8 +55,8 @@ class RouteCalculatorTest { ) } - private fun setupRoute(steps: List, currentStepIndex: Int = 0): Route { - val leg = Leg(steps = steps) + private fun setupRoute(steps: List, currentStepIndex: Int = 0, summary: Summary): Route { + val leg = Leg(steps = steps, summary) val routes = Routes( legs = listOf(leg), summary = Summary(), @@ -74,7 +74,7 @@ class RouteCalculatorTest { fun `findStep updates currentStepIndex to step containing the nearest waypoint`() { val step0 = createStep(index = 0, numWaypoints = 2) val step1 = createStep(index = 1, numWaypoints = 2) - routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1))) + routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1), summary = Summary())) val mockLocation: Location = mock() // step0/wp0: 500F, step0/wp1: 400F, step1/wp0: 300F, step1/wp1: 8F @@ -88,7 +88,7 @@ class RouteCalculatorTest { @Test fun `findStep updates waypointIndex to the nearest waypoint within the step`() { val step0 = createStep(index = 0, numWaypoints = 3) - routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0))) + routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0), summary = Summary())) val mockLocation: Location = mock() // wp0: 100F, wp1: 30F (nearest), wp2: 80F @@ -104,7 +104,7 @@ class RouteCalculatorTest { val step0 = createStep(index = 0, numWaypoints = 2) val step1 = createStep(index = 1, numWaypoints = 2) routeModel.navState = routeModel.navState.copy( - route = setupRoute(listOf(step0, step1), currentStepIndex = 1) + route = setupRoute(listOf(step0, step1), currentStepIndex = 1, summary = Summary()) ) val mockLocation: Location = mock() @@ -123,7 +123,7 @@ class RouteCalculatorTest { val step1 = createStep(index = 1, numWaypoints = 2) val step2 = createStep(index = 2, numWaypoints = 2) routeModel.navState = routeModel.navState.copy( - route = setupRoute(listOf(step0, step1, step2)) + route = setupRoute(listOf(step0, step1, step2), summary = Summary()) ) val mockLocation: Location = mock() @@ -147,7 +147,7 @@ class RouteCalculatorTest { val step0 = createStep(index = 0, numWaypoints = 2, duration = 60.0, waypointIndex = 0) val step1 = createStep(index = 1, numWaypoints = 2, duration = 120.0) val step2 = createStep(index = 2, numWaypoints = 2, duration = 90.0) - routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1, step2))) + routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1, step2), summary = Summary())) val result = routeCalculator.travelLeftTime() @@ -160,7 +160,7 @@ class RouteCalculatorTest { // waypointIndex=2, waypoints=4 → percent = 100*(4-2)/4 = 50 → time = 80*50/100 = 40s val step0 = createStep(index = 0, numWaypoints = 4, duration = 80.0, waypointIndex = 2) val step1 = createStep(index = 1, numWaypoints = 2, duration = 40.0) - routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1))) + routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1), summary = Summary())) val result = routeCalculator.travelLeftTime() @@ -172,7 +172,7 @@ class RouteCalculatorTest { fun `travelLeftTime returns only future steps when at last step`() { val step0 = createStep(index = 0, numWaypoints = 2, duration = 60.0, waypointIndex = 1) routeModel.navState = routeModel.navState.copy( - route = setupRoute(listOf(step0), currentStepIndex = 0) + route = setupRoute(listOf(step0), currentStepIndex = 0, summary = Summary()) ) val result = routeCalculator.travelLeftTime() @@ -189,7 +189,7 @@ class RouteCalculatorTest { fun `leftStepDistance returns 0 when waypointIndex is at the last position`() { // Loop range: waypointIndex.., currentStepIndex: Int = 0): Route { - val leg = Leg(steps = steps) + val leg = Leg(steps = steps, summary = Summary()) val routes = Routes( legs = listOf(leg), summary = Summary(),