Navigation Screen to Session, Remove NavigationService
This commit is contained in:
@@ -43,6 +43,7 @@ import com.google.android.gms.location.LocationServices
|
|||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
||||||
import com.kouros.navigation.car.TextToSpeechManager
|
import com.kouros.navigation.car.TextToSpeechManager
|
||||||
|
import com.kouros.navigation.car.navigation.NavigationService
|
||||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||||
import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE
|
import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE
|
||||||
import com.kouros.navigation.data.Constants.TAG
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
@@ -79,6 +80,8 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
var navigationService: NavigationService? = null
|
||||||
|
|
||||||
var isBound: Boolean = false
|
var isBound: Boolean = false
|
||||||
val routeData = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
@@ -99,6 +102,20 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Monitors the state of the connection to the navigation service.
|
||||||
|
private val serviceConnection: ServiceConnection = object : ServiceConnection {
|
||||||
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
|
val binder: NavigationService.LocalBinder = service as NavigationService.LocalBinder
|
||||||
|
navigationService = binder.service
|
||||||
|
isBound = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(name: ComponentName?) {
|
||||||
|
navigationService = null
|
||||||
|
isBound = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val cameraPosition = MutableLiveData(
|
val cameraPosition = MutableLiveData(
|
||||||
CameraPosition(
|
CameraPosition(
|
||||||
zoom = 15.0, target = Position(latitude = 48.1857475, longitude = 11.5793627)
|
zoom = 15.0, target = Position(latitude = 48.1857475, longitude = 11.5793627)
|
||||||
@@ -151,6 +168,27 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
Log.i(TAG, "In onStart()")
|
||||||
|
bindService(
|
||||||
|
Intent(this, NavigationService::class.java),
|
||||||
|
serviceConnection,
|
||||||
|
BIND_AUTO_CREATE
|
||||||
|
)
|
||||||
|
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
Log.i(TAG, "In onStop(). bound $isBound")
|
||||||
|
if (isBound) {
|
||||||
|
unbindService(serviceConnection)
|
||||||
|
isBound = false
|
||||||
|
navigationService = null
|
||||||
|
}
|
||||||
|
super.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun StartScreen(
|
fun StartScreen(
|
||||||
@@ -318,6 +356,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
|
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
|
||||||
routeModel.startNavigation(newRoute)
|
routeModel.startNavigation(newRoute)
|
||||||
routeData.value = routeModel.curRoute.routeGeoJson
|
routeData.value = routeModel.curRoute.routeGeoJson
|
||||||
|
navigationService?.startNavigation()
|
||||||
}
|
}
|
||||||
fun stopNavigation(closeSheet: () -> Unit) {
|
fun stopNavigation(closeSheet: () -> Unit) {
|
||||||
closeSheet()
|
closeSheet()
|
||||||
@@ -325,6 +364,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
getSettingsViewModel(applicationContext).onLastRouteChanged("")
|
getSettingsViewModel(applicationContext).onLastRouteChanged("")
|
||||||
routeData.value = ""
|
routeData.value = ""
|
||||||
stepData.value = StepData("", "", 0.0, 0, 0, 0, 0.0)
|
stepData.value = StepData("", "", 0.0, 0, 0, 0, 0.0)
|
||||||
|
navigationService?.stopNavigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun textToSpeech() {
|
fun textToSpeech() {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.car.screen.NavigationListener
|
import com.kouros.navigation.car.screen.NavigationListener
|
||||||
import com.kouros.navigation.car.screen.NavigationScreen
|
import com.kouros.navigation.car.screen.NavigationScreen
|
||||||
|
import com.kouros.navigation.data.Place
|
||||||
import kotlinx.coroutines.awaitCancellation
|
import kotlinx.coroutines.awaitCancellation
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ internal class ClusterSession : Session(), NavigationListener {
|
|||||||
OnClickListener {})
|
OnClickListener {})
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
mNavigationCarSurface = SurfaceRenderer(carContext, lifecycle, routeModel, viewModelStoreOwner)
|
mNavigationCarSurface = SurfaceRenderer(carContext, lifecycle, viewModelStoreOwner)
|
||||||
|
|
||||||
// mNavigationScreen =
|
// mNavigationScreen =
|
||||||
// new NavigationScreen(getCarContext(), mSettingsAction, this, mNavigationCarSurface);
|
// new NavigationScreen(getCarContext(), mSettingsAction, this, mNavigationCarSurface);
|
||||||
@@ -99,6 +100,10 @@ internal class ClusterSession : Session(), NavigationListener {
|
|||||||
override fun updateTrip(trip: Trip) {
|
override fun updateTrip(trip: Trip) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun navigateToPlace(place: Place) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = ClusterSession::class.java.getSimpleName()
|
val TAG: String = ClusterSession::class.java.getSimpleName()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import com.kouros.navigation.data.Constants.TAG
|
|||||||
|
|
||||||
class NavigationCarAppService : CarAppService() {
|
class NavigationCarAppService : CarAppService() {
|
||||||
|
|
||||||
val INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP =
|
val intentActionNavNotificationOpenApp =
|
||||||
"com.kouros.navigation.INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP"
|
"com.kouros.navigation.INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP"
|
||||||
|
|
||||||
val channelId: String = "NavigationSessionChannel"
|
val channelId: String = "NavigationSessionChannel"
|
||||||
@@ -31,7 +31,6 @@ class NavigationCarAppService : CarAppService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateSession(sessionInfo: SessionInfo): Session {
|
override fun onCreateSession(sessionInfo: SessionInfo): Session {
|
||||||
Log.d(TAG, "Display Type: ${sessionInfo.displayType}")
|
|
||||||
if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) {
|
if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) {
|
||||||
return ClusterSession()
|
return ClusterSession()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -136,9 +136,9 @@ class NavigationNotificationService : Service() {
|
|||||||
// heads-up notification or the rail widget.
|
// heads-up notification or the rail widget.
|
||||||
val pendingIntent = CarPendingIntent.getCarApp(
|
val pendingIntent = CarPendingIntent.getCarApp(
|
||||||
context,
|
context,
|
||||||
NavigationCarAppService().INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP.hashCode(),
|
NavigationCarAppService().intentActionNavNotificationOpenApp.hashCode(),
|
||||||
Intent(
|
Intent(
|
||||||
NavigationCarAppService().INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP
|
NavigationCarAppService().intentActionNavNotificationOpenApp
|
||||||
).setComponent(
|
).setComponent(
|
||||||
ComponentName(
|
ComponentName(
|
||||||
context,
|
context,
|
||||||
@@ -146,7 +146,7 @@ class NavigationNotificationService : Service() {
|
|||||||
)
|
)
|
||||||
).setData(
|
).setData(
|
||||||
NavigationCarAppService().createDeepLinkUri(
|
NavigationCarAppService().createDeepLinkUri(
|
||||||
NavigationCarAppService().INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP
|
NavigationCarAppService().intentActionNavNotificationOpenApp
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -40,25 +40,38 @@ import com.kouros.navigation.car.screen.NavigationType
|
|||||||
import com.kouros.navigation.car.screen.RequestPermissionScreen
|
import com.kouros.navigation.car.screen.RequestPermissionScreen
|
||||||
import com.kouros.navigation.car.screen.SearchScreen
|
import com.kouros.navigation.car.screen.SearchScreen
|
||||||
import com.kouros.navigation.car.screen.checkPermission
|
import com.kouros.navigation.car.screen.checkPermission
|
||||||
|
import com.kouros.navigation.car.screen.observers.NavigationObserverCallback
|
||||||
|
import com.kouros.navigation.car.screen.observers.NavigationObserverManager
|
||||||
import com.kouros.navigation.data.Constants.AUTOMOTIVE_CAR_SPEED_PERMISSION
|
import com.kouros.navigation.data.Constants.AUTOMOTIVE_CAR_SPEED_PERMISSION
|
||||||
|
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||||
import com.kouros.navigation.data.Constants.GMS_CAR_SPEED_PERMISSION
|
import com.kouros.navigation.data.Constants.GMS_CAR_SPEED_PERMISSION
|
||||||
import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE
|
import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE
|
||||||
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
||||||
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
||||||
import com.kouros.navigation.data.Constants.TAG
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
|
import com.kouros.navigation.data.Constants.TRAFFIC_UPDATE
|
||||||
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.data.ViewStyle
|
import com.kouros.navigation.data.ViewStyle
|
||||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.NavigationViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
|
import com.kouros.navigation.model.RouteModel
|
||||||
|
import com.kouros.navigation.utils.GeoUtils
|
||||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getViewModel
|
import com.kouros.navigation.utils.NavigationUtils.getViewModel
|
||||||
|
import com.kouros.navigation.utils.formattedDistance
|
||||||
import com.kouros.navigation.utils.getSettingsRepository
|
import com.kouros.navigation.utils.getSettingsRepository
|
||||||
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import com.kouros.navigation.utils.location
|
||||||
import kotlinx.coroutines.awaitCancellation
|
import kotlinx.coroutines.awaitCancellation
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +80,7 @@ import java.time.ZoneOffset
|
|||||||
* car hardware sensors, routing engine selection, and screen navigation.
|
* car hardware sensors, routing engine selection, and screen navigation.
|
||||||
* Implements NavigationScreen.Listener for handling navigation events.
|
* Implements NavigationScreen.Listener for handling navigation events.
|
||||||
*/
|
*/
|
||||||
class NavigationSession : Session(), NavigationListener {
|
class NavigationSession : Session(), NavigationListener, NavigationObserverCallback {
|
||||||
|
|
||||||
// Flag to enable/disable contact access feature
|
// Flag to enable/disable contact access feature
|
||||||
val useContacts = false
|
val useContacts = false
|
||||||
@@ -75,6 +88,8 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
// Model for managing route state and navigation logic for Android Auto
|
// Model for managing route state and navigation logic for Android Auto
|
||||||
lateinit var routeModel: RouteCarModel
|
lateinit var routeModel: RouteCarModel
|
||||||
|
|
||||||
|
var route = ""
|
||||||
|
|
||||||
// Main navigation screen displayed to the user
|
// Main navigation screen displayed to the user
|
||||||
lateinit var navigationScreen: NavigationScreen
|
lateinit var navigationScreen: NavigationScreen
|
||||||
|
|
||||||
@@ -95,6 +110,15 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
|
|
||||||
val simulation = Simulation()
|
val simulation = Simulation()
|
||||||
|
|
||||||
|
private var routingEngine = 0
|
||||||
|
|
||||||
|
private var showTraffic = false;
|
||||||
|
|
||||||
|
var lastCameraSearch = 0
|
||||||
|
|
||||||
|
var speedCameras = listOf<Elements>()
|
||||||
|
|
||||||
|
var lastRouteDate: LocalDateTime = LocalDateTime.now()
|
||||||
var navigationManagerStarted = false
|
var navigationManagerStarted = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,8 +154,22 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
|
|
||||||
var guidanceAudio = 0
|
var guidanceAudio = 0
|
||||||
|
|
||||||
|
var lastTrafficDate: LocalDateTime = LocalDateTime.MIN
|
||||||
|
lateinit var observerManager: NavigationObserverManager
|
||||||
|
|
||||||
|
val repository = getSettingsRepository(carContext)
|
||||||
|
|
||||||
|
val settingsViewModel = getSettingsViewModel(carContext)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycle.addObserver(lifecycleObserver)
|
lifecycle.addObserver(lifecycleObserver)
|
||||||
|
repository.routingEngineFlow.asLiveData().observe(this, Observer {
|
||||||
|
routingEngine = it
|
||||||
|
})
|
||||||
|
|
||||||
|
repository.trafficFlow.asLiveData().observe(this, Observer {
|
||||||
|
showTraffic = it
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,6 +182,8 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||||
else -> NavigationViewModel(TomTomRepository())
|
else -> NavigationViewModel(TomTomRepository())
|
||||||
}
|
}
|
||||||
|
observerManager = NavigationObserverManager(navigationViewModel, this)
|
||||||
|
observerManager.attachAllObservers(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,11 +206,13 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
when (connectionState) {
|
when (connectionState) {
|
||||||
CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> Unit
|
CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> Unit
|
||||||
CarConnection.CONNECTION_TYPE_NATIVE -> {
|
CarConnection.CONNECTION_TYPE_NATIVE -> {
|
||||||
navigationViewModel.permissionGranted.value = checkPermission(carContext,AUTOMOTIVE_CAR_SPEED_PERMISSION)
|
navigationViewModel.permissionGranted.value =
|
||||||
|
checkPermission(carContext, AUTOMOTIVE_CAR_SPEED_PERMISSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
CarConnection.CONNECTION_TYPE_PROJECTION -> {
|
CarConnection.CONNECTION_TYPE_PROJECTION -> {
|
||||||
navigationViewModel.permissionGranted.value = checkPermission(carContext, GMS_CAR_SPEED_PERMISSION)
|
navigationViewModel.permissionGranted.value =
|
||||||
|
checkPermission(carContext, GMS_CAR_SPEED_PERMISSION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +284,7 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel, viewModelStoreOwner)
|
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, viewModelStoreOwner)
|
||||||
|
|
||||||
carSensorManager = CarSensorManager(
|
carSensorManager = CarSensorManager(
|
||||||
carContext = carContext,
|
carContext = carContext,
|
||||||
@@ -281,7 +323,6 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
navigationScreen = NavigationScreen(
|
navigationScreen = NavigationScreen(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
routeModel,
|
|
||||||
this,
|
this,
|
||||||
navigationViewModel
|
navigationViewModel
|
||||||
)
|
)
|
||||||
@@ -369,6 +410,11 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
* Handles route snapping, deviation detection for rerouting, and map updates.
|
* Handles route snapping, deviation detection for rerouting, and map updates.
|
||||||
*/
|
*/
|
||||||
fun updateLocation(location: Location) {
|
fun updateLocation(location: Location) {
|
||||||
|
val streetName = if (routeModel.isNavigating()) {
|
||||||
|
routeModel.currentStep().street
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
if (routeModel.navState.carConnection == CarConnection.CONNECTION_TYPE_PROJECTION) {
|
if (routeModel.navState.carConnection == CarConnection.CONNECTION_TYPE_PROJECTION) {
|
||||||
surfaceRenderer.updateCarSpeed(location.speed)
|
surfaceRenderer.updateCarSpeed(location.speed)
|
||||||
}
|
}
|
||||||
@@ -376,8 +422,8 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
if (routeModel.isNavigating()) {
|
if (routeModel.isNavigating()) {
|
||||||
handleNavigationLocation(location)
|
handleNavigationLocation(location)
|
||||||
} else {
|
} else {
|
||||||
navigationScreen.checkTraffic(LocalDateTime.now(ZoneOffset.UTC), location)
|
checkTraffic(LocalDateTime.now(ZoneOffset.UTC), location)
|
||||||
surfaceRenderer.updateLocation(location)
|
surfaceRenderer.updateLocation(location, streetName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,11 +441,19 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
* Snaps location to route and checks for deviation requiring reroute.
|
* Snaps location to route and checks for deviation requiring reroute.
|
||||||
*/
|
*/
|
||||||
private fun handleNavigationLocation(location: Location) {
|
private fun handleNavigationLocation(location: Location) {
|
||||||
|
|
||||||
if (guidanceAudio == 1) {
|
if (guidanceAudio == 1) {
|
||||||
handleGuidanceAudio()
|
handleGuidanceAudio()
|
||||||
}
|
}
|
||||||
navigationScreen.updateTrip(location)
|
val streetName = routeModel.currentStep().street
|
||||||
|
val currentDate = LocalDateTime.now(ZoneOffset.UTC)
|
||||||
|
checkTraffic(currentDate, location)
|
||||||
|
updateSpeedCamera(location)
|
||||||
|
checkRoute(currentDate, location)
|
||||||
|
routeModel.updateLocation(location, navigationViewModel)
|
||||||
|
checkArrival()
|
||||||
|
|
||||||
|
updateTripNavigationScreen(location)
|
||||||
|
|
||||||
if (routeModel.navState.arrived) return
|
if (routeModel.navState.arrived) return
|
||||||
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
||||||
val distance = location.distanceTo(snappedLocation)
|
val distance = location.distanceTo(snappedLocation)
|
||||||
@@ -409,15 +463,81 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
distance < MAXIMAL_SNAP_CORRECTION -> {
|
distance < MAXIMAL_SNAP_CORRECTION -> {
|
||||||
surfaceRenderer.updateLocation(snappedLocation)
|
surfaceRenderer.updateLocation(snappedLocation, streetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
surfaceRenderer.updateLocation(location)
|
surfaceRenderer.updateLocation(location, streetName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateTripNavigationScreen(location: Location) {
|
||||||
|
val travelEstimateTrip = routeModel.travelEstimateTrip(carContext, 0)
|
||||||
|
val travelEstimateStep = routeModel.travelEstimateStep(carContext, 0)
|
||||||
|
val steps = mutableListOf<Step>()
|
||||||
|
val street = if (routeModel.navState.destination.street != null) {
|
||||||
|
routeModel.navState.destination.street!!
|
||||||
|
} else {
|
||||||
|
// routeModel.navState.destination.name!!
|
||||||
|
"Street"
|
||||||
|
}
|
||||||
|
val destination = Destination.Builder()
|
||||||
|
.setName(street)
|
||||||
|
.setAddress(street)
|
||||||
|
.build()
|
||||||
|
val distance =
|
||||||
|
formattedDistance(0, routeModel.routeCalculator.leftStepDistance())
|
||||||
|
steps.add(routeModel.currentStep(carContext))
|
||||||
|
if (routeModel.navState.nextStep) {
|
||||||
|
steps.add(routeModel.nextStep(carContext = carContext))
|
||||||
|
|
||||||
|
}
|
||||||
|
navigationScreen.updateTrip(
|
||||||
|
isNavigating = routeModel.isNavigating(),
|
||||||
|
isRerouting = false,
|
||||||
|
hasArrived = routeModel.isArrival(),
|
||||||
|
destinationTravelEstimate = travelEstimateTrip,
|
||||||
|
stepTravelEstimate = travelEstimateStep,
|
||||||
|
destinations = mutableListOf(destination),
|
||||||
|
steps = steps,
|
||||||
|
nextStepRemainingDistance = Distance.create(distance.first, distance.second),
|
||||||
|
shouldShowNextStep = false,
|
||||||
|
shouldShowLanes = true,
|
||||||
|
junctionImage = null,
|
||||||
|
backGroundColor = routeModel.backGroundColor()
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the trip information and notifies the listener with a new Trip object.
|
||||||
|
* This includes destination name, address, travel estimate, and loading status.
|
||||||
|
*/
|
||||||
|
|
||||||
|
val tripBuilder = Trip.Builder()
|
||||||
|
tripBuilder.addDestination(
|
||||||
|
destination,
|
||||||
|
travelEstimateTrip
|
||||||
|
)
|
||||||
|
tripBuilder.setLoading(false)
|
||||||
|
tripBuilder.setCurrentRoad(destination.name.toString())
|
||||||
|
tripBuilder.addStep(steps.first(), travelEstimateStep)
|
||||||
|
updateTrip(tripBuilder.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for arrival
|
||||||
|
*/
|
||||||
|
fun checkArrival() {
|
||||||
|
if (routeModel.isArrival()
|
||||||
|
&& routeModel.routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||||
|
) {
|
||||||
|
stopNavigation()
|
||||||
|
settingsViewModel.onLastRouteChanged("")
|
||||||
|
routeModel.navState = routeModel.navState.copy(arrived = true)
|
||||||
|
surfaceRenderer.routeData.value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops active navigation and clears route state.
|
* Stops active navigation and clears route state.
|
||||||
* Called when user exits navigation or arrives at destination.
|
* Called when user exits navigation or arrives at destination.
|
||||||
@@ -430,6 +550,7 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
autoDriveEnabled = false
|
autoDriveEnabled = false
|
||||||
}
|
}
|
||||||
surfaceRenderer.routeData.value = ""
|
surfaceRenderer.routeData.value = ""
|
||||||
|
lastCameraSearch = 0
|
||||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||||
navigationScreen.navigationType = NavigationType.VIEW
|
navigationScreen.navigationType = NavigationType.VIEW
|
||||||
}
|
}
|
||||||
@@ -470,6 +591,195 @@ class NavigationSession : Session(), NavigationListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the received route string.
|
||||||
|
* Starts navigation and invalidates the screen.
|
||||||
|
*/
|
||||||
|
override fun onRouteReceived(route: String) {
|
||||||
|
if (route.isNotEmpty()) {
|
||||||
|
this.route = route
|
||||||
|
if (routeModel.isNavigating()) {
|
||||||
|
updateRoute(route)
|
||||||
|
} else {
|
||||||
|
prepareRoute(route)
|
||||||
|
}
|
||||||
|
updateTripNavigationScreen(surfaceRenderer.lastLocation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare route and start navigation
|
||||||
|
*/
|
||||||
|
private fun prepareRoute(route: String) {
|
||||||
|
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
|
||||||
|
routeModel.startNavigation(route)
|
||||||
|
if (routeModel.hasLegs()) {
|
||||||
|
settingsViewModel.onLastRouteChanged(route)
|
||||||
|
}
|
||||||
|
surfaceRenderer.setRouteData(routeModel.curRoute.routeGeoJson)
|
||||||
|
startNavigation()
|
||||||
|
updateTripNavigationScreen(surfaceRenderer.lastLocation)
|
||||||
|
//navigationScreen.updateTrip(surfaceRenderer.lastLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update route and traffic data
|
||||||
|
*/
|
||||||
|
private fun updateRoute(route: String) {
|
||||||
|
val newRouteModel = RouteModel()
|
||||||
|
newRouteModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
|
||||||
|
newRouteModel.startNavigation(route)
|
||||||
|
routeModel.curRoute.summary.trafficDelay = newRouteModel.curRoute.summary.trafficDelay
|
||||||
|
//navigationScreen.updateTrip(surfaceRenderer.lastLocation)
|
||||||
|
updateTripNavigationScreen(surfaceRenderer.lastLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun isNavigating(): Boolean = routeModel.isNavigating()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles received traffic data and updates the surface renderer.
|
||||||
|
*/
|
||||||
|
override fun onTrafficReceived(traffic: Map<String, String>) {
|
||||||
|
if (traffic.isNotEmpty()) {
|
||||||
|
surfaceRenderer.setTrafficData(traffic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the received place search result.
|
||||||
|
* Navigates to the specified place.
|
||||||
|
*/
|
||||||
|
override fun onPlaceSearchResultReceived(place: Place) {
|
||||||
|
navigateToPlace(place)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles received speed camera data.
|
||||||
|
* Updates the surface renderer with the camera locations.
|
||||||
|
*/
|
||||||
|
override fun onSpeedCamerasReceived(cameras: List<Elements>) {
|
||||||
|
speedCameras = cameras
|
||||||
|
val coordinates = mutableListOf<List<Double>>()
|
||||||
|
cameras.forEach {
|
||||||
|
coordinates.add(listOf(it.lon, it.lat))
|
||||||
|
}
|
||||||
|
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
||||||
|
surfaceRenderer.speedCamerasData.value = speedData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles received maximum speed data and updates the surface renderer.
|
||||||
|
*/
|
||||||
|
override fun onMaxSpeedReceived(speed: Int) {
|
||||||
|
surfaceRenderer.maxSpeed.value = speed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidateScreen() {
|
||||||
|
navigationScreen.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a route to the specified place and sets it as the destination.
|
||||||
|
*/
|
||||||
|
override fun navigateToPlace(place: Place) {
|
||||||
|
val preview = place.route //navigationViewModel.previewRoute.value
|
||||||
|
navigationViewModel.previewRoute.value = ""
|
||||||
|
val location = location(place.longitude, place.latitude)
|
||||||
|
navigationViewModel.saveRecent(carContext, place)
|
||||||
|
//currentNavigationLocation = location
|
||||||
|
if (preview.isEmpty()) {
|
||||||
|
navigationViewModel.loadRoute(
|
||||||
|
carContext,
|
||||||
|
surfaceRenderer.lastLocation,
|
||||||
|
location,
|
||||||
|
surfaceRenderer.carOrientation
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
routeModel.navState = routeModel.navState.copy(currentRouteIndex = place.routeIndex)
|
||||||
|
onRouteReceived(preview)
|
||||||
|
}
|
||||||
|
routeModel.navState = routeModel.navState.copy(destination = place)
|
||||||
|
surfaceRenderer.activateNavigationView()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if traffic data needs to be updated based on the time since the last update.
|
||||||
|
*/
|
||||||
|
fun checkTraffic(current: LocalDateTime, location: Location) {
|
||||||
|
val duration = Duration.between(current, lastTrafficDate)
|
||||||
|
if (showTraffic && duration.abs().seconds > TRAFFIC_UPDATE) {
|
||||||
|
lastTrafficDate = current
|
||||||
|
navigationViewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Periodically requests speed camera information near the current location.
|
||||||
|
*/
|
||||||
|
private fun updateSpeedCamera(location: Location) {
|
||||||
|
if (lastCameraSearch++ % 100 == 0) {
|
||||||
|
navigationViewModel.getSpeedCameras(location, 5.0)
|
||||||
|
}
|
||||||
|
if (speedCameras.isNotEmpty()) {
|
||||||
|
updateDistance(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates distances to nearby speed cameras and checks for proximity alerts.
|
||||||
|
*/
|
||||||
|
private fun updateDistance(
|
||||||
|
location: Location,
|
||||||
|
) {
|
||||||
|
val updatedCameras = mutableListOf<Elements>()
|
||||||
|
speedCameras.forEach {
|
||||||
|
val plLocation =
|
||||||
|
location(longitude = it.lon, latitude = it.lat)
|
||||||
|
val distance = plLocation.distanceTo(location)
|
||||||
|
it.distance = distance.toDouble()
|
||||||
|
updatedCameras.add(it)
|
||||||
|
}
|
||||||
|
val sortedList = updatedCameras.sortedWith(compareBy { it.distance })
|
||||||
|
val camera = sortedList.firstOrNull() ?: return
|
||||||
|
val bearingRoute = surfaceRenderer.lastLocation.bearingTo(location)
|
||||||
|
val bearingSpeedCamera = if (camera.tags.direction != null) {
|
||||||
|
try {
|
||||||
|
camera.tags.direction!!.toFloat()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
0F
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
location.bearingTo(location(camera.lon, camera.lat)).absoluteValue
|
||||||
|
}
|
||||||
|
if (camera.distance < 80) {
|
||||||
|
if ((bearingSpeedCamera - bearingRoute.absoluteValue).absoluteValue < 15.0) {
|
||||||
|
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a new route is needed based on the time since the last update.
|
||||||
|
*/
|
||||||
|
private fun checkRoute(currentDate: LocalDateTime, location: Location) {
|
||||||
|
val duration = Duration.between(currentDate, lastRouteDate)
|
||||||
|
val routeUpdate = routeModel.curRoute.summary.duration / 4
|
||||||
|
if (duration.abs().seconds > routeUpdate) {
|
||||||
|
lastRouteDate = currentDate
|
||||||
|
val destination = location(
|
||||||
|
routeModel.navState.destination.longitude,
|
||||||
|
routeModel.navState.destination.latitude
|
||||||
|
)
|
||||||
|
navigationViewModel.loadRoute(
|
||||||
|
carContext,
|
||||||
|
location,
|
||||||
|
destination,
|
||||||
|
surfaceRenderer.carOrientation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// URI host for deep linking
|
// URI host for deep linking
|
||||||
var uriHost: String = "navigation"
|
var uriHost: String = "navigation"
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ import java.time.LocalDateTime
|
|||||||
class SurfaceRenderer(
|
class SurfaceRenderer(
|
||||||
private var carContext: CarContext,
|
private var carContext: CarContext,
|
||||||
private var lifecycle: Lifecycle,
|
private var lifecycle: Lifecycle,
|
||||||
private var routeModel: RouteCarModel,
|
//private var routeModel: RouteCarModel,
|
||||||
private var viewModelStoreOwner: ViewModelStoreOwner
|
private var viewModelStoreOwner: ViewModelStoreOwner
|
||||||
) : DefaultLifecycleObserver {
|
) : DefaultLifecycleObserver {
|
||||||
|
|
||||||
@@ -362,13 +362,9 @@ class SurfaceRenderer(
|
|||||||
* Calculates appropriate bearing, zoom, and maintains view style.
|
* Calculates appropriate bearing, zoom, and maintains view style.
|
||||||
* Uses car orientation sensor if available, otherwise falls back to location bearing.
|
* Uses car orientation sensor if available, otherwise falls back to location bearing.
|
||||||
*/
|
*/
|
||||||
fun updateLocation(location: Location) {
|
fun updateLocation(location: Location, streetName : String) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (routeModel.isNavigating()) {
|
street.value = streetName
|
||||||
street.value = routeModel.currentStep().street
|
|
||||||
} else {
|
|
||||||
street.value = ""
|
|
||||||
}
|
|
||||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||||
val bearing = if (carOrientation == 999F) {
|
val bearing = if (carOrientation == 999F) {
|
||||||
if (location.hasBearing()) {
|
if (location.hasBearing()) {
|
||||||
@@ -402,8 +398,8 @@ class SurfaceRenderer(
|
|||||||
/**
|
/**
|
||||||
* Sets route data for active navigation and switches to VIEW mode.
|
* Sets route data for active navigation and switches to VIEW mode.
|
||||||
*/
|
*/
|
||||||
fun setRouteData() {
|
fun setRouteData(routeGeoJson: String) {
|
||||||
routeData.value = routeModel.curRoute.routeGeoJson
|
routeData.value = routeGeoJson
|
||||||
viewStyle = ViewStyle.VIEW
|
viewStyle = ViewStyle.VIEW
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +409,7 @@ class SurfaceRenderer(
|
|||||||
fun activateNavigationView() {
|
fun activateNavigationView() {
|
||||||
viewStyle = ViewStyle.VIEW
|
viewStyle = ViewStyle.VIEW
|
||||||
tilt = TILT
|
tilt = TILT
|
||||||
updateLocation(lastLocation)
|
updateLocation(lastLocation, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -481,11 +477,11 @@ class SurfaceRenderer(
|
|||||||
* Updates car location from the connected car system.
|
* Updates car location from the connected car system.
|
||||||
* Only updates location when using OSRM routing engine.
|
* Only updates location when using OSRM routing engine.
|
||||||
*/
|
*/
|
||||||
fun updateCarLocation(location: Location) {
|
fun updateCarLocation(location: Location, streetName: String) {
|
||||||
val repository = getSettingsRepository(carContext)
|
val repository = getSettingsRepository(carContext)
|
||||||
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
|
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
|
||||||
if (routingEngine == RouteEngine.OSRM.ordinal) {
|
if (routingEngine == RouteEngine.OSRM.ordinal) {
|
||||||
updateLocation(location)
|
updateLocation(location, streetName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kouros.navigation.car.screen
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import androidx.car.app.navigation.model.Trip
|
import androidx.car.app.navigation.model.Trip
|
||||||
|
import com.kouros.navigation.data.Place
|
||||||
|
|
||||||
|
|
||||||
/** A listener for navigation start and stop signals. */
|
/** A listener for navigation start and stop signals. */
|
||||||
@@ -14,4 +14,6 @@ interface NavigationListener {
|
|||||||
|
|
||||||
/** Updates trip information. */
|
/** Updates trip information. */
|
||||||
fun updateTrip(trip: Trip)
|
fun updateTrip(trip: Trip)
|
||||||
|
|
||||||
|
fun navigateToPlace(place: Place)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.car.app.Screen
|
|||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
import androidx.car.app.model.Action.FLAG_IS_PERSISTENT
|
import androidx.car.app.model.Action.FLAG_IS_PERSISTENT
|
||||||
import androidx.car.app.model.ActionStrip
|
import androidx.car.app.model.ActionStrip
|
||||||
|
import androidx.car.app.model.CarColor
|
||||||
import androidx.car.app.model.CarIcon
|
import androidx.car.app.model.CarIcon
|
||||||
import androidx.car.app.model.Distance
|
import androidx.car.app.model.Distance
|
||||||
import androidx.car.app.model.Header
|
import androidx.car.app.model.Header
|
||||||
@@ -21,7 +22,8 @@ import androidx.car.app.navigation.model.MapWithContentTemplate
|
|||||||
import androidx.car.app.navigation.model.MessageInfo
|
import androidx.car.app.navigation.model.MessageInfo
|
||||||
import androidx.car.app.navigation.model.NavigationTemplate
|
import androidx.car.app.navigation.model.NavigationTemplate
|
||||||
import androidx.car.app.navigation.model.RoutingInfo
|
import androidx.car.app.navigation.model.RoutingInfo
|
||||||
import androidx.car.app.navigation.model.Trip
|
import androidx.car.app.navigation.model.Step
|
||||||
|
import androidx.car.app.navigation.model.TravelEstimate
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
@@ -30,29 +32,16 @@ import androidx.lifecycle.asLiveData
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
|
||||||
import com.kouros.navigation.car.screen.observers.NavigationObserverCallback
|
|
||||||
import com.kouros.navigation.car.screen.observers.NavigationObserverManager
|
|
||||||
import com.kouros.navigation.car.screen.settings.SettingsScreen
|
import com.kouros.navigation.car.screen.settings.SettingsScreen
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
|
||||||
import com.kouros.navigation.data.Constants.TRAFFIC_UPDATE
|
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.ViewStyle
|
import com.kouros.navigation.data.ViewStyle
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
|
||||||
import com.kouros.navigation.model.NavigationViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.model.RouteModel
|
|
||||||
import com.kouros.navigation.utils.GeoUtils
|
|
||||||
import com.kouros.navigation.utils.formattedDistance
|
|
||||||
import com.kouros.navigation.utils.getSettingsRepository
|
import com.kouros.navigation.utils.getSettingsRepository
|
||||||
import com.kouros.navigation.utils.getSettingsViewModel
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.time.Duration
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.ZoneOffset
|
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main screen for car navigation.
|
* Main screen for car navigation.
|
||||||
@@ -61,10 +50,9 @@ import kotlin.math.absoluteValue
|
|||||||
open class NavigationScreen(
|
open class NavigationScreen(
|
||||||
carContext: CarContext,
|
carContext: CarContext,
|
||||||
private var surfaceRenderer: SurfaceRenderer,
|
private var surfaceRenderer: SurfaceRenderer,
|
||||||
private var routeModel: RouteCarModel,
|
|
||||||
private var listener: NavigationListener,
|
private var listener: NavigationListener,
|
||||||
private val navigationViewModel: NavigationViewModel
|
private val navigationViewModel: NavigationViewModel
|
||||||
) : Screen(carContext), NavigationObserverCallback {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
|
||||||
@@ -73,13 +61,6 @@ open class NavigationScreen(
|
|||||||
var recentPlace: Place = Place()
|
var recentPlace: Place = Place()
|
||||||
var navigationType = NavigationType.VIEW
|
var navigationType = NavigationType.VIEW
|
||||||
|
|
||||||
var lastTrafficDate: LocalDateTime = LocalDateTime.MIN
|
|
||||||
|
|
||||||
var lastRouteDate: LocalDateTime = LocalDateTime.now()
|
|
||||||
var lastCameraSearch = 0
|
|
||||||
var speedCameras = listOf<Elements>()
|
|
||||||
val observerManager = NavigationObserverManager(navigationViewModel, this)
|
|
||||||
|
|
||||||
val repository = getSettingsRepository(carContext)
|
val repository = getSettingsRepository(carContext)
|
||||||
|
|
||||||
val settingsViewModel = getSettingsViewModel(carContext)
|
val settingsViewModel = getSettingsViewModel(carContext)
|
||||||
@@ -89,13 +70,31 @@ open class NavigationScreen(
|
|||||||
private var tripSuggestion = false
|
private var tripSuggestion = false
|
||||||
|
|
||||||
private var tripSuggestionCalled = false
|
private var tripSuggestionCalled = false
|
||||||
|
|
||||||
private var routingEngine = 0
|
|
||||||
|
|
||||||
private var showTraffic = false;
|
|
||||||
private var arrivalTimer: CountDownTimer? = null
|
private var arrivalTimer: CountDownTimer? = null
|
||||||
private var reRouteTimer: CountDownTimer? = null
|
private var reRouteTimer: CountDownTimer? = null
|
||||||
|
|
||||||
|
private var isNavigating = false
|
||||||
|
private var isRerouting = false
|
||||||
|
private var hasArrived = false
|
||||||
|
|
||||||
|
private lateinit var destinations: MutableList<Destination>
|
||||||
|
|
||||||
|
private lateinit var stepRemainingDistance: Distance
|
||||||
|
|
||||||
|
private lateinit var destinationTravelEstimate: TravelEstimate
|
||||||
|
|
||||||
|
private lateinit var stepTravelEstimate: TravelEstimate
|
||||||
|
|
||||||
|
private var shouldShowNextStep = false
|
||||||
|
|
||||||
|
private var shouldShowLanes = false
|
||||||
|
|
||||||
|
private lateinit var steps: MutableList<Step>
|
||||||
|
|
||||||
|
var junctionImage: CarIcon? = null
|
||||||
|
|
||||||
|
var backGroundColor = CarColor.BLUE
|
||||||
|
|
||||||
val observerRecentPlaces = Observer<List<Place>> { newPlaces ->
|
val observerRecentPlaces = Observer<List<Place>> { newPlaces ->
|
||||||
recentPlaces.addAll(newPlaces)
|
recentPlaces.addAll(newPlaces)
|
||||||
if (newPlaces.isNotEmpty() && !tripSuggestionCalled) {
|
if (newPlaces.isNotEmpty() && !tripSuggestionCalled) {
|
||||||
@@ -106,25 +105,17 @@ open class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observerManager.attachAllObservers(this)
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
settingsViewModel.tripSuggestion.first()
|
settingsViewModel.tripSuggestion.first()
|
||||||
settingsViewModel.routingEngine.first()
|
|
||||||
}
|
}
|
||||||
repository.distanceModeFlow.asLiveData().observe(this, Observer {
|
repository.distanceModeFlow.asLiveData().observe(this, Observer {
|
||||||
distanceMode = it
|
distanceMode = it
|
||||||
})
|
})
|
||||||
|
|
||||||
repository.trafficFlow.asLiveData().observe(this, Observer {
|
|
||||||
showTraffic = it
|
|
||||||
})
|
|
||||||
repository.tripSuggestionFlow.asLiveData().observe(this, Observer {
|
repository.tripSuggestionFlow.asLiveData().observe(this, Observer {
|
||||||
navigationViewModel.recentPlaces.observe(this, observerRecentPlaces)
|
navigationViewModel.recentPlaces.observe(this, observerRecentPlaces)
|
||||||
tripSuggestion = it
|
tripSuggestion = it
|
||||||
})
|
})
|
||||||
repository.routingEngineFlow.asLiveData().observe(this, Observer {
|
|
||||||
routingEngine = it
|
|
||||||
})
|
|
||||||
lifecycle.addObserver(object : DefaultLifecycleObserver {
|
lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||||
override fun onStop(owner: LifecycleOwner) {
|
override fun onStop(owner: LifecycleOwner) {
|
||||||
arrivalTimer?.cancel()
|
arrivalTimer?.cancel()
|
||||||
@@ -164,12 +155,11 @@ open class NavigationScreen(
|
|||||||
0,
|
0,
|
||||||
{ stopNavigation() })
|
{ stopNavigation() })
|
||||||
)
|
)
|
||||||
updateTrip()
|
|
||||||
return NavigationTemplate.Builder()
|
return NavigationTemplate.Builder()
|
||||||
.setNavigationInfo(
|
.setNavigationInfo(
|
||||||
getRoutingInfo()
|
getRoutingInfo()
|
||||||
)
|
)
|
||||||
.setDestinationTravelEstimate(routeModel.travelEstimateTrip(carContext, distanceMode))
|
.setDestinationTravelEstimate(destinationTravelEstimate)
|
||||||
.setActionStrip(actionStripBuilder.build())
|
.setActionStrip(actionStripBuilder.build())
|
||||||
.setMapActionStrip(
|
.setMapActionStrip(
|
||||||
mapActionStrip(
|
mapActionStrip(
|
||||||
@@ -185,7 +175,7 @@ open class NavigationScreen(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.setBackgroundColor(routeModel.backGroundColor())
|
.setBackgroundColor(backGroundColor)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +184,7 @@ open class NavigationScreen(
|
|||||||
*/
|
*/
|
||||||
private fun navigationViewTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
private fun navigationViewTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||||
return NavigationTemplate.Builder()
|
return NavigationTemplate.Builder()
|
||||||
.setBackgroundColor(routeModel.backGroundColor())
|
.setBackgroundColor(backGroundColor)
|
||||||
.setActionStrip(actionStripBuilder.build())
|
.setActionStrip(actionStripBuilder.build())
|
||||||
.setMapActionStrip(
|
.setMapActionStrip(
|
||||||
mapActionStrip(
|
mapActionStrip(
|
||||||
@@ -221,7 +211,7 @@ open class NavigationScreen(
|
|||||||
arrivalTimer = object : CountDownTimer(8000, 1000) {
|
arrivalTimer = object : CountDownTimer(8000, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {}
|
override fun onTick(millisUntilFinished: Long) {}
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
routeModel.navState = routeModel.navState.copy(arrived = false)
|
// routeModel.navState = routeModel.navState.copy(arrived = false)
|
||||||
navigationType = NavigationType.VIEW
|
navigationType = NavigationType.VIEW
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
@@ -235,8 +225,8 @@ open class NavigationScreen(
|
|||||||
*/
|
*/
|
||||||
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
|
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
|
||||||
var street = ""
|
var street = ""
|
||||||
if (routeModel.navState.destination.street != null) {
|
if (destinations.first().address != null) {
|
||||||
street = routeModel.navState.destination.street!!
|
street = destinations.first().address.toString()
|
||||||
}
|
}
|
||||||
return NavigationTemplate.Builder()
|
return NavigationTemplate.Builder()
|
||||||
.setNavigationInfo(
|
.setNavigationInfo(
|
||||||
@@ -255,7 +245,7 @@ open class NavigationScreen(
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setBackgroundColor(routeModel.backGroundColor())
|
// .setBackgroundColor(routeModel.backGroundColor())
|
||||||
.setActionStrip(actionStripBuilder.build())
|
.setActionStrip(actionStripBuilder.build())
|
||||||
.setMapActionStrip(
|
.setMapActionStrip(
|
||||||
mapActionStrip(
|
mapActionStrip(
|
||||||
@@ -299,7 +289,7 @@ open class NavigationScreen(
|
|||||||
createNavigateAction(it)
|
createNavigateAction(it)
|
||||||
)
|
)
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
navigateToPlace(it)
|
listener.navigateToPlace(it)
|
||||||
}
|
}
|
||||||
listBuilder.addItem(
|
listBuilder.addItem(
|
||||||
row.build()
|
row.build()
|
||||||
@@ -349,7 +339,7 @@ open class NavigationScreen(
|
|||||||
return NavigationTemplate.Builder()
|
return NavigationTemplate.Builder()
|
||||||
.setNavigationInfo(RoutingInfo.Builder().setLoading(true).build())
|
.setNavigationInfo(RoutingInfo.Builder().setLoading(true).build())
|
||||||
.setActionStrip(actionStripBuilder.build())
|
.setActionStrip(actionStripBuilder.build())
|
||||||
.setBackgroundColor(routeModel.backGroundColor())
|
// .setBackgroundColor(routeModel.backGroundColor())
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,16 +347,13 @@ open class NavigationScreen(
|
|||||||
* Builds and returns RoutingInfo based on the current step and distance.
|
* Builds and returns RoutingInfo based on the current step and distance.
|
||||||
*/
|
*/
|
||||||
fun getRoutingInfo(): RoutingInfo {
|
fun getRoutingInfo(): RoutingInfo {
|
||||||
val distance =
|
|
||||||
formattedDistance(distanceMode, routeModel.routeCalculator.leftStepDistance())
|
|
||||||
val routingInfo = RoutingInfo.Builder()
|
val routingInfo = RoutingInfo.Builder()
|
||||||
.setCurrentStep(
|
.setCurrentStep(
|
||||||
routeModel.currentStep(carContext = carContext),
|
steps.first(),
|
||||||
Distance.create(distance.first, distance.second)
|
stepRemainingDistance
|
||||||
)
|
)
|
||||||
if (routeModel.navState.nextStep) {
|
if (shouldShowNextStep && steps.size > 1) {
|
||||||
val nextStep = routeModel.nextStep(carContext = carContext)
|
routingInfo.setNextStep(steps[1])
|
||||||
routingInfo.setNextStep(nextStep)
|
|
||||||
}
|
}
|
||||||
return routingInfo.build()
|
return routingInfo.build()
|
||||||
}
|
}
|
||||||
@@ -391,7 +378,7 @@ open class NavigationScreen(
|
|||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
navigateToPlace(place)
|
listener.navigateToPlace(place)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -475,44 +462,18 @@ open class NavigationScreen(
|
|||||||
)
|
)
|
||||||
// result see observer
|
// result see observer
|
||||||
} else {
|
} else {
|
||||||
navigateToPlace(place)
|
listener.navigateToPlace(place)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a route to the specified place and sets it as the destination.
|
|
||||||
*/
|
|
||||||
fun navigateToPlace(place: Place) {
|
|
||||||
val preview = navigationViewModel.previewRoute.value
|
|
||||||
navigationViewModel.previewRoute.value = ""
|
|
||||||
val location = location(place.longitude, place.latitude)
|
|
||||||
navigationViewModel.saveRecent(carContext, place)
|
|
||||||
currentNavigationLocation = location
|
|
||||||
if (preview.isNullOrEmpty()) {
|
|
||||||
navigationViewModel.loadRoute(
|
|
||||||
carContext,
|
|
||||||
surfaceRenderer.lastLocation,
|
|
||||||
location,
|
|
||||||
surfaceRenderer.carOrientation
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
routeModel.navState = routeModel.navState.copy(currentRouteIndex = place.routeIndex)
|
|
||||||
navigationViewModel.route.value = preview
|
|
||||||
}
|
|
||||||
routeModel.navState = routeModel.navState.copy(destination = place)
|
|
||||||
surfaceRenderer.activateNavigationView()
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops navigation, resets state, and notifies listeners.
|
* Stops navigation, resets state, and notifies listeners.
|
||||||
*/
|
*/
|
||||||
fun stopNavigation() {
|
fun stopNavigation() {
|
||||||
navigationType = NavigationType.VIEW
|
navigationType = NavigationType.VIEW
|
||||||
listener.stopNavigation()
|
listener.stopNavigation()
|
||||||
lastCameraSearch = 0
|
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,227 +511,40 @@ open class NavigationScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates navigation state with the current location, checks for arrival, and traffic updates.
|
* Updates navigation state with the current location, checks for arrival, and traffic updates.
|
||||||
*/
|
*/
|
||||||
fun updateTrip(location: Location) {
|
fun updateTrip(
|
||||||
val currentDate = LocalDateTime.now(ZoneOffset.UTC)
|
isNavigating: Boolean,
|
||||||
checkRoute(currentDate, location)
|
isRerouting: Boolean,
|
||||||
checkTraffic(currentDate, location)
|
hasArrived: Boolean,
|
||||||
|
destinations: MutableList<Destination>,
|
||||||
updateSpeedCamera(location)
|
steps: MutableList<Step>,
|
||||||
|
destinationTravelEstimate: TravelEstimate,
|
||||||
routeModel.updateLocation(location, navigationViewModel)
|
stepTravelEstimate: TravelEstimate,
|
||||||
checkArrival()
|
nextStepRemainingDistance: Distance,
|
||||||
|
shouldShowNextStep: Boolean,
|
||||||
invalidate()
|
shouldShowLanes: Boolean,
|
||||||
}
|
junctionImage: CarIcon?,
|
||||||
|
backGroundColor: CarColor
|
||||||
/**
|
|
||||||
* Checks if a new route is needed based on the time since the last update.
|
|
||||||
*/
|
|
||||||
private fun checkRoute(currentDate: LocalDateTime, location: Location) {
|
|
||||||
val duration = Duration.between(currentDate, lastRouteDate)
|
|
||||||
val routeUpdate = routeModel.curRoute.summary.duration / 4
|
|
||||||
if (duration.abs().seconds > routeUpdate) {
|
|
||||||
lastRouteDate = currentDate
|
|
||||||
val destination = location(
|
|
||||||
routeModel.navState.destination.longitude,
|
|
||||||
routeModel.navState.destination.latitude
|
|
||||||
)
|
|
||||||
navigationViewModel.loadRoute(
|
|
||||||
carContext,
|
|
||||||
location,
|
|
||||||
destination,
|
|
||||||
surfaceRenderer.carOrientation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if traffic data needs to be updated based on the time since the last update.
|
|
||||||
*/
|
|
||||||
fun checkTraffic(current: LocalDateTime, location: Location) {
|
|
||||||
val duration = Duration.between(current, lastTrafficDate)
|
|
||||||
if (showTraffic && duration.abs().seconds > TRAFFIC_UPDATE) {
|
|
||||||
lastTrafficDate = current
|
|
||||||
navigationViewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks for arrival
|
|
||||||
*/
|
|
||||||
fun checkArrival() {
|
|
||||||
if (routeModel.isArrival()
|
|
||||||
&& routeModel.routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
|
||||||
) {
|
) {
|
||||||
listener.stopNavigation()
|
this.isNavigating = isNavigating
|
||||||
settingsViewModel.onLastRouteChanged("")
|
this.isRerouting = isRerouting
|
||||||
routeModel.navState = routeModel.navState.copy(arrived = true)
|
this.hasArrived = hasArrived
|
||||||
surfaceRenderer.routeData.value = ""
|
this.destinations = destinations
|
||||||
navigationType = NavigationType.ARRIVAL
|
this.steps = steps
|
||||||
invalidate()
|
stepRemainingDistance = nextStepRemainingDistance
|
||||||
}
|
this.destinationTravelEstimate = destinationTravelEstimate
|
||||||
}
|
this.stepTravelEstimate = stepTravelEstimate
|
||||||
|
this.shouldShowNextStep = shouldShowNextStep
|
||||||
|
this.shouldShowLanes = shouldShowLanes
|
||||||
|
this.junctionImage = junctionImage
|
||||||
|
this.backGroundColor = backGroundColor
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the trip information and notifies the listener with a new Trip object.
|
|
||||||
* This includes destination name, address, travel estimate, and loading status.
|
|
||||||
*/
|
|
||||||
private fun updateTrip() {
|
|
||||||
if (routeModel.isNavigating() && !routeModel.navState.destination.name.isNullOrEmpty()) {
|
|
||||||
val tripBuilder = Trip.Builder()
|
|
||||||
val destination = Destination.Builder()
|
|
||||||
.setName(routeModel.navState.destination.name ?: "")
|
|
||||||
.setAddress(routeModel.navState.destination.street ?: "")
|
|
||||||
.build()
|
|
||||||
tripBuilder.addDestination(
|
|
||||||
destination,
|
|
||||||
routeModel.travelEstimateTrip(carContext, distanceMode)
|
|
||||||
)
|
|
||||||
tripBuilder.setLoading(false)
|
|
||||||
tripBuilder.setCurrentRoad(routeModel.currentStep.street)
|
|
||||||
tripBuilder.addStep(routeModel.currentStep(carContext), routeModel.travelEstimateStep(carContext, distanceMode ))
|
|
||||||
listener.updateTrip(tripBuilder.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Periodically requests speed camera information near the current location.
|
|
||||||
*/
|
|
||||||
private fun updateSpeedCamera(location: Location) {
|
|
||||||
if (lastCameraSearch++ % 100 == 0) {
|
|
||||||
navigationViewModel.getSpeedCameras(location, 5.0)
|
|
||||||
}
|
|
||||||
if (speedCameras.isNotEmpty()) {
|
|
||||||
updateDistance(location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates distances to nearby speed cameras and checks for proximity alerts.
|
|
||||||
*/
|
|
||||||
private fun updateDistance(
|
|
||||||
location: Location,
|
|
||||||
) {
|
|
||||||
val updatedCameras = mutableListOf<Elements>()
|
|
||||||
speedCameras.forEach {
|
|
||||||
val plLocation =
|
|
||||||
location(longitude = it.lon, latitude = it.lat)
|
|
||||||
val distance = plLocation.distanceTo(location)
|
|
||||||
it.distance = distance.toDouble()
|
|
||||||
updatedCameras.add(it)
|
|
||||||
}
|
|
||||||
val sortedList = updatedCameras.sortedWith(compareBy { it.distance })
|
|
||||||
val camera = sortedList.firstOrNull() ?: return
|
|
||||||
val bearingRoute = surfaceRenderer.lastLocation.bearingTo(location)
|
|
||||||
val bearingSpeedCamera = if (camera.tags.direction != null) {
|
|
||||||
try {
|
|
||||||
camera.tags.direction!!.toFloat()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
0F
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
location.bearingTo(location(camera.lon, camera.lat)).absoluteValue
|
|
||||||
}
|
|
||||||
if (camera.distance < 80) {
|
|
||||||
if ((bearingSpeedCamera - bearingRoute.absoluteValue).absoluteValue < 15.0) {
|
|
||||||
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the received route string.
|
|
||||||
* Starts navigation and invalidates the screen.
|
|
||||||
*/
|
|
||||||
override fun onRouteReceived(route: String) {
|
|
||||||
if (route.isNotEmpty()) {
|
|
||||||
if (routeModel.isNavigating()) {
|
|
||||||
updateRoute(route)
|
|
||||||
} else {
|
|
||||||
prepareRoute(route)
|
|
||||||
}
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare route and start navigation
|
|
||||||
*/
|
|
||||||
private fun prepareRoute(route: String) {
|
|
||||||
routeModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
|
|
||||||
navigationType = NavigationType.NAVIGATION
|
navigationType = NavigationType.NAVIGATION
|
||||||
routeModel.startNavigation(route)
|
|
||||||
if (routeModel.hasLegs()) {
|
|
||||||
settingsViewModel.onLastRouteChanged(route)
|
|
||||||
}
|
|
||||||
surfaceRenderer.setRouteData()
|
|
||||||
listener.startNavigation()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update route and traffic data
|
|
||||||
*/
|
|
||||||
private fun updateRoute(route: String) {
|
|
||||||
val newRouteModel = RouteModel()
|
|
||||||
newRouteModel.navState = routeModel.navState.copy(routingEngine = routingEngine)
|
|
||||||
navigationType = NavigationType.NAVIGATION
|
|
||||||
newRouteModel.startNavigation(route)
|
|
||||||
routeModel.curRoute.summary.trafficDelay = newRouteModel.curRoute.summary.trafficDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if navigation is currently active.
|
|
||||||
*/
|
|
||||||
override fun isNavigating(): Boolean = routeModel.isNavigating()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles received traffic data and updates the surface renderer.
|
|
||||||
*/
|
|
||||||
override fun onTrafficReceived(traffic: Map<String, String>) {
|
|
||||||
if (traffic.isNotEmpty()) {
|
|
||||||
surfaceRenderer.setTrafficData(traffic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the received place search result.
|
|
||||||
* Navigates to the specified place.
|
|
||||||
*/
|
|
||||||
override fun onPlaceSearchResultReceived(place: Place) {
|
|
||||||
navigateToPlace(place)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles received speed camera data.
|
|
||||||
* Updates the surface renderer with the camera locations.
|
|
||||||
*/
|
|
||||||
override fun onSpeedCamerasReceived(cameras: List<Elements>) {
|
|
||||||
speedCameras = cameras
|
|
||||||
val coordinates = mutableListOf<List<Double>>()
|
|
||||||
cameras.forEach {
|
|
||||||
coordinates.add(listOf(it.lon, it.lat))
|
|
||||||
}
|
|
||||||
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
|
||||||
surfaceRenderer.speedCamerasData.value = speedData
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles received maximum speed data and updates the surface renderer.
|
|
||||||
*/
|
|
||||||
override fun onMaxSpeedReceived(speed: Int) {
|
|
||||||
surfaceRenderer.maxSpeed.value = speed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates the screen.
|
|
||||||
*/
|
|
||||||
override fun invalidateScreen() {
|
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -347,6 +347,7 @@ class RoutePreviewScreen(
|
|||||||
|
|
||||||
private fun onNavigate(index: Int) {
|
private fun onNavigate(index: Int) {
|
||||||
destination.routeIndex = index
|
destination.routeIndex = index
|
||||||
|
destination.route = navigationViewModel.previewRoute.value.toString()
|
||||||
setResult(destination)
|
setResult(destination)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.kouros.navigation.car.screen.observers
|
package com.kouros.navigation.car.screen.observers
|
||||||
|
|
||||||
|
import com.kouros.navigation.car.NavigationSession
|
||||||
import com.kouros.navigation.model.NavigationViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,16 +18,12 @@ class NavigationObserverManager(
|
|||||||
val speedCameraObserver = SpeedCameraObserver(callback)
|
val speedCameraObserver = SpeedCameraObserver(callback)
|
||||||
val maxSpeedObserver = MaxSpeedObserver(callback)
|
val maxSpeedObserver = MaxSpeedObserver(callback)
|
||||||
|
|
||||||
/**
|
fun attachAllObservers(session: NavigationSession) {
|
||||||
* Attaches all observers to the ViewModel.
|
viewModel.route.observe(session, routeObserver)
|
||||||
* Call this from NavigationScreen's init block or lifecycle method.
|
viewModel.traffic.observe(session, trafficObserver)
|
||||||
*/
|
viewModel.placeLocation.observe(session, placeSearchObserver)
|
||||||
fun attachAllObservers(screen: androidx.car.app.Screen) {
|
viewModel.speedCameras.observe(session, speedCameraObserver)
|
||||||
viewModel.route.observe(screen, routeObserver)
|
viewModel.maxSpeed.observe(session, maxSpeedObserver)
|
||||||
viewModel.traffic.observe(screen, trafficObserver)
|
|
||||||
viewModel.placeLocation.observe(screen, placeSearchObserver)
|
|
||||||
viewModel.speedCameras.observe(screen, speedCameraObserver)
|
|
||||||
viewModel.maxSpeed.observe(screen, maxSpeedObserver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ data class Place(
|
|||||||
var distance: Float = 0F,
|
var distance: Float = 0F,
|
||||||
//var avatar: Uri? = null,
|
//var avatar: Uri? = null,
|
||||||
var lastDate: Long = 0,
|
var lastDate: Long = 0,
|
||||||
var routeIndex: Int = 0
|
var routeIndex: Int = 0,
|
||||||
|
var route: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ContactData(
|
data class ContactData(
|
||||||
|
|||||||
Reference in New Issue
Block a user