Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

proper place to use location api in android mvvm architecture

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)
  }
}
like image 544
sagar suri Avatar asked Aug 11 '19 09:08

sagar suri


1 Answers

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.

like image 169
Abdul Mateen Avatar answered Sep 21 '22 21:09

Abdul Mateen