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)]*
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.