From b99ebfd36f7af16ef917b7cb86d41f4854616b76 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Sat, 4 Apr 2026 13:16:15 +0200 Subject: [PATCH] Service --- .../car/NavigationServiceSession.kt | 48 ++++------ .../navigation/car/NavigationSession.kt | 3 + .../car/navigation/NavigationService.kt | 91 +++++++++---------- .../car/navigation/RouteCarModel.kt | 33 ++++++- .../navigation/car/screen/NavigationScreen.kt | 39 ++++---- .../observers/NavigationObserverCallback.kt | 3 +- .../observers/NavigationObserverManager.kt | 6 ++ .../screen/observers/RecentPlacesObserver.kt | 18 ++++ 8 files changed, 145 insertions(+), 96 deletions(-) create mode 100644 common/car/src/main/java/com/kouros/navigation/car/screen/observers/RecentPlacesObserver.kt diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationServiceSession.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationServiceSession.kt index 458da7d..fbc3b02 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/NavigationServiceSession.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationServiceSession.kt @@ -6,7 +6,6 @@ import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.content.pm.PackageManager -import android.graphics.Color import android.location.Location import android.os.IBinder import android.util.Log @@ -29,11 +28,8 @@ import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.asLiveData -import androidx.lifecycle.coroutineScope import androidx.lifecycle.lifecycleScope import com.kouros.navigation.car.navigation.NavigationService -import com.kouros.navigation.car.navigation.RouteCarModel -import com.kouros.navigation.car.navigation.Simulation import com.kouros.navigation.car.screen.NavigationListener import com.kouros.navigation.car.screen.NavigationScreen import com.kouros.navigation.car.screen.NavigationType @@ -43,30 +39,22 @@ 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.DESTINATION_ARRIVAL_DISTANCE import com.kouros.navigation.data.Constants.GMS_CAR_SPEED_PERMISSION -import com.kouros.navigation.data.Constants.INSTRUCTION_DISTANCE -import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION -import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.Constants.TRAFFIC_UPDATE import com.kouros.navigation.data.Constants.homeHohenwaldeck import com.kouros.navigation.data.Place import com.kouros.navigation.data.RouteEngine -import com.kouros.navigation.data.SearchFilter import com.kouros.navigation.data.ViewStyle 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.valhalla.ValhallaRepository import com.kouros.navigation.model.NavigationViewModel -import com.kouros.navigation.model.RouteModel import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.repository.SettingsRepository import com.kouros.navigation.utils.GeoUtils -import com.kouros.navigation.utils.GeoUtils.snapLocation import com.kouros.navigation.utils.NavigationUtils.getViewModel -import com.kouros.navigation.utils.formattedDistance import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsViewModel import com.kouros.navigation.utils.location @@ -115,8 +103,11 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs var speedCameras = listOf() + var recentPlaces = mutableListOf() + var lastRouteDate: LocalDateTime = LocalDateTime.now() + var destination = Place() var notificationActive = false var navigationService: NavigationService? = null @@ -129,9 +120,9 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs hasArrived: Boolean, destinations: MutableList, steps: MutableList, - destinationTravelEstimate: TravelEstimate?, - stepTravelEstimate: TravelEstimate?, - stepRemainingDistance: Distance?, + destinationTravelEstimate: TravelEstimate, + stepTravelEstimate: TravelEstimate, + stepRemainingDistance: Distance, shouldShowNextStep: Boolean, shouldShowLanes: Boolean, junctionImage: CarIcon?, @@ -142,11 +133,11 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs isNavigating = isNavigating, isRerouting = isRerouting, hasArrived = hasArrived, - destinationTravelEstimate = destinationTravelEstimate!!, - stepTravelEstimate = stepTravelEstimate!!, + destinationTravelEstimate = destinationTravelEstimate, + stepTravelEstimate = stepTravelEstimate, destinations = destinations, steps = steps, - stepRemainingDistance = stepRemainingDistance!!, + stepRemainingDistance = stepRemainingDistance, shouldShowNextStep = shouldShowNextStep, shouldShowLanes = shouldShowLanes, junctionImage = junctionImage, @@ -162,12 +153,6 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs surfaceRenderer.carOrientation, ) initialLocation = false - navigationViewModel.loadRoute( - carContext, - location, - listOf(homeHohenwaldeck), - surfaceRenderer.carOrientation - ) } updateLocation(location) } @@ -466,7 +451,7 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs private fun handleNavigateIntent(screenManager: ScreenManager) { screenManager.popToRoot() screenManager.pushForResult( - SearchScreen(carContext, surfaceRenderer, navigationViewModel, mutableListOf()) + SearchScreen(carContext, surfaceRenderer, navigationViewModel, recentPlaces) ) { result -> // Handle search result if needed } @@ -500,7 +485,6 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs updateBearing(location) checkTraffic(LocalDateTime.now(ZoneOffset.UTC), location) surfaceRenderer.updateLocation(location, "") - } /** @@ -512,14 +496,13 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs } } - /** * Start navigation process. * Called when user starts navigation */ override fun startNavigation() { Log.d(TAG, "startNavigation") - navigationService!!.startNavigation(route) + navigationService!!.startNavigation(route, destination) if (notificationActive) notificationManager.startNotificationService() } @@ -621,6 +604,14 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs surfaceRenderer.maxSpeed.value = speed } + override fun onRecentPlacesReceived(places: List) { + Log.d(TAG, "onRecentPlacesReceived ${places.size}") + recentPlaces = places.toMutableList() + navigationScreen.recentPlaces = places.toMutableList() + navigationScreen.invalidate() + + } + override fun invalidateScreen() { navigationScreen.invalidate() } @@ -647,6 +638,7 @@ class NavigationServiceSession : CarSession(), NavigationListener, NavigationObs } navigationViewModel.saveRecent(carContext, place) + destination = place // routeModel.navState = routeModel.navState.copy(destination = place) if (preview.isEmpty()) { navigationViewModel.loadRoute( diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt index 8b198f7..3572d89 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt @@ -752,6 +752,9 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa surfaceRenderer.maxSpeed.value = speed } + override fun onRecentPlacesReceived(places: List) { + } + override fun invalidateScreen() { navigationScreen.invalidate() } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/NavigationService.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/NavigationService.kt index 846b12b..014a60a 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/NavigationService.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/NavigationService.kt @@ -6,6 +6,7 @@ import android.app.NotificationManager import android.app.Service import android.content.Intent import android.location.Location +import android.location.LocationManager import android.os.Binder import android.os.IBinder import android.text.TextUtils @@ -16,6 +17,7 @@ import androidx.car.app.CarToast import androidx.car.app.model.CarColor import androidx.car.app.model.CarIcon import androidx.car.app.model.Distance +import androidx.car.app.model.Distance.UNIT_METERS import androidx.car.app.navigation.NavigationManager import androidx.car.app.navigation.NavigationManagerCallback import androidx.car.app.navigation.model.Destination @@ -29,6 +31,7 @@ import com.kouros.navigation.car.screen.NavigationType import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION +import com.kouros.navigation.data.Place import com.kouros.navigation.model.NavigationViewModel import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.repository.SettingsRepository @@ -37,6 +40,7 @@ import com.kouros.navigation.utils.NavigationUtils.getViewModel import com.kouros.navigation.utils.formattedDistance import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsViewModel +import com.kouros.navigation.utils.location import kotlin.collections.copy import kotlin.compareTo @@ -81,6 +85,8 @@ class NavigationService : Service() { // Manages device GPS location updates lateinit var deviceLocationManager: DeviceLocationManagerService + var currentLocation = location(0.0, 0.0) + lateinit var navigationViewModel: NavigationViewModel private lateinit var navigationManager: NavigationManager @@ -97,16 +103,15 @@ class NavigationService : Service() { hasArrived: Boolean, destinations: MutableList, steps: MutableList, - destinationTravelEstimate: TravelEstimate?, - stepTravelEstimate: TravelEstimate?, - stepRemainingDistance: Distance?, + destinationTravelEstimate: TravelEstimate, + stepTravelEstimate: TravelEstimate, + stepRemainingDistance: Distance, shouldShowNextStep: Boolean, shouldShowLanes: Boolean, junctionImage: CarIcon?, backGroundColor: CarColor ) - fun updateServiceLocation(location: Location) } @@ -225,29 +230,32 @@ class NavigationService : Service() { } /** Starts navigation. */ - fun startNavigation(route: String) { + fun startNavigation(route: String, destination: Place) { Log.i(TAG, "Starting Navigation") startService(Intent(applicationContext, NavigationService::class.java)) + routeModel.navState = routeModel.navState.copy(destination = destination) routeModel.navState = routeModel.navState.copy(routingEngine = 2) routeModel.startNavigation(route) if (routeModel.isNavigating()) { -// listener.navigationStateChanged( -// isNavigating = true, -// isRerouting = true, -// hasArrived = false, -// destinations = emptyList().toMutableList(), -// steps = emptyList().toMutableList(), -// destinationTravelEstimate = null, -// stepTravelEstimate = null, -// stepRemainingDistance = null, -// shouldShowNextStep = false, -// shouldShowLanes = false, -// junctionImage = null, -// backGroundColor = CarColor.BLUE -// ) + routeModel.updateLocation(currentLocation, navigationViewModel) + listener.navigationStateChanged( + isNavigating = true, + isRerouting = false, + hasArrived = false, + destinations = mutableListOf(routeModel.getDestination()), + destinationTravelEstimate = routeModel.getTravelEstimateTrip(carContext!!), + stepTravelEstimate = routeModel.getTravelEstimateStep(carContext!!), + steps = routeModel.getSteps(carContext!!), + stepRemainingDistance = routeModel.getDistance(), + shouldShowNextStep = false, + shouldShowLanes = false, + junctionImage = null, + backGroundColor = routeModel.backGroundColor() + ) } } + /** Starts navigation. */ fun stopNavigation() { if (autoDriveEnabled) { @@ -261,9 +269,9 @@ class NavigationService : Service() { hasArrived = false, destinations = emptyList().toMutableList(), steps = emptyList().toMutableList(), - destinationTravelEstimate = null, - stepTravelEstimate = null, - stepRemainingDistance = null, + destinationTravelEstimate = routeModel.travelEstimate(carContext!!, 0.0, 0), + stepTravelEstimate = routeModel.travelEstimate(carContext!!, 0.0, 0), + stepRemainingDistance = Distance.create(0.0, UNIT_METERS), shouldShowNextStep = false, shouldShowLanes = false, junctionImage = null, @@ -275,6 +283,7 @@ class NavigationService : Service() { fun updateLocation(location: Location) { Log.d(TAG, "updateLocation") + currentLocation = location if (routeModel.isNavigating()) { routeModel.updateLocation(location, navigationViewModel) val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations()) @@ -296,49 +305,33 @@ class NavigationService : Service() { ) { return } - val travelEstimateTrip = routeModel.travelEstimateTrip(carContext!!, distanceMode) - val travelEstimateStep = routeModel.travelEstimateStep(carContext!!, distanceMode) - val steps = mutableListOf() - val destination = Destination.Builder() - .setName(routeModel.navState.destination.name) - .setAddress(routeModel.navState.destination.street) - .build() - val distance = - formattedDistance(0, routeModel.routeCalculator.leftStepDistance()) - steps.add(routeModel.currentStep(carContext!!)) - if (routeModel.navState.nextStep) { - steps.add(routeModel.nextStep(carContext = carContext!!)) - - } - listener.navigationStateChanged( isNavigating = routeModel.isNavigating(), isRerouting = false, hasArrived = routeModel.isArrival(), - destinationTravelEstimate = travelEstimateTrip, - stepTravelEstimate = travelEstimateStep, - destinations = mutableListOf(destination), - steps = steps, - stepRemainingDistance = Distance.create(distance.first, distance.second), + destinations = mutableListOf(routeModel.getDestination()), + destinationTravelEstimate = routeModel.getTravelEstimateTrip(carContext!!), + stepTravelEstimate = routeModel.getTravelEstimateStep(carContext!!), + steps = routeModel.getSteps(carContext!!), + stepRemainingDistance = routeModel.getDistance(), shouldShowNextStep = false, - shouldShowLanes = true, + shouldShowLanes = false, junctionImage = null, - backGroundColor = routeModel.backGroundColor() + 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 + routeModel.getDestination(), + routeModel.getTravelEstimateTrip(carContext!!) ) tripBuilder.setLoading(false) - tripBuilder.setCurrentRoad(destination.name.toString()) - tripBuilder.addStep(steps.first(), travelEstimateStep) + tripBuilder.setCurrentRoad(routeModel.getDestination().name.toString()) + tripBuilder.addStep(routeModel.getSteps(carContext!!).first(), routeModel.getTravelEstimateStep(carContext!!)) navigationManager.updateTrip(tripBuilder.build()) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt index 8cd52af..5ad9f76 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt @@ -18,6 +18,7 @@ import androidx.car.app.model.DateTimeWithZone import androidx.car.app.model.Distance import androidx.car.app.model.DurationSpan import androidx.car.app.model.ForegroundCarColorSpan +import androidx.car.app.navigation.model.Destination import androidx.car.app.navigation.model.Lane import androidx.car.app.navigation.model.LaneDirection import androidx.car.app.navigation.model.Maneuver @@ -99,7 +100,6 @@ class RouteCarModel : RouteModel() { } fun travelEstimate(carContext: CarContext, timeLeft: Double, distanceMode: Int): TravelEstimate { - val timeToDestinationMillis = TimeUnit.SECONDS.toMillis(timeLeft.toLong()) val distance = formattedDistance(distanceMode, routeCalculator.travelLeftDistance()) @@ -134,6 +134,37 @@ class RouteCarModel : RouteModel() { } return travelBuilder.build() } + + fun getSteps(carContext: CarContext): MutableList { + val steps = mutableListOf() + steps.add(currentStep(carContext)) + if (navState.nextStep) { + steps.add(nextStep(carContext = carContext)) + } + return steps + } + + fun getDistance(): Distance { + val distance = + formattedDistance(0, routeCalculator.leftStepDistance()) + return Distance.create(distance.first, distance.second) + } + + fun getTravelEstimateTrip(carContext: CarContext): TravelEstimate { + return travelEstimateTrip(carContext, 0) + } + + fun getTravelEstimateStep(carContext: CarContext): TravelEstimate { + return travelEstimateStep(carContext, 0) + } + + fun getDestination(): Destination { + return Destination.Builder() + .setName(navState.destination.name) + .setAddress(navState.destination.street) + .build() + } + private fun createDelay(delay: Int): CarText { val delayBuilder = SpannableStringBuilder() delayBuilder.append( diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt index 5a5121a..b20ae12 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt @@ -154,10 +154,11 @@ open class NavigationScreen( .setDestinationTravelEstimate(destinationTravelEstimate) .setActionStrip(actionStripBuilder.build()) .setMapActionStrip( - mapActionStrip(carContext, + mapActionStrip( + carContext, surfaceRenderer.viewStyle, { zoomPlus() }, { zoomMinus() }, { - Action.Builder() + Action.Builder() .setIcon(createCarIcon(carContext, R.drawable.ic_recenter_24)) .setFlags(0) .setOnClickListener { @@ -175,17 +176,18 @@ open class NavigationScreen( * Creates and returns a template for the default view state. */ private fun navigationView(actionStripBuilder: ActionStrip.Builder): Template { - val mapActionStrip = mapActionStrip(carContext, - surfaceRenderer.viewStyle, - { zoomPlus() }, { zoomMinus() }, { - createAction( - carContext = carContext, R.drawable.ic_recenter_24, - 0, - onClickAction = { - surfaceRenderer.viewStyle = ViewStyle.VIEW - invalidate() - }) - }) + val mapActionStrip = mapActionStrip( + carContext, + surfaceRenderer.viewStyle, + { zoomPlus() }, { zoomMinus() }, { + createAction( + carContext = carContext, R.drawable.ic_recenter_24, + 0, + onClickAction = { + surfaceRenderer.viewStyle = ViewStyle.VIEW + invalidate() + }) + }) return NavigationTemplate.Builder() .setBackgroundColor(backGroundColor) .setActionStrip(actionStripBuilder.build()) @@ -241,7 +243,8 @@ open class NavigationScreen( .setBackgroundColor(backGroundColor) .setActionStrip(actionStripBuilder.build()) .setMapActionStrip( - mapActionStrip(carContext, + mapActionStrip( + carContext, surfaceRenderer.viewStyle, { zoomPlus() }, { zoomMinus() }, { createAction( @@ -302,7 +305,8 @@ open class NavigationScreen( val builder = MapWithContentTemplate.Builder() .setContentTemplate(contentTemplate) .setActionStrip( - mapActionStrip(carContext, + mapActionStrip( + carContext, ViewStyle.VIEW, { settingsAction() }, { @@ -341,10 +345,12 @@ open class NavigationScreen( */ fun getRoutingInfo(): RoutingInfo { val routingInfo = RoutingInfo.Builder() - .setCurrentStep( + if (steps.isNotEmpty()) { + routingInfo.setCurrentStep( steps.first(), stepRemainingDistance ) + } if (shouldShowNextStep && steps.size > 1) { routingInfo.setNextStep(steps[1]) } @@ -492,7 +498,6 @@ open class NavigationScreen( } - /** * Updates navigation state with the current location, checks for arrival, and traffic updates. */ diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverCallback.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverCallback.kt index 0a6a1e4..b21a61d 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverCallback.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverCallback.kt @@ -1,7 +1,6 @@ package com.kouros.navigation.car.screen.observers import com.kouros.navigation.data.Place -import com.kouros.navigation.data.nominatim.SearchResult import com.kouros.navigation.data.overpass.Elements /** @@ -26,6 +25,8 @@ interface NavigationObserverCallback { /** Called when max speed is updated */ fun onMaxSpeedReceived(speed: Int) + fun onRecentPlacesReceived(places: List) + /** Called to request UI invalidation/refresh */ fun invalidateScreen() diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverManager.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverManager.kt index da72bbd..cdda49e 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverManager.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/observers/NavigationObserverManager.kt @@ -19,12 +19,18 @@ class NavigationObserverManager( val speedCameraObserver = SpeedCameraObserver(callback) val maxSpeedObserver = MaxSpeedObserver(callback) + val recentPlacesObserver = RecentPlacesObserver(callback) + + + fun attachAllObservers(session: CarSession) { viewModel.route.observe(session, routeObserver) viewModel.traffic.observe(session, trafficObserver) viewModel.placeLocation.observe(session, placeSearchObserver) viewModel.speedCameras.observe(session, speedCameraObserver) viewModel.maxSpeed.observe(session, maxSpeedObserver) + viewModel.recentPlaces.observe(session, recentPlacesObserver) + } /** diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/observers/RecentPlacesObserver.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/observers/RecentPlacesObserver.kt new file mode 100644 index 0000000..9085659 --- /dev/null +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/observers/RecentPlacesObserver.kt @@ -0,0 +1,18 @@ +package com.kouros.navigation.car.screen.observers + +import androidx.lifecycle.Observer +import com.kouros.navigation.data.Place + +/** + * Observer for route updates. Triggers navigation start when a non-empty route is received. + */ +class RecentPlacesObserver( + private val callback: NavigationObserverCallback +) : Observer> { + + override fun onChanged(value: List) { + if (value.isNotEmpty()) { + callback.onRecentPlacesReceived(value) + } + } +}