Service
This commit is contained in:
+63
-32
@@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user