This commit is contained in:
Dimitris
2026-03-23 07:10:26 +01:00
parent d1968cfa68
commit bc8a53a5d8
13 changed files with 78 additions and 59 deletions

View File

@@ -13,8 +13,8 @@ android {
applicationId = "com.kouros.navigation" applicationId = "com.kouros.navigation"
minSdk = 33 minSdk = 33
targetSdk = 36 targetSdk = 36
versionCode = 73 versionCode = 76
versionName = "0.2.0.73" versionName = "0.2.0.76"
base.archivesName = "navi-$versionName" base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@@ -1,6 +1,7 @@
package com.kouros.navigation.car package com.kouros.navigation.car
import android.location.Location import android.location.Location
import android.util.Log
import androidx.car.app.CarContext import androidx.car.app.CarContext
import androidx.car.app.connection.CarConnection import androidx.car.app.connection.CarConnection
import androidx.car.app.hardware.CarHardwareManager import androidx.car.app.hardware.CarHardwareManager
@@ -14,6 +15,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsRepository
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@@ -35,6 +35,7 @@ import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.osrm.OsrmRepository import com.kouros.navigation.data.osrm.OsrmRepository
import com.kouros.navigation.data.tomtom.TomTomRepository import com.kouros.navigation.data.tomtom.TomTomRepository
@@ -140,7 +141,7 @@ class NavigationSession : Session(), NavigationListener {
* Initializes car hardware sensors if available. * Initializes car hardware sensors if available.
*/ */
fun onPermissionGranted(permission: Boolean) { fun onPermissionGranted(permission: Boolean) {
if (::carSensorManager.isInitialized) { if (::carSensorManager.isInitialized && permission) {
carSensorManager.updateConnectionState(routeModel.navState.carConnection) carSensorManager.updateConnectionState(routeModel.navState.carConnection)
} }
} }
@@ -333,7 +334,7 @@ class NavigationSession : Session(), NavigationListener {
private fun handleNavigateIntent(screenManager: ScreenManager) { private fun handleNavigateIntent(screenManager: ScreenManager) {
screenManager.popToRoot() screenManager.popToRoot()
screenManager.pushForResult( screenManager.pushForResult(
SearchScreen(carContext, surfaceRenderer, navigationViewModel, emptyList()) SearchScreen(carContext, surfaceRenderer, navigationViewModel, mutableListOf())
) { result -> ) { result ->
// Handle search result if needed // Handle search result if needed
} }

View File

@@ -68,7 +68,7 @@ class SurfaceRenderer(
var carOrientation = 999F var carOrientation = 999F
// Current camera position state for the map // Current camera position state for the map
private val cameraPosition = MutableLiveData( val cameraPosition = MutableLiveData(
CameraPosition( CameraPosition(
zoom = 16.0, zoom = 16.0,
target = Position(latitude = homeVogelhart.latitude, longitude = homeVogelhart.longitude) target = Position(latitude = homeVogelhart.latitude, longitude = homeVogelhart.longitude)
@@ -392,7 +392,6 @@ class SurfaceRenderer(
* Sets route data for active navigation and switches to VIEW mode. * Sets route data for active navigation and switches to VIEW mode.
*/ */
fun setRouteData() { fun setRouteData() {
println("SetRouteData")
routeData.value = routeModel.curRoute.routeGeoJson routeData.value = routeModel.curRoute.routeGeoJson
viewStyle = ViewStyle.VIEW viewStyle = ViewStyle.VIEW
} }
@@ -401,7 +400,7 @@ class SurfaceRenderer(
* Updates camera position with new bearing, zoom, and target. * Updates camera position with new bearing, zoom, and target.
* Posts update to LiveData for UI observation. * Posts update to LiveData for UI observation.
*/ */
private fun updateCameraPosition(bearing: Double, zoom: Double, target: Position, tilt: Double) { fun updateCameraPosition(bearing: Double, zoom: Double, target: Position, tilt: Double) {
synchronized(this) { synchronized(this) {
cameraPosition.postValue( cameraPosition.postValue(
cameraPosition.value!!.copy( cameraPosition.value!!.copy(
@@ -458,11 +457,6 @@ class SurfaceRenderer(
} }
fun updateTrafficData(routeModel: RouteCarModel) {
}
/** /**
* Displays a specific location (e.g., amenity/POI) on the map. * Displays a specific location (e.g., amenity/POI) on the map.
*/ */

View File

@@ -4,11 +4,9 @@ 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.util.Log
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
import androidx.car.app.model.Action.FLAG_DEFAULT
import androidx.car.app.model.Action.FLAG_IS_PERSISTENT import androidx.car.app.model.Action.FLAG_IS_PERSISTENT
import androidx.car.app.model.ActionStrip import androidx.car.app.model.ActionStrip
import androidx.car.app.model.CarColor import androidx.car.app.model.CarColor
@@ -40,19 +38,21 @@ import com.kouros.navigation.car.screen.observers.NavigationObserverManager
import com.kouros.navigation.car.screen.settings.SettingsScreen import com.kouros.navigation.car.screen.settings.SettingsScreen
import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
import com.kouros.navigation.data.Constants.ROUTE_UPDATE import com.kouros.navigation.data.Constants.TILT
import com.kouros.navigation.data.Constants.TRAFFIC_UPDATE import com.kouros.navigation.data.Constants.TRAFFIC_UPDATE
import com.kouros.navigation.data.Place import com.kouros.navigation.data.Place
import com.kouros.navigation.data.overpass.Elements import com.kouros.navigation.data.overpass.Elements
import com.kouros.navigation.model.NavigationViewModel import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.model.RouteModel import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.utils.GeoUtils import com.kouros.navigation.utils.GeoUtils
import com.kouros.navigation.utils.calculateZoom
import com.kouros.navigation.utils.formattedDistance import com.kouros.navigation.utils.formattedDistance
import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location import com.kouros.navigation.utils.location
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.maplibre.spatialk.geojson.Position
import java.time.Duration import java.time.Duration
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneOffset import java.time.ZoneOffset
@@ -74,14 +74,14 @@ open class NavigationScreen(
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER) var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
var recentPlaces = emptyList<Place>() var recentPlaces = mutableListOf<Place>()
var recentPlace: Place = Place() var recentPlace: Place = Place()
var navigationType = NavigationType.VIEW var navigationType = NavigationType.VIEW
var lastTrafficDate: LocalDateTime = LocalDateTime.MIN var lastTrafficDate: LocalDateTime = LocalDateTime.MIN
var lastRouteDate: LocalDateTime = LocalDateTime.MIN var lastRouteDate: LocalDateTime = LocalDateTime.now()
var lastCameraSearch = 0 var lastCameraSearch = 0
var speedCameras = listOf<Elements>() var speedCameras = listOf<Elements>()
val observerManager = NavigationObserverManager(navigationViewModel, this) val observerManager = NavigationObserverManager(navigationViewModel, this)
@@ -103,7 +103,7 @@ open class NavigationScreen(
private var reRouteTimer: CountDownTimer? = null private var reRouteTimer: CountDownTimer? = null
val observerRecentPlaces = Observer<List<Place>> { newPlaces -> val observerRecentPlaces = Observer<List<Place>> { newPlaces ->
recentPlaces = newPlaces recentPlaces.addAll(newPlaces)
if (newPlaces.isNotEmpty() && !tripSuggestionCalled) { if (newPlaces.isNotEmpty() && !tripSuggestionCalled) {
tripSuggestionCalled = true tripSuggestionCalled = true
navigationType = NavigationType.RECENT navigationType = NavigationType.RECENT
@@ -147,7 +147,6 @@ open class NavigationScreen(
createAction( createAction(
carContext, carContext,
R.drawable.search_48px, R.drawable.search_48px,
FLAG_IS_PERSISTENT,
onClickAction = { startSearchScreen() } onClickAction = { startSearchScreen() }
) )
}, { settingsAction() }) }, { settingsAction() })
@@ -165,7 +164,11 @@ open class NavigationScreen(
*/ */
private fun navigationTemplate(actionStripBuilder: ActionStrip.Builder): Template { private fun navigationTemplate(actionStripBuilder: ActionStrip.Builder): Template {
actionStripBuilder.addAction( actionStripBuilder.addAction(
createAction(carContext, R.drawable.ic_close_white_24dp, FLAG_IS_PERSISTENT,{ stopNavigation() }) createAction(
carContext,
R.drawable.ic_close_white_24dp,
0,
{ stopNavigation() })
) )
updateTrip() updateTrip()
return NavigationTemplate.Builder() return NavigationTemplate.Builder()
@@ -179,8 +182,8 @@ open class NavigationScreen(
surfaceRenderer.viewStyle, surfaceRenderer.viewStyle,
{ zoomPlus() }, { zoomMinus() }, { { zoomPlus() }, { zoomMinus() }, {
createAction( createAction(
carContext = carContext, R.drawable.ic_zoom_out_24, carContext = carContext, R.drawable.ic_pan_24,
FLAG_IS_PERSISTENT, 0,
onClickAction = { onClickAction = {
surfaceRenderer.viewStyle = ViewStyle.VIEW surfaceRenderer.viewStyle = ViewStyle.VIEW
invalidate() invalidate()
@@ -204,7 +207,8 @@ open class NavigationScreen(
surfaceRenderer.viewStyle, surfaceRenderer.viewStyle,
{ zoomPlus() }, { zoomMinus() }, { { zoomPlus() }, { zoomMinus() }, {
createAction( createAction(
carContext = carContext, R.drawable.ic_zoom_out_24, carContext = carContext, R.drawable.ic_pan_24,
0,
onClickAction = { onClickAction = {
surfaceRenderer.viewStyle = ViewStyle.VIEW surfaceRenderer.viewStyle = ViewStyle.VIEW
invalidate() invalidate()
@@ -264,7 +268,8 @@ open class NavigationScreen(
surfaceRenderer.viewStyle, surfaceRenderer.viewStyle,
{ zoomPlus() }, { zoomMinus() }, { { zoomPlus() }, { zoomMinus() }, {
createAction( createAction(
carContext = carContext, R.drawable.ic_zoom_out_24, carContext = carContext, R.drawable.ic_pan_24,
0,
onClickAction = { onClickAction = {
surfaceRenderer.viewStyle = ViewStyle.VIEW surfaceRenderer.viewStyle = ViewStyle.VIEW
invalidate() invalidate()
@@ -286,7 +291,7 @@ open class NavigationScreen(
createAction( createAction(
carContext, carContext,
R.drawable.search_48px, R.drawable.search_48px,
FLAG_IS_PERSISTENT, 0,
{ startSearchScreen() }) { startSearchScreen() })
}, },
{ settingsAction() }) { settingsAction() })
@@ -323,7 +328,13 @@ open class NavigationScreen(
mapActionStrip( mapActionStrip(
ViewStyle.VIEW, ViewStyle.VIEW,
{ settingsAction() }, { settingsAction() },
{ createAction(carContext, R.drawable.search_48px, FLAG_IS_PERSISTENT,{ startSearchScreen() }) }, {
createAction(
carContext,
R.drawable.search_48px,
FLAG_IS_PERSISTENT,
{ startSearchScreen() })
},
{ {
createAction( createAction(
carContext = carContext, R.drawable.ic_zoom_out_24, carContext = carContext, R.drawable.ic_zoom_out_24,
@@ -410,9 +421,15 @@ open class NavigationScreen(
* Creates an action to start the settings screen. * Creates an action to start the settings screen.
*/ */
private fun settingsAction(): Action { private fun settingsAction(): Action {
// return Action.Builder()
// .setIcon(createCarIcon(carContext, R.drawable.settings_48px))
// .setOnClickListener {
// screenManager.push(SettingsScreen(carContext, navigationViewModel))
// }
// .build()
return createAction( return createAction(
carContext, R.drawable.settings_48px, carContext, R.drawable.settings_48px,
FLAG_IS_PERSISTENT, 0,
onClickAction = { onClickAction = {
screenManager.push(SettingsScreen(carContext, navigationViewModel)) screenManager.push(SettingsScreen(carContext, navigationViewModel))
} }
@@ -425,7 +442,7 @@ open class NavigationScreen(
private fun zoomPlus(): Action { private fun zoomPlus(): Action {
return createAction( return createAction(
carContext, R.drawable.ic_zoom_in_24, carContext, R.drawable.ic_zoom_in_24,
FLAG_IS_PERSISTENT, 0,
onClickAction = { onClickAction = {
surfaceRenderer.handleScale(1) surfaceRenderer.handleScale(1)
invalidate() invalidate()
@@ -439,6 +456,7 @@ open class NavigationScreen(
private fun zoomMinus(): Action { private fun zoomMinus(): Action {
return createAction( return createAction(
carContext, R.drawable.ic_zoom_out_24, carContext, R.drawable.ic_zoom_out_24,
0,
onClickAction = { onClickAction = {
surfaceRenderer.handleScale(-1) surfaceRenderer.handleScale(-1)
invalidate() invalidate()
@@ -497,6 +515,12 @@ open class NavigationScreen(
} }
routeModel.navState = routeModel.navState.copy(destination = place) routeModel.navState = routeModel.navState.copy(destination = place)
surfaceRenderer.viewStyle = ViewStyle.VIEW surfaceRenderer.viewStyle = ViewStyle.VIEW
surfaceRenderer.updateCameraPosition(
0.0,
16.0,
Position(surfaceRenderer.lastLocation.longitude, surfaceRenderer.lastLocation.latitude),
TILT
)
invalidate() invalidate()
} }
@@ -550,7 +574,7 @@ open class NavigationScreen(
*/ */
fun updateTrip(location: Location) { fun updateTrip(location: Location) {
val current = LocalDateTime.now(ZoneOffset.UTC) val current = LocalDateTime.now(ZoneOffset.UTC)
//checkRoute(current, location) checkRoute(current, location)
checkTraffic(current, location) checkTraffic(current, location)
updateSpeedCamera(location) updateSpeedCamera(location)
@@ -566,7 +590,8 @@ open class NavigationScreen(
*/ */
private fun checkRoute(current: LocalDateTime, location: Location) { private fun checkRoute(current: LocalDateTime, location: Location) {
val duration = Duration.between(current, lastRouteDate) val duration = Duration.between(current, lastRouteDate)
if (duration.abs().seconds > ROUTE_UPDATE) { val routeUpdate = routeModel.curRoute.summary.duration / 6
if (duration.abs().seconds > routeUpdate) {
lastRouteDate = current lastRouteDate = current
val destination = location( val destination = location(
routeModel.navState.destination.longitude, routeModel.navState.destination.longitude,
@@ -706,10 +731,11 @@ open class NavigationScreen(
* Update route and traffic data * Update route and traffic data
*/ */
private fun updateRoute(route: String) { private fun updateRoute(route: String) {
val routeModel = RouteModel() val newRouteModel = RouteModel()
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine) newRouteModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
navigationType = NavigationType.NAVIGATION navigationType = NavigationType.NAVIGATION
routeModel.startNavigation(route) newRouteModel.startNavigation(route)
routeModel.curRoute.summary.trafficDelay = newRouteModel.curRoute.summary.trafficDelay
} }
/** /**

View File

@@ -36,15 +36,13 @@ class PlaceListScreen(
private val surfaceRenderer: SurfaceRenderer, private val surfaceRenderer: SurfaceRenderer,
private val category: String, private val category: String,
private val navigationViewModel: NavigationViewModel, private val navigationViewModel: NavigationViewModel,
private val recentPlaces: List<Place>, private val recentPlaces: MutableList<Place>,
) : Screen(carContext) { ) : Screen(carContext) {
val routeModel = RouteCarModel() val routeModel = RouteCarModel()
var place = Place() var place = Place()
var mPlaces = mutableListOf<Place>()
val repository = getSettingsRepository(carContext) val repository = getSettingsRepository(carContext)
private var routingEngine = 0 private var routingEngine = 0
@@ -149,7 +147,7 @@ class PlaceListScreen(
carContext, carContext,
R.string.recent_Item_deleted, CarToast.LENGTH_LONG R.string.recent_Item_deleted, CarToast.LENGTH_LONG
).show() ).show()
mPlaces.remove(place) recentPlaces.remove(place)
invalidate() invalidate()
} }

View File

@@ -62,7 +62,6 @@ class RoutePreviewScreen(
private var isFavorite = false private var isFavorite = false
val maxListItems: Int = 3 val maxListItems: Int = 3
val navigationUtils = NavigationUtils(carContext)
val repository = getSettingsRepository(carContext) val repository = getSettingsRepository(carContext)
@@ -245,6 +244,7 @@ class RoutePreviewScreen(
private fun zoomMinus(): Action { private fun zoomMinus(): Action {
return createAction( return createAction(
carContext, R.drawable.ic_zoom_out_24, carContext, R.drawable.ic_zoom_out_24,
FLAG_IS_PERSISTENT,
onClickAction = { onClickAction = {
surfaceRenderer.handleScale(-1) surfaceRenderer.handleScale(-1)
invalidate() invalidate()
@@ -355,7 +355,6 @@ class RoutePreviewScreen(
private fun onRouteSelected(index: Int) { private fun onRouteSelected(index: Int) {
routeModel.navState = routeModel.navState.copy(currentRouteIndex = index) routeModel.navState = routeModel.navState.copy(currentRouteIndex = index)
surfaceRenderer.setPreviewRouteData(routeModel) surfaceRenderer.setPreviewRouteData(routeModel)
surfaceRenderer.updateTrafficData(routeModel)
routeSelected = true routeSelected = true
invalidate() invalidate()
} }

View File

@@ -28,7 +28,7 @@ class SearchScreen(
carContext: CarContext, carContext: CarContext,
private var surfaceRenderer: SurfaceRenderer, private var surfaceRenderer: SurfaceRenderer,
private val navigationViewModel: NavigationViewModel, private val navigationViewModel: NavigationViewModel,
private val recentPlaces: List<Place>, private val recentPlaces: MutableList<Place>,
) : Screen(carContext) { ) : Screen(carContext) {
var isSearchComplete: Boolean = false var isSearchComplete: Boolean = false
@@ -52,7 +52,6 @@ class SearchScreen(
} }
} }
init { init {
navigationViewModel.searchPlaces.observe(this, observer) navigationViewModel.searchPlaces.observe(this, observer)
} }
@@ -182,6 +181,7 @@ class SearchScreen(
postalCode = result.address.postcode, postalCode = result.address.postcode,
distance = result.distance distance = result.distance
) )
recentPlaces.add(place)
setResult(place) setResult(place)
finish() finish()
} }

View File

@@ -31,7 +31,6 @@ class NavigationSettings(
private var ferryToggleState = false private var ferryToggleState = false
private var carLocationToggleState = false private var carLocationToggleState = false
val settingsViewModel = getSettingsViewModel(carContext) val settingsViewModel = getSettingsViewModel(carContext)

View File

@@ -130,9 +130,8 @@ object Constants {
const val TRAFFIC_UPDATE = 300 const val TRAFFIC_UPDATE = 300
const val ROUTE_UPDATE = 60
const val INSTRUCTION_DISTANCE = 50 const val INSTRUCTION_DISTANCE = 50
const val GMS_CAR_SPEED_PERMISSION = "com.google.android.gms.permission.CAR_SPEED" const val GMS_CAR_SPEED_PERMISSION = "com.google.android.gms.permission.CAR_SPEED"
const val AUTOMOTIVE_CAR_SPEED_PERMISSION = "android.car.permission.CAR_SPEED" const val AUTOMOTIVE_CAR_SPEED_PERMISSION = "android.car.permission.CAR_SPEED"

View File

@@ -105,15 +105,6 @@ data class Route(
} }
} }
fun nextStep(add: Int): Step {
val nextIndex = currentStepIndex + add
return if (isRouteValid() && nextIndex < legs().first().steps.size) {
legs().first().steps[nextIndex]
} else {
currentStep()
}
}
fun maneuverLocations(): List<Point> { fun maneuverLocations(): List<Point> {
val waypoints = currentStep().maneuver.waypoints val waypoints = currentStep().maneuver.waypoints
val points = mutableListOf<Point>() val points = mutableListOf<Point>()
@@ -123,4 +114,13 @@ data class Route(
} }
return points return points
} }
fun nextStep(add: Int): Step {
val nextIndex = currentStepIndex + add
return if (isRouteValid() && nextIndex < legs().first().steps.size) {
legs().first().steps[nextIndex]
} else {
currentStep()
}
}
} }

View File

@@ -19,9 +19,9 @@ const val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incident
private const val tomtomFields = private const val tomtomFields =
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}" "{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
const val useLocal = false const val useLocal = true
const val useLocalTraffic = false const val useLocalTraffic = true
class TomTomRepository : NavigationRepository() { class TomTomRepository : NavigationRepository() {

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.location.Location import android.location.Location
import android.location.LocationManager import android.location.LocationManager
import androidx.car.app.model.Distance import androidx.car.app.model.Distance
import com.kouros.navigation.data.Constants.TILT
import com.kouros.navigation.data.RouteEngine import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.osrm.OsrmRepository import com.kouros.navigation.data.osrm.OsrmRepository
import com.kouros.navigation.data.tomtom.TomTomRepository import com.kouros.navigation.data.tomtom.TomTomRepository
@@ -93,7 +94,7 @@ fun calculateTilt(newZoom: Double, tilt: Double): Double =
0.0 0.0
} else { } else {
if (tilt == 0.0) { if (tilt == 0.0) {
55.0 TILT
} else { } else {
tilt tilt
} }