Skip to main content

Overview

The LocationService class provides location functionality including GPS coordinate retrieval and reverse geocoding to convert coordinates into human-readable addresses. It handles permission checks and supports both modern and legacy Android versions. Location: com.demodogo.ev_sum_2.services.LocationService

Constructor

class LocationService(private val context: Context)
context
Context
required
The Android application context required for accessing location services and geocoding

Data Classes

LocationResult

Represents a location with coordinates and optional address.
data class LocationResult(
    val coords: DeviceLocation,
    val address: String?
)
coords
DeviceLocation
The GPS coordinates containing latitude and longitude
address
String?
The human-readable address, or null if geocoding failed

Methods

hasPermission

Checks if the app has location permissions.
fun hasPermission(): Boolean
Returns: Boolean - true if location permissions are granted, false otherwise

Usage Example

val locationService = LocationService(context)

if (locationService.hasPermission()) {
    // Proceed with location retrieval
} else {
    // Request location permissions
}

getLocationWithAddress

Retrieves the device’s current location with a reverse-geocoded address.
@RequiresPermission(anyOf = [
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.ACCESS_COARSE_LOCATION
])
suspend fun getLocationWithAddress(): LocationResult
Returns: LocationResult - Contains coordinates and optional address Throws: IllegalStateException if location permission is not granted
This method requires either ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission. Always check permissions before calling this method.
The address is geocoded using Spanish (Chile) locale (“es-CL”). If geocoding fails, the address will be null, but the coordinates will still be available.

Usage Example

val locationService = LocationService(context)

if (locationService.hasPermission()) {
    try {
        val result = locationService.getLocationWithAddress()
        println("Coordinates: ${result.coords.latitude}, ${result.coords.longitude}")
        println("Address: ${result.address ?: "Unknown"}")
    } catch (e: IllegalStateException) {
        println("Permission error: ${e.message}")
    } catch (e: Exception) {
        println("Location error: ${e.message}")
    }
} else {
    // Request permissions
}

reverseGeocodeSafe (Private)

Internal method that performs reverse geocoding with error handling.
private suspend fun reverseGeocodeSafe(lat: Double, lon: Double): String?
This is a private method that handles both modern (API 33+) and legacy Android geocoding APIs. It automatically falls back to null if geocoding fails for any reason.

Android Version Compatibility

The service automatically handles different Android API levels:
  • API 33+: Uses the modern callback-based Geocoder.getFromLocation() method
  • API < 33: Uses the deprecated synchronous Geocoder.getFromLocation() method

Permission Requirements

Before using getLocationWithAddress(), you must request and obtain one of these permissions:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Requesting Permissions

val locationPermissionRequest = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
    when {
        permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true -> {
            // Precise location access granted
        }
        permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true -> {
            // Approximate location access granted
        }
        else -> {
            // No location access granted
        }
    }
}

locationPermissionRequest.launch(
    arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
)

Complete Usage Example

class LocationViewModel(application: Application) : AndroidViewModel(application) {
    private val locationService = LocationService(application)
    private val _locationState = MutableStateFlow<LocationState>(LocationState.Idle)
    val locationState: StateFlow<LocationState> = _locationState.asStateFlow()

    sealed class LocationState {
        object Idle : LocationState()
        object Loading : LocationState()
        data class Success(val result: LocationResult) : LocationState()
        data class Error(val message: String) : LocationState()
        object PermissionDenied : LocationState()
    }

    fun checkAndGetLocation() {
        if (!locationService.hasPermission()) {
            _locationState.value = LocationState.PermissionDenied
            return
        }

        viewModelScope.launch {
            _locationState.value = LocationState.Loading
            try {
                val result = locationService.getLocationWithAddress()
                _locationState.value = LocationState.Success(result)
            } catch (e: IllegalStateException) {
                _locationState.value = LocationState.Error("Permission not granted")
            } catch (e: Exception) {
                _locationState.value = LocationState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

Error Handling

SecurityException

Thrown when location permissions are not granted:
try {
    val result = locationService.getLocationWithAddress()
} catch (e: IllegalStateException) {
    // Handle permission error
    showPermissionDialog()
}

Geocoding Failures

Geocoding failures are handled gracefully - the address will be null, but coordinates are still available:
val result = locationService.getLocationWithAddress()
if (result.address == null) {
    // Show coordinates only
    showLocation(result.coords)
} else {
    // Show both coordinates and address
    showLocationWithAddress(result.coords, result.address)
}

See Also

Build docs developers (and LLMs) love