mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 22:25:26 +00:00
Refactor: Add timeout and tracking to SystemLocationProvider
Implements robust cleanup and timeout logic for location requests. - SystemLocationProvider: Adds 30s timeout and listener tracking for legacy one-shot requests to prevent memory leaks on pre-Android 11 devices. - FusedLocationProvider: Adds 30s duration to requests. - LocationProvider: Adds cancel() method for full resource cleanup. - LocationChannelManager: Ensures cancel() is called during cleanup.
This commit is contained in:
@@ -58,6 +58,7 @@ class FusedLocationProvider(private val context: Context) : LocationProvider {
|
|||||||
try {
|
try {
|
||||||
val request = CurrentLocationRequest.Builder()
|
val request = CurrentLocationRequest.Builder()
|
||||||
.setPriority(Priority.PRIORITY_HIGH_ACCURACY)
|
.setPriority(Priority.PRIORITY_HIGH_ACCURACY)
|
||||||
|
.setDurationMillis(30000)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fusedLocationClient.getCurrentLocation(request, null)
|
fusedLocationClient.getCurrentLocation(request, null)
|
||||||
@@ -124,4 +125,18 @@ class FusedLocationProvider(private val context: Context) : LocationProvider {
|
|||||||
Log.e(TAG, "Error removing fused updates: ${e.message}")
|
Log.e(TAG, "Error removing fused updates: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
try {
|
||||||
|
synchronized(activeCallbacks) {
|
||||||
|
for ((callback, locationCallback) in activeCallbacks) {
|
||||||
|
fusedLocationClient.removeLocationUpdates(locationCallback)
|
||||||
|
}
|
||||||
|
activeCallbacks.clear()
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Cancelled all fused updates")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error cancelling fused provider: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -576,6 +576,7 @@ class LocationChannelManager private constructor(private val context: Context) {
|
|||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
Log.d(TAG, "Cleaning up LocationChannelManager")
|
Log.d(TAG, "Cleaning up LocationChannelManager")
|
||||||
endLiveRefresh()
|
endLiveRefresh()
|
||||||
|
locationProvider.cancel()
|
||||||
|
|
||||||
geocodingJob?.cancel()
|
geocodingJob?.cancel()
|
||||||
geocodingJob = null
|
geocodingJob = null
|
||||||
|
|||||||
@@ -32,4 +32,9 @@ interface LocationProvider {
|
|||||||
* @param callback The same callback instance passed to requestLocationUpdates.
|
* @param callback The same callback instance passed to requestLocationUpdates.
|
||||||
*/
|
*/
|
||||||
fun removeLocationUpdates(callback: (Location) -> Unit)
|
fun removeLocationUpdates(callback: (Location) -> Unit)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel any pending one-shot location requests and cleanup resources.
|
||||||
|
*/
|
||||||
|
fun cancel()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,12 @@ class SystemLocationProvider(private val context: Context) : LocationProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
private val handler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||||
|
|
||||||
// Map to keep track of listeners to unregister them later
|
// Map to keep track of listeners to unregister them later
|
||||||
private val activeListeners = mutableMapOf<(Location) -> Unit, LocationListener>()
|
private val activeListeners = mutableMapOf<(Location) -> Unit, LocationListener>()
|
||||||
|
private val activeOneShotListeners = mutableMapOf<(Location?) -> Unit, LocationListener>()
|
||||||
|
private val activeOneShotRunnables = mutableMapOf<(Location?) -> Unit, Runnable>()
|
||||||
|
|
||||||
private fun hasLocationPermission(): Boolean {
|
private fun hasLocationPermission(): Boolean {
|
||||||
return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
|
return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
|
||||||
@@ -80,17 +84,47 @@ class SystemLocationProvider(private val context: Context) : LocationProvider {
|
|||||||
callback(location)
|
callback(location)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For older versions, use requestSingleUpdate
|
// For older versions, use requestSingleUpdate with timeout mechanism
|
||||||
|
val timeoutRunnable = Runnable {
|
||||||
|
Log.w(TAG, "Location request timed out")
|
||||||
|
synchronized(activeOneShotListeners) {
|
||||||
|
val listener = activeOneShotListeners.remove(callback)
|
||||||
|
activeOneShotRunnables.remove(callback)
|
||||||
|
if (listener != null) {
|
||||||
|
try {
|
||||||
|
locationManager.removeUpdates(listener)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error removing timed out listener: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(null)
|
||||||
|
}
|
||||||
|
|
||||||
val listener = object : LocationListener {
|
val listener = object : LocationListener {
|
||||||
override fun onLocationChanged(location: Location) {
|
override fun onLocationChanged(location: Location) {
|
||||||
callback(location)
|
synchronized(activeOneShotListeners) {
|
||||||
|
activeOneShotListeners.remove(callback)
|
||||||
|
val runnable = activeOneShotRunnables.remove(callback)
|
||||||
|
if (runnable != null) {
|
||||||
|
handler.removeCallbacks(runnable)
|
||||||
|
}
|
||||||
|
}
|
||||||
locationManager.removeUpdates(this)
|
locationManager.removeUpdates(this)
|
||||||
|
callback(location)
|
||||||
}
|
}
|
||||||
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
|
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
|
||||||
override fun onProviderEnabled(provider: String) {}
|
override fun onProviderEnabled(provider: String) {}
|
||||||
override fun onProviderDisabled(provider: String) {}
|
override fun onProviderDisabled(provider: String) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized(activeOneShotListeners) {
|
||||||
|
activeOneShotListeners[callback] = listener
|
||||||
|
activeOneShotRunnables[callback] = timeoutRunnable
|
||||||
|
}
|
||||||
|
|
||||||
locationManager.requestSingleUpdate(provider, listener, null)
|
locationManager.requestSingleUpdate(provider, listener, null)
|
||||||
|
handler.postDelayed(timeoutRunnable, 30000L) // 30s timeout
|
||||||
}
|
}
|
||||||
providerFound = true
|
providerFound = true
|
||||||
break
|
break
|
||||||
@@ -169,4 +203,32 @@ class SystemLocationProvider(private val context: Context) : LocationProvider {
|
|||||||
Log.e(TAG, "Error removing updates: ${e.message}")
|
Log.e(TAG, "Error removing updates: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
try {
|
||||||
|
// Cancel continuous updates
|
||||||
|
synchronized(activeListeners) {
|
||||||
|
for ((_, listener) in activeListeners) {
|
||||||
|
try { locationManager.removeUpdates(listener) } catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
activeListeners.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel one-shot requests
|
||||||
|
synchronized(activeOneShotListeners) {
|
||||||
|
for ((_, listener) in activeOneShotListeners) {
|
||||||
|
try { locationManager.removeUpdates(listener) } catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
activeOneShotListeners.clear()
|
||||||
|
|
||||||
|
for ((_, runnable) in activeOneShotRunnables) {
|
||||||
|
handler.removeCallbacks(runnable)
|
||||||
|
}
|
||||||
|
activeOneShotRunnables.clear()
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Cancelled all system location requests")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error cancelling system provider: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user