This commit is contained in:
Dimitris
2026-04-03 10:00:00 +02:00
parent 1d67b3cc06
commit 8af2d3ad0b
2 changed files with 129 additions and 37 deletions
@@ -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)
}
/**
@@ -96,6 +104,7 @@ class NavigationService : Service() {
}
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)
// }
}
@@ -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)
}
@@ -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()
@@ -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<TrackSegment?>? = 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()
}
}