Fetching user's location asynchronously

120 Views Asked by At

I want to get user's location when user entered to AddComplaintFragment I'm using LocationManager for this but it lasts about at least 4 or 5 seconds to get the location but I need to add this location information to my Complaint model when the user clicks to makeComplaint button. But since I don't know when the LocationManager exactly gets location it may return null at the mean time ( while user push the makeComplaint button ). How can I prevent this ?

Thanks in advance.


Function to get location:

private fun requestLocationPermission() {
     when {
         ContextCompat.checkSelfPermission(
             requireContext(),
             Manifest.permission.ACCESS_COARSE_LOCATION
         ) == PackageManager.PERMISSION_GRANTED -> {
             locationManager!!.requestSingleUpdate(
                 LocationManager.GPS_PROVIDER,
                 object : LocationListener {
                     override fun onLocationChanged(location: Location) {
                         val geocoder = Geocoder(requireContext(), Locale.getDefault())
                         val addresses =
                             geocoder.
                             getFromLocation(location!!.latitude, location.longitude, 1)
                         val address = addresses[0]
                         addressString = address.locality
                         latLongMap = hashMapOf()
                         val latitude = address.latitude
                         val longitude = address.longitude
                         latLongMap["latitude"] = latitude
                         latLongMap["longitude"] = longitude
                         println(addressString)
                     }

                     override fun onStatusChanged(
                         provider: String?,
                         status: Int,
                         extras: Bundle?
                     ) {
                     }

                     override fun onProviderEnabled(provider: String) {
                         println("gps enabled")
                     }

                     override fun onProviderDisabled(provider: String) {
                         println("gps disabled")

                     }
                 },
                 Looper.getMainLooper()
             )
         }

         else -> {
             locationPermissionRequest.launch(
                 arrayOf(
                     Manifest.permission.ACCESS_FINE_LOCATION,
                     Manifest.permission.ACCESS_COARSE_LOCATION
                 )
             )
         }
     }
}

Adding complaint to the model in the fragment's onViewCreated method. I'm storing lat and long in a global map variable:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        btn_make_complaint.setOnClickListener {
            val givenAddress = et_complaint_location.text.toString()
            val complaintContent = et_complaint.text.toString()
            val complaintLocation: String? = addressString
            val complaint = Complaint(
                userName = GlobalValues.userName.value.toString(),
                content = complaintContent,
                location = complaintLocation ?: givenAddress,
                status = "Solving",
                urgency = "Not urgent",
                latitude = latLongMap["latitude"],
                longitude = latLongMap["longitude"],
            )
            viewModel.saveImageToStorage(selectedPicture!!, complaint)

        }
    }

1

There are 1 best solutions below

0
On

Here is a sample code of how I get Location using coroutines:

val isValidLocation = suspendCoroutine<Boolean> { cont ->
            MyLocationProvider.getInstance(context, prefsProvider,
                object : MyLocationProvider.OnMyLocationUpdate {
                    override fun onUpdated(myLocation: MyLocation?, context: Context) {
                        Log.d(TAG, "onUpdated: $myLocation")
                        user.lat = myLocation?.latitude ?: 0.0
                        user.lon = myLocation?.longitude ?: 0.0
                        if (myLocation == null) {
                            cont.resume(false)
                        } else {
                            Utils.getDecodedLocation(
                                context,
                                myLocation.latitude,
                                myLocation.longitude
                            ) { address ->
                                Log.d(TAG, "onUpdated: decoded: $address")
                                user.locationAddress = address ?: "Unknown"
                                cont.resume(true)
                            }
                        }
                    }
                }
            ).setUpLocationFormalities(true)
        }
        if (isValidLocation.not()) {
            emit(DataResponse.Error("Failed to get location"))
            return@flow
        }

Here MyLocationProvider is a class which implements the whole flow of getting location and finally triggers MyLocationProvider.OnMyLocationUpdate on result. I am using suspendCoroutine but you can do the same thing using await method.