This commit is contained in:
Dimitris
2026-01-12 13:12:22 +01:00
parent e274011080
commit e22865bd73
24 changed files with 3670 additions and 918 deletions

View File

@@ -18,6 +18,7 @@ import androidx.car.app.hardware.common.CarValue
import androidx.car.app.hardware.common.OnCarDataAvailableListener
import androidx.car.app.hardware.info.CarHardwareLocation
import androidx.car.app.hardware.info.CarSensors
import androidx.car.app.hardware.info.Compass
import androidx.car.app.hardware.info.Speed
import androidx.core.location.LocationListenerCompat
import androidx.core.net.toUri
@@ -104,6 +105,17 @@ class NavigationSession : Session(), NavigationScreen.Listener {
}
}
val carCompassListener: OnCarDataAvailableListener<Compass?> =
OnCarDataAvailableListener { data ->
if (data.orientations.status == CarValue.STATUS_SUCCESS) {
val orientation = data.orientations.value
if (orientation != null) {
surfaceRenderer.carOrientation = orientation[0]
}
}
}
val carSpeedListener = OnCarDataAvailableListener<Speed> { data ->
if (data.displaySpeedMetersPerSecond.status == CarValue.STATUS_SUCCESS) {
val speed = data.displaySpeedMetersPerSecond.value
@@ -172,6 +184,9 @@ class NavigationSession : Session(), NavigationScreen.Listener {
val useCarLocation = getBooleanKeyValue(carContext, CAR_LOCATION)
if (useCarLocation) {
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
carSensors.addCompassListener(CarSensors.UPDATE_RATE_FASTEST,
carContext.mainExecutor,
carCompassListener)
carSensors.addCarHardwareLocationListener(
CarSensors.UPDATE_RATE_FASTEST,
carContext.mainExecutor,
@@ -191,8 +206,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
SearchScreen(
carContext,
surfaceRenderer,
location,
navigationViewModel,
navigationViewModel
// TODO: Uri
)
) { obj: Any? ->
@@ -229,7 +243,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if (location != null) {
navigationViewModel.loadRecentPlace(location = location, carContext)
navigationViewModel.loadRecentPlace(location = location, surfaceRenderer.carOrientation, carContext)
updateLocation(location)
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,

View File

@@ -54,6 +54,8 @@ class SurfaceRenderer(
) : DefaultLifecycleObserver {
var lastLocation = location(0.0, 0.0)
var carOrientation = 0F
private val cameraPosition = MutableLiveData(
CameraPosition(
zoom = 15.0,
@@ -303,7 +305,7 @@ class SurfaceRenderer(
routeData.value = route
updateCameraPosition(
0.0,
12.0,
14.0,
target = Position(location.longitude, location.latitude)
)
}

View File

@@ -160,8 +160,10 @@ fun AmenityLayer(routeData: String?) {
if (routeData != null && routeData.isNotEmpty()) {
val color = if (routeData.contains(Constants.PHARMACY)) {
const(Color.Red)
} else if (routeData.contains(Constants.CHARGING_STATION)) {
const(Color.Blue)
} else {
const(Color.Green)
const(Color.Black)
}
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
SymbolLayer(
@@ -169,6 +171,7 @@ fun AmenityLayer(routeData: String?) {
source = routes,
iconImage = image(painterResource(R.drawable.ev_station_48px), drawAsSdf = true),
iconColor = color,
iconOpacity = const(2.0f),
iconSize = const(3.0f),
)
}
@@ -188,10 +191,10 @@ fun SpeedCameraLayer(speedCameras: String?) {
interpolate(
type = exponential(1.2f),
input = zoom(),
5 to const(0.4f),
6 to const(0.7f),
7 to const(1.75f),
20 to const(3f),
5 to const(0.7f),
6 to const(1.0f),
7 to const(2.0f),
20 to const(4f),
),
)
}

View File

@@ -58,7 +58,9 @@ class RouteCarModel() : RouteModel() {
.setIcon(createCarIcon(carContext, stepData.icon))
.build()
)
.setRoad(destination.street!!)
if (destination.street != null) {
step.setRoad(destination.street!!)
}
if (stepData.lane.isNotEmpty()) {
addLanes(carContext, step, stepData)
}
@@ -222,13 +224,17 @@ class RouteCarModel() : RouteModel() {
fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String?) {
carContext.getCarService<AppManager?>(AppManager::class.java)
.showAlert(createAlert(carContext, distance, maxSpeed))
.showAlert(createAlert(carContext, distance, maxSpeed, createCarIcon(carContext, R.drawable.speed_camera_24px)))
}
fun createAlert(carContext: CarContext, distance: Double, maxSpeed: String?): Alert {
fun createAlert(
carContext: CarContext,
distance: Double,
maxSpeed: String?,
icon: CarIcon
): Alert {
val title = createCarText(carContext, R.string.speed_camera)
val subtitle = CarText.create(maxSpeed!!)
val icon = CarIcon.ALERT
val dismissAction: Action = createToastAction(
carContext,

View File

@@ -14,6 +14,7 @@ import androidx.core.graphics.drawable.IconCompat
import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.ViewStyle
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Category
import com.kouros.navigation.data.Constants.CHARGING_STATION
import com.kouros.navigation.data.Constants.FUEL_STATION
@@ -23,8 +24,7 @@ import com.kouros.navigation.model.ViewModel
class CategoriesScreen(
private val carContext: CarContext,
private val surfaceRenderer: SurfaceRenderer,
private val location: Location,
private val viewModel: ViewModel
private val viewModel: ViewModel,
) : Screen(carContext) {
var categories: List<Category> = listOf(
@@ -47,7 +47,6 @@ class CategoriesScreen(
CategoryScreen(
carContext,
surfaceRenderer,
location,
it.id,
viewModel
)

View File

@@ -19,8 +19,9 @@ import androidx.lifecycle.Observer
import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.navigation.NavigationMessage
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.overpass.Elements
import com.kouros.navigation.model.ViewModel
import com.kouros.navigation.utils.GeoUtils.createPointCollection
@@ -31,9 +32,8 @@ import kotlin.math.min
class CategoryScreen(
private val carContext: CarContext,
private val surfaceRenderer: SurfaceRenderer,
location: Location,
private val category: String,
private val viewModel: ViewModel
private val viewModel: ViewModel,
) : Screen(carContext) {
var elements = listOf<Elements>()
@@ -58,7 +58,7 @@ class CategoryScreen(
init {
viewModel.elements.observe(this, observer)
viewModel.getAmenities(category, location)
viewModel.getAmenities(category, surfaceRenderer.lastLocation)
}
@@ -126,6 +126,28 @@ class CategoryScreen(
} else {
row.addText(carText("${it.tags.openingHours}"))
}
val navigationMessage = NavigationMessage(carContext)
row.addAction(
Action.Builder()
.setOnClickListener {
viewModel.loadRoute(
carContext,
currentLocation = surfaceRenderer.lastLocation,
location(it.lon!!, it.lat!!),
surfaceRenderer.carOrientation
)
setResult(
Place(
name = name,
category = Constants.CHARGING_STATION,
latitude = it.lat!!,
longitude = it.lon!!
)
)
finish()
}
.setIcon(navigationMessage.createCarIcon(R.drawable.navigation_48px))
.build())
return row.build()
}

View File

@@ -98,6 +98,7 @@ class NavigationScreen(
surfaceRenderer.speedCamerasData.value = speedData
}
init {
viewModel.route.observe(this, observer)
viewModel.recentPlace.observe(this, recentObserver)
@@ -297,7 +298,12 @@ class NavigationScreen(
)
.setOnClickListener {
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, navigateTo)
viewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
navigateTo,
surfaceRenderer.carOrientation
)
routeModel.destination = recentPlace
}
.build()
@@ -394,7 +400,11 @@ class NavigationScreen(
private fun startSearchScreen() {
screenManager
.pushForResult(
SearchScreen(carContext, surfaceRenderer, surfaceRenderer.lastLocation, viewModel)
SearchScreen(
carContext,
surfaceRenderer,
viewModel
)
) { obj: Any? ->
if (obj != null) {
val place = obj as Place
@@ -416,7 +426,12 @@ class NavigationScreen(
val location = location(place.longitude, place.latitude)
viewModel.saveRecent(place)
currentNavigationLocation = location
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
viewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
location,
surfaceRenderer.carOrientation
)
routeModel.destination = place
invalidate()
}
@@ -447,7 +462,12 @@ class NavigationScreen(
fun reRoute(destination: Place) {
val dest = location(destination.longitude, destination.latitude)
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, dest)
viewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
dest,
surfaceRenderer.carOrientation
)
}
fun updateTrip(location: Location) {
@@ -495,10 +515,10 @@ class NavigationScreen(
val bearingSpeedCamera = location.bearingTo(location(camera.lon!!, camera.lat!!))
val bearingRoute = surfaceRenderer.lastLocation.bearingTo(location)
if (camera.distance < 80
&& (bearingSpeedCamera.absoluteValue - bearingRoute.absoluteValue).absoluteValue < 15.0
) {
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
if (camera.distance < 80) {
if ((bearingSpeedCamera.absoluteValue - bearingRoute.absoluteValue).absoluteValue < 20.0) {
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
}
}
}
}

View File

@@ -31,7 +31,6 @@ import com.kouros.navigation.model.ViewModel
class PlaceListScreen(
private val carContext: CarContext,
private val surfaceRenderer: SurfaceRenderer,
private val location: Location,
private val category: String,
private val viewModel: ViewModel
) : Screen(carContext) {
@@ -63,13 +62,21 @@ class PlaceListScreen(
fun loadPlaces() {
if (category == RECENT) {
viewModel.loadRecentPlaces(carContext, location)
viewModel.loadRecentPlaces(
carContext,
surfaceRenderer.lastLocation,
surfaceRenderer.carOrientation
)
}
if (category == CONTACTS) {
viewModel.loadContacts(carContext)
}
if (category == FAVORITES) {
viewModel.loadFavorites(carContext, location)
viewModel.loadFavorites(
carContext,
surfaceRenderer.lastLocation,
surfaceRenderer.carOrientation
)
}
}
@@ -77,9 +84,14 @@ class PlaceListScreen(
val itemListBuilder = ItemList.Builder()
.setNoItemsMessage(carContext.getString(R.string.no_places))
places.forEach {
val street = if (it.street != null) {
it.street
} else {
""
}
val row = Row.Builder()
.setImage(contactIcon(it.avatar, it.category))
.setTitle("${it.street!!} ${it.city}")
.setTitle("$street ${it.city}")
.setOnClickListener {
val place = Place(
0,

View File

@@ -61,7 +61,12 @@ class RoutePreviewScreen(
init {
viewModel.previewRoute.observe(this, observer)
val location = location(destination.longitude, destination.latitude)
viewModel.loadPreviewRoute(carContext, surfaceRenderer.lastLocation, location)
viewModel.loadPreviewRoute(
carContext,
surfaceRenderer.lastLocation,
location,
surfaceRenderer.carOrientation
)
}
override fun onGetTemplate(): Template {
@@ -173,7 +178,8 @@ class RoutePreviewScreen(
private fun createRouteText(): CarText {
val time = routeModel.route.summary!!.duration
val length = BigDecimal(routeModel.route.summary!!.distance).setScale(1, RoundingMode.HALF_EVEN)
val length =
BigDecimal(routeModel.route.summary!!.distance).setScale(1, RoundingMode.HALF_EVEN)
val firstRoute = SpannableString(" \u00b7 $length km")
firstRoute.setSpan(
DurationSpan.create(time.toLong()), 0, 1, 0

View File

@@ -2,7 +2,6 @@ package com.kouros.navigation.car.screen
import android.annotation.SuppressLint
import android.location.Location
import android.net.Uri
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
@@ -17,9 +16,9 @@ import androidx.lifecycle.Observer
import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.ViewStyle
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Category
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.nominatim.SearchResult
import com.kouros.navigation.model.ViewModel
@@ -28,8 +27,7 @@ import com.kouros.navigation.model.ViewModel
class SearchScreen(
carContext: CarContext,
private var surfaceRenderer: SurfaceRenderer,
private var location: Location,
private val viewModel: ViewModel
private val viewModel: ViewModel,
) : Screen(carContext) {
var isSearchComplete: Boolean = false
@@ -73,7 +71,6 @@ class SearchScreen(
CategoriesScreen(
carContext,
surfaceRenderer,
location,
viewModel
)
) { obj: Any? ->
@@ -89,7 +86,6 @@ class SearchScreen(
PlaceListScreen(
carContext,
surfaceRenderer,
location,
it.id,
viewModel
)
@@ -119,7 +115,7 @@ class SearchScreen(
object : SearchCallback {
override fun onSearchSubmitted(searchTerm: String) {
isSearchComplete = true
viewModel.searchPlaces(searchTerm, location)
viewModel.searchPlaces(searchTerm, surfaceRenderer.lastLocation)
}
})
.setHeaderAction(Action.BACK)