Stopover and Legs, ManeuverType

This commit is contained in:
Dimitris
2026-04-01 16:24:51 +02:00
parent 6838ad09c4
commit 52f8dec2e6
28 changed files with 208 additions and 281 deletions
@@ -104,6 +104,10 @@ internal class ClusterSession : Session(), NavigationListener {
}
override fun recalcRoute(destination: Place) {
}
companion object {
val TAG: String = ClusterSession::class.java.getSimpleName()
}
@@ -475,26 +475,27 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
val streetName = routeModel.currentStep().street
val currentDate = LocalDateTime.now(ZoneOffset.UTC)
snapLocation(location, streetName)
checkTraffic(currentDate, location)
updateSpeedCamera(location)
checkRoute(currentDate, location)
updateNavigationScreen()
checkArrival()
if (snapLocation(location, streetName)) {
checkTraffic(currentDate, location)
updateSpeedCamera(location)
checkRoute(currentDate, location)
updateNavigationScreen()
checkArrival()
}
}
/**
* Updates the surface renderer with snapped location and street name.
* Checks if maximal route deviation is exceeded and reroutes if needed.
*/
private fun snapLocation(location: Location, streetName: String) {
private fun snapLocation(location: Location, streetName: String) : Boolean {
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
val distance = location.distanceTo(snappedLocation)
when {
distance > MAXIMAL_ROUTE_DEVIATION -> {
stopNavigation()
navigationScreen.calculateNewRoute(routeModel.navState.destination)
return false
}
distance < MAXIMAL_SNAP_CORRECTION -> {
@@ -505,6 +506,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
surfaceRenderer.updateLocation(location, streetName)
}
}
return true
}
/**
@@ -616,7 +618,6 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
navigationScreen.navigationType = NavigationType.VIEW
if (notificationActive)
notificationManager.stopNotificationService()
Log.d(TAG, "end stopNavigation")
}
override fun updateTrip(trip: Trip) {
@@ -625,6 +626,19 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
}
}
/**
* Recalculates a route for the specified place.
*/
override fun recalcRoute(destination: Place) {
val destination = location(destination.longitude, destination.latitude)
navigationViewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
listOf(destination),
surfaceRenderer.carOrientation
)
}
/**
* Handle guidance audio
* Called when user wants to hear the step-by-step instructions
@@ -646,6 +660,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
* Starts navigation and invalidates the screen.
*/
override fun onRouteReceived(route: String) {
Log.d(TAG, "onRouteReceived")
if (route.isNotEmpty()) {
this.route = route
if (routeModel.isNavigating()) {
@@ -31,7 +31,7 @@ import com.kouros.navigation.car.map.getPaddingValues
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Constants.TILT
import com.kouros.navigation.data.Constants.homeVogelhart
import com.kouros.navigation.data.DarkMode
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.model.BaseStyleModel
@@ -59,7 +59,7 @@ import java.time.LocalDateTime
*/
class SurfaceRenderer(
private var carContext: CarContext,
private var lifecycle: Lifecycle,
lifecycle: Lifecycle,
//private var routeModel: RouteCarModel,
private var viewModelStoreOwner: ViewModelStoreOwner
) : DefaultLifecycleObserver {
@@ -74,10 +74,6 @@ class SurfaceRenderer(
val cameraPosition = MutableLiveData(
CameraPosition(
zoom = 16.0,
target = Position(
latitude = homeVogelhart.latitude,
longitude = homeVogelhart.longitude
)
)
)
@@ -164,7 +160,7 @@ class SurfaceRenderer(
lifecycleOwner = CustomLifecycleOwner()
lifecycleOwner.performRestore(null)
// technically, we only really need any one of these instead of all 3
// i add them to be consistent with the actual lifecycle.
// add them to be consistent with the actual lifecycle.
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -251,10 +247,6 @@ class SurfaceRenderer(
speed.value = 0F
}
fun onBaseStyleStateUpdated(style: BaseStyle) {
}
/**
* Composable function that renders the map and navigation UI.
* Observes various LiveData sources and updates the map accordingly.
@@ -272,7 +264,8 @@ class SurfaceRenderer(
val paddingValues = getPaddingValues(height, viewStyle)
val cameraState = cameraState(paddingValues, position, tilt)
val baseStyle = BaseStyleModel().readStyle(carContext, darkMode, carContext.isDarkMode)
val dark = darkMode == 1 || darkMode == 2 && carContext.isDarkMode
val dark = darkMode == DarkMode.DARK.ordinal
|| (darkMode == DarkMode.USE_CAR.ordinal && carContext.isDarkMode)
MapLibre(
cameraState,
@@ -334,7 +327,6 @@ class SurfaceRenderer(
}
override fun onCreate(owner: LifecycleOwner) {
style.observe(owner, ::onBaseStyleStateUpdated)
Log.i(TAG, "SurfaceRenderer created")
carContext.getCarService(AppManager::class.java)
.setSurfaceCallback(mSurfaceCallback)
@@ -499,18 +491,6 @@ class SurfaceRenderer(
}
}
/**
* Updates car location from the connected car system.
* Only updates location when using OSRM routing engine.
*/
fun updateCarLocation(location: Location, streetName: String) {
val repository = getSettingsRepository(carContext)
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
if (routingEngine == RouteEngine.OSRM.ordinal) {
updateLocation(location, streetName)
}
}
/**
* Updates current speed for display.
*/
@@ -146,10 +146,10 @@ fun RouteLayer(routeData: String?, trafficData: Map<String, String>) {
interpolate(
type = exponential(1.2f),
input = zoom(),
5 to const(0.4.dp),
6 to const(0.7.dp),
7 to const(1.75.dp),
20 to const(22.dp),
5 to const(0.7.dp),
6 to const(1.0.dp),
7 to const(2.4.dp),
20 to const(26.dp),
),
)
}
@@ -3,6 +3,7 @@ package com.kouros.navigation.car.navigation
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.util.Log
import androidx.annotation.StringRes
import androidx.car.app.AppManager
import androidx.car.app.CarContext
@@ -54,7 +55,7 @@ class RouteCarModel : RouteModel() {
val step =
Step.Builder(currentStepCueWithImage)
step.setRoad(navState.destination.street!!)
step.setRoad(navState.destination.street)
if (stepData.lane.isNotEmpty()) {
val lanesAdded = addLanes(carContext, step, stepData)
if (lanesAdded) {
@@ -16,4 +16,6 @@ interface NavigationListener {
fun updateTrip(trip: Trip)
fun navigateToPlace(place: Place)
fun recalcRoute(destination: Place)
}
@@ -2,6 +2,7 @@ package com.kouros.navigation.car.screen
import android.os.CountDownTimer
import android.os.Handler
import android.util.Log
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
@@ -32,6 +33,7 @@ import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.screen.settings.SettingsScreen
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.model.NavigationViewModel
@@ -471,25 +473,14 @@ open class NavigationScreen(
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
navigationType = NavigationType.NAVIGATION
reRoute(destination)
listener.recalcRoute(destination)
}
}
reRouteTimer?.start()
}
}
/**
* Re-requests a route for the specified place.
*/
fun reRoute(place: Place) {
val destination = location(place.longitude, place.latitude)
navigationViewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
listOf(destination),
surfaceRenderer.carOrientation
)
}
/**
* Updates navigation state with the current location, checks for arrival, and traffic updates.