2026-01-19 16:45:49 +01:00

163 lines
5.3 KiB
Kotlin

package org.pupes.mhdrunpathfinder
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import androidx.core.app.ActivityCompat
import ovh.plrapps.mapcompose.api.moveMarker
// Handler for main thread operations
private val LocationMainHandler = Handler(Looper.getMainLooper())
private var locationManager: LocationManager? = null
private var locationListener: LocationListener? = null
private var isLocationTracking = false
// Location update configuration
private const val MIN_TIME_BETWEEN_UPDATES = 1000L // 1 second in milliseconds
private const val MIN_DISTANCE_CHANGE = 0f // 0 meters - update on any movement
// LocationListener callback
private val createLocationListener: (Activity) -> LocationListener = { callee ->
LocationListener { location ->
handleLocationUpdate(location, callee)
}
}
private fun handleLocationUpdate(location: Location, callee: Activity) {
try {
val normalizedX = longitudeToXNormalized(location.longitude)
val normalizedY = latitudeToYNormalized(location.latitude)
// Update marker on main thread
LocationMainHandler.post {
try {
mapState.moveMarker(
"current_position",
x = normalizedX,
y = normalizedY
)
} catch (e: Exception) {
// Marker might not exist yet, silently ignore
}
}
} catch (e: Exception) {
LocationMainHandler.post {
Toast.makeText(callee, "Location error: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
// Check if location permissions are granted
private fun hasLocationPermissions(context: Context): Boolean {
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
// Start continuous location tracking
fun startLocationTracking(callee: Activity) {
if (isLocationTracking) {
Toast.makeText(callee, "Location tracking already active", Toast.LENGTH_SHORT).show()
return
}
if (!hasLocationPermissions(callee)) {
Toast.makeText(callee, "Location permissions not granted", Toast.LENGTH_LONG).show()
// Request permissions
ActivityCompat.requestPermissions(
callee,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
LOCATION_PERMISSION_REQUEST_CODE
)
return
}
try {
locationManager = callee.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationListener = createLocationListener(callee)
// Try GPS first, then network provider
val providers = listOf(
LocationManager.GPS_PROVIDER,
LocationManager.NETWORK_PROVIDER
)
var providerFound = false
for (provider in providers) {
if (locationManager?.isProviderEnabled(provider) == true) {
try {
locationManager?.requestLocationUpdates(
provider,
MIN_TIME_BETWEEN_UPDATES,
MIN_DISTANCE_CHANGE,
locationListener!!,
Looper.getMainLooper()
)
providerFound = true
// Get last known location and update immediately
locationManager?.getLastKnownLocation(provider)?.let { location ->
handleLocationUpdate(location, callee)
}
break
} catch (e: SecurityException) {
// Permission denied
Toast.makeText(callee, "Location permission denied", Toast.LENGTH_LONG).show()
return
}
}
}
if (!providerFound) {
Toast.makeText(callee, "No location provider available. Please enable GPS or network location", Toast.LENGTH_LONG).show()
return
}
isLocationTracking = true
Toast.makeText(callee, "Location tracking started", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
Toast.makeText(callee, "Failed to start location tracking: ${e.message}", Toast.LENGTH_LONG).show()
}
}
// Stop location tracking
fun stopLocationTracking() {
if (!isLocationTracking) return
try {
locationListener?.let { listener ->
locationManager?.removeUpdates(listener)
}
locationListener = null
locationManager = null
isLocationTracking = false
} catch (e: Exception) {
// Silently handle cleanup errors
}
}
// Get current tracking status
fun isLocationTrackingActive(): Boolean {
return isLocationTracking
}
// Permission request code
const val LOCATION_PERMISSION_REQUEST_CODE = 1001