From 3f3bdeb96d9445e34d96102a10f4b4d009600648 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Mon, 17 Nov 2025 10:11:15 +0100 Subject: [PATCH] Bearing --- .../com/kouros/navigation/MainActivity.kt | 3 +- .../navigation/car/NavigationSession.kt | 2 +- .../kouros/navigation/car/SurfaceRenderer.kt | 111 +++++++++++------- .../car/navigation/RouteCarModel.kt | 4 +- .../navigation/car/screen/PlaceListScreen.kt | 2 +- .../car/screen/RoutePreviewScreen.kt | 11 +- .../com/kouros/navigation/car/UnitTest.kt | 1 + .../navigation/data/NavigationRepository.kt | 8 ++ .../com/kouros/navigation/model/Contacts.kt | 2 +- .../com/kouros/navigation/model/RouteModel.kt | 49 +++++--- .../com/kouros/navigation/model/ViewModel.kt | 41 ++++--- .../navigation/utils/NavigationUtils.kt | 7 ++ 12 files changed, 153 insertions(+), 88 deletions(-) diff --git a/app/src/main/java/com/kouros/navigation/MainActivity.kt b/app/src/main/java/com/kouros/navigation/MainActivity.kt index f69ebda..68c6073 100644 --- a/app/src/main/java/com/kouros/navigation/MainActivity.kt +++ b/app/src/main/java/com/kouros/navigation/MainActivity.kt @@ -48,7 +48,6 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf @@ -152,7 +151,7 @@ class MainActivity : ComponentActivity() { val curLocation = Location(LocationManager.GPS_PROVIDER) curLocation.longitude = loc[0] curLocation.latitude = loc[1] - routeModel.findManeuver(curLocation) + routeModel.updateLocation(curLocation) val leftTime = routeModel.travelLeftTime() val leftDistance = routeModel.travelLeftDistance() Log.i(TAG, " leftTime: ${leftTime / 60}") 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 62295ba..1914b37 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 @@ -187,7 +187,7 @@ class NavigationSession : Session() { fun update(location: Location) { surfaceRenderer.updateLocation(location) if (routeModel.isNavigating()) { - routeModel.findManeuver(location) + routeModel.updateLocation(location) // if (routeModel.distanceToRoute > 50) { // routeModel.stopNavigation() // locationIndex = 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 65473bb..9295e60 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 @@ -11,11 +11,9 @@ import androidx.car.app.AppManager import androidx.car.app.CarContext import androidx.car.app.SurfaceCallback import androidx.car.app.SurfaceContainer -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.graphics.Color @@ -28,8 +26,8 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.kouros.navigation.car.navigation.RouteCarModel +import com.kouros.navigation.model.RouteModel import com.kouros.navigation.utils.NavigationUtils -import kotlinx.coroutines.flow.onSubscription import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.expressions.dsl.const @@ -65,6 +63,7 @@ class SurfaceRenderer( val previewRouteData = MutableLiveData("") + lateinit var centerLocation : Location var preview = false lateinit var mapView: ComposeView @@ -72,7 +71,7 @@ class SurfaceRenderer( val tilt = 55.0 val padding = PaddingValues(start = 150.dp, top = 250.dp) - val prePadding = PaddingValues(start = 150.dp, bottom = 300.dp) + val prePadding = PaddingValues(start = 150.dp, bottom = 0.dp) val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback { @@ -107,7 +106,7 @@ class SurfaceRenderer( this.setViewTreeLifecycleOwner(lifecycleOwner) this.setViewTreeSavedStateRegistryOwner(lifecycleOwner) setContent { - Map() + MapView() } } presentation = Presentation(carContext, virtualDisplay.display) @@ -154,7 +153,9 @@ class SurfaceRenderer( } @Composable - fun Map() { + fun MapView() { + val locationProvider = rememberDefaultLocationProvider() + val locationState = rememberUserLocationState(locationProvider) val position: CameraPosition? by cameraPosition.observeAsState() val route: String? by routeData.observeAsState() val previewRoute: String? by previewRouteData.observeAsState() @@ -171,8 +172,6 @@ class SurfaceRenderer( padding = getPaddingValues() ) ) - val locationProvider = rememberDefaultLocationProvider() - val locationState = rememberUserLocationState(locationProvider) MaplibreMap( cameraState = cameraState, //baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"), @@ -191,17 +190,32 @@ class SurfaceRenderer( ) } - LaunchedEffect(position) { - cameraState.animateTo( - finalPosition = CameraPosition( - bearing = position!!.bearing, - zoom = position!!.zoom, - target = position!!.target, - tilt = tilt, - padding = getPaddingValues() - ), - duration = 3.seconds - ) + if (!preview) { + LaunchedEffect(position) { + cameraState.animateTo( + finalPosition = CameraPosition( + bearing = position!!.bearing, + zoom = position!!.zoom, + target = position!!.target, + tilt = tilt, + padding = getPaddingValues() + ), + duration = 3.seconds + ) + } + } else { + LaunchedEffect(position) { + cameraState.animateTo( + finalPosition = CameraPosition( + bearing = 0.0, + zoom = 9.0, + target = Position(centerLocation.longitude, centerLocation.latitude), + tilt = 0.0, + padding = getPaddingValues() + ), + duration = 3.seconds + ) + } } } @@ -266,41 +280,52 @@ class SurfaceRenderer( fun updateLocation(location: Location) { synchronized(this) { - var bearing: Double - if (routeModel.isNavigating()) { - bearing = routeModel.currentStep().bearing - } else { - bearing = cameraPosition.value!!.bearing - if (lastLocation.latitude != location.latitude) { - if (lastLocation.distanceTo(location) > 5) { - bearing = lastLocation.bearingTo(location).toDouble() + if (!preview) { + var bearing: Double + if (routeModel.isNavigating()) { + bearing = routeModel.currentStep().bearing + } else { + bearing = cameraPosition.value!!.bearing + if (lastLocation.latitude != location.latitude) { + if (lastLocation.distanceTo(location) > 5) { + bearing = lastLocation.bearingTo(location).toDouble() + } } } - } - var zoom = NavigationUtils().calculateZoom(location.speed.toDouble()) - if (preview) { - bearing = 0.0 - zoom = 11.0 - } - cameraPosition.postValue( - cameraPosition.value!!.copy( - bearing = bearing, - zoom = zoom, - padding = getPaddingValues(), - target = Position(location.longitude, location.latitude), + val zoom = NavigationUtils().calculateZoom(location.speed.toDouble()) + cameraPosition.postValue( + cameraPosition.value!!.copy( + bearing = bearing, + zoom = zoom, + padding = getPaddingValues(), + target = Position(location.longitude, location.latitude), + ) ) - ) - lastLocation = location + lastLocation = location + } else { + val bearing = 0.0 + val zoom = 11.0 + cameraPosition.postValue( + cameraPosition.value!!.copy( + bearing = bearing, + zoom = zoom, + tilt = 0.0, + target = Position(centerLocation.longitude, centerLocation.latitude) + ) + ) + } } } + fun setRouteData() { routeData.value = routeModel.route preview = false } - fun setPreviewRouteData(route: String) { - previewRouteData.value = route + fun setPreviewRouteData(routeModel: RouteModel) { + previewRouteData.value = routeModel.route + centerLocation = routeModel.centerLocation preview = true } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt index 479ff8d..1fcf11c 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt @@ -134,7 +134,7 @@ class RouteCarModel() : RouteModel() { ManeuverType.Left.value -> { type = Maneuver.TYPE_TURN_NORMAL_LEFT - currentTurnIcon = createCarIcon(carContext, R.drawable.ic_turn_slight_left) + currentTurnIcon = createCarIcon(carContext, R.drawable.ic_turn_normal_left) } ManeuverType.RampLeft.value -> { @@ -149,7 +149,7 @@ class RouteCarModel() : RouteModel() { ManeuverType.StayLeft.value -> { type = Maneuver.TYPE_KEEP_LEFT - currentTurnIcon = createCarIcon(carContext, R.drawable.ic_turn_slight_left) + currentTurnIcon = createCarIcon(carContext, R.drawable.ic_turn_name_change) } } maneuverType = type diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt index 619c659..71b2041 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt @@ -53,7 +53,7 @@ class PlaceListScreen( } if (category == Constants.CONTACTS) { viewModel.contactAddress.observe(this, observerAddress) - viewModel.loadContacts(carContext) + viewModel.loadContacts(carContext, location) } } diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt index a42072e..3a4346c 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt @@ -47,6 +47,9 @@ import com.kouros.navigation.car.navigation.RouteCarModel import com.kouros.navigation.data.NavigationRepository import com.kouros.navigation.data.Place import com.kouros.navigation.model.ViewModel +import java.math.BigDecimal +import java.math.RoundingMode +import kotlin.math.roundToInt /** Creates a screen using the new [androidx.car.app.navigation.model.MapWithContentTemplate] */ class RoutePreviewScreen( @@ -68,7 +71,7 @@ class RoutePreviewScreen( val observer = Observer { route -> if (route.isNotEmpty()) { routeModel.startNavigation(route) - surfaceRenderer.setPreviewRouteData(routeModel.route) + surfaceRenderer.setPreviewRouteData(routeModel) val geocoder = Geocoder(carContext) geocoder.getFromLocation(destination.latitude, destination.longitude, 1) { for (address in it) { @@ -198,8 +201,9 @@ class RoutePreviewScreen( private fun createRouteText(index: Int): CarText { - val time = routeModel.summary.getInt("time") - val length = routeModel.summary.getInt("length") + val time = routeModel.routeTime + + val length = BigDecimal(routeModel.routeDistance).setScale(1, RoundingMode.HALF_EVEN) val firstRoute = SpannableString(" \u00b7 $length km") firstRoute.setSpan( DurationSpan.create(time.toLong()), 0, 1,0 @@ -208,7 +212,6 @@ class RoutePreviewScreen( .build() } - private fun onNavigate() { setResult(destination) finish() diff --git a/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt b/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt index 75688ab..7c7e2a5 100644 --- a/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt +++ b/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt @@ -9,6 +9,7 @@ import com.kouros.navigation.model.RouteModel import com.kouros.navigation.model.ViewModel import org.junit.Assert.assertEquals import org.junit.Test +import org.junit.runner.RunWith /** * Example local unit test, which will execute on the development machine (host). diff --git a/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt b/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt index b155fe9..93eee9c 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/NavigationRepository.kt @@ -17,6 +17,7 @@ package com.kouros.navigation.data import android.location.Location +import com.kouros.navigation.model.RouteModel import org.json.JSONArray import java.net.Authenticator import java.net.HttpURLConnection @@ -47,6 +48,13 @@ class NavigationRepository { return fetchUrl(routeUrl + routeLocation) } + fun getRouteDistance(currentLocation : Location, location: Location): Double { + val route = getRoute(currentLocation, location) + val routeModel = RouteModel() + routeModel.startNavigation(route) + return routeModel.routeDistance + } + fun getPlaces(): List { val places: MutableList = ArrayList() val placesStr = fetchUrl(placesUrl) diff --git a/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt b/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt index 3f12f7e..c204109 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt @@ -36,7 +36,7 @@ class Contacts(private var context: Context) { if (name.contains("Jola") || name.contains("Dominic") || name.contains("Martha") - || name.contains("Μεντή") + || name.contains("Μεντη") || name.contains("David")) { val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE)) if (mimeType == ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) { 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 5896ce8..2081732 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 @@ -4,22 +4,26 @@ import android.location.Location import android.location.LocationManager import com.kouros.navigation.data.Place import com.kouros.navigation.data.StepData +import com.kouros.navigation.utils.NavigationUtils import com.kouros.navigation.utils.NavigationUtils.Utils.createGeoJson import com.kouros.navigation.utils.NavigationUtils.Utils.decodePolyline import org.json.JSONArray import org.json.JSONObject +import kotlin.math.absoluteValue import kotlin.math.roundToInt - -// Source - https://stackoverflow.com/a -// Posted by Dmitrii Bychkov -// Retrieved 2025-11-14, License - CC BY-SA 4.0 open class RouteModel () { var polylineLocations: List> = emptyList() lateinit var maneuvers: JSONArray lateinit var locations: JSONArray - lateinit var summary: JSONObject + private lateinit var summary: JSONObject + var routeDistance = 0.0 + + var routeTime = 0.0 + + lateinit var centerLocation: Location lateinit var destination: Place + var navigating = false var arrived = false var maneuverIndex = 0 @@ -28,12 +32,13 @@ open class RouteModel () { var distanceToStepEnd = 0F + var bearing = 0F + var beginIndex = 0 var endIndex = 0 - var routingManeuvers = mutableListOf() - var distanceToRoute = 0F + var routingManeuvers = mutableListOf() var route = "" @@ -56,11 +61,20 @@ open class RouteModel () { locations = trip.getJSONArray("locations") val legs = trip.getJSONArray("legs") summary = trip.getJSONObject("summary") + routeTime = summary.getDouble("time") + routeDistance = summary.getDouble("length") + centerLocation = createCenterLocation() maneuvers = legs.getJSONObject(0).getJSONArray("maneuvers") val shape = legs.getJSONObject(0).getString("shape") polylineLocations = decodePolyline(shape) } + private fun createCenterLocation() : Location { + val latitude = summary.getDouble("max_lat") - (summary.getDouble("max_lat") - summary.getDouble("min_lat")) + val longitude = summary.getDouble("max_lon") - (summary.getDouble("max_lon") - summary.getDouble("min_lon")) + return NavigationUtils().location(latitude, longitude) + } + fun startNavigation(valhallaRoute: String) { decodeValhallaRoute(valhallaRoute) for (i in 0.. 1) { + val plLocation = NavigationUtils().location(adr.latitude, adr.longitude) + val distance = repository.getRouteDistance(currentLocation, plLocation) + contactList.add( + Place( + id = address.contactId, + name = address.name + " " + addressLines[0] + " " + addressLines[1], + Constants.CONTACTS, + street = addressLines[0], + city = addressLines[1], + latitude = adr.latitude, + longitude = adr.longitude, + avatar = address.avatar, + distance = distance.toFloat() + ) ) - ) + } + } contactAddress.postValue(contactList) } 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 d3cc8b4..826cd29 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 @@ -121,4 +121,11 @@ class NavigationUtils() { } return zoom.toDouble() } + + fun location(latitude: Double, longitude: Double): Location { + val location = Location(LocationManager.GPS_PROVIDER) + location.longitude = longitude + location.latitude = latitude + return location + } } \ No newline at end of file