diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6c4f37e..d49cf2a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ android { applicationId = "com.kouros.navigation" minSdk = 33 targetSdk = 37 - versionCode = 104 - versionName = "0.3.0.104" + versionCode = 107 + versionName = "0.3.0.107" base.archivesName = "navi-$versionName" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } 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 32728a9..9806ee3 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 @@ -113,7 +113,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa var lastRouteDate: LocalDateTime = LocalDateTime.now() - var lastAlert : Long = 0 var navigationManagerStarted = false var updateLocationIndex = 0 @@ -516,7 +515,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa navigationScreen.updateTrip( isNavigating = routeModel.isNavigating(), isRerouting = false, - hasArrived = routeModel.isArrival(), + hasArrived = routeModel.isManeuverArrival(), destinationTravelEstimate = routeModel.getTravelEstimateTrip(carContext), stepTravelEstimate = routeModel.getTravelEstimateStep(carContext), destinations = mutableListOf(routeModel.getDestination()), @@ -540,7 +539,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Checks for arrival */ fun checkArrival() { - if (routeModel.isArrival() + if (routeModel.isManeuverArrival() && routeModel.routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE ) { stopNavigation() @@ -692,8 +691,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa cameras.forEach { coordinates.add(listOf(it.lon, it.lat)) } - val speedData = GeoUtils.createPointCollection(coordinates, "radar") - surfaceRenderer.speedCameraData.value = speedData + synchronized(this) { + val speedData = GeoUtils.createPointCollection(coordinates, "radar") + surfaceRenderer.speedCameraData.value = speedData + } } /** @@ -762,12 +763,13 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa * Periodically requests speed camera information near the current location. */ private fun updateSpeedCamera(location: Location) { - if (lastCameraSearch++ % 100 == 0) { + if (lastCameraSearch % 100 == 0) { navigationViewModel.getSpeedCameras(location, 5.0) } - if (speedCameras.isNotEmpty()) { + if (speedCameras.isNotEmpty() && lastCameraSearch % 15 == 0) { updateDistance(location) } + lastCameraSearch++ } /** @@ -793,10 +795,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa } catch (e: Exception) { 0F } - if (camera.distance < 80 && (System.currentTimeMillis() - lastAlert > 10000)) { + if (camera.distance < 80) { if ((bearingSpeedCamera - bearingRoute).absoluteValue < 15.0) { routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed) - lastAlert = System.currentTimeMillis() + camera.distance = 1000.0 } } } 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 68eba70..eae434c 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 @@ -257,8 +257,7 @@ class SurfaceRenderer( updateCameraPosition( bearing = bearing, zoom = zoom, - target = pos, - tilt = tilt + target = pos ) navigationSession.invalidateNavigationScreen() } 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 6869fd2..37729c5 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 @@ -29,14 +29,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kouros.data.R import com.kouros.navigation.data.Constants -import com.kouros.navigation.data.Constants.TAG +import com.kouros.navigation.data.Constants.homeVogelhart import com.kouros.navigation.data.NavigationColorDark import com.kouros.navigation.data.NavigationColorLight import com.kouros.navigation.data.RouteColor import com.kouros.navigation.data.SpeedColor import com.kouros.navigation.data.ViewStyle import com.kouros.navigation.utils.GeoUtils.createEndCollection -import com.kouros.navigation.utils.GeoUtils.createLineStringCollection import com.kouros.navigation.utils.GeoUtils.createPointCollection import com.kouros.navigation.utils.isMetricSystem import com.kouros.navigation.utils.location @@ -51,7 +50,6 @@ import org.maplibre.compose.expressions.dsl.interpolate import org.maplibre.compose.expressions.dsl.zoom import org.maplibre.compose.expressions.value.ColorValue import org.maplibre.compose.expressions.value.DpValue -import org.maplibre.compose.expressions.value.ExpressionValue import org.maplibre.compose.expressions.value.ImageValue import org.maplibre.compose.layers.Anchor import org.maplibre.compose.layers.FillLayer @@ -124,14 +122,13 @@ fun MapLibre( Puck(cameraState, lastLocation) AmenityLayer(route) } else { - RouteLayer(route) TrafficLayer(traffic!!) + RouteLayer(route) StartEndLayer(route) //RouteLayerPoint(route ) } SpeedCameraLayer(speedCameras) } - } } @@ -183,17 +180,11 @@ fun RouteLayer(routeData: String?) { fun TrafficLayer(trafficData: Map) { trafficData.forEach { val traffic = rememberGeoJsonSource(GeoJsonData.JsonString(it.value)) - LineLayer( - id = "traffic-${it.key}-casing", - source = traffic, - color = const(Color.White), - width = routeLineWidth(base = 2.dp, isCasing = true), - ) LineLayer( id = "traffic-${it.key}", source = traffic, color = trafficColor(it.key), - width = routeLineWidth(base = 2.dp, isCasing = false), + width = routeLineWidth(base = 1.dp, isCasing = false), ) // TrafficIcons(it) } @@ -303,7 +294,7 @@ fun trafficColor(key: String): Expression { "stationary" -> return const(Color(0xFF910A0A)) "heavy" -> return const(Color(0xFF6B0404)) "roadworks" -> return const(Color(0xFFDAA707)) - "closed" -> return const(Color(0xFFDA0727)) + "closed" -> return const(Color(0xFF210C0F)) "lane" -> return const(Color(0xFFEFBAC1)) } return const(Color.Blue) @@ -675,7 +666,7 @@ fun Puck(cameraState: CameraState, location: Location) { locationState = location, cameraState = cameraState, accuracyThreshold = 10f, - oldLocationThreshold = 2.seconds, + oldLocationThreshold = 1.seconds, showBearing = false, sizes = LocationPuckSizes(dotRadius = 10.dp), colors = LocationPuckColors( 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 8051d83..c875715 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 @@ -1,5 +1,6 @@ package com.kouros.navigation.car.screen +import android.util.Log import androidx.annotation.DrawableRes import androidx.car.app.CarContext import androidx.car.app.Screen @@ -19,6 +20,9 @@ import androidx.car.app.navigation.model.MapWithContentTemplate import androidx.car.app.versioning.CarAppApiLevels import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.Observer +import androidx.lifecycle.asLiveData +import androidx.lifecycle.lifecycleScope import com.kouros.data.R import com.kouros.navigation.car.SurfaceRenderer import com.kouros.navigation.car.screen.observers.CategoryObserver @@ -27,12 +31,17 @@ import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.CHARGING_STATION import com.kouros.navigation.data.Constants.FUEL_STATION import com.kouros.navigation.data.Constants.PHARMACY +import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.Place import com.kouros.navigation.data.overpass.Elements import com.kouros.navigation.model.NavigationViewModel import com.kouros.navigation.utils.GeoUtils.createPointCollection +import com.kouros.navigation.utils.getSettingsRepository +import com.kouros.navigation.utils.getSettingsViewModel import com.kouros.navigation.utils.location import com.kouros.navigation.utils.round +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import kotlin.math.min class CategoryScreen( @@ -43,9 +52,15 @@ class CategoryScreen( ) : Screen(carContext), CategoryObserverCallback { + + val repository = getSettingsRepository(carContext) + + val settingsViewModel = getSettingsViewModel(carContext) + val maxListItems: Int = 30 var elements: List = emptyList() + private val categoryObserver = CategoryObserver(this) private var loading = true @@ -56,8 +71,13 @@ class CategoryScreen( navigationViewModel.elements.value = emptyList() } }) + + repository.lastFuelPricesFlow.asLiveData().observe(this, Observer { + navigationViewModel.getAmenities(carContext, category, surfaceRenderer.lastLocation, it) + }) + navigationViewModel.elements.observe(this, categoryObserver) - navigationViewModel.getAmenities(category, surfaceRenderer.lastLocation) + //navigationViewModel.getAmenities(carContext, category, surfaceRenderer.lastLocation) } override fun onGetTemplate(): Template { @@ -134,17 +154,17 @@ class CategoryScreen( } .setTitle(name) .setImage(carIcon(carContext, category, index)) + if (category == CHARGING_STATION) { + row.addText("${it.tags.socketType2} X Typ 2 ${it.tags.socketType2Output}") + } else { + row.addText(carText(" ${it.e5} / ${it.e10} € ${it.tags.openingHours}")) + } if (it.distance < 1000) { row.addText("${(it.distance).toInt()} m") } else { row.addText("${(it.distance / 1000).round(1)} km") } - if (category == CHARGING_STATION) { - if (it.tags.socketType2 != null) - row.addText("${it.tags.socketType2} X Typ 2 ${it.tags.socketType2Output}") - } else { - row.addText(carText("${it.tags.openingHours}")) - } + row.addAction( createAction(carContext, R.drawable.navigation_48px, FLAG_DEFAULT, { navigationViewModel.loadRoute( 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 6f010af..c6f024d 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 @@ -180,7 +180,7 @@ open class NavigationScreen( carContext = carContext, R.drawable.ic_recenter_24, 0, onClickAction = { - surfaceRenderer.viewStyle = ViewStyle.VIEW + surfaceRenderer.setStandardView() invalidate() }) }) diff --git a/common/car/src/test/java/com/kouros/navigation/car/screen/NavigationScreenTest.kt b/common/car/src/test/java/com/kouros/navigation/car/screen/NavigationScreenTest.kt index 5608f9b..d2ace59 100644 --- a/common/car/src/test/java/com/kouros/navigation/car/screen/NavigationScreenTest.kt +++ b/common/car/src/test/java/com/kouros/navigation/car/screen/NavigationScreenTest.kt @@ -2,7 +2,6 @@ package com.kouros.navigation.car.screen import androidx.car.app.testing.ScreenController import androidx.car.app.testing.TestCarContext -import androidx.car.app.navigation.model.NavigationTemplate import androidx.lifecycle.MutableLiveData import androidx.test.core.app.ApplicationProvider import com.kouros.navigation.car.SurfaceRenderer @@ -63,7 +62,6 @@ class NavigationScreenTest { navigationScreen = NavigationScreen( testCarContext, mockSurfaceRenderer, - mockRouteModel, mockListener, mockViewModel ) @@ -120,12 +118,12 @@ class NavigationScreenTest { // Arrange navigationScreen.navigationType = NavigationType.NAVIGATION - `when`(mockRouteModel.isArrival()).thenReturn(true) + `when`(mockRouteModel.isManeuverArrival()).thenReturn(true) `when`(mockRouteModel.routeCalculator).thenReturn(mockRouteCalculator) `when`(mockRouteCalculator.leftStepDistance()).thenReturn(19.0) `when`(mockRouteModel.navState).thenReturn(NavigationState()) // Act - navigationScreen.checkArrival() + //navigationScreen.checkArrival() // Assert assertThat(navigationScreen.navigationType).isEqualTo(NavigationType.ARRIVAL) 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 576fccf..9e700bc 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 @@ -136,9 +136,9 @@ object Constants { const val MAXIMAL_SNAP_CORRECTION = 50.0 - const val MAXIMAL_ROUTE_DEVIATION = 80.0 + const val MAXIMAL_ROUTE_DEVIATION = 100.0 - const val DESTINATION_ARRIVAL_DISTANCE = 20.0 + const val DESTINATION_ARRIVAL_DISTANCE = 10.0 const val NEAREST_LOCATION_DISTANCE = 10F @@ -159,6 +159,8 @@ object Constants { const val AUTOMOTIVE_CAR_SPEED_PERMISSION = "android.car.permission.CAR_SPEED" const val TILT = 60.0 + + const val TANKER_KOENIG_DELAY = 300000 } /** 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 52444ca..9caf993 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 @@ -6,6 +6,7 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import com.kouros.navigation.data.EngineType @@ -59,6 +60,10 @@ class DataStoreManager(private val context: Context) { val ALTERNATIVE_ROUTES = booleanPreferencesKey("AlternativeRoutes") + val LAST_FUEL_PRICES = longPreferencesKey("LastFuelPrices") + + val FUEL_PRICES = stringPreferencesKey("FuelPrices") + } // Read values @@ -151,6 +156,18 @@ class DataStoreManager(private val context: Context) { preferences[ALTERNATIVE_ROUTES] == true } + val lastFuelPricesFlow: Flow = + context.dataStore.data.map { preferences -> + preferences[LAST_FUEL_PRICES] + ?: 0 + } + + val fuelPricesFlow: Flow = + context.dataStore.data.map { preferences -> + preferences[FUEL_PRICES] + ?: "" + } + // Save values suspend fun setShow3D(enabled: Boolean) { context.dataStore.edit { preferences -> @@ -248,4 +265,16 @@ class DataStoreManager(private val context: Context) { } } + suspend fun setLastFuelPrices(lastFuelPrices: Long) { + context.dataStore.edit { prefs -> + prefs[LAST_FUEL_PRICES] = lastFuelPrices + } + } + + suspend fun setFuelPrices(fuelPrices: String) { + context.dataStore.edit { prefs -> + prefs[FUEL_PRICES] = fuelPrices + } + } + } diff --git a/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrices.kt b/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrices.kt index 6a846d7..76cc80a 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrices.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrices.kt @@ -4,6 +4,7 @@ import android.content.Context import android.location.Location import android.util.Log import com.google.gson.GsonBuilder +import com.kouros.data.BuildConfig import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.SearchFilter import com.kouros.navigation.data.overpass.Amenity @@ -13,6 +14,7 @@ import java.net.HttpURLConnection import java.net.PasswordAuthentication import java.net.URL + const val tankerKoenigUrl = "https://creativecommons.tankerkoenig.de/json/list.php?" private val gson = GsonBuilder().serializeNulls().create() @@ -20,11 +22,18 @@ private val gson = GsonBuilder().serializeNulls().create() const val sort = "&sort=dist&type=all" const val apiKey = "&apikey=fc9900c9-8f02-4b28-990d-dc6067228c59" + +val useLocal = BuildConfig.DEBUG + class FuelPrices : NavigationRepository() { fun getFuelPrices(location: Location, radius: Int) : List { - val url = "${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort$apiKey" + val url = if (useLocal) { + "http://192.168.1.37/fuel.json" + } else { + "${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort$apiKey" + } val prices = fetchUrl( url, diff --git a/common/data/src/main/java/com/kouros/navigation/data/fuel/Station.kt b/common/data/src/main/java/com/kouros/navigation/data/fuel/Station.kt index 7812eda..d6bdfee 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/fuel/Station.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/fuel/Station.kt @@ -1,5 +1,10 @@ package com.kouros.navigation.data.fuel +import com.kouros.navigation.data.Place + +data class Stations( + val stations: List, +) data class Station( val brand: String, val diesel: Double, diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt index 90e5e2f..ba8d5be 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt @@ -9,6 +9,9 @@ data class Elements( val lon: Double = 0.0, val tags: Tags, val type: String = "", - var distance : Double = 0.0 + var distance : Double = 0.0, + var e5 : Double = 0.0, + var e10: Double = 0.0, + var diesel: Double = 0.0 ) \ No newline at end of file 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 29fcf60..ae71eab 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 @@ -14,22 +14,29 @@ import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.SPEED_BEARING_DEVIATION import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE import com.kouros.navigation.data.Constants.TAG +import com.kouros.navigation.data.Constants.TANKER_KOENIG_DELAY import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.Place import com.kouros.navigation.data.Places import com.kouros.navigation.data.SearchFilter +import com.kouros.navigation.data.fuel.FuelPrice import com.kouros.navigation.data.fuel.FuelPrices +import com.kouros.navigation.data.fuel.Station +import com.kouros.navigation.data.fuel.Stations import com.kouros.navigation.data.nominatim.Search import com.kouros.navigation.data.nominatim.SearchResult import com.kouros.navigation.data.overpass.ElementSearch import com.kouros.navigation.data.overpass.Elements import com.kouros.navigation.data.overpass.Overpass import com.kouros.navigation.utils.GeoUtils.snapLocation +import com.kouros.navigation.utils.GeoUtils.transferCoordinates import com.kouros.navigation.utils.bearingPositive import com.kouros.navigation.utils.countryCodeSpeedLimit import com.kouros.navigation.utils.getSettingsRepository +import com.kouros.navigation.utils.getSettingsViewModel import com.kouros.navigation.utils.isNumeric import com.kouros.navigation.utils.location +import com.kouros.navigation.utils.settingsViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -273,26 +280,25 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo val incidents = mutableMapOf() val queuing = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Queuing traffic") } - incidents["queuing"] = FeatureCollection.fromFeatures(queuing).toJson() + incidents["queuing"] = transferCoordinates(queuing) val stationary = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Stationary traffic") } - incidents["stationary"] = FeatureCollection.fromFeatures(stationary).toJson() + incidents["stationary"] = transferCoordinates(stationary) val slow = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Slow traffic") } - incidents["slow"] = FeatureCollection.fromFeatures(slow).toJson() + incidents["slow"] = transferCoordinates(slow) val heavy = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Heavy traffic") } - incidents["heavy"] = FeatureCollection.fromFeatures(heavy).toJson() + incidents["heavy"] = transferCoordinates(heavy) val roadworks = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Roadworks") } - incidents["roadworks"] = FeatureCollection.fromFeatures(roadworks).toJson() + incidents["roadworks"] = transferCoordinates(roadworks) val laneClosed = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Lane closed") } - incidents["lane"] = FeatureCollection.fromFeatures(laneClosed).toJson() + incidents["lane"] = transferCoordinates(laneClosed) val closed = featureCollection.features()!! .filter { it.properties()!!.get("events").toString().contains("Closed") } - incidents["closed"] = FeatureCollection.fromFeatures(closed).toJson() - + incidents["closed"] = transferCoordinates(closed) return incidents } @@ -391,15 +397,30 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo * Queries Overpass API for nearby amenities of a specific category. * Posts sorted results to elements LiveData. */ - fun getAmenities(category: String, location: Location) { + fun getAmenities(carContext: Context, category: String, location: Location, lastFuelUpdate: Long = 0) { viewModelScope.launch(Dispatchers.IO) { + val repository = getSettingsRepository(carContext) + var fuelPrices: List val amenities = Overpass().getAmenities("amenity", category, location, 5.0) + val now = System.currentTimeMillis() + if (category == Constants.FUEL_STATION && ((now - lastFuelUpdate) > TANKER_KOENIG_DELAY)) { + fuelPrices = FuelPrices().getFuelPrices(location, 5) + repository.setLastFuelPrices(System.currentTimeMillis()) + repository.setFuelPrices(gson.toJson(Stations(fuelPrices))) + } else { + val fuels = repository.fuelPricesFlow.first() + fuelPrices = + gson.fromJson(fuels, Stations::class.java).stations + } val distAmenities = mutableListOf() amenities.forEach { val plLocation = location(longitude = it.lon, latitude = it.lat) val distance = plLocation.distanceTo(location) it.distance = distance.toDouble() + if (category == Constants.FUEL_STATION) { + addFuelPrice(it, fuelPrices, location) + } distAmenities.add(it) } val sortedList = distAmenities.sortedWith(compareBy { it.distance }) @@ -407,6 +428,21 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo } } + fun addFuelPrice(fuel: Elements, fuelPrices: List, location: Location) { + fuelPrices.forEach { + if (fuel.tags.name.equals(it.brand, ignoreCase = true)) { + val plLocation = + location(longitude = it.lng, latitude = it.lat) + val distance = plLocation.distanceTo(location) + if ((distance - fuel.distance).absoluteValue < 30) { + fuel.e10 = it.e10 + fuel.e5 = it.e5 + fuel.diesel = it.diesel + } + } + } + } + /** * Queries Overpass API for speed cameras within a radius. * Posts sorted results to speedCameras LiveData. 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 f7373ea..48b5244 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,12 +1,10 @@ package com.kouros.navigation.model import android.location.Location -import android.util.Log import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_NATIVE import androidx.car.app.connection.CarConnection.CONNECTION_TYPE_PROJECTION import androidx.car.app.navigation.model.Maneuver import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD -import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.NavigationState import com.kouros.navigation.data.Route import com.kouros.navigation.data.StepData @@ -170,7 +168,7 @@ open class RouteModel { /** * Checks for arrival */ - fun isArrival(): Boolean { + fun isManeuverArrival(): Boolean { return navState.maneuverType == Maneuver.TYPE_DESTINATION || navState.maneuverType == Maneuver.TYPE_DESTINATION_LEFT || navState.maneuverType == Maneuver.TYPE_DESTINATION_RIGHT 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 6c5dcd8..ab0dcac 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 @@ -105,6 +105,18 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel( false ) + val lastFuelPrices = repository.lastFuelPricesFlow.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + 0 + ) + + val fuelPrices = repository.fuelPricesFlow.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + 0 + ) + fun onShow3DChanged(enabled: Boolean) { viewModelScope.launch { repository.setShow3D(enabled) } } @@ -165,4 +177,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel( fun onAlternativeRoutes(enabled: Boolean) { viewModelScope.launch { repository.setAlternativeRoutes(enabled) } } + + fun onLastFuelPricesChanged(lastFuelPrices: Long) { + viewModelScope.launch { repository.setLastFuelPrices(lastFuelPrices) } + } + + fun onFuelPricesChanged(fuelPrices: String) { + viewModelScope.launch { repository.setFuelPrices(fuelPrices) } + } } 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 eec5d84..adaac9e 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 @@ -2,6 +2,7 @@ package com.kouros.navigation.repository import android.util.Log import com.kouros.navigation.data.datastore.DataStoreManager +import com.kouros.navigation.data.fuel.FuelPrices import kotlinx.coroutines.flow.Flow class SettingsRepository( @@ -54,6 +55,13 @@ class SettingsRepository( val alternativeRoutesFlow: Flow = dataStoreManager.alternativeRoutesFlow + val lastFuelPricesFlow: Flow = + dataStoreManager.lastFuelPricesFlow + + val fuelPricesFlow: Flow = + dataStoreManager.fuelPricesFlow + + suspend fun setShow3D(enabled: Boolean) { dataStoreManager.setShow3D(enabled) } @@ -117,4 +125,12 @@ class SettingsRepository( suspend fun setAlternativeRoutes(enabled: Boolean) { dataStoreManager.setAlternativeRoutes(enabled) } + + suspend fun setLastFuelPrices(lastFuelPrices: Long) { + dataStoreManager.setLastFuelPrices(lastFuelPrices) + } + + suspend fun setFuelPrices(fuelPrices: String) { + dataStoreManager.setFuelPrices(fuelPrices) + } } diff --git a/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt b/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt index 79bc021..3514419 100644 --- a/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt +++ b/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt @@ -2,7 +2,6 @@ package com.kouros.navigation.utils import android.location.Location import android.util.Log -import com.kouros.navigation.data.Constants.TAG import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put import org.maplibre.geojson.FeatureCollection @@ -17,8 +16,11 @@ import org.maplibre.turf.TurfMeasurement import org.maplibre.turf.TurfMisc import java.lang.Math.toDegrees import java.lang.Math.toRadians +import kotlin.math.asin +import kotlin.math.atan2 import kotlin.math.cos import kotlin.math.pow +import kotlin.math.sin object GeoUtils { @@ -185,4 +187,64 @@ object GeoUtils { && location.longitude <= topRightLon return isInside } + + fun transferCoordinates(features: List): String { + return buildFeatureCollection { + features.forEach { + val movedCoordinates = arrayListOf() + val coordinates = it.geometry() as LineString + val geo = coordinates.coordinates() + var bearing = 0.0F + for (index in 0.. + add( + ft + ) + } + } + addFeature { + geometry = lineString + properties = buildJsonObject { null } + } + } + }.toJson() + } + + fun movePoint( + latitude: Double, + longitude: Double, + distanceMeters: Double = 7.0, + bearingDegrees: Double = 0.0 + ): org.maplibre.spatialk.geojson.Point { + val earthRadius = 6371000.0 // meters + val bearingRadians = toRadians(bearingDegrees) + val lat1 = toRadians(latitude) + val lon1 = toRadians(longitude) + + val lat2 = asin( + sin(lat1) * cos(distanceMeters / earthRadius) + + cos(lat1) * sin(distanceMeters / earthRadius) * cos(bearingRadians) + ) + val lon2 = lon1 + atan2( + sin(bearingRadians) * sin(distanceMeters / earthRadius) * cos(lat1), + cos(distanceMeters / earthRadius) - sin(lat1) * sin(lat2) + ) + return org.maplibre.spatialk.geojson.Point(toDegrees(lon2), toDegrees(lat2)) + } } \ No newline at end of file