From 8af2d3ad0b588628b338b41b28824033b1e6debb Mon Sep 17 00:00:00 2001 From: Dimitris Date: Mon, 30 Mar 2026 13:17:02 +0200 Subject: [PATCH] Service --- .../car/navigation/NavigationService.kt | 95 ++++++++++++------- .../navigation/car/navigation/Simulation.kt | 71 +++++++++++++- 2 files changed, 129 insertions(+), 37 deletions(-) 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 14e05de..a605548 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 @@ -8,24 +8,25 @@ import android.content.Intent import android.location.Location import android.location.LocationManager import android.os.Binder -import android.os.Build import android.os.IBinder +import android.os.SystemClock import android.util.Log import androidx.annotation.RequiresPermission import androidx.car.app.CarContext +import androidx.car.app.CarToast import androidx.car.app.model.CarIcon import androidx.car.app.model.Distance import androidx.car.app.navigation.NavigationManager +import androidx.car.app.navigation.NavigationManagerCallback import androidx.car.app.navigation.model.Destination import androidx.car.app.navigation.model.Step import androidx.car.app.navigation.model.TravelEstimate -import androidx.core.location.LocationListenerCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import com.kouros.navigation.car.DeviceLocationManager import com.kouros.navigation.car.DeviceLocationManagerService -import com.kouros.navigation.car.NavigationNotificationService -import com.kouros.navigation.data.Constants.TAG +import com.kouros.navigation.data.tomtom.TomTomRepository +import io.ticofab.androidgpxparser.parser.GPXParser +import io.ticofab.androidgpxparser.parser.domain.Gpx +import io.ticofab.androidgpxparser.parser.domain.TrackSegment +import org.joda.time.DateTime class NavigationService : Service() { @@ -56,6 +57,11 @@ class NavigationService : Service() { private var notificationManager: NotificationManager? = null private var carContext: CarContext? = null + var autoDriveEnabled = false + + val simulation = Simulation() + + private lateinit var listener: Listener // Model for managing route state and navigation logic for Android Auto @@ -84,6 +90,8 @@ class NavigationService : Service() { shouldShowLanes: Boolean, junctionImage: CarIcon? ) + + fun updateServiceLocation(location: Location) } /** @@ -95,7 +103,8 @@ class NavigationService : Service() { get() = this@NavigationService } - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + return START_STICKY } @@ -132,8 +141,6 @@ class NavigationService : Service() { } - - /** Sets the [CarContext] to use while the service is connected. */ @RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]) fun setCarContext( @@ -150,23 +157,40 @@ class NavigationService : Service() { Log.d(TAG, "Initial location: $location") } ) - deviceLocationManager.startLocationUpdates() + navigationManagerInitialized = true - // navigationManager = - // carContext.getCarService(NavigationManager::class.java) - // navigationManager.setNavigationManagerCallback( - // object : NavigationManagerCallback { - // override fun onStopNavigation() { - // this@NavigationService.stopNavigation() - // } - // - // override fun onAutoDriveEnabled() { - // Log.d(TAG, "onAutoDriveEnabled called") - // CarToast.makeText(carContext, "Auto drive enabled", CarToast.LENGTH_LONG) - // .show() - // } - // }) +// navigationManager = +// carContext.getCarService(NavigationManager::class.java) +// navigationManager.setNavigationManagerCallback(object : NavigationManagerCallback { +// override fun onAutoDriveEnabled() { +// // Called when the app should simulate navigation (e.g., for testing) +// deviceLocationManager.stopLocationUpdates() +// autoDriveEnabled = true +// +// simulation() +// +// startNavigation() +// CarToast.makeText(carContext, "Auto drive enabled", CarToast.LENGTH_LONG) +// .show() +// } +// +// private fun simulation() { +// //simulation.gpxSimulation() { +// // listener.updateServiceLocation(it) +// //} +// } +// +// override fun onStopNavigation() { +// // Called when the user stops navigation in the car screen +// // Stop turn-by-turn logic and clean up +// stopNavigation() +// if (autoDriveEnabled) { +// deviceLocationManager.startLocationUpdates() +// } +// } +// }) + this.listener = listener // Uncomment if navigating @@ -177,12 +201,12 @@ class NavigationService : Service() { fun clearCarContext() { Log.i(TAG, "clearContext"); carContext = null; - deviceLocationManager.stopLocationUpdates() - // navigationManager.clearNavigationManagerCallback(); + //navigationManager.clearNavigationManagerCallback(); } /** Starts navigation. */ fun startNavigation() { + //deviceLocationManager.startLocationUpdates() Log.i(TAG, "Starting Navigation") startService(Intent(applicationContext, NavigationService::class.java)) Log.i(TAG, "Starting foreground service") @@ -204,6 +228,10 @@ class NavigationService : Service() { /** Starts navigation. */ fun stopNavigation() { + if (autoDriveEnabled) { + autoDriveEnabled = false + } + deviceLocationManager.stopLocationUpdates() // if (navigationManagerInitialized) // navigationManager.navigationEnded() listener.navigationStateChanged( @@ -224,11 +252,14 @@ class NavigationService : Service() { fun updateLocation(location: Location) { Log.d(TAG, "Update location: $location") + if (autoDriveEnabled) { + + } } - // private fun createMainActivityPendingIntent(): PendingIntent? { - // val intent: Intent = Intent(this, MainActivity::class.java) - // intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true) - // return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) - // } +// private fun createMainActivityPendingIntent(): PendingIntent? { +// val intent: Intent = Intent(this, MainActivity::class.java) +// intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true) +// return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) +// } } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt index 76179b2..f8942e2 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt @@ -3,8 +3,10 @@ package com.kouros.navigation.car.navigation import android.location.Location import android.location.LocationManager import android.os.SystemClock +import android.util.Log import androidx.lifecycle.LifecycleCoroutineScope import com.kouros.data.BuildConfig +import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.tomtom.TomTomRepository import io.ticofab.androidgpxparser.parser.GPXParser import io.ticofab.androidgpxparser.parser.domain.Gpx @@ -27,8 +29,9 @@ class Simulation { ) { if (routeModel.navState.route.isRouteValid()) { if (BuildConfig.DEBUG) { - //gpxSimulation(routeModel, lifecycleScope, updateLocation) - currentSimulation(routeModel, lifecycleScope, updateLocation) + gpxSimulation(routeModel, lifecycleScope, updateLocation) + //gpxSimulation(updateLocation) + //currentSimulation(routeModel, lifecycleScope, updateLocation) } else { currentSimulation(routeModel, lifecycleScope, updateLocation) } @@ -66,7 +69,7 @@ class Simulation { lastLocation = fakeLocation } } - // routeModel.stopNavigation() + // routeModel.stopNavigation() } } @@ -87,7 +90,7 @@ class Simulation { simulationJob?.join() } simulationJob?.cancel() - simulationJob =lifecycleScope.launch() { + simulationJob = lifecycleScope.launch() { var lastLocation = Location(LocationManager.FUSED_PROVIDER) var curBearing = 0f val parser = GPXParser() @@ -118,7 +121,7 @@ class Simulation { updateLocation(fakeLocation) // Wait before moving to the next point (e.g., every 1 second) if (duration > 100) { - // delay(duration / 4) + // delay(duration / 4) } delay(500) lastTime = p.time @@ -134,4 +137,62 @@ class Simulation { fun stopSimulation() { simulationJob?.cancel() } + + private fun gpxSimulation( + updateLocation: (Location) -> Unit + ) { + Runnable { + var route = "" + simulationJob?.cancel() + runBlocking { + simulationJob = launch(Dispatchers.IO) { + route = TomTomRepository().fetchUrl( + "https://kouros-online.de/vh.gpx", + false + ) + } + simulationJob?.join() + } + simulationJob?.cancel() + var lastLocation = Location(LocationManager.FUSED_PROVIDER) + var curBearing = 0f + val parser = GPXParser() + val parsedGpx: Gpx? = + parser.parse(route.byteInputStream()) + parsedGpx?.let { + val tracks = parsedGpx.tracks + tracks.forEach { tr -> + val segments: MutableList? = tr.trackSegments + segments!!.forEach { seg -> + var lastTime = DateTime.now() + seg!!.trackPoints.forEach { p -> + val ext = p.extensions + var curSpeed = 0F + if (ext != null) { + curSpeed = ext.speed.toFloat() + } + val duration = p.time.millis - lastTime.millis + val fakeLocation = Location(LocationManager.FUSED_PROVIDER).apply { + latitude = p.latitude + longitude = p.longitude + speedAccuracyMetersPerSecond = 1.0f // ~1 m/s + speed = curSpeed + time = System.currentTimeMillis() + elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() + } + // Update your app's state as if a real GPS update occurred + updateLocation(fakeLocation) + // Wait before moving to the next point (e.g., every 1 second) + if (duration > 100) { + // delay(duration / 4) + } + Thread.sleep(500) + lastTime = p.time + lastLocation = fakeLocation + } + } + } + } + }.run() + } } \ No newline at end of file