Loading cardinal-android/app/src/main/java/earth/maps/cardinal/routing/FerrostarWrapperRepository.kt +126 −76 Original line number Diff line number Diff line Loading @@ -25,8 +25,13 @@ import earth.maps.cardinal.data.LocationRepository import earth.maps.cardinal.data.OrientationRepository import earth.maps.cardinal.data.RoutingMode import earth.maps.cardinal.data.room.RoutingProfileRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import javax.inject.Inject import javax.inject.Singleton import kotlin.concurrent.thread @Singleton class FerrostarWrapperRepository @Inject constructor( Loading @@ -36,17 +41,38 @@ class FerrostarWrapperRepository @Inject constructor( private val orientationRepository: OrientationRepository, private val routingProfileRepository: RoutingProfileRepository ) { lateinit var walking: FerrostarWrapper lateinit var cycling: FerrostarWrapper lateinit var driving: FerrostarWrapper lateinit var truck: FerrostarWrapper lateinit var motorScooter: FerrostarWrapper lateinit var motorcycle: FerrostarWrapper private val _isInitialized = MutableStateFlow(false) val isInitialized = _isInitialized.asStateFlow() private var _walking: FerrostarWrapper? = null private var _cycling: FerrostarWrapper? = null private var _driving: FerrostarWrapper? = null private var _truck: FerrostarWrapper? = null private var _motorScooter: FerrostarWrapper? = null private var _motorcycle: FerrostarWrapper? = null val walking: FerrostarWrapper get() = _walking ?: throw IllegalStateException("Walking wrapper not initialized") val cycling: FerrostarWrapper get() = _cycling ?: throw IllegalStateException("Cycling wrapper not initialized") val driving: FerrostarWrapper get() = _driving ?: throw IllegalStateException("Driving wrapper not initialized") val truck: FerrostarWrapper get() = _truck ?: throw IllegalStateException("Truck wrapper not initialized") val motorScooter: FerrostarWrapper get() = _motorScooter ?: throw IllegalStateException("MotorScooter wrapper not initialized") val motorcycle: FerrostarWrapper get() = _motorcycle ?: throw IllegalStateException("Motorcycle wrapper not initialized") private val pendingOptions = mutableMapOf<RoutingMode, RoutingOptions>() val androidTtsObserver = AndroidTtsObserver(context) /** * Suspends until the repository is initialized with a Valhalla endpoint. */ suspend fun awaitInitialization() { _isInitialized.filter { it }.first() } fun setValhallaEndpoint(endpoint: String) { walking = FerrostarWrapper( thread { Thread.sleep(30000) _walking = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -55,7 +81,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) cycling = FerrostarWrapper( _cycling = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -64,7 +90,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) driving = FerrostarWrapper( _driving = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -73,7 +99,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) truck = FerrostarWrapper( _truck = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -82,7 +108,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) motorScooter = FerrostarWrapper( _motorScooter = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -91,7 +117,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) motorcycle = FerrostarWrapper( _motorcycle = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -100,36 +126,60 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) _isInitialized.value = true } // Apply pending options synchronized(pendingOptions) { pendingOptions.forEach { (mode, options) -> setOptionsForMode(mode, options) } pendingOptions.clear() } } /** * Updates the routing options for the specified mode by modifying the existing wrapper. * If the wrapper is not yet initialized, the options are stored and applied once initialized. */ fun setOptionsForMode(mode: RoutingMode, routingOptions: RoutingOptions) { when (mode) { RoutingMode.PEDESTRIAN -> walking.setOptions(routingOptions) RoutingMode.BICYCLE -> cycling.setOptions(routingOptions) RoutingMode.AUTO -> driving.setOptions(routingOptions) RoutingMode.TRUCK -> truck.setOptions(routingOptions) RoutingMode.MOTOR_SCOOTER -> motorScooter.setOptions(routingOptions) RoutingMode.MOTORCYCLE -> motorcycle.setOptions(routingOptions) else -> {} val wrapper = getWrapperForMode(mode) if (wrapper != null) { wrapper.setOptions(routingOptions) } else { synchronized(pendingOptions) { pendingOptions[mode] = routingOptions } } } /** * Resets the routing options for the specified mode to defaults by recreating the wrapper. * If the wrapper is not yet initialized, the default options are stored and applied once initialized. */ fun resetOptionsToDefaultsForMode(mode: RoutingMode) { val defaultOptions = routingProfileRepository.createDefaultOptionsForMode(mode) when (mode) { RoutingMode.PEDESTRIAN -> walking.setOptions(defaultOptions) RoutingMode.BICYCLE -> cycling.setOptions(defaultOptions) RoutingMode.AUTO -> driving.setOptions(defaultOptions) RoutingMode.TRUCK -> truck.setOptions(defaultOptions) RoutingMode.MOTOR_SCOOTER -> motorScooter.setOptions(defaultOptions) RoutingMode.MOTORCYCLE -> motorcycle.setOptions(defaultOptions) else -> {} val wrapper = getWrapperForMode(mode) if (wrapper != null) { wrapper.setOptions(defaultOptions) } else { defaultOptions?.let { synchronized(pendingOptions) { pendingOptions[mode] = it } } } } private fun getWrapperForMode(mode: RoutingMode): FerrostarWrapper? = when (mode) { RoutingMode.PEDESTRIAN -> _walking RoutingMode.BICYCLE -> _cycling RoutingMode.AUTO -> _driving RoutingMode.TRUCK -> _truck RoutingMode.MOTOR_SCOOTER -> _motorScooter RoutingMode.MOTORCYCLE -> _motorcycle else -> null } } cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsViewModel.kt +6 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,9 @@ class DirectionsViewModel @Inject constructor( suspend fun initializeRoutingMode() { // Wait for FerrostarWrapperRepository to be initialized before setting options ferrostarWrapperRepository.awaitInitialization() // Set initial routing mode from preferences selectedRoutingMode = appPreferenceRepository.lastRoutingMode.value.let { modeString -> RoutingMode.entries.find { it.value == modeString } ?: RoutingMode.AUTO Loading Loading @@ -161,6 +164,9 @@ class DirectionsViewModel @Inject constructor( private fun fetchDrivingDirections(origin: Place, destination: Place) { viewModelScope.launch { // Wait for FerrostarWrapperRepository to be initialized ferrostarWrapperRepository.awaitInitialization() routeStateRepository.setLoading(true) try { Loading Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/routing/FerrostarWrapperRepository.kt +126 −76 Original line number Diff line number Diff line Loading @@ -25,8 +25,13 @@ import earth.maps.cardinal.data.LocationRepository import earth.maps.cardinal.data.OrientationRepository import earth.maps.cardinal.data.RoutingMode import earth.maps.cardinal.data.room.RoutingProfileRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import javax.inject.Inject import javax.inject.Singleton import kotlin.concurrent.thread @Singleton class FerrostarWrapperRepository @Inject constructor( Loading @@ -36,17 +41,38 @@ class FerrostarWrapperRepository @Inject constructor( private val orientationRepository: OrientationRepository, private val routingProfileRepository: RoutingProfileRepository ) { lateinit var walking: FerrostarWrapper lateinit var cycling: FerrostarWrapper lateinit var driving: FerrostarWrapper lateinit var truck: FerrostarWrapper lateinit var motorScooter: FerrostarWrapper lateinit var motorcycle: FerrostarWrapper private val _isInitialized = MutableStateFlow(false) val isInitialized = _isInitialized.asStateFlow() private var _walking: FerrostarWrapper? = null private var _cycling: FerrostarWrapper? = null private var _driving: FerrostarWrapper? = null private var _truck: FerrostarWrapper? = null private var _motorScooter: FerrostarWrapper? = null private var _motorcycle: FerrostarWrapper? = null val walking: FerrostarWrapper get() = _walking ?: throw IllegalStateException("Walking wrapper not initialized") val cycling: FerrostarWrapper get() = _cycling ?: throw IllegalStateException("Cycling wrapper not initialized") val driving: FerrostarWrapper get() = _driving ?: throw IllegalStateException("Driving wrapper not initialized") val truck: FerrostarWrapper get() = _truck ?: throw IllegalStateException("Truck wrapper not initialized") val motorScooter: FerrostarWrapper get() = _motorScooter ?: throw IllegalStateException("MotorScooter wrapper not initialized") val motorcycle: FerrostarWrapper get() = _motorcycle ?: throw IllegalStateException("Motorcycle wrapper not initialized") private val pendingOptions = mutableMapOf<RoutingMode, RoutingOptions>() val androidTtsObserver = AndroidTtsObserver(context) /** * Suspends until the repository is initialized with a Valhalla endpoint. */ suspend fun awaitInitialization() { _isInitialized.filter { it }.first() } fun setValhallaEndpoint(endpoint: String) { walking = FerrostarWrapper( thread { Thread.sleep(30000) _walking = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -55,7 +81,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) cycling = FerrostarWrapper( _cycling = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -64,7 +90,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) driving = FerrostarWrapper( _driving = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -73,7 +99,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) truck = FerrostarWrapper( _truck = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -82,7 +108,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) motorScooter = FerrostarWrapper( _motorScooter = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -91,7 +117,7 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) motorcycle = FerrostarWrapper( _motorcycle = FerrostarWrapper( context, locationRepository, orientationRepository, Loading @@ -100,36 +126,60 @@ class FerrostarWrapperRepository @Inject constructor( androidTtsObserver, routingProfileRepository ) _isInitialized.value = true } // Apply pending options synchronized(pendingOptions) { pendingOptions.forEach { (mode, options) -> setOptionsForMode(mode, options) } pendingOptions.clear() } } /** * Updates the routing options for the specified mode by modifying the existing wrapper. * If the wrapper is not yet initialized, the options are stored and applied once initialized. */ fun setOptionsForMode(mode: RoutingMode, routingOptions: RoutingOptions) { when (mode) { RoutingMode.PEDESTRIAN -> walking.setOptions(routingOptions) RoutingMode.BICYCLE -> cycling.setOptions(routingOptions) RoutingMode.AUTO -> driving.setOptions(routingOptions) RoutingMode.TRUCK -> truck.setOptions(routingOptions) RoutingMode.MOTOR_SCOOTER -> motorScooter.setOptions(routingOptions) RoutingMode.MOTORCYCLE -> motorcycle.setOptions(routingOptions) else -> {} val wrapper = getWrapperForMode(mode) if (wrapper != null) { wrapper.setOptions(routingOptions) } else { synchronized(pendingOptions) { pendingOptions[mode] = routingOptions } } } /** * Resets the routing options for the specified mode to defaults by recreating the wrapper. * If the wrapper is not yet initialized, the default options are stored and applied once initialized. */ fun resetOptionsToDefaultsForMode(mode: RoutingMode) { val defaultOptions = routingProfileRepository.createDefaultOptionsForMode(mode) when (mode) { RoutingMode.PEDESTRIAN -> walking.setOptions(defaultOptions) RoutingMode.BICYCLE -> cycling.setOptions(defaultOptions) RoutingMode.AUTO -> driving.setOptions(defaultOptions) RoutingMode.TRUCK -> truck.setOptions(defaultOptions) RoutingMode.MOTOR_SCOOTER -> motorScooter.setOptions(defaultOptions) RoutingMode.MOTORCYCLE -> motorcycle.setOptions(defaultOptions) else -> {} val wrapper = getWrapperForMode(mode) if (wrapper != null) { wrapper.setOptions(defaultOptions) } else { defaultOptions?.let { synchronized(pendingOptions) { pendingOptions[mode] = it } } } } private fun getWrapperForMode(mode: RoutingMode): FerrostarWrapper? = when (mode) { RoutingMode.PEDESTRIAN -> _walking RoutingMode.BICYCLE -> _cycling RoutingMode.AUTO -> _driving RoutingMode.TRUCK -> _truck RoutingMode.MOTOR_SCOOTER -> _motorScooter RoutingMode.MOTORCYCLE -> _motorcycle else -> null } }
cardinal-android/app/src/main/java/earth/maps/cardinal/ui/directions/DirectionsViewModel.kt +6 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,9 @@ class DirectionsViewModel @Inject constructor( suspend fun initializeRoutingMode() { // Wait for FerrostarWrapperRepository to be initialized before setting options ferrostarWrapperRepository.awaitInitialization() // Set initial routing mode from preferences selectedRoutingMode = appPreferenceRepository.lastRoutingMode.value.let { modeString -> RoutingMode.entries.find { it.value == modeString } ?: RoutingMode.AUTO Loading Loading @@ -161,6 +164,9 @@ class DirectionsViewModel @Inject constructor( private fun fetchDrivingDirections(origin: Place, destination: Place) { viewModelScope.launch { // Wait for FerrostarWrapperRepository to be initialized ferrostarWrapperRepository.awaitInitialization() routeStateRepository.setLoading(true) try { Loading