mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 08:05:22 +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())
|
private val _locationNames = MutableLiveData<Map<GeohashChannelLevel, String>>(emptyMap())
|
||||||
val locationNames: LiveData<Map<GeohashChannelLevel, String>> = _locationNames
|
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 {
|
init {
|
||||||
updatePermissionState()
|
updatePermissionState()
|
||||||
// Initialize DataManager and load persisted channel selection
|
// 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")
|
Log.d(TAG, "Requesting one-shot location")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get last known location first for quick result
|
// Try to get last known location from all available providers
|
||||||
val lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
var lastKnownLocation: Location? = null
|
||||||
?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
|
|
||||||
|
|
||||||
|
// 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) {
|
if (lastKnownLocation != null) {
|
||||||
Log.d(TAG, "Using last known location: ${lastKnownLocation.latitude}, ${lastKnownLocation.longitude}")
|
Log.d(TAG, "Using last known location: ${lastKnownLocation.latitude}, ${lastKnownLocation.longitude}")
|
||||||
lastLocation = lastKnownLocation
|
lastLocation = lastKnownLocation
|
||||||
|
_isLoadingLocation.postValue(false) // Make sure loading state is off
|
||||||
computeChannels(lastKnownLocation)
|
computeChannels(lastKnownLocation)
|
||||||
reverseGeocodeIfNeeded(lastKnownLocation)
|
reverseGeocodeIfNeeded(lastKnownLocation)
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "No last known location available")
|
Log.d(TAG, "No last known location available")
|
||||||
// For demo purposes, use a default location (San Francisco)
|
// Set loading state to true so UI can show a spinner
|
||||||
val demoLocation = Location("demo").apply {
|
_isLoadingLocation.postValue(true)
|
||||||
latitude = 37.7749
|
|
||||||
longitude = -122.4194
|
// Request a fresh location only when we don't have a last known location
|
||||||
}
|
Log.d(TAG, "Requesting fresh location...")
|
||||||
Log.d(TAG, "Using demo location (San Francisco): ${demoLocation.latitude}, ${demoLocation.longitude}")
|
requestFreshLocation()
|
||||||
lastLocation = demoLocation
|
|
||||||
computeChannels(demoLocation)
|
|
||||||
reverseGeocodeIfNeeded(demoLocation)
|
|
||||||
}
|
}
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
Log.e(TAG, "Security exception requesting location: ${e.message}")
|
Log.e(TAG, "Security exception requesting location: ${e.message}")
|
||||||
|
_isLoadingLocation.postValue(false) // Turn off loading state on error
|
||||||
updatePermissionState()
|
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
|
// MARK: - Helpers
|
||||||
|
|
||||||
private fun getCurrentPermissionStatus(): PermissionState {
|
private fun getCurrentPermissionStatus(): PermissionState {
|
||||||
@@ -469,5 +578,17 @@ 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()
|
||||||
|
|
||||||
|
// 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