feat: implement two-location speed calculation when native speed unavailable

This commit is contained in:
fiatcode 2026-05-02 16:22:02 +07:00
parent eca39e542c
commit c9e49c6cbd
No known key found for this signature in database

View file

@ -13,7 +13,9 @@ import android.net.ConnectivityManager
import android.location.Location import android.location.Location
import android.os.Binder import android.os.Binder
import android.os.Build import android.os.Build
import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import dev.fiatcode.tracpulse.BridgeModule import dev.fiatcode.tracpulse.BridgeModule
@ -57,6 +59,9 @@ class LocationTrackingService : Service() {
private var bypassFilterOnce = false private var bypassFilterOnce = false
private var currentConfig: TrackingConfig? = null private var currentConfig: TrackingConfig? = null
private var pendingSpeedLocation: Location? = null
private var speedRequestPending = false
private val SPEED_CALC_DELAY_MS = 2500L
inner class LocalBinder : Binder() { inner class LocalBinder : Binder() {
fun getService(): LocationTrackingService = this@LocationTrackingService fun getService(): LocationTrackingService = this@LocationTrackingService
@ -143,6 +148,8 @@ class LocationTrackingService : Service() {
isTracking = true isTracking = true
distanceFilterProcessor.reset() distanceFilterProcessor.reset()
pendingSpeedLocation = null
speedRequestPending = false
prefs.edit().putBoolean("tracking_active", true).apply() prefs.edit().putBoolean("tracking_active", true).apply()
Log.d(TAG, "startTracking: set tracking_active=true") Log.d(TAG, "startTracking: set tracking_active=true")
@ -226,15 +233,22 @@ class LocationTrackingService : Service() {
} }
bypassFilterOnce = false bypassFilterOnce = false
val nativeSpeed = if (location.hasSpeed()) location.speed else null
if (nativeSpeed == null || nativeSpeed <= 0) {
pendingSpeedLocation = location
requestLocationForSpeedCalculation()
}
val traccarLocation = TraccarLocation( val traccarLocation = TraccarLocation(
timestamp = location.time, timestamp = location.time,
latitude = location.latitude, latitude = location.latitude,
longitude = location.longitude, longitude = location.longitude,
accuracy = if (location.hasAccuracy()) location.accuracy else null, accuracy = if (location.hasAccuracy()) location.accuracy else null,
speed = if (location.hasSpeed()) location.speed else null, speed = nativeSpeed,
heading = if (location.hasBearing()) location.bearing else null, heading = if (location.hasBearing()) location.bearing else null,
altitude = if (location.hasAltitude()) location.altitude else null, altitude = if (location.hasAltitude()) location.altitude else null,
isMoving = location.speed > 1.0f // Consider moving if speed > 1 m/s isMoving = (nativeSpeed ?: 0f) > 1.0f
) )
logEvent("LOCATION", "Lat: ${location.latitude}, Lon: ${location.longitude}, Speed: ${location.speed}") logEvent("LOCATION", "Lat: ${location.latitude}, Lon: ${location.longitude}, Speed: ${location.speed}")
@ -262,10 +276,12 @@ class LocationTrackingService : Service() {
apply() apply()
} }
if (isNetworkAvailable) { if (nativeSpeed != null && nativeSpeed > 0) {
sendLocationToServer(traccarLocation) if (isNetworkAvailable) {
} else { sendLocationToServer(traccarLocation)
bufferLocation(traccarLocation) } else {
bufferLocation(traccarLocation)
}
} }
} }
@ -276,6 +292,58 @@ class LocationTrackingService : Service() {
} }
} }
private fun requestLocationForSpeedCalculation() {
if (speedRequestPending) return
speedRequestPending = true
Handler(Looper.getMainLooper()).postDelayed({
fusedLocationProvider.getLastLocation { location ->
location?.let {
val calculatedSpeed = distanceFilterProcessor.getCalculatedSpeed(it)
onSpeedLocationReceived(it, calculatedSpeed)
}
speedRequestPending = false
pendingSpeedLocation = null
}
}, SPEED_CALC_DELAY_MS)
}
private fun onSpeedLocationReceived(location: Location, calculatedSpeed: Float?) {
val config = currentConfig ?: return
val finalSpeed = if (location.hasSpeed() && location.speed > 0) {
location.speed
} else {
calculatedSpeed
}
if (finalSpeed == null || finalSpeed <= 0) {
logEvent("SPEED_CALC", "Speed calculation failed - insufficient data")
return
}
val traccarLocation = TraccarLocation(
timestamp = location.time,
latitude = location.latitude,
longitude = location.longitude,
accuracy = if (location.hasAccuracy()) location.accuracy else null,
speed = finalSpeed,
heading = if (location.hasBearing()) location.bearing else null,
altitude = if (location.hasAltitude()) location.altitude else null,
isMoving = finalSpeed > 1.0f
)
logEvent("LOCATION", "Lat: ${location.latitude}, Lon: ${location.longitude}, Calc Speed: ${finalSpeed}")
updateNotificationSpeed(location, finalSpeed)
if (isNetworkAvailable) {
sendLocationToServer(traccarLocation)
} else {
bufferLocation(traccarLocation)
}
}
private fun sendLocationToServer(location: TraccarLocation) { private fun sendLocationToServer(location: TraccarLocation) {
val config = currentConfig ?: return val config = currentConfig ?: return
val traccarConfig = TraccarConfig( val traccarConfig = TraccarConfig(
@ -407,6 +475,20 @@ class LocationTrackingService : Service() {
notificationManager.notify(NOTIFICATION_ID, notification) notificationManager.notify(NOTIFICATION_ID, notification)
} }
private fun updateNotificationSpeed(location: Location, speed: Float) {
val content = buildString {
append("Lat: %.4f Lon: %.4f".format(location.latitude, location.longitude))
append("\nSpeed: %.0f km/h (calc)".format(speed * 3.6))
val time = java.text.SimpleDateFormat("HH:mm:ss", java.util.Locale.getDefault())
.format(java.util.Date(location.time))
append("\nLast: $time")
}
val notification = createNotification(content)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.notify(NOTIFICATION_ID, notification)
}
private fun acquireWakeLock() { private fun acquireWakeLock() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock( wakeLock = powerManager.newWakeLock(