Bug fix: the app should show the correct geohash also if a location is not yet known to the system. Remove the default location in San Francisco and show a spinner until the correct position becomes available. (#308)

This commit is contained in:
mario7421
2025-08-28 08:10:48 +02:00
committed by GitHub
parent 6fe6e049ad
commit 846cd976c0
@@ -68,6 +68,10 @@ class LocationChannelManager private constructor(private val context: Context) {
private val _locationNames = MutableLiveData<Map<GeohashChannelLevel, String>>(emptyMap())
val locationNames: LiveData<Map<GeohashChannelLevel, String>> = _locationNames
// Add a new LiveData property to indicate when location is being fetched
private val _isLoadingLocation = MutableLiveData(false)
val isLoadingLocation: LiveData<Boolean> = _isLoadingLocation
init {
updatePermissionState()
// Initialize DataManager and load persisted channel selection
@@ -192,33 +196,138 @@ class LocationChannelManager private constructor(private val context: Context) {
Log.d(TAG, "Requesting one-shot location")
try {
// Get last known location first for quick result
val lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
// Try to get last known location from all available providers
var lastKnownLocation: Location? = null
// Get all available providers and try each one
val providers = locationManager.getProviders(true)
for (provider in providers) {
val location = locationManager.getLastKnownLocation(provider)
if (location != null) {
// If we find a location, check if it's more recent than what we have
if (lastKnownLocation == null || location.time > lastKnownLocation.time) {
lastKnownLocation = location
}
}
}
if (lastKnownLocation == null) {
lastKnownLocation = lastLocation;
}
// Use last known location if we have one
if (lastKnownLocation != null) {
Log.d(TAG, "Using last known location: ${lastKnownLocation.latitude}, ${lastKnownLocation.longitude}")
lastLocation = lastKnownLocation
_isLoadingLocation.postValue(false) // Make sure loading state is off
computeChannels(lastKnownLocation)
reverseGeocodeIfNeeded(lastKnownLocation)
} else {
Log.d(TAG, "No last known location available")
// For demo purposes, use a default location (San Francisco)
val demoLocation = Location("demo").apply {
latitude = 37.7749
longitude = -122.4194
}
Log.d(TAG, "Using demo location (San Francisco): ${demoLocation.latitude}, ${demoLocation.longitude}")
lastLocation = demoLocation
computeChannels(demoLocation)
reverseGeocodeIfNeeded(demoLocation)
// Set loading state to true so UI can show a spinner
_isLoadingLocation.postValue(true)
// Request a fresh location only when we don't have a last known location
Log.d(TAG, "Requesting fresh location...")
requestFreshLocation()
}
} catch (e: SecurityException) {
Log.e(TAG, "Security exception requesting location: ${e.message}")
_isLoadingLocation.postValue(false) // Turn off loading state on error
updatePermissionState()
}
}
// One-time location listener to get a fresh location update
private val oneShotLocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.d(TAG, "Fresh location received: ${location.latitude}, ${location.longitude}")
lastLocation = location
computeChannels(location)
reverseGeocodeIfNeeded(location)
// Update loading state to indicate we have a location now
_isLoadingLocation.postValue(false)
// Remove this listener after getting the update
try {
locationManager.removeUpdates(this)
} catch (e: SecurityException) {
Log.e(TAG, "Error removing location listener: ${e.message}")
}
}
}
// Request a fresh location update using getCurrentLocation instead of continuous updates
private fun requestFreshLocation() {
if (!hasLocationPermission()) {
_isLoadingLocation.postValue(false) // Turn off loading state if no permission
return
}
try {
// Set loading state to true to indicate we're actively trying to get a location
_isLoadingLocation.postValue(true)
// Try common providers in order of preference
val providers = listOf(
LocationManager.GPS_PROVIDER,
LocationManager.NETWORK_PROVIDER,
LocationManager.PASSIVE_PROVIDER
)
var providerFound = false
for (provider in providers) {
if (locationManager.isProviderEnabled(provider)) {
Log.d(TAG, "Getting current location from $provider")
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
// For Android 11+ (API 30+), use getCurrentLocation
locationManager.getCurrentLocation(
provider,
null, // No cancellation signal
context.mainExecutor,
{ location ->
if (location != null) {
Log.d(TAG, "Fresh location received: ${location.latitude}, ${location.longitude}")
lastLocation = location
computeChannels(location)
reverseGeocodeIfNeeded(location)
} else {
Log.w(TAG, "Received null location from getCurrentLocation")
}
// Update loading state to indicate we have a location now
_isLoadingLocation.postValue(false)
}
)
} else {
// For older versions, fall back to one-shot requestSingleUpdate
locationManager.requestSingleUpdate(
provider,
oneShotLocationListener,
null // Looper - null uses the main thread
)
}
providerFound = true
break
}
}
// If no provider was available, turn off loading state
if (!providerFound) {
Log.w(TAG, "No location providers available")
_isLoadingLocation.postValue(false)
}
} catch (e: SecurityException) {
Log.e(TAG, "Security exception requesting location: ${e.message}")
_isLoadingLocation.postValue(false) // Turn off loading state on error
} catch (e: Exception) {
Log.e(TAG, "Error requesting location: ${e.message}")
_isLoadingLocation.postValue(false) // Turn off loading state on error
}
}
// MARK: - Helpers
private fun getCurrentPermissionStatus(): PermissionState {
@@ -469,5 +578,17 @@ class LocationChannelManager private constructor(private val context: Context) {
fun cleanup() {
Log.d(TAG, "Cleaning up LocationChannelManager")
endLiveRefresh()
// For older Android versions, remove any remaining location listener to prevent memory leaks
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) {
try {
locationManager.removeUpdates(oneShotLocationListener)
} catch (e: SecurityException) {
Log.e(TAG, "Error removing location listener during cleanup: ${e.message}")
} catch (e: Exception) {
Log.e(TAG, "Error during cleanup: ${e.message}")
}
}
// For Android 11+, getCurrentLocation doesn't need explicit cleanup as it's a one-time operation
}
}