Search code examples
androidkotlinlocation

Use location on API GET call after requesting permissions


Essentially my problem is in the title, where I get permissions for location and from there try to get an API response based on the location. Problem is that the thread seems to continue before getting the answer? Here are the relevant snippets of code (and apologies for any beginner mistakes)

class ApiViewModel : ViewModel() {
    private val _listOfRegions = MutableLiveData<List<Region>>()
    val listOfRegions: LiveData<List<Region>> = _listOfRegions

    fun getRegionsData(country: String) {
        viewModelScope.launch {
            Log.d("Workflow", "We will try to fetch info for $country")
            try {
                val listResult = SpotApi.retrofitService.getRegions(country)
                Log.d("Workflow", listResult.toString())
                if (listResult.isSuccessful) {
                    _listOfRegions.postValue(listResult.body())
                    Log.d("Workflow", listResult.body().toString())
                } else {
                    _listOfRegions.value = emptyList()
                }
            } catch (e: Exception) {
                Log.d("Workflow", "Failure: ${e.message}")
                _listOfRegions.value = emptyList()
            }
        }
    }
private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .baseUrl(BASE_URL)
    .build()

interface ApiService {
    @GET("{country}")
    suspend fun getRegions(@Path("country") country: String): Response<List<Region>>
}

object SpotApi {
    val retrofitService: ApiService by lazy {
        retrofit.create(ApiService::class.java)
    }
}
object RegionsHelper {

    fun getCurrentLocation(context: Context, lat: Double, long: Double): String {
        val geocoder = Geocoder(context)
        val locationResult = geocoder.getFromLocation(lat, long, 1)
        val country = locationResult[0].countryCode
        Log.d("Workflow", "Country is $country")
        Log.d(
            "Workflow",
            locationResult[0].latitude.toString() + locationResult[0].longitude.toString()
        )
        return if (locationResult[0] != null) {
            country
        } else {
            "Something Else"
        }
    }
}
class MainActivity : AppCompatActivity() {

    private lateinit var fusedLocationClient: FusedLocationProviderClient
    val viewModel: ApiViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        // TODO: If LastLocation available use that to trigger result, else use default value
        getLastLocation()
    }

    private fun getLastLocation() {
        if (checkLocationPermission()) {
            if (isLocationEnabled()) {
                fusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->
                    val location: Location? = task.result
                    if (location == null) {
                        requestNewLocationData()
                    } else {
                        Log.d("Workflow", "Permission Granted")
                        Log.d("Workflow", "Location is $location")
                        val retrievedCurrentCountry = getCurrentLocation(this@MainActivity, location.latitude, location.longitude)
                        Log.d("Workflow", "Current country is $retrievedCurrentCountry")
                        viewModel.getRegionsData(retrievedCurrentCountry)
                        //Log.d("Workflow", listOfRegions.toString())
                    }
                }
            }
        }
    }
}

Logs result are:

D/Workflow: Country is PT
D/Workflow: 38.7211345-9.139605
D/Workflow: Current country is PT
D/Workflow: We will try to fetch info for PT
D/Workflow: null
D/Workflow: Response{protocol=http/1.0, code=200, message=OK, url=http://192.168.1.181:5000/PT/}

D/Workflow: [Region(region=cascais, country=PT, long=-9.4207, lat=38.6968), Region(region=sintra, country=PT, long=-9.3817, lat=38.8029), Region(region=caparica, country=PT, long=-9.2334, lat=38.6446), Region(region=ericeira, country=PT, long=-9.4176, lat=38.9665)]*


Solution

  • Even though getting last location using fused location provider is very quick, sometimes it may take some time (upto 1000ms).

    In your code, your API is called before getting last location. you can achieve this by using observer for your retrievedCurrentLocation.

    class MainActivity : AppCompatActivity() {
    
        private lateinit var fusedLocationClient: FusedLocationProviderClient
        val viewModel: ApiViewModel by viewModels()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    
            // TODO: If LastLocation available use that to trigger result, else use default value
            getLastLocation()
    
            observeRetrievedLocation()
        }
    
        private fun observeRetrievedLocation() {
            viewModel.retrievedLocation.observe(this) {
                if (it.isNotEmpty) {
                  viewModel.getRegionsData(it)
                }
            }
        }
    
        private fun getLastLocation() {
            if (checkLocationPermission()) {
                if (isLocationEnabled()) {
                    fusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->
                        val location: Location? = task.result
                        if (location == null) {
                            requestNewLocationData()
                        } else {
                            Log.d("Workflow", "Permission Granted")
                            Log.d("Workflow", "Location is $location")
                            val retrievedCurrentCountry = getCurrentLocation(this@MainActivity, location.latitude, location.longitude)
                            Log.d("Workflow", "Current country is $retrievedCurrentCountry")
                            viewModel.loadRetrievedLocation(retrievedCurrentCountry)
                        }
                    }
                }
            }
        }
    }
    

    And your viewModel,

    class ApiViewModel : ViewModel() {
        private val _listOfRegions = MutableLiveData<List<Region>>()
        val listOfRegions: LiveData<List<Region>> = _listOfRegions
    
        private val _retrievedLocation = MutableLiveData<String>()
        val retrievedLocation: LiveData<String> = _retrievedLocation
    
    
        fun loadRetrievedLocation(retrievedLocation: String) {
            _retrievedLocation.value = retrievedLocation
        }
    
        fun getRegionsData(country: String) {
            viewModelScope.launch {
                Log.d("Workflow", "We will try to fetch info for $country")
                try {
                    val listResult = SpotApi.retrofitService.getRegions(country)
                    Log.d("Workflow", listResult.toString())
                    if (listResult.isSuccessful) {
                        _listOfRegions.postValue(listResult.body())
                        Log.d("Workflow", listResult.body().toString())
                    } else {
                        _listOfRegions.value = emptyList()
                    }
                } catch (e: Exception) {
                    Log.d("Workflow", "Failure: ${e.message}")
                    _listOfRegions.value = emptyList()
                }
            }
        }
    

    so your API will be called only when the value of retrievedCurrentLocation Livedata changes and not null.