MapView, Navigation to RecentPlace

This commit is contained in:
Dimitris
2025-12-01 19:45:17 +01:00
parent da209a4354
commit cddb193260
16 changed files with 346 additions and 154 deletions

View File

@@ -10,7 +10,7 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.kouros.navigation" applicationId = "com.kouros.navigation"
minSdk = 30 minSdk = 33
targetSdk = 36 targetSdk = 36
versionCode = 1 versionCode = 1
versionName = "0.1.3" versionName = "0.1.3"
@@ -46,11 +46,11 @@ android {
// Specifies one flavor dimension. // Specifies one flavor dimension.
flavorDimensions += "version" flavorDimensions += "version"
productFlavors { productFlavors {
create("demo") { // create("demo") {
dimension = "version" // dimension = "version"
applicationIdSuffix = ".demo" // applicationIdSuffix = ".demo"
versionNameSuffix = "-demo" // versionNameSuffix = "-demo"
} // }
create("full") { create("full") {
dimension = "version" dimension = "version"
applicationIdSuffix = ".full" applicationIdSuffix = ".full"

View File

@@ -9,6 +9,7 @@ android {
compileSdk = 36 compileSdk = 36
defaultConfig { defaultConfig {
minSdk = 33
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro") consumerProguardFiles("consumer-rules.pro")
} }

View File

@@ -1,8 +1,10 @@
package com.kouros.navigation.car package com.kouros.navigation.car
import android.location.Location import android.location.Location
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.Badge import androidx.compose.material3.Badge
@@ -11,13 +13,12 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.rememberTextMeasurer
@@ -26,6 +27,7 @@ import androidx.compose.ui.unit.sp
import com.kouros.android.cars.carappservice.R import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.data.NavigationColor import com.kouros.navigation.data.NavigationColor
import com.kouros.navigation.data.RouteColor import com.kouros.navigation.data.RouteColor
import com.kouros.navigation.data.SpeedColor
import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.camera.rememberCameraState
@@ -134,6 +136,7 @@ fun NavigationImage(height: Int, street: String) {
} }
) { ) {
Icon( Icon(
modifier = Modifier.size(72.dp, 72.dp),
imageVector = vector, imageVector = vector,
contentDescription = "Navigation", contentDescription = "Navigation",
tint = color tint = color
@@ -142,35 +145,69 @@ fun NavigationImage(height: Int, street: String) {
Text(text = street) Text(text = street)
} }
} }
@Composable @Composable
private fun Speed( private fun Speed(
width: Int, width: Int,
height: Int, height: Int,
location: Location location: Location
) { ) {
val radius = 32
val textMeasurer = rememberTextMeasurer()
Box( Box(
modifier = Modifier modifier = Modifier
.size(30.dp, 30.dp)
.padding( .padding(
start = width.dp - percent(width, 20).dp, start = width.dp- 300.dp,
top = height.dp - 60.dp top = height.dp- 80.dp
),
contentAlignment = Alignment.Center
) {
val textMeasurerSpeed = rememberTextMeasurer()
val textMeasurerKm = rememberTextMeasurer()
val speed = (location.speed * 3.6).toInt().toString()
val kmh = "km/h"
val styleSpeed = TextStyle(
fontSize = 22.sp,
color = Color.White,
) )
.drawWithCache { val styleKm = TextStyle(
val measuredText = fontSize = 12.sp,
textMeasurer.measure( color = Color.White,
AnnotatedString("${(location.speed * 3.6).toInt()}"),
style = TextStyle(color = Color.White, fontSize = 22.sp)
) )
onDrawBehind { val textLayoutSpeed = remember(speed) {
textMeasurerSpeed.measure(speed, styleSpeed)
}
val textLayoutKm = remember(kmh) {
textMeasurerSpeed.measure(kmh, styleKm)
}
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle( drawCircle(
Color.Black, radius = 30.dp.toPx(), center = Offset(15f, 12f) center = Offset(
x = center.x,
y = center.y
),
radius = radius.toFloat(),
color = SpeedColor,
)
drawText(
textMeasurer = textMeasurerSpeed,
text = speed,
style = styleSpeed,
topLeft = Offset(
x = center.x - textLayoutSpeed.size.width / 2,
y = center.y - textLayoutSpeed.size.height / 2 - 5,
)
)
drawText(
textMeasurer = textMeasurerKm,
text = "km/h",
style = styleKm,
topLeft = Offset(
x = center.x - textLayoutKm.size.width / 2,
y = center.y - textLayoutKm.size.height / 2 + 15,
)
) )
drawText(measuredText)
} }
} }
)
} }
fun getPaddingValues(height: Int, preView: Boolean): PaddingValues { fun getPaddingValues(height: Int, preView: Boolean): PaddingValues {

View File

@@ -87,7 +87,6 @@ class NavigationSession : Session(), NavigationScreen.Listener {
lifecycle.addObserver(mLifeCycleObserver) lifecycle.addObserver(mLifeCycleObserver)
} }
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreateScreen(intent: Intent): Screen { override fun onCreateScreen(intent: Intent): Screen {
routeModel = RouteCarModel() routeModel = RouteCarModel()

View File

@@ -54,7 +54,7 @@ class SurfaceRenderer(
) )
) )
var visibleArea = MutableLiveData( var visibleArea = MutableLiveData(
Rect() Rect(0,0,0,0)
) )
var stableArea = Rect() var stableArea = Rect()
@@ -76,6 +76,7 @@ class SurfaceRenderer(
var panView = false var panView = false
val tilt = 55.0 val tilt = 55.0
var previewDistance = 0.0
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback { val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
lateinit var lifecycleOwner: CustomLifecycleOwner lateinit var lifecycleOwner: CustomLifecycleOwner
@@ -84,7 +85,6 @@ class SurfaceRenderer(
lateinit var presentation: Presentation lateinit var presentation: Presentation
@RequiresApi(Build.VERSION_CODES.M)
override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) { override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
synchronized(this@SurfaceRenderer) { synchronized(this@SurfaceRenderer) {
Log.i(TAG, "Surface available $surfaceContainer") Log.i(TAG, "Surface available $surfaceContainer")
@@ -134,7 +134,6 @@ class SurfaceRenderer(
} }
} }
@RequiresApi(Build.VERSION_CODES.KITKAT)
override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) { override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
synchronized(this@SurfaceRenderer) { synchronized(this@SurfaceRenderer) {
Log.i(TAG, "SurfaceRenderer destroyed") Log.i(TAG, "SurfaceRenderer destroyed")
@@ -307,25 +306,29 @@ class SurfaceRenderer(
previewRouteData.value = routeModel.route.routeGeoJson previewRouteData.value = routeModel.route.routeGeoJson
centerLocation = routeModel.centerLocation centerLocation = routeModel.centerLocation
preview = true preview = true
previewDistance = routeModel.route.distance
} }
private fun previewZoom(): Double { private fun previewZoom(): Double {
if (routeModel.isNavigating()) { when (previewDistance) {
when (routeModel.route.distance) {
in 0.0..10.0 -> { in 0.0..10.0 -> {
return 14.0 return 13.0
} }
in 10.0..20.0 -> { in 10.0..20.0 -> {
return 12.0
}
in 20.0..30.0 -> {
return 11.0 return 11.0
} }
} in 20.0..30.0 -> {
}
return 10.0 return 10.0
} }
}
return 9.0
}
fun setPreViewDistance(): Double {
return previewDistance
}
companion companion
object { object {
private const val TAG = "MapRenderer" private const val TAG = "MapRenderer"

View File

@@ -6,7 +6,6 @@ import android.location.Location
import android.location.LocationManager import android.location.LocationManager
import android.os.CountDownTimer import android.os.CountDownTimer
import android.os.Handler import android.os.Handler
import android.os.Looper
import androidx.car.app.CarContext import androidx.car.app.CarContext
import androidx.car.app.Screen import androidx.car.app.Screen
import androidx.car.app.model.Action import androidx.car.app.model.Action
@@ -14,8 +13,12 @@ import androidx.car.app.model.ActionStrip
import androidx.car.app.model.CarColor import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon import androidx.car.app.model.CarIcon
import androidx.car.app.model.Distance 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.model.Template
import androidx.car.app.navigation.model.Maneuver import androidx.car.app.navigation.model.Maneuver
import androidx.car.app.navigation.model.MapController
import androidx.car.app.navigation.model.MapWithContentTemplate
import androidx.car.app.navigation.model.MessageInfo import androidx.car.app.navigation.model.MessageInfo
import androidx.car.app.navigation.model.NavigationTemplate import androidx.car.app.navigation.model.NavigationTemplate
import androidx.car.app.navigation.model.RoutingInfo import androidx.car.app.navigation.model.RoutingInfo
@@ -31,6 +34,7 @@ import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.Place import com.kouros.navigation.data.Place
import com.kouros.navigation.model.ViewModel import com.kouros.navigation.model.ViewModel
import com.kouros.navigation.utils.location
class NavigationScreen( class NavigationScreen(
carContext: CarContext, carContext: CarContext,
@@ -49,40 +53,39 @@ class NavigationScreen(
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER) var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
lateinit var recentPlace: Place
var recentPlaceFound = false
var recentPlaceActive = true
var calculateNewRoute = false var calculateNewRoute = false
val vieModel = ViewModel(NavigationRepository()) val viewModel = ViewModel(NavigationRepository())
val observer = Observer<String> { route -> val observer = Observer<String> { route ->
if (route.isNotEmpty()) { if (route.isNotEmpty()) {
routeModel.startNavigation(route) routeModel.startNavigation(route)
surfaceRenderer.setRouteData() surfaceRenderer.setRouteData()
recentPlaceActive = false
invalidate()
}
}
val recentObserver = Observer<Place> { lastPlace ->
if (!routeModel.isNavigating()) {
recentPlace = lastPlace
recentPlaceFound = true
invalidate() invalidate()
} }
} }
init { init {
vieModel.route.observe(this, observer) viewModel.route.observe(this, observer)
viewModel.recentPlace.observe(this, recentObserver)
viewModel.loadRecentPlace(location = surfaceRenderer.lastLocation)
} }
override fun onGetTemplate(): Template { override fun onGetTemplate(): Template {
val actionStripBuilder: ActionStrip.Builder = ActionStrip.Builder() val actionStripBuilder = createActionStripBuilder()
actionStripBuilder.addAction(
Action.Builder()
.setIcon(routeModel.createCarIcon(carContext, R.drawable.ic_search_black36dp))
.setOnClickListener {
startSearchScreen()
}
//.setFlags(Action.FLAG_IS_PERSISTENT)
.build()
)
actionStripBuilder.addAction(
Action.Builder()
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_24px))
.setOnClickListener {
screenManager.push(SettingsScreen(carContext))
}
//.setFlags(Action.FLAG_IS_PERSISTENT)
.build()
)
return if (routeModel.isNavigating()) { return if (routeModel.isNavigating()) {
if (calculateNewRoute) { if (calculateNewRoute) {
getNavigationLoadingTemplate(actionStripBuilder) getNavigationLoadingTemplate(actionStripBuilder)
@@ -96,21 +99,7 @@ class NavigationScreen(
private fun getNavigationTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate { private fun getNavigationTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
actionStripBuilder.addAction( actionStripBuilder.addAction(
Action.Builder() stopAction()
.setTitle(carContext.getString(R.string.stop_action_title))
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.ic_close_white_24dp
)
)
.build()
)
.setOnClickListener {
stopNavigation()
}
.build()
) )
return NavigationTemplate.Builder() return NavigationTemplate.Builder()
.setNavigationInfo( .setNavigationInfo(
@@ -123,7 +112,7 @@ class NavigationScreen(
.build() .build()
} }
private fun getNavigationEndTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate { private fun getNavigationEndTemplate(actionStripBuilder: ActionStrip.Builder): Template {
if (routeModel.isArrived()) { if (routeModel.isArrived()) {
val timer = object : CountDownTimer(10000, 10000) { val timer = object : CountDownTimer(10000, 10000) {
override fun onTick(millisUntilFinished: Long) {} override fun onTick(millisUntilFinished: Long) {}
@@ -155,13 +144,42 @@ class NavigationScreen(
.setMapActionStrip(mapActionStripBuilder().build()) .setMapActionStrip(mapActionStripBuilder().build())
.build() .build()
} else { } else {
return NavigationTemplate.Builder() return if (recentPlaceFound && recentPlaceActive) {
return getRecentPlaceTemplate()
} else {
NavigationTemplate.Builder()
.setBackgroundColor(CarColor.SECONDARY) .setBackgroundColor(CarColor.SECONDARY)
.setActionStrip(actionStripBuilder.build()) .setActionStrip(actionStripBuilder.build())
.setMapActionStrip(mapActionStripBuilder().build()) .setMapActionStrip(mapActionStripBuilder().build())
.build() .build()
} }
} }
}
fun getRecentPlaceTemplate(): Template {
val messageTemplate = MessageTemplate.Builder(
recentPlace.name + "\n"
+ recentPlace.city
)
.setHeader(
Header.Builder()
.setTitle(carContext.getString(R.string.drive_now))
.build()
)
.addAction(navigateAction())
.addAction(closeAction())
.build()
val builder = MapWithContentTemplate.Builder()
.setContentTemplate(messageTemplate)
.setActionStrip(
mapActionStripBuilder()
.addAction(settingsAction())
.addAction(searchAction())
.build()
)
return builder.build()
}
fun getNavigationLoadingTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate { fun getNavigationLoadingTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
return NavigationTemplate.Builder() return NavigationTemplate.Builder()
@@ -198,11 +216,105 @@ class NavigationScreen(
} }
} }
private fun createActionStripBuilder(): ActionStrip.Builder {
val actionStripBuilder: ActionStrip.Builder = ActionStrip.Builder()
actionStripBuilder.addAction(
searchAction()
)
actionStripBuilder.addAction(
settingsAction()
)
return actionStripBuilder
}
private fun mapActionStripBuilder(): ActionStrip.Builder { private fun mapActionStripBuilder(): ActionStrip.Builder {
val actionStripBuilder = ActionStrip.Builder() val actionStripBuilder = ActionStrip.Builder()
.addAction(zoomPlus())
.addAction(zoomMinus())
if (surfaceRenderer.panView) {
actionStripBuilder
.addAction( .addAction(
Action.Builder() panAction()
)
}
return actionStripBuilder
}
private fun stopAction(): Action {
return Action.Builder()
.setTitle(carContext.getString(R.string.stop_action_title))
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.ic_close_white_24dp
)
)
.build()
)
.setOnClickListener {
stopNavigation()
}
.build()
}
private fun navigateAction(): Action {
return Action.Builder()
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.assistant_navigation_48px
)
)
.build()
)
.setOnClickListener {
val navigateTo = location(recentPlace.latitude, recentPlace.longitude)
viewModel.loadRoute(surfaceRenderer.lastLocation, navigateTo)
routeModel.destination = recentPlace
}
.build()
}
private fun closeAction(): Action {
return Action.Builder()
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.ic_close_white_24dp
)
)
.build()
)
.setOnClickListener {
recentPlaceActive = false
invalidate()
}
.build()
}
private fun searchAction(): Action {
return Action.Builder()
.setIcon(routeModel.createCarIcon(carContext, R.drawable.ic_search_black36dp))
.setOnClickListener {
startSearchScreen()
}
.build()
}
private fun settingsAction(): Action {
return Action.Builder()
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_24px))
.setOnClickListener {
screenManager.push(SettingsScreen(carContext))
}
.build()
}
private fun zoomPlus(): Action {
return Action.Builder()
.setIcon( .setIcon(
CarIcon.Builder( CarIcon.Builder(
IconCompat.createWithResource( IconCompat.createWithResource(
@@ -215,9 +327,10 @@ class NavigationScreen(
surfaceRenderer.handleScale(1) surfaceRenderer.handleScale(1)
} }
.build() .build()
) }
.addAction(
Action.Builder() private fun zoomMinus(): Action {
return Action.Builder()
.setIcon( .setIcon(
CarIcon.Builder( CarIcon.Builder(
IconCompat.createWithResource( IconCompat.createWithResource(
@@ -229,10 +342,11 @@ class NavigationScreen(
).setOnClickListener { ).setOnClickListener {
surfaceRenderer.handleScale(-1) surfaceRenderer.handleScale(-1)
} }
.build()) .build()
if (surfaceRenderer.panView) { }
actionStripBuilder.addAction(
Action.Builder() private fun panAction(): Action {
return Action.Builder()
.setIcon( .setIcon(
CarIcon.Builder( CarIcon.Builder(
IconCompat.createWithResource( IconCompat.createWithResource(
@@ -245,9 +359,6 @@ class NavigationScreen(
surfaceRenderer.panView = false surfaceRenderer.panView = false
} }
.build() .build()
)
}
return actionStripBuilder
} }
private fun getSuggestion(title: Int, subtitle: Int, icon: CarIcon): Suggestion { private fun getSuggestion(title: Int, subtitle: Int, icon: CarIcon): Suggestion {
@@ -282,8 +393,8 @@ class NavigationScreen(
val location = Location(LocationManager.GPS_PROVIDER) val location = Location(LocationManager.GPS_PROVIDER)
location.latitude = place.latitude location.latitude = place.latitude
location.longitude = place.longitude location.longitude = place.longitude
vieModel.saveRecent(place) viewModel.saveRecent(place)
vieModel.loadRoute(surfaceRenderer.lastLocation, location) viewModel.loadRoute(surfaceRenderer.lastLocation, location)
currentNavigationLocation = location currentNavigationLocation = location
routeModel.destination = place routeModel.destination = place
invalidate() invalidate()
@@ -314,7 +425,7 @@ class NavigationScreen(
fun reRoute() { fun reRoute() {
NavigationMessage(carContext).createAlert() NavigationMessage(carContext).createAlert()
vieModel.loadRoute(surfaceRenderer.lastLocation, currentNavigationLocation) viewModel.loadRoute(surfaceRenderer.lastLocation, currentNavigationLocation)
} }
fun updateTrip() { fun updateTrip() {

View File

@@ -2,9 +2,11 @@ package com.kouros.navigation.car.screen
import android.location.Location import android.location.Location
import android.net.Uri import android.net.Uri
import android.os.Build
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi
import androidx.car.app.CarContext import androidx.car.app.CarContext
import androidx.car.app.CarToast import androidx.car.app.CarToast
import androidx.car.app.Screen import androidx.car.app.Screen

View File

@@ -15,7 +15,6 @@
*/ */
package com.kouros.navigation.car.screen package com.kouros.navigation.car.screen
import android.location.Geocoder
import android.location.Location import android.location.Location
import android.location.LocationManager import android.location.LocationManager
import android.os.CountDownTimer import android.os.CountDownTimer
@@ -60,7 +59,6 @@ class RoutePreviewScreen(
private var mItemLimit = 0 private var mItemLimit = 0
private var street = ""
val vieModel = ViewModel(NavigationRepository()) val vieModel = ViewModel(NavigationRepository())
val routeModel = RouteCarModel() val routeModel = RouteCarModel()
@@ -70,16 +68,9 @@ class RoutePreviewScreen(
if (route.isNotEmpty()) { if (route.isNotEmpty()) {
routeModel.startNavigation(route) routeModel.startNavigation(route)
surfaceRenderer.setPreviewRouteData(routeModel) surfaceRenderer.setPreviewRouteData(routeModel)
val geocoder = Geocoder(carContext)
// nominatim ->
geocoder.getFromLocation(destination.latitude, destination.longitude, 1) {
for (address in it) {
street = address.getAddressLine(0)
}
invalidate() invalidate()
} }
} }
}
init { init {
vieModel.previewRoute.observe(this, observer) vieModel.previewRoute.observe(this, observer)
@@ -192,7 +183,7 @@ class RoutePreviewScreen(
return Row.Builder() return Row.Builder()
.setTitle(route) .setTitle(route)
.setOnClickListener { onRouteSelected(index) } .setOnClickListener { onRouteSelected(index) }
.addText(street) .addText( "${destination.street!!} ${destination.postalCode} ${destination.city}")
.addAction(action) .addAction(action)
.build() .build()
} }

View File

@@ -37,7 +37,6 @@ class SearchScreen(
lateinit var searchResult: List<SearchResult> lateinit var searchResult: List<SearchResult>
val observer = Observer<List<SearchResult>> { newSearch -> val observer = Observer<List<SearchResult>> { newSearch ->
println(newSearch)
searchResult = newSearch searchResult = newSearch
invalidate() invalidate()
} }

View File

@@ -364,4 +364,5 @@
<string name="route_preview">Routen</string> <string name="route_preview">Routen</string>
<string name="recent_destinations">Letzte Ziele</string> <string name="recent_destinations">Letzte Ziele</string>
<string name="contacts">Kontakte</string> <string name="contacts">Kontakte</string>
<string name="drive_now">Jetzt losfahren</string>
</resources> </resources>

View File

@@ -491,4 +491,5 @@
<string name="route_preview">Routes</string> <string name="route_preview">Routes</string>
<string name="recent_destinations">Recent</string> <string name="recent_destinations">Recent</string>
<string name="contacts">Contacts</string> <string name="contacts">Contacts</string>
<string name="drive_now">Drive now</string>
</resources> </resources>

View File

@@ -15,6 +15,7 @@ android {
compileSdk = 36 compileSdk = 36
defaultConfig { defaultConfig {
minSdk = 33
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro") consumerProguardFiles("consumer-rules.pro")
} }

View File

@@ -5,3 +5,5 @@ import androidx.compose.ui.graphics.Color
val NavigationColor = Color(0xFF052086) val NavigationColor = Color(0xFF052086)
val RouteColor = Color(0xFF5582D0) val RouteColor = Color(0xFF5582D0)
val SpeedColor = Color(0xFF262525)

View File

@@ -32,7 +32,8 @@ class NavigationRepository {
private val routeUrl = "https://kouros-online.de/valhalla/route?json=" private val routeUrl = "https://kouros-online.de/valhalla/route?json="
private val nominatimUrl = "https://nominatim.openstreetmap.org/search?q=" private val nominatimUrl = "https://nominatim.openstreetmap.org/"
fun getRoute(currentLocation : Location, location: Location): String { fun getRoute(currentLocation : Location, location: Location): String {
val vLocation = listOf( val vLocation = listOf(
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude), Locations(lat = currentLocation.latitude, lon = currentLocation.longitude),
@@ -57,7 +58,11 @@ class NavigationRepository {
} }
fun searchPlaces(search : String) : String { fun searchPlaces(search : String) : String {
return fetchUrl("$nominatimUrl$search&format=jsonv2&addressdetails=true&countrycodes=de", false) return fetchUrl("${nominatimUrl}search?q=$search&format=jsonv2&addressdetails=true&countrycodes=de", false)
}
fun reverseAddress(location: Location) : String {
return fetchUrl("${nominatimUrl}reverse?lat=${location.latitude}&lon=${location.longitude}&format=jsonv2&addressdetails=true&countrycodes=de", false)
} }
fun getPlaces(): List<Place> { fun getPlaces(): List<Place> {
@@ -103,6 +108,7 @@ class NavigationRepository {
httpURLConnection.setRequestProperty("User-Agent", "email=nominatim@kouros-online.de"); httpURLConnection.setRequestProperty("User-Agent", "email=nominatim@kouros-online.de");
httpURLConnection.requestMethod = "GET" httpURLConnection.requestMethod = "GET"
val responseCode = httpURLConnection.responseCode val responseCode = httpURLConnection.responseCode
println(responseCode)
if (responseCode == HttpURLConnection.HTTP_OK) { if (responseCode == HttpURLConnection.HTTP_OK) {
val response = httpURLConnection.inputStream.bufferedReader() val response = httpURLConnection.inputStream.bufferedReader()
.use { it.readText() } // defaults to UTF-8 .use { it.readText() } // defaults to UTF-8

View File

@@ -16,8 +16,8 @@ data class SearchResult(
@SerializedName("osm_type") var osmType: String = "", @SerializedName("osm_type") var osmType: String = "",
@SerializedName("osm_id") var osmId: Long = 0, @SerializedName("osm_id") var osmId: Long = 0,
@SerializedName("lat") var lat: String = "", @SerializedName("lat") var lat: String = "",
@SerializedName("lon") var lon: String = "",
@SerializedName("category") var category: String = "", @SerializedName("category") var category: String = "",
@SerializedName("lon") var lon: String = "",
@SerializedName("type") var type: String = "", @SerializedName("type") var type: String = "",
@SerializedName("place_rank") var placeRank: Int = 0, @SerializedName("place_rank") var placeRank: Int = 0,
@SerializedName("importance") var importance: Double = 0.0, @SerializedName("importance") var importance: Double = 0.0,

View File

@@ -3,6 +3,8 @@ package com.kouros.navigation.model
import android.content.Context import android.content.Context
import android.location.Geocoder import android.location.Geocoder
import android.location.Location import android.location.Location
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -32,6 +34,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
MutableLiveData<String>() MutableLiveData<String>()
} }
val recentPlace: MutableLiveData<Place> by lazy {
MutableLiveData<Place>()
}
val places: MutableLiveData<List<Place>> by lazy { val places: MutableLiveData<List<Place>> by lazy {
MutableLiveData<List<Place>>() MutableLiveData<List<Place>>()
} }
@@ -45,6 +50,31 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
} }
fun loadRecentPlace(location: Location) {
viewModelScope.launch(Dispatchers.IO) {
try {
val placeBox = boxStore.boxFor(Place::class)
val query = placeBox
.query(Place_.name.notEqual(""))
.orderDesc(Place_.lastDate)
.build()
val results = query.find()
query.close()
for (place in results) {
val plLocation = location(place.latitude, place.longitude)
// val distance = repository.getRouteDistance(location, plLocation)
//place.distance = distance.toFloat()
if (place.distance == 0F) {
recentPlace.postValue(place)
println("RecentPlace $recentPlace")
return@launch
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun loadPlaces(location: Location) { fun loadPlaces(location: Location) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
@@ -59,7 +89,6 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
val plLocation = location(place.latitude, place.longitude) val plLocation = location(place.latitude, place.longitude)
val distance = repository.getRouteDistance(location, plLocation) val distance = repository.getRouteDistance(location, plLocation)
place.distance = distance.toFloat() place.distance = distance.toFloat()
println(place.lastDate)
} }
places.postValue(results) places.postValue(results)
} catch (e: Exception) { } catch (e: Exception) {
@@ -148,6 +177,15 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
} }
} }
fun reverseAddress(location: Location ): String {
val address = repository.reverseAddress(location)
println(address)
val gson = GsonBuilder().serializeNulls().create()
val place = gson.fromJson(address, SearchResult::class.java)
println(place.address.road)
return place.address.road
}
fun saveRecent(place: Place) { fun saveRecent(place: Place) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
place.category = Constants.RECENT place.category = Constants.RECENT