Lanes
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user