From fdf2ee9f48dd274db71e294d029e4fe7e1617248 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Sat, 3 Jan 2026 14:04:50 +0100 Subject: [PATCH] CarInfo and CarSensors Osrm --- app/build.gradle.kts | 4 +- .../com/kouros/navigation/MainApplication.kt | 13 -- .../com/kouros/navigation/ui/MainActivity.kt | 25 +-- .../navigation/car/NavigationSession.kt | 41 +++-- .../kouros/navigation/car/SurfaceRenderer.kt | 72 ++++----- .../com/kouros/navigation/car/map/MapView.kt | 4 +- .../car/navigation/RouteCarModel.kt | 9 +- .../navigation/car/screen/NavigationScreen.kt | 33 ++-- .../car/screen/NavigationSettings.kt | 18 ++- .../car/screen/RoutePreviewScreen.kt | 1 - .../navigation/car/screen/RoutingSettings.kt | 81 ++++++++++ .../navigation/car/screen/SettingsScreen.kt | 4 +- .../java/com/kouros/navigation/data/Data.kt | 3 +- .../navigation/data/NavigationRepository.kt | 23 --- .../java/com/kouros/navigation/data/Route.kt | 3 - .../navigation/data/osrm/OsrmRepository.kt | 4 +- .../kouros/navigation/data/osrm/OsrmRoute.kt | 21 ++- .../navigation/data/overpass/Overpass.kt | 26 +-- .../com/kouros/navigation/data/route/Step.kt | 4 + .../navigation/data/valhalla/ValhallaRoute.kt | 61 ++++++- .../com/kouros/navigation/model/RouteModel.kt | 152 +++++++++++++----- .../com/kouros/navigation/model/ViewModel.kt | 5 + .../navigation/utils/NavigationUtils.kt | 4 +- .../main/res/drawable/lanes_leftleftssr.png | Bin 0 -> 5872 bytes .../data/src/main/res/drawable/lanes_ssr.png | Bin 0 -> 1277 bytes .../src/main/res/drawable/left_not_valid.png | Bin 0 -> 4927 bytes .../data/src/main/res/drawable/left_valid.png | Bin 0 -> 914 bytes .../drawable/left_valid_right_not_valid.png | Bin 0 -> 883 bytes .../src/main/res/drawable/right_not_valid.png | Bin 0 -> 4927 bytes .../src/main/res/drawable/right_valid.png | Bin 0 -> 888 bytes .../res/drawable/slight_right_not_valid.png | Bin 0 -> 4927 bytes .../main/res/drawable/slight_right_valid.png | Bin 0 -> 888 bytes .../main/res/drawable/speed_camera_24px.xml | 10 ++ .../main/res/drawable/straight_not_valid.png | Bin 0 -> 4954 bytes .../straight_not_valid_right_valid.png | Bin 0 -> 895 bytes .../src/main/res/drawable/straight_valid.png | Bin 0 -> 723 bytes common/data/src/main/res/values/strings.xml | 3 + 37 files changed, 416 insertions(+), 208 deletions(-) create mode 100644 common/car/src/main/java/com/kouros/navigation/car/screen/RoutingSettings.kt create mode 100644 common/data/src/main/res/drawable/lanes_leftleftssr.png create mode 100644 common/data/src/main/res/drawable/lanes_ssr.png create mode 100644 common/data/src/main/res/drawable/left_not_valid.png create mode 100644 common/data/src/main/res/drawable/left_valid.png create mode 100644 common/data/src/main/res/drawable/left_valid_right_not_valid.png create mode 100644 common/data/src/main/res/drawable/right_not_valid.png create mode 100644 common/data/src/main/res/drawable/right_valid.png create mode 100644 common/data/src/main/res/drawable/slight_right_not_valid.png create mode 100644 common/data/src/main/res/drawable/slight_right_valid.png create mode 100644 common/data/src/main/res/drawable/speed_camera_24px.xml create mode 100644 common/data/src/main/res/drawable/straight_not_valid.png create mode 100644 common/data/src/main/res/drawable/straight_not_valid_right_valid.png create mode 100644 common/data/src/main/res/drawable/straight_valid.png diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6eecabd..bfdd12e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { applicationId = "com.kouros.navigation" minSdk = 33 targetSdk = 36 - versionCode = 15 - versionName = "0.1.3.15" + versionCode = 18 + versionName = "0.1.3.18" base.archivesName = "navi-$versionName" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/kouros/navigation/MainApplication.kt b/app/src/main/java/com/kouros/navigation/MainApplication.kt index 2fbae36..bb254e9 100644 --- a/app/src/main/java/com/kouros/navigation/MainApplication.kt +++ b/app/src/main/java/com/kouros/navigation/MainApplication.kt @@ -2,26 +2,14 @@ package com.kouros.navigation import android.app.Application import android.content.Context -import com.kouros.navigation.data.Constants.DARK_MODE_SETTINGS -import com.kouros.navigation.data.Constants.ROUTE_ENGINE -import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING -import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.ObjectBox -import com.kouros.navigation.data.RouteEngine -import com.kouros.navigation.data.osrm.OsrmRepository -import com.kouros.navigation.data.valhalla.ValhallaRepository import com.kouros.navigation.di.appModule -import com.kouros.navigation.model.BaseStyleModel import com.kouros.navigation.model.ViewModel -import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue -import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.NavigationUtils.getRouteEngine -import com.kouros.navigation.utils.NavigationUtils.setIntKeyValue import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.context.startKoin import org.koin.core.logger.Level -import org.maplibre.compose.expressions.dsl.switch class MainApplication : Application() { @@ -29,7 +17,6 @@ class MainApplication : Application() { super.onCreate() ObjectBox.init(this); appContext = applicationContext - setIntKeyValue(appContext!!, RouteEngine.VALHALLA.ordinal, ROUTE_ENGINE) navigationViewModel = getRouteEngine(appContext!!) startKoin { androidLogger(Level.DEBUG) diff --git a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt index 2befa83..f498476 100644 --- a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt +++ b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt @@ -47,6 +47,7 @@ import com.kouros.navigation.model.BaseStyleModel import com.kouros.navigation.model.MockLocation import com.kouros.navigation.model.RouteModel import com.kouros.navigation.ui.theme.NavigationTheme +import com.kouros.navigation.utils.GeoUtils.snapLocation import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.bearing import com.kouros.navigation.utils.calculateZoom @@ -61,6 +62,7 @@ import org.maplibre.compose.location.Location import org.maplibre.compose.location.rememberDefaultLocationProvider import org.maplibre.compose.location.rememberUserLocationState import org.maplibre.compose.style.BaseStyle +import org.maplibre.geojson.Point import org.maplibre.spatialk.geojson.Position import kotlin.time.Duration.Companion.seconds @@ -102,7 +104,7 @@ class MainActivity : ComponentActivity() { private var overpass = false - lateinit var baseStyle : BaseStyle.Json + lateinit var baseStyle: BaseStyle.Json init { navigationViewModel.route.observe(this, observer) @@ -112,14 +114,14 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val darkModeSettings = getIntKeyValue(applicationContext, Constants.DARK_MODE_SETTINGS) - baseStyle = BaseStyleModel().readStyle(applicationContext, darkModeSettings, false) + baseStyle = BaseStyleModel().readStyle(applicationContext, darkModeSettings, false) if (useMock) { checkMockLocationEnabled() } locationManager = getSystemService(LOCATION_SERVICE) as LocationManager fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) fusedLocationClient.lastLocation - .addOnSuccessListener { location : android.location.Location? -> + .addOnSuccessListener { location: android.location.Location? -> if (useMock) { mock = MockLocation(locationManager) mock.setMockLocation( @@ -235,6 +237,7 @@ class MainActivity : ComponentActivity() { && lastLocation.longitude != location.position.longitude ) { val currentLocation = location(location.position.longitude, location.position.latitude) + val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing) with(routeModel) { if (isNavigating()) { updateLocation(currentLocation, navigationViewModel) @@ -245,13 +248,12 @@ class MainActivity : ComponentActivity() { if (routeState.maneuverType == 39 && leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE ) { - // stopNavigation() + // stopNavigation() routeState = routeState.copy(arrived = true) routeData.value = "" } } } - val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing) val zoom = calculateZoom(location.speed) cameraPosition.postValue( cameraPosition.value!!.copy( @@ -303,14 +305,13 @@ class MainActivity : ComponentActivity() { } } - fun simulate() { + fun simulate() { CoroutineScope(Dispatchers.IO).launch { - for ((index, step) in routeModel.legs.steps.withIndex()) { - for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) { - if (routeModel.isNavigating()) { - mock.setMockLocation(waypoint[1], waypoint[0]) - delay(800L) // - } + if (routeModel.isNavigating()) { + for ((index, waypoint) in routeModel.route.waypoints!!.withIndex()) { + var deviation = 0.0 + mock.setMockLocation(waypoint[1] + deviation, waypoint[0]) + delay(500L) // } } } 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 fea4e74..791c882 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 @@ -19,7 +19,6 @@ import androidx.car.app.hardware.common.OnCarDataAvailableListener import androidx.car.app.hardware.info.CarHardwareLocation import androidx.car.app.hardware.info.CarSensors import androidx.car.app.hardware.info.Speed -import androidx.compose.foundation.isSystemInDarkTheme import androidx.core.location.LocationListenerCompat import androidx.core.net.toUri import androidx.lifecycle.DefaultLifecycleObserver @@ -33,10 +32,13 @@ import com.kouros.navigation.car.screen.SearchScreen import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION +import com.kouros.navigation.data.Constants.ROUTING_ENGINE import com.kouros.navigation.data.Constants.TAG +import com.kouros.navigation.data.RouteEngine +import com.kouros.navigation.data.osrm.OsrmRepository +import com.kouros.navigation.data.valhalla.ValhallaRepository import com.kouros.navigation.model.BaseStyleModel import com.kouros.navigation.model.ViewModel -import com.kouros.navigation.utils.GeoUtils.snapLocation import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.NavigationUtils.getRouteEngine import org.maplibre.compose.style.BaseStyle @@ -52,7 +54,10 @@ class NavigationSession : Session(), NavigationScreen.Listener { lateinit var surfaceRenderer: SurfaceRenderer var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? -> - updateLocation(location!!) + val routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE) + if (routingEngine == RouteEngine.VALHALLA.ordinal) { + updateLocation(location!!) + } } private val mLifeCycleObserver: LifecycleObserver = object : DefaultLifecycleObserver { @@ -75,8 +80,8 @@ class NavigationSession : Session(), NavigationScreen.Listener { override fun onDestroy(owner: LifecycleOwner) { val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo - carSensors.removeCarHardwareLocationListener(locationListener) - carInfo.removeSpeedListener(speedListener) + carSensors.removeCarHardwareLocationListener(carLocationListener) + carInfo.removeSpeedListener(carSpeedListener) Log.i(TAG, "In onDestroy()") val locationManager = carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager @@ -88,7 +93,7 @@ class NavigationSession : Session(), NavigationScreen.Listener { lateinit var baseStyle: BaseStyle.Json - val locationListener: OnCarDataAvailableListener = + val carLocationListener: OnCarDataAvailableListener = OnCarDataAvailableListener { data -> if (data.location.status == CarValue.STATUS_SUCCESS) { val location = data.location.value @@ -96,7 +101,7 @@ class NavigationSession : Session(), NavigationScreen.Listener { } } - val speedListener = OnCarDataAvailableListener { data -> + val carSpeedListener = OnCarDataAvailableListener { data -> if (data.displaySpeedMetersPerSecond.status == CarValue.STATUS_SUCCESS) { val speed = data.displaySpeedMetersPerSecond.value surfaceRenderer.updateCarSpeed(speed!!) @@ -106,9 +111,19 @@ class NavigationSession : Session(), NavigationScreen.Listener { val lifecycle: Lifecycle = lifecycle lifecycle.addObserver(mLifeCycleObserver) } + + fun onRoutingEngineStateUpdated(routeEngine : Int) { + navigationViewModel = when (routeEngine) { + RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository()) + else -> ViewModel(OsrmRepository()) + } + } + override fun onCreateScreen(intent: Intent): Screen { navigationViewModel = getRouteEngine(carContext) + + navigationViewModel.routingEngine.observe(this, ::onRoutingEngineStateUpdated) routeModel = RouteCarModel() val darkMode = getIntKeyValue(carContext, Constants.DARK_MODE_SETTINGS) @@ -155,9 +170,9 @@ class NavigationSession : Session(), NavigationScreen.Listener { carSensors.addCarHardwareLocationListener( CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, - locationListener + carLocationListener ) - carInfo.addSpeedListener(carContext.mainExecutor, speedListener) + carInfo.addSpeedListener(carContext.mainExecutor, carSpeedListener) } override fun onNewIntent(intent: Intent) { @@ -221,15 +236,15 @@ class NavigationSession : Session(), NavigationScreen.Listener { fun updateLocation(location: Location) { if (routeModel.isNavigating()) { - val snapedLocation = snapLocation(location, routeModel.route.maneuverLocations()) - val distance = location.distanceTo(snapedLocation) + navigationScreen.updateTrip(location) + val wayPointLocation = routeModel.route.currentStep().wayPointLocation + val distance = location.distanceTo(wayPointLocation) if (distance > MAXIMAL_ROUTE_DEVIATION) { navigationScreen.calculateNewRoute(routeModel.routeState.destination) return } - navigationScreen.updateTrip(location) if (distance < MAXIMAL_SNAP_CORRECTION) { - surfaceRenderer.updateLocation(snapedLocation) + surfaceRenderer.updateLocation(wayPointLocation) } else { surfaceRenderer.updateLocation(location) } 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 df2453d..7813b3b 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 @@ -5,8 +5,6 @@ import android.graphics.Rect import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay import android.location.Location -import android.os.CountDownTimer -import android.os.Handler import android.util.Log import androidx.car.app.AppManager import androidx.car.app.CarContext @@ -31,9 +29,12 @@ import com.kouros.navigation.car.map.cameraState import com.kouros.navigation.car.map.getPaddingValues import com.kouros.navigation.car.map.rememberBaseStyle import com.kouros.navigation.car.navigation.RouteCarModel +import com.kouros.navigation.data.Constants.ROUTING_ENGINE import com.kouros.navigation.data.Constants.homeLocation import com.kouros.navigation.data.ObjectBox +import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.model.RouteModel +import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.bearing import com.kouros.navigation.utils.calculateTilt import com.kouros.navigation.utils.calculateZoom @@ -161,7 +162,9 @@ class SurfaceRenderer( fun onConnectionStateUpdated(connectionState: Int) { when (connectionState) { - CarConnection.CONNECTION_TYPE_NATIVE -> ObjectBox.init(carContext) + CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit" + CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto" + CarConnection.CONNECTION_TYPE_NATIVE -> ObjectBox.init(carContext) // Automotive OS } } @@ -256,44 +259,22 @@ class SurfaceRenderer( ) lastBearing = cameraPosition.value!!.bearing lastLocation = location - //speed.value = location.speed - if (!countDownTimerActive) { - countDownTimerActive = true - val mainThreadHandler = Handler(carContext.mainLooper) - val lastLocationTimer = lastLocation - checkUpdate(mainThreadHandler, lastLocationTimer) - } } } } - private fun checkUpdate( - mainThreadHandler: Handler, - lastLocationTimer: Location - ) { - mainThreadHandler.post { - object : CountDownTimer(3000, 1000) { - override fun onTick(millisUntilFinished: Long) {} - override fun onFinish() { - countDownTimerActive = false - if (lastLocation.time - lastLocationTimer.time < 1500) { - speed.postValue(0F) - } - } - }.start() - } - } - private fun updateCameraPosition(bearing: Double, zoom: Double, target: Position) { - cameraPosition.postValue( - cameraPosition.value!!.copy( - bearing = bearing, - zoom = zoom, - tilt = tilt, - padding = getPaddingValues(height, viewStyle), - target = target + synchronized(this) { + cameraPosition.postValue( + cameraPosition.value!!.copy( + bearing = bearing, + zoom = zoom, + tilt = tilt, + padding = getPaddingValues(height, viewStyle), + target = target + ) ) - ) + } } fun setRouteData() { @@ -316,17 +297,22 @@ class SurfaceRenderer( } fun setCategories(location: Location, route: String) { - viewStyle = ViewStyle.AMENITY_VIEW - routeData.value = route - updateCameraPosition( - 0.0, - 12.0, - target = Position(location.longitude, location.latitude) - ) + synchronized(this) { + viewStyle = ViewStyle.AMENITY_VIEW + routeData.value = route + updateCameraPosition( + 0.0, + 12.0, + target = Position(location.longitude, location.latitude) + ) + } } fun updateCarLocation(location: Location) { - // updateLocation(location) + val routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE) + if (routingEngine == RouteEngine.OSRM.ordinal) { + updateLocation(location) + } } fun updateCarSpeed(newSpeed: Float) { 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 1a30125..5fd0206 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 @@ -177,12 +177,12 @@ fun AmenityLayer(routeData: String?) { @Composable fun SpeedCameraLayer(speedCameras: String?) { if (speedCameras != null && speedCameras.isNotEmpty()) { - val color = const(Color.DarkGray) + val color = const(Color.Red) val cameraSource = rememberGeoJsonSource(GeoJsonData.JsonString(speedCameras)) SymbolLayer( id = "speed-camera-layer", source = cameraSource, - iconImage = image(painterResource(R.drawable.speed_camera_48px), drawAsSdf = true), + iconImage = image(painterResource(R.drawable.speed_camera_24px), drawAsSdf = true), iconColor = color, iconSize = interpolate( 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 ce72655..2aadf31 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 @@ -48,13 +48,10 @@ class RouteCarModel() : RouteModel() { val stepData = currentStep() val currentStepCueWithImage: SpannableString = createString(stepData.instruction) - - val straightNormal = Lane.Builder() .addDirection(LaneDirection.create(LaneDirection.SHAPE_STRAIGHT, false)) .build() - val step = Step.Builder(currentStepCueWithImage) .setManeuver( @@ -65,7 +62,7 @@ class RouteCarModel() : RouteModel() { .setRoad(routeState.destination.street!!) stepData.lane.forEach { if (it.indications.isNotEmpty() ) { - step.setLanesImage(createCarIcon(carContext, R.drawable.lanes)) + step.setLanesImage(createCarIcon(createLaneIcon(carContext, stepData))) step.addLane(straightNormal) } } @@ -139,6 +136,10 @@ class RouteCarModel() : RouteModel() { return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build() } + fun createCarIcon(iconCompat: IconCompat): CarIcon { + return CarIcon.Builder(iconCompat).build() + } + fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String?) { carContext.getCarService(AppManager::class.java) .showAlert(createAlert(carContext, distance, maxSpeed)) 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 48a0c43..561c9ac 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 @@ -15,8 +15,6 @@ import androidx.car.app.model.Distance import androidx.car.app.model.Header import androidx.car.app.model.MessageTemplate import androidx.car.app.model.Template -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.MapWithContentTemplate import androidx.car.app.navigation.model.MessageInfo @@ -92,12 +90,8 @@ class NavigationScreen( var speedCameras = listOf() val speedObserver = Observer> { cameras -> speedCameras = cameras - val coordinates = mutableListOf>() - val loc = location(0.0, 0.0) cameras.forEach { - val loc = - location(longitude = it.lon!!, latitude = it.lat!!) coordinates.add(listOf(it.lon!!, it.lat!!)) } val speedData = GeoUtils.createPointCollection(coordinates, "radar") @@ -238,22 +232,13 @@ class NavigationScreen( Distance.UNIT_METERS } val nextStep = routeModel.nextStep(carContext = carContext) - if (nextStep != null) { - return RoutingInfo.Builder() - .setCurrentStep( - routeModel.currentStep(carContext = carContext), - Distance.create(currentDistance, displayUnit) - ) - .setNextStep(nextStep) - .build() - } else { - return RoutingInfo.Builder() - .setCurrentStep( - routeModel.currentStep(carContext = carContext), - Distance.create(currentDistance, displayUnit) - ) - .build() - } + return RoutingInfo.Builder() + .setCurrentStep( + routeModel.currentStep(carContext = carContext), + Distance.create(currentDistance, displayUnit) + ) + .setNextStep(nextStep) + .build() } private fun createActionStripBuilder(): ActionStrip.Builder { @@ -350,7 +335,7 @@ class NavigationScreen( return Action.Builder() .setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_48px)) .setOnClickListener { - screenManager.push(SettingsScreen(carContext)) + screenManager.push(SettingsScreen(carContext, viewModel)) } .build() } @@ -466,7 +451,7 @@ class NavigationScreen( } fun updateTrip(location: Location) { - updateSpeedCamera(location) + updateSpeedCamera(surfaceRenderer.lastLocation) with(routeModel) { updateLocation(location, viewModel) if (routeState.maneuverType == Maneuver.TYPE_DESTINATION diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationSettings.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationSettings.kt index 1e36110..042aca2 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationSettings.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationSettings.kt @@ -12,11 +12,12 @@ import androidx.car.app.model.Toggle import com.kouros.data.R import com.kouros.navigation.data.Constants.AVOID_MOTORWAY import com.kouros.navigation.data.Constants.AVOID_TOLLWAY +import com.kouros.navigation.model.ViewModel import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue -class NavigationSettings(private val carContext: CarContext) : Screen(carContext) { +class NavigationSettings(private val carContext: CarContext, private var viewModel: ViewModel) : Screen(carContext) { private var motorWayToggleState = false @@ -52,7 +53,12 @@ class NavigationSettings(private val carContext: CarContext) : Screen(carContext tollWayToggleState = !tollWayToggleState }.setChecked(tollWayToggleState).build() listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle)) - + listBuilder.addItem( + buildRowForScreenTemplate( + RoutingSettings(carContext, viewModel), + R.string.routing_engine + ) + ) return ListTemplate.Builder() .setSingleList(listBuilder.build()) .setHeader( @@ -70,4 +76,12 @@ class NavigationSettings(private val carContext: CarContext) : Screen(carContext .setToggle(toggle) .build() } + + private fun buildRowForScreenTemplate(screen: Screen, title: Int): Row { + return Row.Builder() + .setTitle(carContext.getString(title)) + .setOnClickListener { screenManager.push(screen) } + .setBrowsable(true) + .build() + } } \ No newline at end of file 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 7ca4558..7a2db84 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 @@ -80,7 +80,6 @@ class RoutePreviewScreen( val header = Header.Builder() .setStartHeaderAction(Action.BACK) .setTitle(carContext.getString(R.string.route_preview)) - //.addEndHeaderAction(navigateAction) .addEndHeaderAction( favoriteAction() ) diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutingSettings.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutingSettings.kt new file mode 100644 index 0000000..90cdede --- /dev/null +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutingSettings.kt @@ -0,0 +1,81 @@ +package com.kouros.navigation.car.screen + +import androidx.car.app.CarContext +import androidx.car.app.CarToast +import androidx.car.app.Screen +import androidx.car.app.model.Action +import androidx.car.app.model.Header +import androidx.car.app.model.ItemList +import androidx.car.app.model.ListTemplate +import androidx.car.app.model.Row +import androidx.car.app.model.SectionedItemList +import androidx.car.app.model.Template +import com.kouros.data.R +import com.kouros.navigation.data.Constants.ROUTING_ENGINE +import com.kouros.navigation.data.RouteEngine +import com.kouros.navigation.model.ViewModel +import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue +import com.kouros.navigation.utils.NavigationUtils.setIntKeyValue + +class RoutingSettings(private val carContext: CarContext, private var viewModel: ViewModel) : Screen(carContext) { + private var routingEngine = RouteEngine.VALHALLA.ordinal + + init { + routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE) + } + + override fun onGetTemplate(): Template { + val templateBuilder = ListTemplate.Builder() + val radioList = + ItemList.Builder() + .addItem( + buildRowForTemplate( + R.string.valhalla, + ) + ) + .addItem( + buildRowForTemplate( + R.string.osrm, + ) + ) + .setOnSelectedListener { index: Int -> + this.onSelected(index) + } + .setSelectedIndex(routingEngine) + .build() + + return templateBuilder + .addSectionedList(SectionedItemList.create( + radioList, + carContext.getString(R.string.routing_engine) + )) + .setHeader( + Header.Builder() + .setTitle(carContext.getString(R.string.routing_engine)) + .setStartHeaderAction(Action.BACK) + .build() + ) + .build() + } + + + private fun onSelected(index: Int) { + setIntKeyValue(carContext, index, ROUTING_ENGINE) + viewModel.routingEngine.value = index + CarToast.makeText( + carContext, + (carContext + .getString(R.string.routing_engine) + + ":" + + " " + index), CarToast.LENGTH_LONG + ) + .show() + } + + private fun buildRowForTemplate(title: Int): Row { + return Row.Builder() + .setTitle(carContext.getString(title)) + .build() + } + +} \ No newline at end of file diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt index 644dad8..7c2be97 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt @@ -24,10 +24,12 @@ import androidx.car.app.model.ListTemplate import androidx.car.app.model.Row import androidx.car.app.model.Template import com.kouros.data.R +import com.kouros.navigation.model.ViewModel /** A screen demonstrating selectable lists. */ class SettingsScreen( carContext: CarContext, + private var viewModel: ViewModel, ) : Screen(carContext) { override fun onGetTemplate(): Template { @@ -40,7 +42,7 @@ class SettingsScreen( ) listBuilder.addItem( buildRowForTemplate( - NavigationSettings(carContext), + NavigationSettings(carContext, viewModel), R.string.navigation_settings ) ) 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 8f7c368..ea3ea24 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 @@ -174,6 +174,8 @@ object Constants { const val AVOID_TOLLWAY = "AvoidTollway" + const val ROUTING_ENGINE = "RoutingEngine" + const val NEXT_STEP_THRESHOLD = 100.0 const val MAXIMAL_SNAP_CORRECTION = 50.0 @@ -182,7 +184,6 @@ object Constants { const val DESTINATION_ARRIVAL_DISTANCE = 40.0 - val ROUTE_ENGINE = RouteEngine.VALHALLA.name } 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 e852219..9b5ac38 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 @@ -30,8 +30,6 @@ import kotlinx.serialization.json.Json abstract class NavigationRepository { - private val placesUrl = "https://kouros-online.de/maps/placespwd"; - private val nominatimUrl = "https://nominatim.openstreetmap.org/" @@ -58,27 +56,6 @@ abstract class NavigationRepository { return fetchUrl("${nominatimUrl}reverse?lat=${location.latitude}&lon=${location.longitude}&format=jsonv2&addressdetails=true&countrycodes=de", false) } - fun getPlaces(): List { - val places: MutableList = ArrayList() - val placesStr = fetchUrl(placesUrl, true) - val jArray = JSONArray(placesStr) - for (i in 0.. { newType = androidx.car.app.navigation.model.Maneuver.TYPE_STRAIGHT } - ManeuverType.turn.value -> { + ManeuverType.turn.value, + ManeuverType.endOfRoad.value -> { if (maneuver.modifier == "right") { newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_RIGHT } } - ManeuverType.turn.value -> { + ManeuverType.turn.value, + ManeuverType.endOfRoad.value, + ManeuverType.onRamp.value + -> { if (maneuver.modifier == "left") { newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_LEFT } } + ManeuverType.fork.value + -> { + if (maneuver.modifier == "slight left") { + newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT + } + } + ManeuverType.fork.value + -> { + if (maneuver.modifier == "slight right") { + newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT + } + } } + return newType } } diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt index fe1845b..5d797e3 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt @@ -64,18 +64,20 @@ class Overpass { } fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String) : List { - val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream) - outputStreamWriter.write(searchQuery) - outputStreamWriter.flush() - // Check if the connection is successful - val responseCode = httpURLConnection.responseCode - if (responseCode == HttpURLConnection.HTTP_OK) { - val response = httpURLConnection.inputStream.bufferedReader() - .use { it.readText() } // defaults to UTF-8 - val gson = GsonBuilder().serializeNulls().create() - val overpass = gson.fromJson(response, Amenity::class.java) - // println("Overpass: $response") - return overpass.elements + try { + val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream) + outputStreamWriter.write(searchQuery) + outputStreamWriter.flush() + // Check if the connection is successful + val responseCode = httpURLConnection.responseCode + if (responseCode == HttpURLConnection.HTTP_OK) { + val response = httpURLConnection.inputStream.bufferedReader() + .use { it.readText() } // defaults to UTF-8 + val gson = GsonBuilder().serializeNulls().create() + val overpass = gson.fromJson(response, Amenity::class.java) + return overpass.elements + } + } catch (e: Exception) { } return emptyList() } diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Step.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Step.kt index f54853d..709ff1d 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Step.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Step.kt @@ -1,8 +1,12 @@ package com.kouros.navigation.data.route +import android.location.Location +import com.kouros.navigation.utils.location + data class Step( var index : Int = 0, var waypointIndex : Int = 0, + var wayPointLocation : Location = location(0.0,0.0), val maneuver: Maneuver, val duration: Double = 0.0, val distance: Double = 0.0, diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt index 438ffdd..2a6e18d 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ValhallaRoute.kt @@ -1,8 +1,10 @@ package com.kouros.navigation.data.valhalla +import androidx.car.app.navigation.model.Maneuver +import com.kouros.data.R import com.kouros.navigation.data.Route import com.kouros.navigation.data.route.Leg -import com.kouros.navigation.data.route.Maneuver +import com.kouros.navigation.data.route.Maneuver as RouteManeuver import com.kouros.navigation.data.route.Step import com.kouros.navigation.data.route.Summary import com.kouros.navigation.utils.GeoUtils.createLineStringCollection @@ -18,10 +20,11 @@ class ValhallaRoute { val steps = mutableListOf() var stepIndex = 0 routeJson.legs[0].maneuvers.forEach { - val maneuver = Maneuver( + val maneuver = RouteManeuver( bearingBefore = 0, bearingAfter = it.bearingAfter, - type = it.type, + //type = it.type, + type = convertType(it), waypoints =waypoints.subList(it.beginShapeIndex, it.endShapeIndex+1) ) var name = "" @@ -40,4 +43,56 @@ class ValhallaRoute { .legs(listOf(leg)) .waypoints(waypoints) } + + fun convertType(maneuver: Maneuvers): Int { + var newType = 0 + when (maneuver.type) { + ManeuverType.None.value -> { + newType = Maneuver.TYPE_STRAIGHT + } + ManeuverType.Destination.value, + ManeuverType.DestinationRight.value, + ManeuverType.DestinationLeft.value, + -> { + newType = Maneuver.TYPE_DESTINATION + } + + ManeuverType.Right.value -> { + newType = Maneuver.TYPE_TURN_NORMAL_RIGHT + } + + ManeuverType.Left.value -> { + newType = Maneuver.TYPE_TURN_NORMAL_LEFT + } + + ManeuverType.RampRight.value -> { + newType = Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT + } + + ManeuverType.RampLeft.value -> { + newType = Maneuver.TYPE_OFF_RAMP_SLIGHT_LEFT + } + + ManeuverType.ExitRight.value -> { + newType = Maneuver.TYPE_TURN_SLIGHT_RIGHT + } + + ManeuverType.StayRight.value -> { + newType = Maneuver.TYPE_KEEP_RIGHT + } + + ManeuverType.StayLeft.value -> { + newType = Maneuver.TYPE_KEEP_LEFT + } + + ManeuverType.RoundaboutEnter.value -> { + newType = Maneuver.TYPE_ROUNDABOUT_ENTER_CCW + } + + ManeuverType.RoundaboutExit.value -> { + newType = Maneuver.TYPE_ROUNDABOUT_EXIT_CCW + } + } + return newType + } } \ No newline at end of file diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt index 4667167..fa42b8e 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt @@ -1,28 +1,31 @@ package com.kouros.navigation.model import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Matrix import android.location.Location import androidx.car.app.navigation.model.Maneuver import androidx.car.app.navigation.model.Step +import androidx.core.graphics.createBitmap +import androidx.core.graphics.drawable.IconCompat import com.kouros.data.R import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD -import com.kouros.navigation.data.Constants.ROUTE_ENGINE -import com.kouros.navigation.data.valhalla.ManeuverType +import com.kouros.navigation.data.Constants.ROUTING_ENGINE import com.kouros.navigation.data.Place import com.kouros.navigation.data.Route -import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.data.StepData 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.valhalla.ManeuverType +import com.kouros.navigation.utils.GeoUtils.createCenterLocation import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue import com.kouros.navigation.utils.location import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.invoke import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.util.concurrent.TimeUnit @@ -40,6 +43,8 @@ open class RouteModel() { val lastSpeedIndex: Int = 0, val maxSpeed: Int = 0, val location: Location = location(0.0, 0.0), + val lastLocation: Location = location(0.0, 0.0), + val bearing : Float = 0F ) var routeState = RouteState() @@ -55,11 +60,14 @@ open class RouteModel() { get() = routeState.route!!.legs!!.first() fun startNavigation(routeString: String, context: Context) { - val routeEngine = getIntKeyValue(context = context, ROUTE_ENGINE) - val newRoute = Route.Builder() + val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE) + var newRoute = Route.Builder() .routeEngine(routeEngine) .route(routeString) .build() + // TODO: + newRoute = newRoute.copy(centerLocation = createCenterLocation(newRoute.routeGeoJson)) + println("Route ${newRoute.centerLocation}") this.routeState = routeState.copy( route = newRoute, isNavigating = true @@ -80,6 +88,7 @@ open class RouteModel() { routeState = routeState.copy(location = location) findStep(location) updateSpeedLimit(location, viewModel) + } private fun findStep(location: Location) { @@ -93,6 +102,9 @@ open class RouteModel() { nearestDistance = distance route.currentStep = step.index step.waypointIndex = wayIndex + step.wayPointLocation = location(waypoint[0], waypoint[1]) + val bearing = routeState.lastLocation.bearingTo(location) + this.routeState = routeState.copy(lastLocation = location, bearing = bearing) } } if (nearestDistance == 0F) { @@ -104,11 +116,9 @@ open class RouteModel() { break } } - //println("Current Index ${route.currentStep} WayPoint: ${route.currentStep().waypointIndex}") } private fun currentIntersection(location: Location): Intersection { - var inter = Intersection() var nearestDistance = 100000.0f route.currentStep().intersection.forEach { @@ -120,6 +130,7 @@ open class RouteModel() { } return inter } + fun updateSpeedLimit(location: Location, viewModel: ViewModel) = runBlocking { CoroutineScope(Dispatchers.IO).launch { // speed limit @@ -150,7 +161,7 @@ open class RouteModel() { var maneuverType = if (hasArrived(currentStep.maneuver.type)) { currentStep.maneuver.type } else { - ManeuverType.None.value + Maneuver.TYPE_STRAIGHT } // Get the single, correct maneuver for this state val relevantStep = if (shouldAdvance) { @@ -187,6 +198,7 @@ open class RouteModel() { when (distanceLeft) { in 0.0..NEXT_STEP_THRESHOLD -> { } + else -> { if (step.name.isNotEmpty()) { text = step.name @@ -262,68 +274,49 @@ open class RouteModel() { } fun maneuverIcon(routeManeuverType: Int): (Pair) { - var type = Maneuver.TYPE_DEPART var currentTurnIcon = R.drawable.ic_turn_name_change when (routeManeuverType) { - ManeuverType.None.value -> { - type = Maneuver.TYPE_STRAIGHT + Maneuver.TYPE_STRAIGHT -> { currentTurnIcon = R.drawable.ic_turn_name_change } - ManeuverType.Destination.value, - ManeuverType.DestinationRight.value, - ManeuverType.DestinationLeft.value, + Maneuver.TYPE_DESTINATION, -> { - type = Maneuver.TYPE_DESTINATION currentTurnIcon = R.drawable.ic_turn_destination } - ManeuverType.Right.value -> { - type = Maneuver.TYPE_TURN_NORMAL_RIGHT + Maneuver.TYPE_TURN_NORMAL_RIGHT -> { currentTurnIcon = R.drawable.ic_turn_normal_right } - ManeuverType.Left.value -> { - type = Maneuver.TYPE_TURN_NORMAL_LEFT + Maneuver.TYPE_TURN_NORMAL_LEFT -> { currentTurnIcon = R.drawable.ic_turn_normal_left } - ManeuverType.RampRight.value -> { - type = Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT + Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT -> { currentTurnIcon = R.drawable.ic_turn_slight_right } - ManeuverType.RampLeft.value -> { - type = Maneuver.TYPE_TURN_NORMAL_LEFT - currentTurnIcon = R.drawable.ic_turn_normal_left - } - - ManeuverType.ExitRight.value -> { - type = Maneuver.TYPE_TURN_SLIGHT_RIGHT + Maneuver.TYPE_TURN_SLIGHT_RIGHT -> { currentTurnIcon = R.drawable.ic_turn_slight_right } - ManeuverType.StayRight.value -> { - type = Maneuver.TYPE_KEEP_RIGHT + Maneuver.TYPE_KEEP_RIGHT -> { currentTurnIcon = R.drawable.ic_turn_name_change } - - ManeuverType.StayLeft.value -> { - type = Maneuver.TYPE_KEEP_LEFT + Maneuver.TYPE_KEEP_LEFT -> { currentTurnIcon = R.drawable.ic_turn_name_change } - - ManeuverType.RoundaboutEnter.value -> { - type = Maneuver.TYPE_ROUNDABOUT_ENTER_CCW + Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> { currentTurnIcon = R.drawable.ic_roundabout_ccw } - ManeuverType.RoundaboutExit.value -> { - type = Maneuver.TYPE_ROUNDABOUT_EXIT_CCW + Maneuver.TYPE_ROUNDABOUT_EXIT_CCW -> { + currentTurnIcon = R.drawable.ic_roundabout_ccw } } - return Pair(type, currentTurnIcon) + return Pair(routeManeuverType, currentTurnIcon) } fun isNavigating(): Boolean { @@ -336,4 +329,79 @@ open class RouteModel() { || type == ManeuverType.Destination.value || type == ManeuverType.DestinationLeft.value } + + fun createLaneIcon(context: Context, stepData: StepData): IconCompat { + val bitmaps = mutableListOf() + stepData.lane.forEach { + if (it.indications.isNotEmpty()) { + it.indications.forEach { it2 -> + val resource = laneToResource(it2, it, stepData) + if (it2 != "none") { + println("Direction $resource") + if (resource.isNotEmpty()) { + val id = resourceId( resource); + val bitMap = BitmapFactory.decodeResource(context.resources, id) + bitmaps.add(bitMap) + } + } + } + } + } + return IconCompat.createWithBitmap(overlay(bitmaps = bitmaps)) + } + + + fun overlay(bitmaps: List): Bitmap { + val matrix = Matrix() + if (bitmaps.size == 1) { + return bitmaps.first() + } + val bmOverlay = createBitmap( + bitmaps.first().getWidth() * bitmaps.size, + bitmaps.first().getHeight(), + bitmaps.first().getConfig()!! + ) + val canvas = Canvas(bmOverlay) + canvas.drawBitmap(bitmaps.first(), matrix, null) + var i = 0 + bitmaps.forEach { + if (i > 0) { + matrix.setTranslate(i * 40F, 0F) + canvas.drawBitmap(it, matrix, null) + } + i++ + } + return bmOverlay + } + + private fun laneToResource(direction: String, lane: Lane, stepData: StepData): String { + println("Maneuver ${stepData.maneuverType}") + return when (val direction = direction.replace(" ", "_")) { + "left" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_valid" else "${direction}_not_valid" + "right" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_valid" else "${direction}_not_valid" + "straight" -> if (stepData.maneuverType == Maneuver.TYPE_STRAIGHT) "${direction}_valid" else "${direction}_not_valid" + "slight_right" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT) "${direction}_valid" else "${direction}_not_valid" + "slight_left" -> if (stepData.maneuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT) "${direction}_valid" else "${direction}_not_valid" + else -> {""} + } + } + + fun resourceId( + variableName: String, + ): Int { + return when(variableName) { + "left_not_valid" -> R.drawable.left_not_valid + "left_valid" -> R.drawable.left_valid + "left_valid_right_not_valid" -> R.drawable.left_valid_right_not_valid + "right_not_valid" -> R.drawable.right_not_valid + "right_valid" -> R.drawable.right_valid + "slight_right_not_valid" -> R.drawable.slight_right_not_valid + "slight_right_valid" -> R.drawable.slight_right_valid + "straight_not_valid" -> R.drawable.straight_not_valid + "straight_not_valid_right_valid" -> R.drawable.straight_not_valid_right_valid + "straight_valid" -> R.drawable.straight_valid + else -> {R.drawable.ic_close_white_24dp} + } + } + } \ No newline at end of file diff --git a/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt index 86d0ac4..9bd52d0 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt @@ -67,6 +67,11 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() { MutableLiveData() } + val routingEngine: MutableLiveData by lazy { + MutableLiveData() + } + + fun loadRecentPlace(location: Location, context: Context) { viewModelScope.launch(Dispatchers.IO) { try { diff --git a/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt b/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt index ffa9035..ddbaafb 100644 --- a/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt +++ b/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt @@ -4,7 +4,7 @@ import android.content.Context import android.location.Location import android.location.LocationManager import androidx.core.content.edit -import com.kouros.navigation.data.Constants.ROUTE_ENGINE +import com.kouros.navigation.data.Constants.ROUTING_ENGINE import com.kouros.navigation.data.Constants.SHARED_PREF_KEY import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.data.osrm.OsrmRepository @@ -26,7 +26,7 @@ import kotlin.time.Duration.Companion.seconds object NavigationUtils { fun getRouteEngine(context: Context): ViewModel { - val routeEngine = getIntKeyValue(context = context, ROUTE_ENGINE) + val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE) return when (routeEngine) { RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository()) else -> ViewModel(OsrmRepository()) diff --git a/common/data/src/main/res/drawable/lanes_leftleftssr.png b/common/data/src/main/res/drawable/lanes_leftleftssr.png new file mode 100644 index 0000000000000000000000000000000000000000..3eb99212893dc05dc91259d2e167b6e11e71936d GIT binary patch literal 5872 zcmeHKdo+~m8lU#wluIh5Tz2g-wcEs)J7zMvxI`Jb=9bGhGv8b?H)GtTQz4X6%4LwY zYhfc2PTCaPy@*IDn$#8&vMbri`DS!o=d5*BYn}67v(|Tc-}m?Y-skx}&->2%#oF2K zSgNS02!p|vT3eYrK+hED(qHlebjKxBw?L0)p-!$M2OtP85b_yp4hR>82tYU(%x1t~ z!S~|r`}+iKmciIiiCzI(vqJOt_8Dqq z`hO>?-Zmh&Bxz9MC*eQ<%!WoPl7ghSr3E9yKnr|d^)e^wgRXa6*H zLjSeASQ2nSsqq{J23v$+o0-{Jo0)xx8;UkPBjMuCdb?H8wNAGjMssvxtmN+;X9!C- z{jg2DQE^W`-uhxnLqg7~!8)T`M@HaFem`7zsC+(NMTIeST{3T+K0W{1i*?sJCF$tj z2@Ge+?40DuWUE(1cAMc^L)Ky-^)-1SvT^!N9Qao*vqn7p&SaPU-srCCO-j>Q-}&x- z)?RSGG_Iom-~pY8<^A?zi$}jq*`BtHzEPaHv3s(q`+lAlbIovG$-V1OQXEYcOnQCf7(4N`|=tJS)dA*qp@9YVw= z?eh^O`_p^UdS^|R#hv3J&$|y{YPMW&UA`ytSN{hIJ@4i&M{DGteKV|83!7=N<9uHy z+6q-qFdM28SBedZ#^)LUbUqa{2<8f)@`1rjwgd|RnlC7VQ$Z%1M@CFssYSrqbTYz) zKtWRkW+01g6)FTBLv5XCp}sUC9kIn!(Il7z5pY2f01xJJc>bhdGD5~ng5IUwC!lP69Oj@uO~@9A*nA#b$_Y^U0U|O20gb~y z_{SAcC?Dy0{_j3x6kQv7sD5e|~_F23iDy zJkk2kDd@D1_JRN*M-~p9h5|Vt7lQgjvtquOa)&j=?xTfN0w$X)kXb=ue}NRS8K1=Z z!Z+!NES%3Bfy_Vheu4fFy9^AWC=`-8pB5l>&)S@fkj_t{^J#24N%l&^&;cACPe+1O z0v?IS7~zpb9K#q%H#Rgv8`F)A7+Ay4sH}PZB7jE&rBo2P0UP3B0B8_LV_=XpDuIB+ z<8V}@F@`}yVi;H~){sV|5%KiTD0U0kP*nn)&%KgT(IF}tjcRB}Km$msk)bgX58$9T zI*p1XpwUDlkicMoI8a7Kr;#lALM{NMlg$N~AWFbv$_Au@leXDelMz@0^rs#>4j^Jc z24sW{n->uLX~2oi1sz3zR85Q#6fzNmGe%?37(9{q3A6_k`a_i{<;0*3a0`M+(?WuR zfrJI5bqW#4ETC9OW~$Ve`x*?S=Yjfas>SrS_@4MTeg(oaM_|D z0knk>{DDAlAz2XD!VrxG@R%U9d%Q2#5B==_PzppENH?Ye3?u^t04RTG8WO-j`NQLg zXa*Gki3ETli|Ql0Kc68A0)*f;CZr>z6;z%wt>8McMCpEs4q}1QEI@)GF<9ik3x;|h zFiN^-e2CZt^>3P($N-;hGLYRuAGCQvdm-xMX82w+$lw3x?|m-*pHsl$-zNDgeZR@| zO|Gv};H$vjs_UCvU!}lTfxlJP|4c5$PoGmD4>|<}K_5$cs_!V!ht_x0T|3NSu!8vY zSg55SuyXTlN}U6 zIy{IiV!@>cMVEsrYhW<>GHdf~POO3PK3~pbXN{(*j#S(J)Vl+mU6pOzD9_c~x1Mw= zH%CXW@u^vKa*fiVwm(8ca-%1sV>ZZprObS{awS2Y)_Gn5uF%;0$GN)JQL*RBt;KCg z zTnC4;GN7jU>}9trm;CC1L5K20wQ8%`Q(i8!_Eq_Hp%t~!_ZO?&aVe^_yy+sMpfroeOn5yRcv^hXg%K$x8iYqw%W3Z&I^|*1T^5V5yF|UWYqGE-Wp`Cvl~IkWB+_u4 zw1M8v)-9dCH=-FI+-rCzSa)Ff7TRS+Y08o}!3Vv(yWReJ_-2yc1DrYN{W5GbXK`i2 zPtmc8LF4ke3ih;<`)g15o=&>tl4e*r5{n8WJwtu3pXd2gt>lGjafBUcM~L>G{F8LG z>s$+W>KASAYdLHsRXDS(&Z}hG&iweStI=1VC)5|m1l^TL(B(grrU>oAtar=>78{?t zG}g1~9l~=gJ!p&f!+i%Xz`GCEgwWR-IZd*f3r!21Z^M6~mA}LWk{)k+-GtlzY8;5Q zcq4w<_H%f~Z>WJ4IK8?v!p_4#`#CSFZTK4{bUrC*n@TTBym<=Gb$7^!yl|*Cu#lq4 zy3M+Vu`fTmx9F#3HHXK@b%NxkWI^wfrrAe9)Tjy%q$Ig0kLO9>`80k6+wGo17wzl% z)q`2IK6-|{thzxy<5aV__q^v76|D@$kuaqUeHGkDOY0lr+{fI~9`2tut?X`kZ;HZ>+qAwSH{Ao%Nzcma zmXuUk?`<3pIUC2^6IrA>vu2Oq^N5I+BmLCZSjVOuyl>Zq9@3pRdDEWMqsNF%?OFlK z#PyBi-SiVp@sUy3x+N#7^P5X&>P+usPqpt{?IbrjuePNeu@}kn&Rfju)EY|;C(-v< zqG=B09^nld?z#Aue&*KYOSQ`2K)Xt<(oj@Y(ht#La~B`>=SI}(JX?37Z*NPZ=jw>W z9Yx-YE^;l@X2SyVI*{c@;>C$)%6$q?2Z%RN(ge*B8rd6fxC+{1$PpgOzHUC+_nzkl znT2cLXBEE7($0LL=i;=AN}S1f?&cJP$~Q=ODXzD8W%SZbN8NtsO9my?&*^aT)ja3y zrwRL2)c?{d(ZoDGw}_^q>1L18$Q!B%WH*UoB0VoQ)hi}Ctt+2ilJ08QX~az&%2deRr0!zEt3cluhT-J1NKIk& zS;es{Crf`(jz0A_wK=Nt`g5>YZuIu9!ftr8BxT6pQcL2%k~yF`;fDOuHz(E~Z~1Mx zxgtkwTcxXVgNyIm;U6+M7j$r&4$i+DMBnl4Msly^Z2P`3oM1A4^M+T-3+~{HbYRZX ztXR=U|93-$j#Y%};aS!nwU$j@w?I(-Rw~jnG zs(7q*{-O9w6y>Dp^{9KTv$Q0_Zd}{l#_|8HuQ|VYTX3O z)Ed}fYjZAqpF(ZA*b~mZ8N<#nxO@%>z#fyh zFDlf1`(%RXV3;zQrR^_d980~udTh_)KD(udfAm`GBNiF^xO?3^HW%1Nq|A-N^!8e& zIx5pyXa149Vd>M`+Us?PYc=Ub4$(ah6_MV>$?YEb_U@(duq-+6gQrYwT1mxjnUQ<4 zReW#p!@*|Ly>)r<#)BSS34Tsp8|U$RM{TKcb<`l^w+9z>x;Xv0PB&~Fb4Z(M+!Uj& z#TC>QcLk}=563NQNDO7Dy(Ki*aE_<;`A~e_yT!ei@(4XP^WP7*cyvG9ec&DR7YDPp Lur)8TX;N literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/lanes_ssr.png b/common/data/src/main/res/drawable/lanes_ssr.png new file mode 100644 index 0000000000000000000000000000000000000000..f9533511231f1bf03263e18aef94b2c0ab00bbd5 GIT binary patch literal 1277 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 literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/left_valid.png b/common/data/src/main/res/drawable/left_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..1cfd533af309780f26f153191ba07775a6b25371 GIT binary patch literal 914 zcmV;D18w|?P)EX>4Tx04R~2kiAO8P!NT`L{aglak7*^7`?FWbA2eA`pc`SYs zZ^v0|*z5*bI*EQX%{8k@esOO4$}K#OImO3e$b>N|HC@$Qog-X!RXs{V?x~1WQxf&2 znyI-FRZ{Xe5cNg6rp0fw@$=;}l*U+E#^H`kn#K?Fzu#*gXU(RyF)P9{Y+7?ldo+Ug zM_3!c`ZFwFw|Fi6ep}0o+syp)jbG{zP-O;E8;3T5bNm5vlEM%q;z__p zf>lz8+6Am^l0s6X38Y9FLi7*tFR-_>k)Q<9h)A#z!6G08NfgC|6xu{Y{iJa9X7={> zc42MgzSX?hH=lX?=DiuIq5CGXOtHclbIz>6V91l9Ic!xn64|fUno+ML=}Cygm86R# zG3J@(D_2xB;e0Prq@64oGI5cE%<~6V8v&mdan8p>g3kX@OQlw5&J&YCeQS!dSxV5@|N|fs+VjPwWN|B zCcg(fmaN|%X3^<4fFHcLNkkHGksTj!i6`-tp8@bXlh+5AEaHCt0&wEnJ*)-5AFks~ znu>8Qfc;7rvKgp}N%~?Im;-*xIacTKfI|m7OF8xp=72i__Vd8OjZ#SGwODb$Z|I3x zw{`TYUoz~0X7W6#XF)2*#H0l07*qoM6N<$f=XSUUjP6A literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/left_valid_right_not_valid.png b/common/data/src/main/res/drawable/left_valid_right_not_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..3b5159ab92707bebf0b35223904e8349867a02c0 GIT binary patch literal 883 zcmV-(1C0EMP)EX>4Tx04R~2kiAO8P!NT`L{aglak7*^7`?FWbA2eA`pc`SYs zZ^v0|*z5*bI*EQX%{8k@esOO4$}K#OImO3e$b>N|HC@$Qog-X!RXs{V?x~1WQxf&2 znyI-FRZ{Xe5cNg6rp0fw@$=;}l*U+E#^H`kn#K?Fzu#*gXU(RyF)P9{Y+7?ldo+Ug zM_3!c`ZFwFw|Fi6ep}0o+syp)jbG{zP-O;E8SlBukLBRR}hMZet^)NGtvUR$3&5zbZu(4O)nhLPQWjq7ag}g&^*=Fo`oS z`!<=Ew}rT;yTh4}^X}X?0|nhN$=?hbp_3#rCM9L8#tp>cpSvPCk#!94h8h5#yQKBO zYB4<|i5Xur^`BU6Ajxa0ti^R%3XIiKUNT5q7T>trkr`Mm;h4&-t|KFSkJOo@06}hk$<{aj6e@wZ(`-@Icr=(&{vFOo@{46)--2EiW-5b3NtTv7> z3AdR$H)2BkPgW0!9=Xb`RaJKD>}ns(cs=VaJ=`*W2As3BbyY(}1?!tp`pNRPj8{L& zvI8WPHbLt3{m;k5*WNqdb_#CTnVutLJr%sTw1lSN9_1z1`iU!W&0!b{FC{A0pG1#Ryj~SHC?^iAdsA zRm#t%jId{~x)zaPJ1ttg*n2@+&cDTr#&*loa!Rd(0(CsA(eB5ysx*Zioz0A`)ox+c z_mdW;RgdW`|8?06#l@^*I<11C3>$gA&N!+)`H0QwCT3p5&qm6BR)^Pax^m%tXX29f z8vn?Ey_I&Le2f?8s(Z#5RWy?^UBt;dN+KJqLqMS^^ z5GQglTLg);0yGlyiqWBqVtmE2m}N2^LUwT`IvV)^K!xHk$*5AQ^?aif*^J8v-zG7Q zOfs9`%bds|B7c%VqeDq7DvJtH+>KZ?gX~NsIqDEOKhVvi7Xpl&$dNd%<egMn&b zQZ+gSjn3ooXb^+OU{HVsMZZFg!$yi)Z)<|+!Ei(MG99MHF^!sJ!i1%oDBOum271z` z_*7bvs1IJP?_~k-K{LWy8l4K!R4Q734?XT44M2JW`cn_R7<3yg5Y=mR2;tJD z2+ItG$oL)_oeBoaiK$=(O4F(pW`&7xzN^2`iOirvpC$fE7?%SFC-Oo}9cBEi5MwHI z5e}Q!q&sjNm~1we&SA4T3>NdVkp$K0K_!|n=@6C4F>6fA!UxF!VqsIA0syldq$WmN27>2PJq>;l10L5 z1qybL-g5mU$NsDoxJ=lA!G$R>;=lwo1Va=lkIkgWr7V=iq(e+Oy{DS`(DfQQZh&>D zs{-%{xB}&A=881iyioRE#$t#>O{)M1qd;`ZXTmtWgwc8vrkVDPPZ>MX{)-bwvq8U2 z2Ke>Jz~%+^LR#Nu*vlD+_pki*uEk$D1&K5;$yf0^K-U0WU&X*z84py~09{|jz*iX$ zRM-EFF5>6w6siWNAOpB8$t@yxfQ!~3sgI`{fp9Rsc z!*QX=efTqrL8FLPgGjn=Aew@^hu{K@%5)+pm=B65LNZ`@B*}D84F2JpNd&@Rg3!%X z>{Zj+=%*A1jj!pr)OulObk-HjXHT44*pUe%h^{QD*C=|!j$_L!hdIo&kv^z6SzCRK zMOSZ9``aeWn>2IBjss_g?v#o~5iQ+51`#tBEWzeoEMi{zSAp+y{~ebTM<`y^U3|A| zNu5(1bxcc~e9tZ>Rx*44&;?#AkUr=lF^9!?Tn@22hz3qBkBcEZY(h%WS4{eh%C#3z>?5J+EUb0>%yW!;>%1=qnD4SzDHe&4ag79m{k}FGNYbGKosgF~e zd4A8f(5bqhYxemG?5R_iQPtrAeN}xE!P*vudAH%@=mtTyli*Z^7PI}(>_c(Zo>(EXR}$Os96p#@FAl~e5+eR%3w|}vWqQ4Bwh-~k|EB0^ zQ3JJo?)tkz6>R^+nYk{b-cD7&2`e}{(LN!hYN+1(eogH2qjAdl+Z|T!uid;pD4p@B zB))cCmrwZB!&c7k+LJ}O^J7PITPDS-uH;HD=fBuxsBm4=l+xV#^3BOhYs&|#^}iT~ z;UlvmItx8l&T#gMuer^7c>L%q!R5&%w|ujM_M;!No@B0{He%dbyHaUj&>4r~aSz*W zY<2#iK5%2Npn;;mUrTfDO-jG=^m2wG$JlORT%) zbz+UwT0OZV-mkJa?Vw|N(fVT@^tFtK8w zZs-oYwJ$U*=-RhAZ(BD=)0Uh&7% cjp{gych=7$T(%S`K*JD(?!In^7c7nc19L%Xr2qf` literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/right_valid.png b/common/data/src/main/res/drawable/right_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7d420133743bd45be6defe4163cce117f437a4 GIT binary patch literal 888 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 literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/slight_right_valid.png b/common/data/src/main/res/drawable/slight_right_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7d420133743bd45be6defe4163cce117f437a4 GIT binary patch literal 888 zcmV-;1Bd*HP)EX>4Tx04R~2kiAO8P!NT`L{aglak7*^7`?FWbA2eA`pc`SYs zZ^v0|*z5*bI*EQX%{8k@esOO4$}K#OImO3e$b>N|HC@$Qog-X!RXs{V?x~1WQxf&2 znyI-FRZ{Xe5cNg6rp0fw@$=;}l*U+E#^H`kn#K?Fzu#*gXU(RyF)P9{Y+7?ldo+Ug zM_3!c`ZFwFw|Fi6ep}0o+syp)jbG{zP-O;E8_2Nw|>go20&ii(4sO3{7}Ep76ercI5D^p_>~ z?(*ew@7+bDt^Xi``02pFz_6_Ek>`{p04j6NT?IorbL4-N%lwnm(pdL0_TU!yrJYug8Le)55U3Q`7Wnnhu zL5AwYC$)TYwKZjn#s;s{R~ldABIxLJVPMcBB0`j7oRlraHNqN|D=xwWSx0D&ah2fQ zFFqMzKu#p$l!L5jceM{XX;@;Cs(}_Q){b`6?71x$fJN?=4=!@Zq{&X9HQ!|h20bLm zD9}fyMbP*z+qXpAG;b-_Ed_c(f7$<8ZUA@V2Av@i%Kl$fyND;?i+T4GY@A&S^qE1E zKS_ag_k%ISjIvp?N<_4kG2YLb1$ye1V1I6dGG(#1z4@2TaATgP?g{qU#$>`|rM<$& zMK@VzUSa!8_c^)t3TvL8FB*S=eQRaY8)IL+!-g%j^$h!KwXtorRnZUNo$d-1KI-iN O0000 + + diff --git a/common/data/src/main/res/drawable/straight_not_valid.png b/common/data/src/main/res/drawable/straight_not_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..f913be16c0f8c1fc239ca4c90f1cb998e7eae0fd GIT binary patch literal 4954 zcmeHKc~leE8jolZf}%XPqO^`+oQP ze)o6pnVTOwDXK4XD3ifp^i@SGX6X!^oS)@`=?EN=#*M*yWz#HtjES9ODGi#Mz&AAf3G|3xpAlI4n! zZ^K8&U71de@`_mq1)aL?8CiDS;N5R;#+e5*+uV=lw<3uix&5%OQtwxtKXF1|Os-vy z-F|5s;<0Ib&|bSokbZsOuv>}GtQAieF^1b^8^YJxSA3cFsJba+t1l;K>n|Ih^Q3$D zxz?JCrH0Y{{Z{;Z@ay+@ISFU$JhjbxKS+mnxK*g3`QJa18_qVj&ub2~@ zQhjl4+;@`eipllQj{p9!_3G(}O5aS*z0%;T4NP9~#CfeFQv-SnH_mTdcDn5ObA8FW z0~?+-4Xa`lCdG=D9?3YzV0cfZfJ~Cq6Q#IO&&CL&mSkt^O+Y^khHPxM3Bxl;8q$(! zltIpVSXsq_C_>Ik6sQrkNkOJl(RMRA#Xd=c+cR(p!5SONlx0f+fS#l=C|jRtut>Az zEGMoMygSSs7UXoHGvur!bu6SXnn{Sy=CctvBAd!WS)oiwW+rsfIAvrz1Zc@w=`?MU zayT}djcwzxjpj5CS0a&c5R`+WFz|pavkWwr4I3;04hR>9lCl} zXR$y(^eR8SNv-aHH(1(P0DN$=F%yT&MmTysr*njbj>rNa?Fs#5ghc~Z8z+vm7_DZU zjL0GlbUNcpoq#Xs1QBDVfGV-f&RIF22mqx+gj^AV5wI53k}x0Biea%1NQB471p-pY z!v%=Y2}R)2NTXSgf$605SQ^PO8Pc2`4#K5ju_`$WWh1XGv6&dH0|9c@M9N^ze%+y= z^yCy8bFj%32}L}26Bg7W zRhUVPHkvg?W2T(tfPfs49o=fMoCu7@lo(9{Py`i95v~+LHC(O~{Q2S#N3)Z^k)U+5 z{+qXB@jx=ylt)t*F#as3$+e=UkTYE^S2L4xE+q(ZE($4zyGpQNStQ}i6JWWz@N~?O zMuOd=U9PX})L%$JtR*D4PN#!WA%Oz{hzS_e5quyi3@7*!Emw?*yP{i+I@*Sr$*?rQ zBj5_i)5#SSTfZRmS;`X?Hnv+D8F3E*sdN!FJE- z*zemp1NpwqU%TAi<`59nJ;)pJ+f7$DU2nv|8##BYtDCMjV&ILOyVdo7ql@|a#7G*z zhn@|b2h*m>J^^P*H|>NdC4;ejSpW)Jo~GyzEewX&0LRsXQB?dcF!rWZ>WJQVd$=*Z zkiaYcb-?6HM?&Rmw0;^y&KzM+|=C!0MJ8 z-xpry4SV=oz)ZJ%A9GNjXrF>umSLV-5hHQjxxP@uVt3DoQiu-z{1dnF1@u9;y#8jN zrOnG0^H-s2IMl~R&camky>=yt2Ga6e9 z#IsHIQ-|&yZ8;)3Ibmyf-l3S;X{YDEw{an{l3kSBc5!*~4DY~3)c+sb7kfOEYOWx{ z@SpTIB3noG^1L_w@;8}5tCJe&%V&3^dzY+et6j7D@Tdl#(Y5yr4iCtYEm*Q5@5`wV z&TLrYyO_+Clts?*@UOec;vd=isB+eXWTrJIO*y!E$%r`xb3(rwl5y8t;P-L*)TrPm z%14?nDt?YkFU~u#8D4YGKKe7J_o}DO*tg}+L#3YQQ54;Mr>G>QIPtRe=Jx9=twUZu zyLp4R|6jRymGb1HgL=+3&+6Y+Z(5Jl#jihFVy!?$&C-mppQ6tVvX#F0u$HJ@DJcHb zz9S&M?a7bxsEmeHHG>M@S(Px-b7ZBsLfcsSd2mBZ;uEr;d*q_z@i7By@3P~np4I1` zNviL;?=HJqxclL!^Ha*bE~WI*CtDMC7~-!bAMn%1TU&~fDAo2|KU~`RZlLybKu>vq zWrna~l)Gp77bpCyneug-su^SOzWGIQrDXbd;g_<6^e;niZ>ayQEaG{*52x&4koCvn zL$l+SQi}^qE{q5Zyu4AC{CA^f%rpxz;R6eil2x=mxpk4eXH<#({L^1|U3j_e5BKFe Wk4=31{m^nCG=?f-l5%HwYVMyyK^YkU literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/straight_not_valid_right_valid.png b/common/data/src/main/res/drawable/straight_not_valid_right_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..70188f190e8adae33d331c381ca58ae46e062bbd GIT binary patch literal 895 zcmV-_1AzRAP)EX>4Tx04R~2kiAO8P!NT`L{aglak7*^7`?FWbA2eA`pc`SYs zZ^v0|*z5*bI*EQX%{8k@esOO4$}K#OImO3e$b>N|HC@$Qog-X!RXs{V?x~1WQxf&2 znyI-FRZ{Xe5cNg6rp0fw@$=;}l*U+E#^H`kn#K?Fzu#*gXU(RyF)P9{Y+7?ldo+Ug zM_3!c`ZFwFw|Fi6ep}0o+syp)jbG{zP-O;E8U+s%lb7s8*tzWc&(8jsoqaQJA+-O@#LKA(BOq(|A8?m-GQ5=E{@6oKNt~yLaTfWk z^Li#Y?Bm*XfO{15wO-Yu0bpf~cSiXxv%pte{?=wJz$zM%t>~?V2s8!p*uw7e-4K1_-`wN9v Vc4xp+Gm-!R002ovPDHLkV1kefmr4Kt literal 0 HcmV?d00001 diff --git a/common/data/src/main/res/drawable/straight_valid.png b/common/data/src/main/res/drawable/straight_valid.png new file mode 100644 index 0000000000000000000000000000000000000000..58d73fb05408861292a889595a06a8fd5a17b2a7 GIT binary patch literal 723 zcmV;^0xbQBP)EX>4Tx04R~2kg-a{P!L5=qPQa3s91dNfsBA7?LcD zf8d|!$7~@8euSlhwf98}3)46-%wd?hciscrNu#Wq--j%(OE2gRE(cdm=M4*+4y){h zQC0MO|Af}~eATq#rE9d>=l+a^IH@AlnKF-xvIc8U>`v>#u#)xLesCy$5Ib>}$KogP zcAUkA&2EsTljuj&T(g|y7w4w0+`{9SQ+ynTOc;|=(^bvYIl^UE)uSZjo{C5{B~fpx znVK6>B_)poQD3BMTKqN}KVL3GX^f?19PY@ZY5Xw%`@QyY)@)iEvmz|RrZuOuMF7T1pmALcTi?PSe>;*q@?V}|Edv*t;0FdD#&<-z;y(++6bu6x7z7w55bu!&24$?`CjT>tGUC4% zLmZM7%nmX`#*z06iIv=Reject OK Search + Valhalla + Osrm + Routing engine \ No newline at end of file