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.Location
import android.location.LocationManager import android.location.LocationManager
import android.os.Binder import android.os.Binder
import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.SystemClock
import android.util.Log import android.util.Log
import androidx.annotation.RequiresPermission import androidx.annotation.RequiresPermission
import androidx.car.app.CarContext import androidx.car.app.CarContext
import androidx.car.app.CarToast
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.navigation.NavigationManager 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.Destination
import androidx.car.app.navigation.model.Step import androidx.car.app.navigation.model.Step
import androidx.car.app.navigation.model.TravelEstimate 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.DeviceLocationManagerService
import com.kouros.navigation.car.NavigationNotificationService import com.kouros.navigation.data.tomtom.TomTomRepository
import com.kouros.navigation.data.Constants.TAG 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() { class NavigationService : Service() {
@@ -56,6 +57,11 @@ class NavigationService : Service() {
private var notificationManager: NotificationManager? = null private var notificationManager: NotificationManager? = null
private var carContext: CarContext? = null private var carContext: CarContext? = null
var autoDriveEnabled = false
val simulation = Simulation()
private lateinit var listener: Listener private lateinit var listener: Listener
// Model for managing route state and navigation logic for Android Auto // Model for managing route state and navigation logic for Android Auto
@@ -84,6 +90,8 @@ class NavigationService : Service() {
shouldShowLanes: Boolean, shouldShowLanes: Boolean,
junctionImage: CarIcon? junctionImage: CarIcon?
) )
fun updateServiceLocation(location: Location)
} }
/** /**
@@ -95,7 +103,8 @@ class NavigationService : Service() {
get() = this@NavigationService 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 return START_STICKY
} }
@@ -132,8 +141,6 @@ class NavigationService : Service() {
} }
/** Sets the [CarContext] to use while the service is connected. */ /** Sets the [CarContext] to use while the service is connected. */
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]) @RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
fun setCarContext( fun setCarContext(
@@ -150,23 +157,40 @@ class NavigationService : Service() {
Log.d(TAG, "Initial location: $location") Log.d(TAG, "Initial location: $location")
} }
) )
deviceLocationManager.startLocationUpdates()
navigationManagerInitialized = true navigationManagerInitialized = true
// navigationManager = // navigationManager =
// carContext.getCarService(NavigationManager::class.java) // carContext.getCarService(NavigationManager::class.java)
// navigationManager.setNavigationManagerCallback( // navigationManager.setNavigationManagerCallback(object : NavigationManagerCallback {
// object : NavigationManagerCallback { // override fun onAutoDriveEnabled() {
// override fun onStopNavigation() { // // Called when the app should simulate navigation (e.g., for testing)
// this@NavigationService.stopNavigation() // deviceLocationManager.stopLocationUpdates()
// } // autoDriveEnabled = true
// //
// override fun onAutoDriveEnabled() { // simulation()
// Log.d(TAG, "onAutoDriveEnabled called") //
// CarToast.makeText(carContext, "Auto drive enabled", CarToast.LENGTH_LONG) // startNavigation()
// .show() // 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 this.listener = listener
// Uncomment if navigating // Uncomment if navigating
@@ -177,12 +201,12 @@ class NavigationService : Service() {
fun clearCarContext() { fun clearCarContext() {
Log.i(TAG, "clearContext"); Log.i(TAG, "clearContext");
carContext = null; carContext = null;
deviceLocationManager.stopLocationUpdates() //navigationManager.clearNavigationManagerCallback();
// navigationManager.clearNavigationManagerCallback();
} }
/** Starts navigation. */ /** Starts navigation. */
fun startNavigation() { fun startNavigation() {
//deviceLocationManager.startLocationUpdates()
Log.i(TAG, "Starting Navigation") Log.i(TAG, "Starting Navigation")
startService(Intent(applicationContext, NavigationService::class.java)) startService(Intent(applicationContext, NavigationService::class.java))
Log.i(TAG, "Starting foreground service") Log.i(TAG, "Starting foreground service")
@@ -204,6 +228,10 @@ class NavigationService : Service() {
/** Starts navigation. */ /** Starts navigation. */
fun stopNavigation() { fun stopNavigation() {
if (autoDriveEnabled) {
autoDriveEnabled = false
}
deviceLocationManager.stopLocationUpdates()
// if (navigationManagerInitialized) // if (navigationManagerInitialized)
// navigationManager.navigationEnded() // navigationManager.navigationEnded()
listener.navigationStateChanged( listener.navigationStateChanged(
@@ -224,11 +252,14 @@ class NavigationService : Service() {
fun updateLocation(location: Location) { fun updateLocation(location: Location) {
Log.d(TAG, "Update location: $location") Log.d(TAG, "Update location: $location")
if (autoDriveEnabled) {
}
} }
// private fun createMainActivityPendingIntent(): PendingIntent? { // private fun createMainActivityPendingIntent(): PendingIntent? {
// val intent: Intent = Intent(this, MainActivity::class.java) // val intent: Intent = Intent(this, MainActivity::class.java)
// intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true) // intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true)
// return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) // 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.Location
import android.location.LocationManager import android.location.LocationManager
import android.os.SystemClock import android.os.SystemClock
import android.util.Log
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import com.kouros.data.BuildConfig import com.kouros.data.BuildConfig
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.tomtom.TomTomRepository import com.kouros.navigation.data.tomtom.TomTomRepository
import io.ticofab.androidgpxparser.parser.GPXParser import io.ticofab.androidgpxparser.parser.GPXParser
import io.ticofab.androidgpxparser.parser.domain.Gpx import io.ticofab.androidgpxparser.parser.domain.Gpx
@@ -27,8 +29,9 @@ class Simulation {
) { ) {
if (routeModel.navState.route.isRouteValid()) { if (routeModel.navState.route.isRouteValid()) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
//gpxSimulation(routeModel, lifecycleScope, updateLocation) gpxSimulation(routeModel, lifecycleScope, updateLocation)
currentSimulation(routeModel, lifecycleScope, updateLocation) //gpxSimulation(updateLocation)
//currentSimulation(routeModel, lifecycleScope, updateLocation)
} else { } else {
currentSimulation(routeModel, lifecycleScope, updateLocation) currentSimulation(routeModel, lifecycleScope, updateLocation)
} }
@@ -66,7 +69,7 @@ class Simulation {
lastLocation = fakeLocation lastLocation = fakeLocation
} }
} }
// routeModel.stopNavigation() // routeModel.stopNavigation()
} }
} }
@@ -87,7 +90,7 @@ class Simulation {
simulationJob?.join() simulationJob?.join()
} }
simulationJob?.cancel() simulationJob?.cancel()
simulationJob =lifecycleScope.launch() { simulationJob = lifecycleScope.launch() {
var lastLocation = Location(LocationManager.FUSED_PROVIDER) var lastLocation = Location(LocationManager.FUSED_PROVIDER)
var curBearing = 0f var curBearing = 0f
val parser = GPXParser() val parser = GPXParser()
@@ -118,7 +121,7 @@ class Simulation {
updateLocation(fakeLocation) updateLocation(fakeLocation)
// Wait before moving to the next point (e.g., every 1 second) // Wait before moving to the next point (e.g., every 1 second)
if (duration > 100) { if (duration > 100) {
// delay(duration / 4) // delay(duration / 4)
} }
delay(500) delay(500)
lastTime = p.time lastTime = p.time
@@ -134,4 +137,62 @@ class Simulation {
fun stopSimulation() { fun stopSimulation() {
simulationJob?.cancel() 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()
}
} }