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 58b9e6b..c964817 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 @@ -273,6 +273,10 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa routeModel = RouteCarModel() CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated) + + navigationViewModel.initialSnapLocation.observe(this, Observer { + surfaceRenderer.updateLocation(it, "") + }) } /** @@ -315,6 +319,7 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa shouldUseCarLocationFlow = carSensorManager.shouldUseCarLocation(), onLocationUpdate = ::updateLocation, onInitialLocation = { location -> + navigationViewModel.loadCurrentLocation(location) navigationViewModel.loadRecentPlaces( carContext, location, @@ -753,12 +758,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa lastTrafficDate = current navigationViewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation) } - if (updateLocationIndex % 4 == 0) { -// navigationViewModel.updateTrafficMessages( -// surfaceRenderer.lastLocation, -// surfaceRenderer.trafficData.value!! -// ) - } } /** diff --git a/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrice.kt b/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrice.kt new file mode 100644 index 0000000..4de1a8e --- /dev/null +++ b/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrice.kt @@ -0,0 +1,9 @@ +package com.kouros.navigation.data.fuel + +data class FuelPrice( + val `data`: String, + val license: String, + val ok: Boolean, + val stations: List, + val status: String +) \ No newline at end of file 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 new file mode 100644 index 0000000..6a846d7 --- /dev/null +++ b/common/data/src/main/java/com/kouros/navigation/data/fuel/FuelPrices.kt @@ -0,0 +1,53 @@ +package com.kouros.navigation.data.fuel + +import android.content.Context +import android.location.Location +import android.util.Log +import com.google.gson.GsonBuilder +import com.kouros.navigation.data.NavigationRepository +import com.kouros.navigation.data.SearchFilter +import com.kouros.navigation.data.overpass.Amenity +import java.io.OutputStreamWriter +import java.net.Authenticator +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() + +const val sort = "&sort=dist&type=all" + +const val apiKey = "&apikey=fc9900c9-8f02-4b28-990d-dc6067228c59" +class FuelPrices : NavigationRepository() { + + fun getFuelPrices(location: Location, radius: Int) : List { + + val url = "${tankerKoenigUrl}lat=${location.latitude}&lng=${location.longitude}&rad=${radius}$sort$apiKey" + + val prices = fetchUrl( + url, + false + ) + return gson.fromJson(prices, FuelPrice::class.java).stations + } + + override fun getRoute( + context: Context, + currentLocation: Location, + location: List, + carOrientation: Float, + searchFilter: SearchFilter + ): String { + TODO("Not yet implemented") + } + + override fun getTraffic( + context: Context, + location: Location, + carOrientation: Float + ): String { + TODO("Not yet implemented") + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..7812eda --- /dev/null +++ b/common/data/src/main/java/com/kouros/navigation/data/fuel/Station.kt @@ -0,0 +1,18 @@ +package com.kouros.navigation.data.fuel + +data class Station( + val brand: String, + val diesel: Double, + val dist: Double, + val e10: Double, + val e5: Double, + val houseNumber: String, + val id: String, + val isOpen: Boolean, + val lat: Double, + val lng: Double, + val name: String, + val place: String, + val postCode: Int, + val street: String +) \ No newline at end of file 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 ddd01ef..fd3e80e 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 @@ -4,7 +4,6 @@ import android.location.Location import android.util.Log import com.google.gson.GsonBuilder import com.kouros.data.BuildConfig -import com.kouros.navigation.data.ApplicationConfig import com.kouros.navigation.utils.GeoUtils.getBoundingBox import java.io.OutputStreamWriter import java.net.Authenticator @@ -69,6 +68,28 @@ class Overpass { } + fun getStreet(location: Location): List { + + val lineString = "${location.latitude},${location.longitude}" + val searchLocation ="way[\"highway\"~\"^(primary|secondary|tertiary|residential|motorway)$\"][name](around:50, $lineString)"; + + val searchQuery = """ + |[out:json]; + |( + | ${searchLocation}; + |); + |out body geom; + """.trimMargin() + + val connection = (URL(overpassUrl).openConnection() as HttpURLConnection).apply { + requestMethod = "POST" + setRequestProperty("Accept", "application/json") + doOutput = true + } + + return overpassApi(connection, searchQuery) + + } fun getAmenities( type: String, category: String, 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 84867cd..29fcf60 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 @@ -10,7 +10,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.kouros.navigation.data.ApplicationConfig import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants.SPEED_BEARING_DEVIATION import com.kouros.navigation.data.Constants.SPEED_UPDATE_DISTANCE @@ -19,11 +18,13 @@ 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.FuelPrices 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.bearingPositive import com.kouros.navigation.utils.countryCodeSpeedLimit import com.kouros.navigation.utils.getSettingsRepository @@ -37,7 +38,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.maplibre.geojson.FeatureCollection -import org.maplibre.geojson.LineString +import org.maplibre.geojson.Point import java.time.LocalDateTime import java.time.ZoneOffset import kotlin.collections.first @@ -125,6 +126,10 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo MutableLiveData() } + val initialSnapLocation: MutableLiveData by lazy { + MutableLiveData() + } + val gson: Gson = GsonBuilder().create() /** @@ -528,6 +533,21 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo } } + /** + * Queries Tankerkoenig API for fuel prices on current road. + * + */ + fun updateFuelPrices( + location: Location, + ) { + viewModelScope.launch(Dispatchers.IO) { + synchronized(this) { + val stations = + FuelPrices().getFuelPrices(location, 3) + Log.d(TAG, "FuelPrices $stations") + } + } + } /** * Saves a place as a favorite in Preferences. @@ -652,4 +672,31 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo } return pl.toMutableStateList() } + + /** + * Loads current location nearest street + * and snap to location to street + */ + fun loadCurrentLocation(location: Location) { + viewModelScope.launch(Dispatchers.IO) { + synchronized(this) { + val elements = Overpass().getStreet(location) + if (elements.isNotEmpty()) { + val points = mutableListOf() + elements.first().geometry.forEach { + points.add(Point.fromLngLat(it.lon, it.lat)) + } + val snappedLocation = snapLocation(location, points) + if (points.size > 1) { + val bearing = location( + points.first().longitude(), + points.first().latitude() + ).bearingPositive(location(points[1].longitude(), points[1].latitude())) + snappedLocation.bearing = bearing + } + initialSnapLocation.postValue(snappedLocation) + } + } + } + } } 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 ad662be..22ca404 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 @@ -151,7 +151,8 @@ fun duration( lastLocationUpdate: LocalDateTime ): Duration { if (viewStyle == ViewStyle.PREVIEW || - viewStyle == ViewStyle.PAN_VIEW) { + viewStyle == ViewStyle.PAN_VIEW || + viewStyle == ViewStyle.AMENITY_VIEW) { return 100.milliseconds } val cameraDuration = if ((lastBearing - bearing).absoluteValue > 20.0) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8bdd2d8..ef444ef 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,12 +8,12 @@ koinAndroid = "4.2.1" koinAndroidxCompose = "4.2.1" koinComposeViewmodel = "4.2.1" koinCore = "4.2.1" -kotlin = "2.3.20" +kotlin = "2.3.21" coreKtx = "1.18.0" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" -kotlinxSerializationJson = "1.10.0" +kotlinxSerializationJson = "1.11.0" lifecycleRuntimeKtx = "2.10.0" composeBom = "2026.04.01" appcompat = "1.7.1"