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.
Related
In my kotlin project, google play console one error in Stability is - "android.os.NetworkOnMainThreadException". Please anyone help me to fix this issue. I have attached the screenshot of this issue and also i have added the signupactivity code i have used in my kotlin project.
class SignUpActivity : AppCompatActivity(), NetworkDialog.NetworkListener, GetResult.MyListener {
private lateinit var signUpViewModel: SignUpViewModel
private lateinit var locationViewModel: LocationViewModel
private var latitude: Double? = null
private var longitude: Double? = null
private var countryName: String? = null
private var countryCode: String? = null
private var state: String? = null
private var city: String? = null
private var nameUser: String? = null
private var email: String? = null
private var year: String? = null
private var month: String? = null
private var passwordUser: String? = null
private var isGPSEnabled = false
private lateinit var locationViewModelLatLong: Locationviewmodell
private lateinit var geoCoder: Geocoder
private val viewModel: MainViewModel by viewModels {
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T =
MainViewModel(
CoLocation.from(this#SignUpActivity),
CoGeocoder.from(this#SignUpActivity)
) as T
}
}
companion object {
const val requestShowSettings = 123
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sign_up)
date.listen()
DPref.init(this#SignUpActivity)
signUpViewModel = ViewModelProviders.of(this#SignUpActivity).get(SignUpViewModel::class.java)
locationViewModel = ViewModelProviders.of(this#SignUpActivity).get(LocationViewModel::class.java)
locationViewModelLatLong = ViewModelProviders.of(this#SignUpActivity).get(Locationviewmodell::class.java)
GpsUtils(this).turnGPSOn(object : GpsUtils.OnGpsListener {
override fun gpsStatus(isGPSEnable: Boolean) {
this#SignUpActivity.isGPSEnabled = isGPSEnable
}
})
signInButton.setOnClickListener {
onBackPressed()
}
date.minDate = dateMinMax(-99)
date.maxDate = dateMinMax(-18)
signUpButton.setOnClickListener {
try {
if (NetworkUtils.checkConnection()) {
invokeLocationAction()
isRegister()
} else {
val networkDialog = NetworkDialog()
networkDialog.showDialog(this#SignUpActivity)
networkDialog.setNetworkListener(this#SignUpActivity)
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
signUpViewModel.getUserSignUpData().observe(this) {
it.apply {
nameUser = userName
email = userEmail
year = userYear
month = userMonth
passwordUser = userPassword
}
}
locationViewModel.getUserSignUpLocationData().observe(this) {
it.apply {
latitude = userLatitude
longitude = userLongitude
countryName = userCountryName
countryCode = userCountryCode
state = userState
city = userCity
}
}
}
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null,
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
#Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
if (requestCode == GPS_REQUEST) {
isGPSEnabled = true
invokeLocationAction()
}
}
}
private fun invokeLocationAction() {
when {
!isGPSEnabled ->
Toast.makeText(this#SignUpActivity, "Please enable your location", Toast.LENGTH_SHORT).show()
isPermissionsGranted() -> startLocationUpdate()
shouldShowRequestPermissionRationale() ->
showSettingsDialog()
else -> ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),
LOCATION_REQUEST
)
}
}
private fun startLocationUpdate() {
locationViewModelLatLong.getLocationData().observe(this, Observer {
val la = it.latitude
val lo = it.longitude
Log.d("SignUpActivity", "getLocationData() it.userLatitude$la")
Log.d("SignUpActivity", "getLocationData() it.userLatitude$lo")
getLocation(la,lo)
})
}
private fun isPermissionsGranted() = ActivityCompat.checkSelfPermission( this,Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&this,Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
private fun shouldShowRequestPermissionRationale() = ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION) && ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_COARSE_LOCATION)
#SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
LOCATION_REQUEST -> {
invokeLocationAction()
}
}
}
private fun getLocation(latitude: Double, longitude: Double){
try {
geoCoder = Geocoder(this#SignUpActivity, Locale.ENGLISH)
val addresses = geoCoder.getFromLocation(latitude, longitude, 1)
val countryName = addresses?.get(0)?.countryName
val countryCode = addresses?.get(0)?.countryCode
val state = addresses?.get(0)?.adminArea
val city = addresses?.get(0)?.locality
val locationData = LocationModelClass(
userLatitude = latitude,
userLongitude = longitude,
userCountryName = countryName,
userCountryCode = countryCode,
userState = state,
userCity = city
)
locationViewModel.setUserSignUpLocationData(locationModelClass = locationData)
} catch (e: IOException) {
e.printStackTrace()
}
}
}
const val LOCATION_REQUEST = 100
const val GPS_REQUEST = 101
view model codes:
class Locationviewmodell (application: Application) : AndroidViewModel(application) {
private val locationData = LocationLiveData(application)
fun getLocationData() = locationData
}
class LocationViewModel : ViewModel() {
private val userLocationData: MutableLiveData<LocationModelClass> =
MutableLiveData<LocationModelClass>()
fun setUserSignUpLocationData(locationModelClass: LocationModelClass) {
userLocationData.value = locationModelClass
}
fun getUserSignUpLocationData(): MutableLiveData<LocationModelClass> {
return userLocationData
}
}
class GpsUtils(private val context: Context) {
private val settingsClient: SettingsClient = LocationServices.getSettingsClient(context)
private val locationSettingsRequest: LocationSettingsRequest?
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
init {
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(LocationLiveData.locationRequest)
locationSettingsRequest = builder.build()
builder.setAlwaysShow(true)
}
fun turnGPSOn(OnGpsListener: OnGpsListener?) {
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
OnGpsListener?.gpsStatus(true)
} else {
settingsClient
.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(context as Activity) {
// GPS is already enable, callback GPS status through listener
OnGpsListener?.gpsStatus(true)
}
.addOnFailureListener(context) { e ->
when ((e as ApiException).statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
try {
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
val rae = e as ResolvableApiException
rae.startResolutionForResult(context, GPS_REQUEST)
} catch (sie: IntentSender.SendIntentException) {
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
val errorMessage =
"Location settings are inadequate, and cannot be " + "fixed here. Fix in Settings."
Log.e(TAG, errorMessage)
Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show()
}
}
}
}
}
interface OnGpsListener {
fun gpsStatus(isGPSEnable: Boolean)
}
}
I want to when turn off the gps I see the settings dialog(requires) to turn of the gps Because my app needs GPS on. I have written code for this and put it in the onCreate() in MainActivity, but the dialog only shows me when the app runs, but I want to see this dialog wherever I turned off the GPS in app.
val settingsClient = LocationServices.getSettingsClient(this)
val locationRequest = LocationRequest()
val builder =
LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
.setAlwaysShow(false)
.setNeedBle(false)
settingsClient.checkLocationSettings(builder.build())
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val response = task.result ?: return#addOnCompleteListener
val locationSettingsStates =
response.locationSettingsStates
Log.e("yyy", locationSettingsStates.toString())
// TODO
}
}
.addOnFailureListener { e ->
Timber.i("checkLocationSetting onFailure:" + e.message)
when ((e as ApiException).statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
Timber.i("Location settings are not satisfied. Attempting to upgrade " + "location settings ")
try {
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
val rae = e as ResolvableApiException
rae.startResolutionForResult(this, 0)
} catch (sie: IntentSender.SendIntentException) {
Timber.i("PendingIntent unable to execute request.")
}
}
else -> {
}
}
}
}
It's a way to do this:
I created a LocationUtil class:
class LocationUtil(context: Context) {
companion object {
const val MIN_TIME: Long = 1000L
const val MIN_DISTANCE: Float = 0.0f
}
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private var locationListener: LocationListener? = null
val locationStateFlow = MutableStateFlow<Location>(Location(LocationManager.GPS_PROVIDER))
val gpsProviderState = mutableStateOf(false)
val isStart: MutableState<Boolean> = mutableStateOf(false)
private val locHandlerThread = HandlerThread("LocationUtil Thread")
init {
locHandlerThread.start()
}
#SuppressLint("MissingPermission")
fun start(minTimeMs: Long = MIN_TIME_MS, minDistanceM: Float = MIN_DISTANCE_M) {
locationListener().let {
locationListener = it
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTimeMs, minDistanceM, it, locHandlerThread.looper)
}
gpsProviderState.value = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
isStart.value = true
}
fun stop() {
locationListener?.let {
locationManager.removeUpdates(it)
}
isStart.value = false
}
private fun locationListener() = object : LocationListener {
override fun onLocationChanged(location: Location) {
locationStateFlow.value = location
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
}
override fun onProviderEnabled(provider: String) {
gpsProviderState.value = true
}
override fun onProviderDisabled(provider: String) {
gpsProviderState.value = false
}
}
}
and I create a file with some functions:
private const val REQUEST_CODE_LOCATION_SOURCE_SETTINGS = 200
fun getLocationManager(context: Context): LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
fun isLocEnable(context: Context): Boolean {
val locationManager = getLocationManager(context)
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
}
#Composable
fun IsLocationEnable(context: Context) {
if (!isLocEnable(context)) {
SimpleAlertDialog(
title = stringResource(id = R.string.title),
text = stringResource(id = R.string.dialog_gps_setting),
singleButton = false,
confirmText = stringResource(R.string.settings),
dismissText = stringResource(R.string.cancel),
onConfirm = {
if (it) {
Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS,
Uri.fromParts(
"package",
context.packageName,
null
)
).also { intent ->
try {
context.startActivity(
intent
)
} catch (e: ActivityNotFoundException) {
Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS
).also { intentCatch ->
context.startActivity(
intentCatch
)
}
}
}
}
})
}
}
#Composable
fun LocationState(context: Activity) {
IsLocationEnable(context)
}
and finally I used codes in the MainActivity:
Box(modifier = Modifier.fillMaxSize()) {
Column(modifier = Modifier.fillMaxSize()) {
ConnectivityStatus()
ClientScaffold(clientNavigator)
}
val gpsEnable by locationUtil.gpsProviderState
if (!gpsEnable) {
IsLocationEnable(this#MainActivity)
}
}
How about having a Data layer LocationRepository with a gpsStatus flow like this
class LocationRepository(/** inject context **/) {
val gpsStatus = flow {
val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
while (currentCoroutineContext().isActive) {
emit(
manager.isProviderEnabled(LocationManager.GPS_PROVIDER)
)
delay(3000)
}
}
}
Then observe it at UI layer, to hide/ show the dialog.
I am fetching weather data from an API. The problem that I am facing is when I get the cityName in textView from the current locations and then I have to send that cityName to the API so I can get the temperature of that city. But I am unable to do this. Any help will be appreciated.
HomeRepositery
suspend fun getWeatherDataFromApi(cityName : String,appid : String) =
weatherServiceApi.getchWeatherData(cityName, appid)
HomeViewModel
val weatherData: MutableLiveData<Resource<WeatherDataClass>> = MutableLiveData()
init {
getWeatherDataFromRepo("" ,"appid")
}
//here we make a network response
fun getWeatherDataFromRepo(cityName: String, appId: String) = viewModelScope.launch {
weatherData.postValue(Resource.Loading())
val response = homeRepositery.getWeatherDataFromApi(cityName, appId)
weatherData.postValue(handleWeatherResponseData(response))
}
Fragment
#SuppressLint("MissingPermission")
private fun getLocation() {
val fusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(requireActivity())
val locationRequest = LocationRequest().setInterval(100000).setFastestInterval(100000)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
fusedLocationProviderClient.requestLocationUpdates(
locationRequest,
object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
for (location in locationResult.locations) {
latTextView.text = location.latitude.toString()
lngTextView.text = location.longitude.toString()
tv_cityName.text = getCityName(location.latitude, location.longitude)
onObserveLiveData()
}
// Few more things we can do here:
// For example: Update the location of user on server
}
},
Looper.myLooper()
)
}
private fun onObserveLiveData() {
viewModel.weatherData.observe(viewLifecycleOwner, androidx.lifecycle.Observer { response ->
when (response) {
is Resource.Success -> {
response.data?.let { weatherResponse ->
tv_humidity.text = weatherResponse.main.humidity.toString()
tv_weather.text = weatherResponse.clouds.all.toString()
tv_temp.text = weatherResponse.main.temp.toString()
}
}
is Resource.Error -> {
response.message?.let {
Toast.makeText(
requireContext(),
"THeir is am error in fetching",
Toast.LENGTH_SHORT
).show()
}
}
is Resource.Loading -> {
Toast.makeText(
requireContext(),
"Is in Loading state so please wait foe while!!",
Toast.LENGTH_SHORT
).show()
}
}
})
}
This class work fine and now i am interesting how write unit test
I have two independent flows:
location
gps status
I use publish subject for make my interactor in reactive manner.
Both flows mix with function Observable.withLatestFrom where i additionally add filtration and transformation.
Is it my gps class written correct ?
Can this class be tested ?
I need to test initFullLocationObservable
class GpsInteractor #Inject constructor(
private val locationManager: LocationManager,
private val googleApiClient: GoogleApiClient,
private val locationRequest: LocationRequest): GoogleApiClient.ConnectionCallbacks, LocationListener, GpsStatus.Listener {
private var gpsStatus: GpsStatus? = null
val locationSubject = PublishSubject.create<LocationModel>()
val satellitesSubject = PublishSubject.create<List<SatelliteModel>>()
lateinit var fullLocationObservable: Observable<FullLocation> private set
#SuppressLint("MissingPermission")
fun callLocationUpdates(updateInterval: Int, smallestDisplacement: Int, minAccuracy: Int, minSatellitesCount: Int, minSnr: Int) {
locationRequest.interval = (updateInterval * 1000).toLong()
locationRequest.fastestInterval = (updateInterval * 1000).toLong()
locationRequest.smallestDisplacement = smallestDisplacement.toFloat()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
googleApiClient.registerConnectionCallbacks(this)
locationManager.addGpsStatusListener(this)
fullLocationObservable = initFullLocationObservable(minAccuracy, minSatellitesCount, minSnr)
googleApiClient.connect()
}
fun removeLocationUpdates() {
locationManager.removeGpsStatusListener(this)
if (googleApiClient.isConnected) {
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this)
googleApiClient.disconnect()
}
}
#SuppressLint("MissingPermission")
override fun onConnected(p0: Bundle?) { LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this)
}
override fun onLocationChanged(location: Location) {
// TODO: make locationModel reusable
locationSubject.onNext(LocationModel(latitude = location.latitude, longitude = location.longitude,
time = location.time, speed = location.speed, accuracy = location.accuracy,
altitude = location.altitude, bearing = location.bearing))
}
#SuppressLint("MissingPermission")
override fun onGpsStatusChanged(event: Int) {
if (event != GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
return
}
gpsStatus = locationManager.getGpsStatus(gpsStatus)
if (gpsStatus != null) {
// TODO: make satellitesModel reusable
val satellites: List<SatelliteModel> = gpsStatus!!
.satellites.filter { it.usedInFix() }
.map { SatelliteModel(it.prn, it.elevation, it.azimuth, it.snr) }
satellitesSubject.onNext(satellites)
}
}
private fun initFullLocationObservable(minAccuracy: Int, minSatellitesCount: Int, minSnr: Int): Observable<FullLocation> {
val locationObservable = locationSubject
.filter { locationModel -> locationModel.accuracy <= minAccuracy }
val satellitesObservable = satellitesSubject
.map { satellites: List<SatelliteModel> ->
satellites.filter { it.snr >= minSnr }
}
.filter { it.size >= minSatellitesCount }
return locationObservable.withLatestFrom(satellitesObservable, BiFunction { locationModel: LocationModel, satellitesModel: List<SatelliteModel> ->
val locationData = LocationData(locationModel)
val satellites = satellitesModel.map { Satellite(it.snr, locationData) }
FullLocation(locationData, satellites)
})
}
}
can anyone tell me why when I call the function initPetrolStationList() in first if condition currentLocation is allways null but when I call te function addPetrolStation(v: View) is initilized? I would like that when the function initPetrolStationList()is called currentLocation will be not null.
class PetrolStationListActivity : AppCompatActivity() {
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private lateinit var listView: RecyclerView
private var currentLocation: Location? = null
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
//some code
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.petrol_station_list_view)
requestLocationPermission()
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
getLastLocation()
initPetrolStationList()
}
fun addPetrolStation(v: View) {
if (currentLocation != null) {
val googleMapController = GoogleMapsController()
googleMapController.getPetrolStationFromGoogle(object : GoogleResponse {
override fun getLocation(): Location = currentLocation!!
override fun getResponse(body: GoogleMapResponse) {
if (body.status == "OK") {
val intent = Intent(applicationContext, PetrolStationActivity::class.java)
v.context.startActivity(intent)
} else {
Toast.makeText(applicationContext, "Petrol station not found", Toast.LENGTH_LONG).show()
}
}
})
}
}
#SuppressLint("MissingPermission")
private fun getLastLocation() {
fusedLocationProviderClient.lastLocation.addOnCompleteListener {
if (it.isSuccessful) {
currentLocation = it.result!!
Log.d("Current location: ", "Lat:${currentLocation!!.latitude}, Lng:${currentLocation!!.longitude}")
} else {
Log.d("Location exception: ", it.exception?.message)
Toast.makeText(this, "Location not found :( ", Toast.LENGTH_LONG).show()
}
}
}
private fun initPetrolStationList() {
if (currentLocation != null) {
val petrolStationController = PetrolStationController()
//some code
} else Toast.makeText(this, "Location not found", Toast.LENGTH_LONG).show()
}
}
If you call initPetrolStationList from inside getLastLocation after currentLocation = it.result!! then currentLocation will not be null.
Try to initialize petrolStationController immediately
Add fusedLocationProviderClient.lastLocation.addOnCompleteListener in onCreate
And update data of petrolStationController in this listener