My android project needs the users last / current location upon launching but I am not getting a valid location for some reason.
My Main activity checks for the permissions like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
permissionHelper = PermissionHelper(this)
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_content_home) as NavHostFragment
val navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home,
R.id.nav_account, R.id.nav_pictures, R.id.nav_settings
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
NavigationUI.setupWithNavController(binding.navView, navController)
navController.addOnDestinationChangedListener { _, destination, _ ->
isCurrentLocationFragmentVisible = destination.id == R.id.nav_home
if (isCurrentLocationFragmentVisible) {
invalidateOptionsMenu()
}
}
handleLocationAndWeather()
currentLocationViewModel.weather.observe(this){
currentLocation = LocationDto(it.location.cityName, it.location.locationIndex)
}
}
private fun handleLocationAndWeather() {
// Check if location permission is granted
if (permissionHelper.isPermissionGranted(Manifest.permission.ACCESS_COARSE_LOCATION)) {
// Permission is granted, load weather data
viewModel.updatePermissionState(LocationPermissionState.Granted)
viewModel.loadCurrentLocationWeather()
} else {
// Permission not granted, request it
permissionHelper.requestPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
"Location permission is required to get weather data for your current location.",
LOCATION_PERMISSION_REQUEST_CODE
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
LOCATION_PERMISSION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, update state and load weather data
viewModel.updatePermissionState(LocationPermissionState.Granted)
viewModel.loadCurrentLocationWeather()
} else {
// Permission denied, update state and handle accordingly
viewModel.updatePermissionState(LocationPermissionState.Denied)
viewModel.loadCurrentLocationWeather()
//TODO show toast here
}
}
}
}
and it uses this class for the permission related stuff:
class PermissionHelper(private val activity: Activity) {
fun isPermissionGranted(permission: String): Boolean {
return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED
}
fun requestPermission(permission: String, rationale: String, requestCode: Int) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
showRationaleDialog(rationale) { _, _ ->
ActivityCompat.requestPermissions(
activity,
arrayOf(permission),
requestCode
)
}
} else {
ActivityCompat.requestPermissions(
activity,
arrayOf(permission),
requestCode
)
}
}
private fun showRationaleDialog(message: String, onPositiveClick: (DialogInterface, Int) -> Unit) {
AlertDialog.Builder(activity)
.setTitle("Location permission Required")
.setMessage(message)
.setPositiveButton("OK", onPositiveClick)
.setNegativeButton("Cancel", null)
.show()
}
}
The main activity calls the viewmodel to fetch the data based on the location:
private val _locationPermissionState = MutableStateFlow<LocationPermissionState>(LocationPermissionState.Granted)
val locationPermissionState: StateFlow<LocationPermissionState> = _locationPermissionState
fun updatePermissionState(state : LocationPermissionState){
_locationPermissionState.value = state
}
@SuppressLint("MissingPermission")
fun loadCurrentLocationWeather() {
viewModelScope.launch {
when (locationPermissionState.value) {
LocationPermissionState.Granted -> {
Log.d(TAG, "permission granted")
locationGetter.client.lastLocation
.addOnSuccessListener { location ->
// Check if the location is not null before proceeding
if (location != null) {
Log.d(TAG, "location not null")
viewModelScope.launch {
weatherRepository.getWeather(
location.latitude,
location.longitude
)
.map { locationWithWeatherDataDto ->
val weatherData =
convertWeatherDtoToWeatherModel(
locationWithWeatherDataDto.weather
)
val locationData = LocationModel(
locationWithWeatherDataDto.location.cityName,
locationWithWeatherDataDto.location.locationIndex
)
LocationWeatherModel(
location = locationData,
weather = weatherData
)
}
.collect {
_weather.value = it
Log.d(TAG, it.weather.cityName)
}
}
} else {
// location is null show seattle
Log.d(TAG, "location is null")
viewModelScope.launch {
weatherRepository.getWeather("Chicago")
.map { locationWithWeatherDataDto ->
val weatherData =
convertWeatherDtoToWeatherModel(locationWithWeatherDataDto.weather)
val locationData = LocationModel(
locationWithWeatherDataDto.location.cityName,
locationWithWeatherDataDto.location.locationIndex
)
LocationWeatherModel(location = locationData, weather = weatherData)
}
.collect {
_weather.value = it
Log.d(TAG, it.weather.cityName)
}
}
}
}
}
else -> {
//location permission is denied
viewModelScope.launch {
weatherRepository.getWeather("Seattle")
.map { locationWithWeatherDataDto ->
val weatherData =
convertWeatherDtoToWeatherModel(locationWithWeatherDataDto.weather)
val locationData = LocationModel(
locationWithWeatherDataDto.location.cityName,
locationWithWeatherDataDto.location.locationIndex
)
LocationWeatherModel(location = locationData, weather = weatherData)
}
}
}
}
}
}
I have figured out that
locationGetter.client.lastLocation
.addOnSuccessListener { location ->
// Check if the location is not null before proceeding
if (location != null) {
is always null which leads me to believe that I am not getting a valid location. I have set a location in the emulator so I doubt that it just doesn't have a last location. Any feeback would be greatly appreciated!
Lastlocation doesn't turn on the location subsystem. It only returns a result if the location already is known by the OS. It will return null 99%+ of the time. It's an optimization, and was probably a design mistake by the OS to add it at all. Use requestLocationUpdates or requestSingleLocation to actually turn on location detection in the OS.