This commit is contained in:
Dimitris
2025-11-21 07:08:14 +01:00
parent 33f5ef4f34
commit b1a9a2c7fe
13 changed files with 711 additions and 454 deletions

View File

@@ -66,6 +66,8 @@ import org.koin.androidx.compose.koinViewModel
import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.expressions.dsl.const 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.FillLayer
import org.maplibre.compose.layers.LineLayer import org.maplibre.compose.layers.LineLayer
import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.compose.location.DesiredAccuracy
@@ -375,7 +377,7 @@ class MainActivity : ComponentActivity() {
var snapedLocation = location var snapedLocation = location
var bearing: Double var bearing: Double
if (routeModel.isNavigating()) { if (routeModel.isNavigating()) {
snapedLocation = snapLocation(location, routeModel.maneuverLocations()) snapedLocation = snapLocation(location, routeModel.route.maneuverLocations())
bearing = routeModel.currentStep().bearing bearing = routeModel.currentStep().bearing
routeModel.updateLocation(snapedLocation) routeModel.updateLocation(snapedLocation)
instruction.value = routeModel.currentStep() instruction.value = routeModel.currentStep()

View File

@@ -35,7 +35,7 @@ class NavigationSession : Session() {
lateinit var surfaceRenderer: SurfaceRenderer lateinit var surfaceRenderer: SurfaceRenderer
var locationIndex = 0 var locationIndex = 0
val test = false val test = true
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? -> var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
updateLocation(location) updateLocation(location)

View File

@@ -27,18 +27,22 @@ import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.kouros.navigation.car.navigation.RouteCarModel import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Constants import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.model.RouteModel import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.NavigationUtils.snapLocation import com.kouros.navigation.utils.NavigationUtils.snapLocation
import com.kouros.navigation.utils.calculateZoom import com.kouros.navigation.utils.calculateZoom
import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.expressions.dsl.const import org.maplibre.compose.expressions.dsl.const
import org.maplibre.compose.layers.Anchor
import org.maplibre.compose.layers.FillLayer import org.maplibre.compose.layers.FillLayer
import org.maplibre.compose.layers.LineLayer import org.maplibre.compose.layers.LineLayer
import org.maplibre.compose.location.LocationPuckColors import org.maplibre.compose.location.LocationPuckColors
import org.maplibre.compose.location.LocationPuckSizes import org.maplibre.compose.location.LocationPuckSizes
import org.maplibre.compose.map.MaplibreMap import org.maplibre.compose.map.MaplibreMap
import org.maplibre.compose.sources.GeoJsonData import org.maplibre.compose.sources.GeoJsonData
import org.maplibre.compose.sources.Source
import org.maplibre.compose.sources.getBaseSource import org.maplibre.compose.sources.getBaseSource
import org.maplibre.compose.sources.rememberGeoJsonSource import org.maplibre.compose.sources.rememberGeoJsonSource
import org.maplibre.compose.style.BaseStyle import org.maplibre.compose.style.BaseStyle
@@ -176,7 +180,7 @@ class SurfaceRenderer(
baseStyle = BaseStyle.Uri(Constants.STYLE), baseStyle = BaseStyle.Uri(Constants.STYLE),
) { ) {
getBaseSource(id = "openmaptiles")?.let { tiles -> getBaseSource(id = "openmaptiles")?.let { tiles ->
FillLayer(id = "example", visible = false, source = tiles, sourceLayer = "building") BuildingLayer(tiles)
RouteLayer(route, previewRoute) RouteLayer(route, previewRoute)
} }
@@ -219,6 +223,20 @@ 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 @Composable
fun RouteLayer(routeData: String?, previewRoute: String?) { fun RouteLayer(routeData: String?, previewRoute: String?) {
if (routeData!!.isNotEmpty()) { if (routeData!!.isNotEmpty()) {
@@ -285,7 +303,7 @@ class SurfaceRenderer(
var snapedLocation = location var snapedLocation = location
var bearing: Double var bearing: Double
if (routeModel.isNavigating()) { if (routeModel.isNavigating()) {
snapedLocation = snapLocation(location, routeModel.maneuverLocations()) snapedLocation = snapLocation(location, routeModel.route.maneuverLocations())
bearing = routeModel.currentStep().bearing bearing = routeModel.currentStep().bearing
} else { } else {
bearing = cameraPosition.value!!.bearing bearing = cameraPosition.value!!.bearing

View File

@@ -0,0 +1,57 @@
package com.kouros.navigation.car.screen
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.Header
import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
import androidx.car.app.model.Toggle
import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
private var buildingToggleState = false
init {
buildingToggleState = getBooleanKeyValue(carContext, SHOW_THREED_BUILDING)
}
override fun onGetTemplate(): Template {
val listBuilder = ItemList.Builder()
val buildingToggle: Toggle =
Toggle.Builder { checked: Boolean ->
if (checked) {
setBooleanKeyValue(carContext, true, SHOW_THREED_BUILDING)
} else {
setBooleanKeyValue(carContext, false, SHOW_THREED_BUILDING)
}
buildingToggleState = !buildingToggleState
}.setChecked(buildingToggleState).build()
listBuilder.addItem(buildRowForTemplate(R.string.threed_building, buildingToggle))
return ListTemplate.Builder()
.setSingleList(listBuilder.build())
.setHeader(
Header.Builder()
.setTitle(carContext.getString(R.string.content_limits))
.setStartHeaderAction(Action.BACK)
.build()
)
.build()
}
private fun buildRowForTemplate(title: Int, toggle: Toggle): Row {
return Row.Builder()
.setTitle(carContext.getString(title))
.setToggle(toggle)
.build()
}
}

View File

@@ -61,6 +61,15 @@ class NavigationScreen(
//.setFlags(Action.FLAG_IS_PERSISTENT) //.setFlags(Action.FLAG_IS_PERSISTENT)
.build() .build()
) )
actionStripBuilder.addAction(
Action.Builder()
.setIcon(routeModel.createCarIcon(carContext, R.drawable.ic_favorite_white_24dp))
.setOnClickListener {
screenManager.push(SettingsScreen(carContext))
}
//.setFlags(Action.FLAG_IS_PERSISTENT)
.build()
)
return if (routeModel.isNavigating()) { return if (routeModel.isNavigating()) {
getNavigationTemplate(actionStripBuilder) getNavigationTemplate(actionStripBuilder)
} else { } else {
@@ -187,6 +196,24 @@ class NavigationScreen(
surfaceRenderer.handleScale(-1) surfaceRenderer.handleScale(-1)
} }
.build()) .build())
if (surfaceRenderer.panView)
{
actionStripBuilder.addAction(
Action.Builder()
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.ic_pan_24
)
)
.build()
).setOnClickListener {
surfaceRenderer.panView = false
}
.build()
)
}
return actionStripBuilder return actionStripBuilder
} }

View File

@@ -0,0 +1,58 @@
package com.kouros.navigation.car.screen
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.model.Action
import androidx.car.app.model.Header
import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
import androidx.car.app.model.Toggle
import androidx.lifecycle.DefaultLifecycleObserver
import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
class NavigationSettings(private val carContext: CarContext) : Screen(carContext) {
override fun onGetTemplate(): Template {
val listBuilder = ItemList.Builder()
listBuilder.addItem(
buildRowForTemplate(
R.string.list_limit,
ConstraintManager.CONTENT_LIMIT_TYPE_LIST
)
)
return ListTemplate.Builder()
.setSingleList(listBuilder.build())
.setHeader(
Header.Builder()
.setTitle(carContext.getString(R.string.content_limits))
.setStartHeaderAction(Action.BACK)
.build()
)
.build()
}
private fun buildRowForTemplate(title: Int, contentLimitType: Int): Row {
return Row.Builder()
.setTitle(carContext.getString(title))
.addText(
carContext
.getCarService(ConstraintManager::class.java)
.getContentLimit(contentLimitType).toString()
)
.build()
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kouros.navigation.car.screen
import android.content.Context
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.Header
import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
import androidx.car.app.model.Toggle
import androidx.core.content.edit
import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.car.NavigationSession
/** A screen demonstrating selectable lists. */
class SettingsScreen(
carContext: CarContext,
) : Screen(carContext) {
override fun onGetTemplate(): Template {
val listBuilder = ItemList.Builder()
listBuilder.addItem(
buildRowForTemplate(
DisplaySettings(carContext),
R.string.display
)
)
listBuilder.addItem(
buildRowForTemplate(
NavigationSettings(carContext),
R.string.navigation_settings
)
)
return ListTemplate.Builder()
.setSingleList(listBuilder.build())
.setHeader(
Header.Builder()
.setTitle(
(carContext.getString(R.string.settings_action_title))
)
.setStartHeaderAction(Action.BACK)
.build()
)
.build()
}
private fun buildRowForTemplate(screen: Screen, title: Int): Row {
return Row.Builder()
.setTitle(carContext.getString(title))
.setOnClickListener { screenManager.push(screen) }
.setBrowsable(true)
.build()
}
}

View File

@@ -351,8 +351,9 @@
<string name="location_description_text_label" msgid="2779911545316756419">"Textlabel"</string> <string name="location_description_text_label" msgid="2779911545316756419">"Textlabel"</string>
<string name="parking_vs_driving_demo_title" msgid="3367862800135053111">"Demo: Parken im Vergleich zu Fahren"</string> <string name="parking_vs_driving_demo_title" msgid="3367862800135053111">"Demo: Parken im Vergleich zu Fahren"</string>
<string name="latest_feature_details" msgid="6843008350392721502">"Displays in Autos."</string> <string name="latest_feature_details" msgid="6843008350392721502">"Displays in Autos."</string>
<string name="latest_feature_title" msgid="7929405790070777460">"Neueste Funktionen"</string> <string name="navigation_settings" msgid="7929405790070777460">"Navigation"</string>
<string name="loading_toggle_enabled" msgid="8828072732804454994">"Laden aktiviert"</string> <string name="display">Anzeige</string>
<string name="threed_building" msgid="8828072732804454994">"3D Gebäude"</string>
<string name="loading_toggle_disabled" msgid="7689738885077382673">"Laden deaktiviert"</string> <string name="loading_toggle_disabled" msgid="7689738885077382673">"Laden deaktiviert"</string>
<string name="loading_screen" msgid="4771507490730308794">"Ladebildschirm"</string> <string name="loading_screen" msgid="4771507490730308794">"Ladebildschirm"</string>
<string name="vector_toggle_details" msgid="1301305340033556819">"Schieberegler zum Hinzufügen/Entfernen von Farben"</string> <string name="vector_toggle_details" msgid="1301305340033556819">"Schieberegler zum Hinzufügen/Entfernen von Farben"</string>

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
Copyright (C) 2021 The Android Open Source Project Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@@ -479,8 +478,9 @@
<string name="parking_vs_driving_demo_title">Parking Vs Driving Demo</string> <string name="parking_vs_driving_demo_title">Parking Vs Driving Demo</string>
<string name="latest_feature_details">Cluster Displays in cars!</string> <string name="latest_feature_details">Cluster Displays in cars!</string>
<string name="latest_feature_title">Latest Features</string> <string name="navigation_settings">Navigation</string>
<string name="loading_toggle_enabled">Loading enabled</string> <string name="display">Display</string>
<string name="threed_building">3D Building</string>
<string name="loading_toggle_disabled">Loading disabled</string> <string name="loading_toggle_disabled">Loading disabled</string>
<string name="loading_screen">Loading screen</string> <string name="loading_screen">Loading screen</string>
<string name="vector_toggle_details">Toggle to add/remove color</string> <string name="vector_toggle_details">Toggle to add/remove color</string>

View File

@@ -118,7 +118,7 @@ data class ValhallaLocation (
object Constants { object Constants {
const val STYLE: String = "https://kouros-online.de/liberty2" const val STYLE: String = "https://kouros-online.de/liberty.json"
//baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"), //baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
const val TAG: String = "Navigation" const val TAG: String = "Navigation"
@@ -138,6 +138,11 @@ object Constants {
home2Location.latitude = 48.1164817 home2Location.latitude = 48.1164817
home2Location.longitude = 11.594322 home2Location.longitude = 11.594322
} }
const val SHARED_PREF_KEY = "NavigationPrefs"
const val SHOW_THREED_BUILDING = "Show3D"
} }

View File

@@ -16,7 +16,7 @@ data class Route (
* *
* @since 1.0.0 * @since 1.0.0
*/ */
val maneuvers: List<Maneuvers>, var maneuvers: List<Maneuvers>,
/** /**
* The distance traveled from origin to destination. * The distance traveled from origin to destination.
@@ -43,8 +43,6 @@ data class Route (
val time: Double, val time: Double,
var routingManeuvers : List<Maneuvers>,
var routeGeoJson : String, var routeGeoJson : String,
var currentIndex: Int var currentIndex: Int
@@ -63,8 +61,6 @@ data class Route (
private lateinit var trip : Trip private lateinit var trip : Trip
private lateinit var routingManeuvers: List<Maneuvers>
private var routeGeoJson = "" private var routeGeoJson = ""
fun route (route: String ) = apply { fun route (route: String ) = apply {
@@ -87,21 +83,22 @@ data class Route (
points.add(point) points.add(point)
} }
pointLocations = points pointLocations = points
val routings = mutableListOf<Maneuvers>()
for (maneuver in maneuvers) {
routings.add(maneuver)
}
this.routingManeuvers = routings
this.routeGeoJson = createGeoJson(waypoints) this.routeGeoJson = createGeoJson(waypoints)
return Route( return Route(
maneuvers, distance, waypoints, pointLocations, summary, trip, time, routingManeuvers, routeGeoJson, 0 maneuvers, distance, waypoints, pointLocations, summary, trip, time, routeGeoJson, 0
) )
} }
} }
fun maneuverLocations(): List<Point> {
val beginShapeIndex = currentManeuver().beginShapeIndex
val endShapeIndex = currentManeuver().endShapeIndex
return pointLocations.subList(beginShapeIndex, endShapeIndex)
}
fun clear() { fun clear() {
waypoints = mutableListOf() waypoints = mutableListOf()
routingManeuvers = mutableListOf() maneuvers = mutableListOf()
routeGeoJson = "" routeGeoJson = ""
} }

View File

@@ -81,7 +81,6 @@ open class RouteModel() {
fun currentStep(): StepData { fun currentStep(): StepData {
val maneuver = route.currentManeuver() val maneuver = route.currentManeuver()
var text = "" var text = ""
println("Maneuver $maneuver")
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) { if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
text = maneuver.streetNames[0] text = maneuver.streetNames[0]
} }
@@ -156,16 +155,10 @@ open class RouteModel() {
return nearestLocation return nearestLocation
} }
fun maneuverLocations(): List<Point> {
val beginShapeIndex = route.currentManeuver().beginShapeIndex
val endShapeIndex = route.currentManeuver().endShapeIndex
return route.pointLocations.subList(beginShapeIndex, endShapeIndex)
}
fun travelLeftTime(): Double { fun travelLeftTime(): Double {
var timeLeft = 0.0 var timeLeft = 0.0
for (i in route.currentIndex + 1..<route.routingManeuvers.size) { for (i in route.currentIndex + 1..<route.maneuvers.size) {
val maneuver = route.routingManeuvers[i] val maneuver = route.maneuvers[i]
timeLeft += maneuver.time timeLeft += maneuver.time
} }
if (endIndex > 0) { if (endIndex > 0) {
@@ -180,7 +173,7 @@ open class RouteModel() {
/** Returns the current [Step] left distance in km. */ /** Returns the current [Step] left distance in km. */
fun leftStepDistance(): Double { fun leftStepDistance(): Double {
val maneuver = route.routingManeuvers[route.currentIndex] val maneuver = route.currentManeuver()
var leftDistance = maneuver.length var leftDistance = maneuver.length
if (endIndex > 0) { if (endIndex > 0) {
leftDistance = (distanceToStepEnd / 1000).toDouble() leftDistance = (distanceToStepEnd / 1000).toDouble()
@@ -190,12 +183,12 @@ open class RouteModel() {
fun travelLeftDistance(): Double { fun travelLeftDistance(): Double {
var leftDistance = 0.0 var leftDistance = 0.0
for (i in route.currentIndex + 1..<route.routingManeuvers.size) { for (i in route.currentIndex + 1..<route.maneuvers.size) {
val maneuver = route.routingManeuvers[i] val maneuver = route.maneuvers[i]
leftDistance += maneuver.length leftDistance += maneuver.length
} }
if (endIndex > 0) { if (endIndex > 0) {
val maneuver = route.routingManeuvers[route.currentIndex] val maneuver = route.currentManeuver()
val curDistance = maneuver.length val curDistance = maneuver.length
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex) val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
val time = curDistance * percent / 100 val time = curDistance * percent / 100
@@ -215,7 +208,6 @@ open class RouteModel() {
fun stopNavigation() { fun stopNavigation() {
route.clear() route.clear()
navigating = false navigating = false
//maneuverIndex = 0
currentIndex = 0 currentIndex = 0
distanceToStepEnd = 0F distanceToStepEnd = 0F
beginIndex = 0 beginIndex = 0

View File

@@ -1,7 +1,11 @@
package com.kouros.navigation.utils package com.kouros.navigation.utils
import android.content.Context
import android.location.Location import android.location.Location
import android.location.LocationManager import android.location.LocationManager
import androidx.core.content.edit
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.data.GeoJsonFeature import com.kouros.navigation.data.GeoJsonFeature
import com.kouros.navigation.data.GeoJsonFeatureCollection import com.kouros.navigation.data.GeoJsonFeatureCollection
import com.kouros.navigation.data.GeoJsonLineString import com.kouros.navigation.data.GeoJsonLineString
@@ -25,6 +29,28 @@ import kotlin.math.sin
object NavigationUtils { object NavigationUtils {
fun getBooleanKeyValue(context: Context, key: String) : Boolean {
return context
.getSharedPreferences(
SHARED_PREF_KEY,
Context.MODE_PRIVATE
)
.getBoolean(key, false)
}
fun setBooleanKeyValue(context: Context, `val`: Boolean, key: String) {
context
.getSharedPreferences(
SHARED_PREF_KEY,
Context.MODE_PRIVATE
)
.edit {
putBoolean(
key, `val`
)
apply()
}
}
fun snapLocation(location: Location, stepCoordinates: List<Point>): Location { fun snapLocation(location: Location, stepCoordinates: List<Point>): Location {
val oldPoint = Point.fromLngLat(location.longitude, location.latitude) val oldPoint = Point.fromLngLat(location.longitude, location.latitude)
if (stepCoordinates.size > 1) { if (stepCoordinates.size > 1) {