android fusedlocationclient not getting location

51 Views Asked by At

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!

2

There are 2 best solutions below

0
On

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.

0
On

you can use getCurrentLocation(int priority, CancellationToken cancellationToken) instead. It will returns the most recent historical location currently available. Here's the docs. You can use that function using coroutine by getCurrentLocation(int priority, CancellationToken cancellationToken).await(). I hope it helps.