I have a scenario, I want to show user current weather data for that I am getting his/her current lat/lng and reverse geocoding it to get the city name. Once I have the city name I will make a network call and show the weather data. Apart from this, there are many location operations I need to perform.
So I have created a class named as LocationUtils.kt
. I am following MVVM architecture and want to know which is the ideal layer to call the LocationUtils
methods, is it the view
layer or the viewmodel
layer or the data
layer. Since FusedLocationProvider
needs context
and if I use it in ViewModel
it will leak. So how to solve this problem?
LocationUtils.kt
:
class LocationUtils {
private lateinit var fusedLocationClient: FusedLocationProviderClient
private fun isLocationEnabled(weakContext: Context?): Boolean {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
// This is new method provided in API 28
val locationManager = weakContext?.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.isLocationEnabled
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> {
// This is Deprecated in API 28
val mode = Settings.Secure.getInt(
weakContext?.contentResolver, Settings.Secure.LOCATION_MODE,
Settings.Secure.LOCATION_MODE_OFF
)
mode != Settings.Secure.LOCATION_MODE_OFF
}
else -> {
val locationProviders = Settings.Secure.getString(weakContext?.contentResolver, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)
return !TextUtils.isEmpty(locationProviders)
}
}
}
@SuppressLint("MissingPermission")
fun getCurrentLocation(
weakContext: WeakReference<Context>,
success: (String?) -> Unit,
error: () -> Unit
) {
if (isLocationEnabled(weakContext.get())) {
weakContext.get()
?.let { context ->
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
getCurrentCity(context, location, success)
}
}
} else {
error()
}
}
private fun getCurrentCity(
context: Context,
location: Location?,
success: (String?) -> Unit
) {
val city = try {
location?.let {
val geocoder = Geocoder(context, Locale.getDefault())
val address = geocoder.getFromLocation(it.latitude, it.longitude, 1)
address[0].locality
}
} catch (e: Exception) {
"Bangalore"
}
success(city)
}
}
I am also working on the same problem. I also have to deal with showing weather data to user using MVVM architecture. At the moment, I am stuck at the same point where you are right now. The solution seems to be something called 'Dependency Injection (DI)'. Basically, we can inject dependencies like Context
to our ViewModel
using tools/frameworks like 'Dagger 2'. DI has lower coupling than directly passing Context
to ViewModel
and results in better compliance with MVVM. So, the actual place of FusedLocationProvider
, IMO, will be in ViewModel but after implementing DI. Maybe someone else can better elaborate on my explanation. I will update my answer once I implement Dependency Injection myself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With