diff --git a/app/src/main/java/com/kouros/navigation/MainActivity.kt b/app/src/main/java/com/kouros/navigation/MainActivity.kt
index 113c383..ad4762e 100644
--- a/app/src/main/java/com/kouros/navigation/MainActivity.kt
+++ b/app/src/main/java/com/kouros/navigation/MainActivity.kt
@@ -52,22 +52,29 @@ import com.example.places.ui.theme.PlacesTheme
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.kouros.android.cars.carappservice.R
+import com.kouros.navigation.car.BuildingLayer
+import com.kouros.navigation.car.Puck
+import com.kouros.navigation.car.RouteLayer
import com.kouros.navigation.data.Category
import com.kouros.navigation.data.Constants
+import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.StepData
import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.model.ViewModel
+import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.NavigationUtils.snapLocation
import com.kouros.navigation.utils.calculateZoom
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.expressions.dsl.const
-import org.maplibre.compose.layers.Anchor
-import org.maplibre.compose.layers.CircleLayer
import org.maplibre.compose.layers.FillLayer
import org.maplibre.compose.layers.LineLayer
import org.maplibre.compose.location.DesiredAccuracy
@@ -88,6 +95,7 @@ import kotlin.time.Duration.Companion.seconds
class MainActivity : ComponentActivity() {
+ private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
val routeData = MutableLiveData("")
val vieModel = ViewModel(NavigationRepository())
@@ -115,10 +123,16 @@ class MainActivity : ComponentActivity() {
var locationIndex = 0
- var test = false
+ var simulate = false
init {
vieModel.route.observe(this, observer)
+ if (simulate) {
+ vieModel.loadRoute(
+ Constants.homeLocation,
+ Constants.home2Location
+ )
+ }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -262,10 +276,10 @@ class MainActivity : ComponentActivity() {
)
val userLocationState = rememberUserLocationState(locationProvider)
val locationState = locationProvider.location.collectAsState()
- if (!test) {
+ if (!simulate) {
updateLocation(locationState.value)
} else {
- test()
+ simulate()
}
if (locationState.value != null && lastLocation.latitude == 0.0) {
lastLocation.latitude = locationState.value?.position!!.latitude
@@ -290,8 +304,16 @@ class MainActivity : ComponentActivity() {
baseStyle = BaseStyle.Uri(Constants.STYLE),
) {
getBaseSource(id = "openmaptiles")?.let { tiles ->
- FillLayer(id = "example", visible = false, source = tiles, sourceLayer = "building")
- RouteLayer(route)
+ if (!getBooleanKeyValue(context = applicationContext, SHOW_THREED_BUILDING) && Constants.STYLE.contains("liberty")) {
+ BuildingLayer(tiles)
+ }
+ RouteLayer(route, "")
+ }
+ if (userLocationState.location != null) {
+ val location = Location(LocationManager.GPS_PROVIDER)
+ location.longitude = userLocationState.location!!.position.longitude
+ location.latitude = userLocationState.location!!.position.latitude
+ Puck(cameraState, location,)
}
LocationPuck(
idPrefix = "user-location1",
@@ -321,39 +343,6 @@ class MainActivity : ComponentActivity() {
duration = 1.seconds
)
}
-
-// LaunchedEffect(position) {
-// println("CameraPosition ${position!!.target.latitude}")
-// cameraState.animateTo(
-// finalPosition = CameraPosition(
-// bearing = position!!.bearing,
-// zoom = position!!.zoom,
-// target = position!!.target,
-// tilt = tilt
-// ),
-// duration = 3.seconds
-// )
-// }
- }
-
- @Composable
- fun RouteLayer(routeData: String?) {
- if (routeData!!.isNotEmpty()) {
- val routes =
- rememberGeoJsonSource(GeoJsonData.JsonString(routeData!!))
- LineLayer(
- id = "routes-casing",
- source = routes,
- color = const(Color.White),
- width = const(10.dp),
- )
- LineLayer(
- id = "routes",
- source = routes,
- color = const(Color.Blue),
- width = const(8.dp),
- )
- }
}
fun updateLocation(location: org.maplibre.compose.location.Location?) {
@@ -380,7 +369,7 @@ class MainActivity : ComponentActivity() {
snapedLocation = snapLocation(location, routeModel.route.maneuverLocations())
bearing = routeModel.currentStep().bearing
routeModel.updateLocation(snapedLocation)
- instruction.value = routeModel.currentStep()
+ instruction.postValue(routeModel.currentStep())
} else {
bearing = cameraPosition.value!!.bearing
}
@@ -394,14 +383,19 @@ class MainActivity : ComponentActivity() {
)
}
- fun test() {
+ fun simulate() {
if (routeModel.isNavigating() && locationIndex < routeModel.route.waypoints.size) {
- val loc = routeModel.route.waypoints[locationIndex]
- lastLocation.longitude = loc[0]
- lastLocation.latitude = loc[1]
- updateTestLocation(lastLocation)
- Thread.sleep(1_000)
- locationIndex++
+ coroutineScope.launch {
+ delay(
+ 100
+ )
+ val loc = routeModel.route.waypoints[locationIndex]
+ lastLocation.longitude = loc[0]
+ lastLocation.latitude = loc[1]
+ updateTestLocation(lastLocation)
+ Thread.sleep(1_000)
+ locationIndex++
+ }
}
}
diff --git a/common/car/build.gradle.kts b/common/car/build.gradle.kts
index 87fb36b..6fda2fb 100644
--- a/common/car/build.gradle.kts
+++ b/common/car/build.gradle.kts
@@ -49,6 +49,9 @@ dependencies {
implementation(project(":common:data"))
implementation(libs.androidx.runtime.livedata)
implementation(libs.androidx.compose.foundation)
+ implementation(libs.androidx.compose.ui)
+ implementation(libs.androidx.material3)
+ implementation(libs.androidx.compose.ui.text)
androidTestImplementation(libs.androidx.junit)
testImplementation(libs.junit)
}
\ No newline at end of file
diff --git a/common/car/src/main/java/com/kouros/navigation/car/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/MapView.kt
new file mode 100644
index 0000000..deabca2
--- /dev/null
+++ b/common/car/src/main/java/com/kouros/navigation/car/MapView.kt
@@ -0,0 +1,179 @@
+package com.kouros.navigation.car
+
+import android.R.attr.strokeWidth
+import android.graphics.Rect
+import android.location.Location
+import androidx.car.app.CarContext
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.drawText
+import androidx.compose.ui.text.rememberTextMeasurer
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.kouros.android.cars.carappservice.R
+import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
+import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
+import org.maplibre.compose.camera.CameraPosition
+import org.maplibre.compose.camera.CameraState
+import org.maplibre.compose.camera.rememberCameraState
+import org.maplibre.compose.expressions.dsl.const
+import org.maplibre.compose.layers.Anchor
+import org.maplibre.compose.layers.FillLayer
+import org.maplibre.compose.layers.LineLayer
+import org.maplibre.compose.location.LocationPuckColors
+import org.maplibre.compose.location.LocationPuckSizes
+import org.maplibre.compose.sources.GeoJsonData
+import org.maplibre.compose.sources.Source
+import org.maplibre.compose.sources.rememberGeoJsonSource
+import org.maplibre.spatialk.geojson.Position
+
+
+@Composable
+fun cameraState(position: CameraPosition?, tilt: Double, preview: Boolean): CameraState {
+ val padding = getPaddingValues(preview)
+ return rememberCameraState(
+ firstPosition =
+ CameraPosition(
+ target = Position(
+ latitude = position!!.target.latitude,
+ longitude = position.target.longitude
+ ),
+ zoom = 15.0,
+ tilt = tilt,
+ padding = padding
+ )
+ )
+}
+
+@Composable
+fun RouteLayer(routeData: String?, previewRoute: String?) {
+ if (routeData!!.isNotEmpty()) {
+ val routes =
+ rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
+ LineLayer(
+ id = "routes-casing",
+ source = routes,
+ color = const(Color.White),
+ width = const(16.dp),
+ )
+ LineLayer(
+ id = "routes",
+ source = routes,
+ color = const(Color.Blue),
+ width = const(14.dp),
+ )
+ }
+ if (previewRoute!!.isNotEmpty()) {
+ val routes =
+ rememberGeoJsonSource(GeoJsonData.JsonString(previewRoute))
+ LineLayer(
+ id = "routes-casing-pre",
+ source = routes,
+ color = const(Color.White),
+ width = const(6.dp),
+ )
+ LineLayer(
+ id = "routes-pre",
+ source = routes,
+ color = const(Color.Cyan),
+ width = const(4.dp),
+ )
+ }
+}
+
+@Composable
+fun BuildingLayer(tiles: Source) {
+ Anchor.Replace("building-3d") {
+ FillLayer(
+ id = "remove-building",
+ visible = false,
+ source = tiles,
+ sourceLayer = "building"
+ )
+ }
+}
+
+@Composable
+fun DrawImage(location: Location) {
+ val textMeasurer = rememberTextMeasurer()
+ val vector = ImageVector.vectorResource(id = R.drawable.assistant_navigation_48px)
+ val painter = rememberVectorPainter(image = vector)
+ val tint = remember { ColorFilter.tint(Color.Blue) }
+
+ Box(
+ modifier = Modifier
+ .padding(start = 450.dp - 30.dp, top = 350.dp - 30.dp)
+ .drawBehind {
+ with(painter) {
+ draw(
+ size = Size(60F, 60F),
+ colorFilter = tint
+ )
+ }
+ })
+
+ Box(
+ modifier = Modifier
+ .size(30.dp, 30.dp)
+ .padding(start = 650.dp, top = 350.dp)
+ .drawWithCache {
+ val measuredText =
+ textMeasurer.measure(
+ AnnotatedString("${(location.speed * 3.6).toInt()}"),
+ style = TextStyle(fontSize = 22.sp)
+ )
+ onDrawBehind {
+ drawCircle(
+ Color.LightGray, radius = 30.dp.toPx(), center = Offset(5f, 10f)
+ )
+ drawText(measuredText)
+ }
+ }
+ .fillMaxSize()
+ )
+}
+
+@Composable
+fun Puck(cameraState: CameraState, location: Location) {
+ LocationPuck(
+ idPrefix = "user-location",
+ locationState = location,
+ cameraState = cameraState,
+ accuracyThreshold = 10f,
+ showBearing = false,
+ sizes = LocationPuckSizes(dotRadius = 10.dp),
+ colors = LocationPuckColors(
+ dotFillColorCurrentLocation = Color.Cyan,
+ accuracyStrokeColor = Color.Green
+ )
+ )
+}
+
+
+fun getPaddingValues(preView: Boolean): PaddingValues {
+ val padding = PaddingValues(start = 100.dp, top = 300.dp)
+ val prePadding = PaddingValues(start = 150.dp, bottom = 0.dp)
+ return if (preView) {
+ prePadding
+ } else {
+ padding
+ }
+}
\ No newline at end of file
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 4d62483..20b03ab 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
@@ -24,6 +24,10 @@ import com.kouros.navigation.car.screen.RequestPermissionScreen
import com.kouros.navigation.car.screen.SearchScreen
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.ObjectBox
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
class NavigationSession : Session() {
val uriScheme = "samples";
@@ -31,16 +35,21 @@ class NavigationSession : Session() {
val uriHost = "navigation";
lateinit var routeModel: RouteCarModel;
- lateinit var navigationScreen: NavigationScreen
- lateinit var surfaceRenderer: SurfaceRenderer
- var locationIndex = 0
- val test = true
+ lateinit var navigationScreen: NavigationScreen
+
+ lateinit var surfaceRenderer: SurfaceRenderer
+
+ var locationIndex = 100
+
+ val simulate = true
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
updateLocation(location)
}
+ private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
+
private val mLifeCycleObserver: LifecycleObserver = object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
Log.i(TAG, "In onCreate()")
@@ -150,7 +159,7 @@ class NavigationSession : Session() {
updateLocation(location)
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
- /* minTimeMs= */ 1000,
+ /* minTimeMs= */ 10,
/* minDistanceM= */ 0f,
mLocationListener
)
@@ -158,26 +167,31 @@ class NavigationSession : Session() {
fun updateLocation(location: Location?) {
if (location != null) {
- if (test) {
- test(location)
+ if (simulate) {
+ simulate(location)
} else {
update(location)
}
}
}
- fun test(location: Location?) {
+ fun simulate(location: Location?) {
if (routeModel.isNavigating() && locationIndex < routeModel.route.waypoints.size) {
- val loc = routeModel.route.waypoints[locationIndex]
- val curLocation = Location(LocationManager.GPS_PROVIDER)
- curLocation.longitude = loc[0] + 0.0003
- curLocation.latitude = loc[1] + 0.0002
- update(curLocation)
- locationIndex += 1
- if (locationIndex > routeModel.route.waypoints.size) {
- val locationManager =
- carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
- locationManager.removeUpdates(mLocationListener)
+ coroutineScope.launch {
+ delay(
+ 100
+ )
+ val loc = routeModel.route.waypoints[locationIndex]
+ val curLocation = Location(LocationManager.GPS_PROVIDER)
+ curLocation.longitude = loc[0] + 0.0003
+ curLocation.latitude = loc[1] + 0.0002
+ update(curLocation)
+ locationIndex += 1
+ if (locationIndex > routeModel.route.waypoints.size) {
+ val locationManager =
+ carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ locationManager.removeUpdates(mLocationListener)
+ }
}
} else {
update(location = location!!)
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 2eedd57..9354696 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
@@ -1,6 +1,7 @@
package com.kouros.navigation.car
import android.app.Presentation
+import android.content.res.Resources.getSystem
import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
@@ -11,14 +12,11 @@ import androidx.car.app.AppManager
import androidx.car.app.CarContext
import androidx.car.app.SurfaceCallback
import androidx.car.app.SurfaceContainer
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.unit.dp
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@@ -33,29 +31,21 @@ import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.NavigationUtils.snapLocation
import com.kouros.navigation.utils.calculateZoom
import org.maplibre.compose.camera.CameraPosition
-import org.maplibre.compose.camera.rememberCameraState
-import org.maplibre.compose.expressions.dsl.const
-import org.maplibre.compose.layers.Anchor
-import org.maplibre.compose.layers.FillLayer
-import org.maplibre.compose.layers.LineLayer
-import org.maplibre.compose.location.LocationPuckColors
-import org.maplibre.compose.location.LocationPuckSizes
+import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.map.MaplibreMap
-import org.maplibre.compose.sources.GeoJsonData
-import org.maplibre.compose.sources.Source
import org.maplibre.compose.sources.getBaseSource
-import org.maplibre.compose.sources.rememberGeoJsonSource
import org.maplibre.compose.style.BaseStyle
import org.maplibre.spatialk.geojson.Position
+import kotlin.math.absoluteValue
+import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
class SurfaceRenderer(
- carContext: CarContext, lifecycle: Lifecycle,
+ private var carContext: CarContext, lifecycle: Lifecycle,
private var routeModel: RouteCarModel
) : DefaultLifecycleObserver {
- private val mCarContext: CarContext = carContext
var lastLocation = Location(LocationManager.GPS_PROVIDER)
val cameraPosition = MutableLiveData(
CameraPosition(
@@ -63,6 +53,19 @@ class SurfaceRenderer(
target = Position(latitude = 48.1857475, longitude = 11.5793627)
)
)
+ var visibleArea = MutableLiveData(
+ Rect()
+ )
+
+ var stableArea = Rect()
+
+ var containerWidth = 0
+
+ var containerHeight = 0
+
+ var containerDpi = 1
+
+ var lastBearing = 0.0
val routeData = MutableLiveData("")
val previewRouteData = MutableLiveData("")
@@ -74,9 +77,6 @@ class SurfaceRenderer(
var panView = false
val tilt = 55.0
- val padding = PaddingValues(start = 150.dp, top = 250.dp)
-
- val prePadding = PaddingValues(start = 150.dp, bottom = 0.dp)
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
@@ -107,6 +107,18 @@ class SurfaceRenderer(
0
)
+ containerWidth = surfaceContainer.width
+ containerHeight = surfaceContainer.height
+ if (surfaceContainer.dpi != 0) {
+ containerDpi = surfaceContainer.dpi
+ }
+ println(surfaceContainer.toString())
+ println(containerDpi)
+ println(carContext.resources.displayMetrics.density)
+ println(carContext.resources.displayMetrics.densityDpi)
+ println(getSystem().displayMetrics.density)
+ println(getSystem().displayMetrics.densityDpi)
+
mapView = ComposeView(carContext).apply {
this.setViewTreeLifecycleOwner(lifecycleOwner)
this.setViewTreeSavedStateRegistryOwner(lifecycleOwner)
@@ -120,13 +132,15 @@ class SurfaceRenderer(
}
}
- override fun onVisibleAreaChanged(visibleArea: Rect) {
+ override fun onVisibleAreaChanged(newVisibleArea: Rect) {
synchronized(this@SurfaceRenderer) {
+ visibleArea.value = newVisibleArea
}
}
- override fun onStableAreaChanged(stableArea: Rect) {
+ override fun onStableAreaChanged(newStableArea: Rect) {
synchronized(this@SurfaceRenderer) {
+ stableArea = newStableArea
}
}
@@ -143,7 +157,6 @@ class SurfaceRenderer(
}
override fun onScroll(distanceX: Float, distanceY: Float) {
- Log.i(TAG, "onScroll")
synchronized(this@SurfaceRenderer) {
}
}
@@ -162,49 +175,41 @@ class SurfaceRenderer(
val position: CameraPosition? by cameraPosition.observeAsState()
val route: String? by routeData.observeAsState()
val previewRoute: String? by previewRouteData.observeAsState()
- val cameraState =
- rememberCameraState(
- firstPosition =
- CameraPosition(
- target = Position(
- latitude = position!!.target.latitude,
- longitude = position!!.target.longitude
- ),
- zoom = 15.0,
- tilt = tilt,
- padding = getPaddingValues()
- )
- )
+ val cameraState = cameraState(position, tilt, preview)
+
MaplibreMap(
cameraState = cameraState,
baseStyle = BaseStyle.Uri(Constants.STYLE),
) {
getBaseSource(id = "openmaptiles")?.let { tiles ->
- BuildingLayer(tiles)
+ if (!getBooleanKeyValue(context = carContext, SHOW_THREED_BUILDING)
+ && Constants.STYLE.contains("liberty")) {
+ BuildingLayer(tiles)
+ }
RouteLayer(route, previewRoute)
}
-
- LocationPuck(
- idPrefix = "user-location",
- locationState = lastLocation,
- cameraState = cameraState,
- accuracyThreshold = 10f,
- sizes = LocationPuckSizes(dotRadius = 10.dp),
- colors = LocationPuckColors(accuracyStrokeColor = Color.Green)
- )
+ //Puck(cameraState, lastLocation)
}
+ ShowPosition(cameraState, position)
+ }
+
+
+ @Composable
+ fun ShowPosition(cameraState: CameraState, position: CameraPosition?) {
if (!preview) {
+ DrawImage(lastLocation)
+ val cameraDuration = duration(position)
LaunchedEffect(position) {
cameraState.animateTo(
finalPosition = CameraPosition(
bearing = position!!.bearing,
- zoom = position!!.zoom,
- target = position!!.target,
+ zoom = position.zoom,
+ target = position.target,
tilt = tilt,
- padding = getPaddingValues()
+ padding = getPaddingValues(preview)
),
- duration = 3.seconds
+ duration = cameraDuration
)
}
} else {
@@ -212,10 +217,10 @@ class SurfaceRenderer(
cameraState.animateTo(
finalPosition = CameraPosition(
bearing = 0.0,
- zoom = 9.0,
+ zoom = 11.0,
target = Position(centerLocation.longitude, centerLocation.latitude),
tilt = 0.0,
- padding = getPaddingValues()
+ padding = getPaddingValues(preview)
),
duration = 3.seconds
)
@@ -223,62 +228,23 @@ class SurfaceRenderer(
}
}
- @Composable
- fun BuildingLayer(tiles: Source) {
- if (!getBooleanKeyValue(context = mCarContext, SHOW_THREED_BUILDING)) {
- Anchor.Replace("building-3d") {
- FillLayer(
- id = "remove-building",
- visible = false,
- source = tiles,
- sourceLayer = "building"
- )
- }
- }
- }
- @Composable
- fun RouteLayer(routeData: String?, previewRoute: String?) {
- if (routeData!!.isNotEmpty()) {
- val routes =
- rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
- LineLayer(
- id = "routes-casing",
- source = routes,
- color = const(Color.White),
- width = const(16.dp),
- )
- LineLayer(
- id = "routes",
- source = routes,
- color = const(Color.Blue),
- width = const(14.dp),
- )
- }
- if (previewRoute!!.isNotEmpty()) {
- val routes =
- rememberGeoJsonSource(GeoJsonData.JsonString(previewRoute))
- LineLayer(
- id = "routes-casing-pre",
- source = routes,
- color = const(Color.White),
- width = const(6.dp),
- )
- LineLayer(
- id = "routes-pre",
- source = routes,
- color = const(Color.Cyan),
- width = const(4.dp),
- )
- }
- }
override fun onCreate(owner: LifecycleOwner) {
Log.i(TAG, "SurfaceRenderer created")
- mCarContext.getCarService(AppManager::class.java)
+ carContext.getCarService(AppManager::class.java)
.setSurfaceCallback(mSurfaceCallback)
}
+ private fun duration(position: CameraPosition?): Duration {
+ val cameraDuration = if ((lastBearing - position!!.bearing).absoluteValue > 20.0) {
+ 0.4.seconds
+ } else {
+ 1.seconds
+ }
+ return cameraDuration
+ }
+
/** Handles the map zoom-in and zoom-out events. */
fun handleScale(zoomSign: Int) {
synchronized(this) {
@@ -304,32 +270,34 @@ class SurfaceRenderer(
var bearing: Double
if (routeModel.isNavigating()) {
snapedLocation = snapLocation(location, routeModel.route.maneuverLocations())
- bearing = routeModel.currentStep().bearing
- } else {
- bearing = cameraPosition.value!!.bearing
- if (lastLocation.latitude != snapedLocation.latitude) {
- if (lastLocation.distanceTo(snapedLocation) > 5) {
- bearing = lastLocation.bearingTo(snapedLocation).toDouble()
- }
+ // stimmt nicht
+ //bearing = routeModel.currentStep().bearing
+ }
+ bearing = cameraPosition.value!!.bearing
+ if (lastLocation.latitude != snapedLocation.latitude) {
+ if (lastLocation.distanceTo(snapedLocation) > 5) {
+ bearing = lastLocation.bearingTo(snapedLocation).toDouble()
}
}
+
val zoom = if (!panView) {
calculateZoom(snapedLocation.speed.toDouble())
} else {
cameraPosition.value!!.zoom
}
+ lastBearing = cameraPosition.value!!.bearing
cameraPosition.postValue(
cameraPosition.value!!.copy(
bearing = bearing,
zoom = zoom,
- padding = getPaddingValues(),
+ padding = getPaddingValues(preview),
target = Position(snapedLocation.longitude, snapedLocation.latitude),
)
)
lastLocation = snapedLocation
} else {
val bearing = 0.0
- val zoom = 11.0
+ val zoom = 14.0
cameraPosition.postValue(
cameraPosition.value!!.copy(
bearing = bearing,
@@ -342,7 +310,6 @@ class SurfaceRenderer(
}
}
-
fun setRouteData() {
routeData.value = routeModel.route.routeGeoJson
preview = false
@@ -355,17 +322,10 @@ class SurfaceRenderer(
preview = true
}
- fun getPaddingValues(): PaddingValues {
- return if (preview) {
- prePadding
- } else {
- padding
- }
- }
-
companion
object {
private const val TAG = "MapRenderer"
}
}
+
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 e5a5bdf..8ba4c33 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
@@ -63,7 +63,7 @@ class NavigationScreen(
)
actionStripBuilder.addAction(
Action.Builder()
- .setIcon(routeModel.createCarIcon(carContext, R.drawable.ic_favorite_white_24dp))
+ .setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_24px))
.setOnClickListener {
screenManager.push(SettingsScreen(carContext))
}
@@ -91,8 +91,8 @@ class NavigationScreen(
.build()
)
.setOnClickListener {
- surfaceRenderer.routeData.postValue("")
routeModel.stopNavigation()
+ surfaceRenderer.routeData.postValue("")
invalidate()
}
.build()
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 1286f6f..3b382b9 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
@@ -21,7 +21,6 @@ import android.location.LocationManager
import android.os.CountDownTimer
import android.text.SpannableString
import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
@@ -164,9 +163,8 @@ class RoutePreviewScreen(
)
.build()
- val timer = object: CountDownTimer(10000, 10000) {
+ val timer = object: CountDownTimer(10000, 15000) {
override fun onTick(millisUntilFinished: Long) {}
-
override fun onFinish() {
onNavigate()
}
@@ -223,18 +221,17 @@ class RoutePreviewScreen(
fun getMapActionStrip(): ActionStrip {
return ActionStrip.Builder()
.addAction(
- createToastAction(R.drawable.ic_zoom_in_24, R.string.zoomed_in_toast_msg)
+ createToastAction(R.drawable.ic_zoom_in_24)
)
.addAction(
- createToastAction(R.drawable.ic_zoom_out_24, R.string.zoomed_out_toast_msg)
+ createToastAction(R.drawable.ic_zoom_out_24)
)
.addAction(Action.PAN)
.build()
}
private fun createToastAction(
- @DrawableRes iconRes: Int,
- @StringRes toastStringRes: Int
+ @DrawableRes iconRes: Int
): Action {
return Action.Builder()
.setOnClickListener { surfaceRenderer.handleScale(-1) }
diff --git a/common/car/src/main/res/drawable-navexposed/assistant_navigation_48px.xml b/common/car/src/main/res/drawable-navexposed/assistant_navigation_48px.xml
new file mode 100644
index 0000000..1077be0
--- /dev/null
+++ b/common/car/src/main/res/drawable-navexposed/assistant_navigation_48px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/car/src/main/res/drawable/assistant_navigation_48px.xml b/common/car/src/main/res/drawable/assistant_navigation_48px.xml
new file mode 100644
index 0000000..1077be0
--- /dev/null
+++ b/common/car/src/main/res/drawable/assistant_navigation_48px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/car/src/main/res/drawable/settings_24px.xml b/common/car/src/main/res/drawable/settings_24px.xml
new file mode 100644
index 0000000..4bcd4aa
--- /dev/null
+++ b/common/car/src/main/res/drawable/settings_24px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/car/src/main/res/layout/permission_request.xml b/common/car/src/main/res/layout/permission_request.xml
deleted file mode 100644
index e7ef3cf..0000000
--- a/common/car/src/main/res/layout/permission_request.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
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 a10fcb8..8c2d370 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
@@ -119,7 +119,9 @@ data class ValhallaLocation (
object Constants {
const val STYLE: String = "https://kouros-online.de/liberty.json"
- //baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
+ //const val STYLE: String = "https://tiles.openfreemap.org/styles/liberty"
+
+ //const val STYLE: String = "https://tiles.openfreemap.org/styles/dark"
const val TAG: String = "Navigation"
const val CONTACTS: String = "Contacts"
diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ExitBranchElements.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ExitBranchElements.kt
new file mode 100644
index 0000000..526f103
--- /dev/null
+++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ExitBranchElements.kt
@@ -0,0 +1,11 @@
+package com.kouros.navigation.data.valhalla
+
+import com.google.gson.annotations.SerializedName
+
+
+data class ExitBranchElements (
+
+ @SerializedName("text" ) var text : String? = null,
+ @SerializedName("consecutive_count" ) var consecutiveCount : Int? = null
+
+)
\ No newline at end of file
diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/ExitTowardElements.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ExitTowardElements.kt
new file mode 100644
index 0000000..dbc106f
--- /dev/null
+++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/ExitTowardElements.kt
@@ -0,0 +1,10 @@
+package com.kouros.navigation.data.valhalla
+
+import com.google.gson.annotations.SerializedName
+
+
+data class ExitTowardElements (
+
+ @SerializedName("text" ) var text : String? = null
+
+)
\ No newline at end of file
diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/Maneuvers.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/Maneuvers.kt
index 10262c1..a446fa6 100644
--- a/common/data/src/main/java/com/kouros/navigation/data/valhalla/Maneuvers.kt
+++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/Maneuvers.kt
@@ -10,8 +10,7 @@ import kotlinx.serialization.json.JsonIgnoreUnknownKeys
@JsonIgnoreUnknownKeys
data class Maneuvers(
- @SerializedName("begin_shape_index") var beginShapeIndex: Int,
- @SerializedName("end_shape_index") var endShapeIndex: Int,
+
@SerializedName("type") var type: Int = 0,
@SerializedName("instruction") var instruction: String = "",
@SerializedName("verbal_succinct_transition_instruction") var verbalSuccinctTransitionInstruction: String = "",
@@ -23,6 +22,10 @@ data class Maneuvers(
@SerializedName("length") var length: Double = 0.0,
@SerializedName("cost") var cost: Double = 0.0,
@SerializedName("verbal_multi_cue") var verbalMultiCue: Boolean = false,
+ @SerializedName("begin_shape_index") var beginShapeIndex: Int,
+ @SerializedName("end_shape_index") var endShapeIndex: Int,
+ @SerializedName("highway") var highway: Boolean = false,
+ @SerializedName("sign") var sign: Sign = Sign(),
@SerializedName("travel_mode") var travelMode: String = "",
@SerializedName("travel_type") var travelType: String = "",
diff --git a/common/data/src/main/java/com/kouros/navigation/data/valhalla/Sign.kt b/common/data/src/main/java/com/kouros/navigation/data/valhalla/Sign.kt
new file mode 100644
index 0000000..8f13b50
--- /dev/null
+++ b/common/data/src/main/java/com/kouros/navigation/data/valhalla/Sign.kt
@@ -0,0 +1,11 @@
+package com.kouros.navigation.data.valhalla
+
+import com.google.gson.annotations.SerializedName
+
+
+data class Sign (
+
+ @SerializedName("exit_branch_elements" ) var exitBranchElements : ArrayList = arrayListOf(),
+ @SerializedName("exit_toward_elements" ) var exitTowardElements : ArrayList = arrayListOf()
+
+)
\ No newline at end of file
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 c204109..e2799aa 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("Rena")
|| 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 d78a5e5..a8d818e 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
@@ -6,8 +6,11 @@ import com.kouros.navigation.data.Constants.homeLocation
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.Route
import com.kouros.navigation.data.StepData
+import com.kouros.navigation.utils.NavigationUtils
import com.kouros.navigation.utils.location
+import org.maplibre.geojson.FeatureCollection
import org.maplibre.geojson.Point
+import org.maplibre.turf.TurfMeasurement
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
@@ -47,15 +50,12 @@ open class RouteModel() {
private fun createCenterLocation(): Location {
- if (route.summary.maxLat == 0.0) {
- return location(homeLocation.latitude, homeLocation.longitude)
- }
- val latitude =
- route.summary.maxLat - (route.summary.maxLat - route.summary.minLat)
- val longitude =
- route.summary.maxLon - (route.summary.maxLon - route.summary.minLon)
- return location(latitude, longitude)
+
+ val future = TurfMeasurement.center(FeatureCollection.fromJson(route.routeGeoJson))
+ val point = future.geometry() as Point
+ return location(point.latitude(), point.longitude())
}
+
val currentDistance: Double
/** Returns the current [Step] with information such as the cue text and images. */
get() {
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 3d8fca6..c1ff837 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
@@ -10,10 +10,14 @@ import com.kouros.navigation.data.GeoJsonFeature
import com.kouros.navigation.data.GeoJsonFeatureCollection
import com.kouros.navigation.data.GeoJsonLineString
import kotlinx.serialization.json.Json
+import org.maplibre.geojson.Feature
+import org.maplibre.geojson.FeatureCollection
+import org.maplibre.geojson.LineString
import org.maplibre.geojson.Point
import org.maplibre.turf.TurfClassification
import org.maplibre.turf.TurfConversion
import org.maplibre.turf.TurfJoins
+import org.maplibre.turf.TurfMeasurement
import org.maplibre.turf.TurfMeta
import org.maplibre.turf.TurfMisc
import org.maplibre.turf.TurfTransformation
@@ -99,7 +103,8 @@ object NavigationUtils {
return coordinates
}
- fun createGeoJson(lineCoordinates: List>): String {
+ fun createGeoJson(lineCoordinates: List>): String {
+
val lineString = GeoJsonLineString(type = "LineString", coordinates = lineCoordinates)
val feature = GeoJsonFeature(type = "Feature", geometry = lineString)
val featureCollection =
@@ -163,7 +168,7 @@ fun calculateZoom(speed: Double?): Double {
in 31..40 -> 16.0
in 41..50 -> 15.0
in 51..60 -> 14.0
- else -> 11
+ else -> 14
}
return zoom.toDouble()
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8a5bff2..6070e35 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -12,22 +12,25 @@ junit = "4.13.2"
junitVersion = "1.3.0"
espressoCore = "3.7.0"
kotlinxSerializationJson = "1.9.0"
-lifecycleRuntimeKtx = "2.9.4"
-composeBom = "2025.11.00"
+lifecycleRuntimeKtx = "2.10.0"
+composeBom = "2025.11.01"
appcompat = "1.7.1"
material = "1.13.0"
carApp = "1.7.0"
objectboxKotlin = "5.0.1"
objectboxProcessor = "5.0.1"
-ui = "1.9.4"
+ui = "1.9.5"
material3 = "1.4.0"
-runtimeLivedata = "1.9.4"
-foundation = "1.9.4"
+runtimeLivedata = "1.9.5"
+foundation = "1.9.5"
maplibre-composeMaterial3 = "0.12.2"
maplibre-compose = "0.12.1"
playServicesLocation = "21.3.0"
-runtime = "1.9.4"
-accompanist = "0.32.0"
+runtime = "1.9.5"
+accompanist = "0.37.3"
+uiVersion = "1.9.5"
+uiText = "1.9.5"
+
[libraries]
android-sdk-turf = { module = "org.maplibre.gl:android-sdk-turf", version.ref = "androidSdkTurf" }
@@ -47,7 +50,6 @@ koin-core = { module = "io.insert-koin:koin-core", version.ref = "koinCore" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-car-app = { group = "androidx.car.app", name = "app", version.ref = "carApp" }
-#objectbox-kotlin = { module = "io.objectbox:objectbox-kotlin", version.ref = "objectboxKotlin" }
objectbox-kotlin = { module = "io.objectbox:objectbox-kotlin", version.ref = "objectboxKotlin" }
objectbox-processor = { module = "io.objectbox:objectbox-processor", version.ref = "objectboxProcessor" }
ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
@@ -59,6 +61,8 @@ androidx-compose-foundation = { group = "androidx.compose.foundation", name = "f
play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" }
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "runtime" }
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "uiVersion" }
+androidx-compose-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "uiText" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }