mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 03:05:20 +00:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user