I have created some classes using those you can easily get the current location. I used FusedLocationProviderClient for getting the current location.
First Add this into your manifest File:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Then Check for location permission:
private fun startCheckingLocation() {
if (checkLocationPermissions() == true) {
checkGPSEnabled()
} else {
askLocationPermission()
}
}
checkLocationPermissions method:
private fun checkLocationPermissions(): Boolean? {
return PermissionUtils.hasPermissions(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
}
checkGPSEnabled method:
private fun checkGPSEnabled() {
GpsUtils(requireContext()) {
it?.let {
startCheckingCurrentLocation()
}
}.apply {
turnGPSOn(gpsDialogCallback)
}
}
As OnActivityResult is Deprecated:
private val gpsDialogCallback = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
activityResult?.let { result ->
when (result.resultCode) {
RESULT_OK -> {
startCheckingCurrentLocation()
}
RESULT_CANCELED -> {
}
}
}
}
startCheckingCurrentLocation method :
private fun startCheckingCurrentLocation() {
LocationUtils(requireContext()) { location ->
Log.d(TAG, ">>>>>>>>>>>>>>" + location.latitude + " " + location.longitude)
startIntentService(location)
}.apply {
startLocationUpdates()
}
}
For GPS I have created one class that you can simply put and use it:
GPSUtils:
class GpsUtils(
private val context: Context,
private val gpsStatus: (isEnable: Boolean?) -> Unit
) {
private val mSettingsClient: SettingsClient = LocationServices.getSettingsClient(context)
private val mLocationSettingsRequest: LocationSettingsRequest
private val locationManager: LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private val locationRequest: LocationRequest = LocationRequest.create()
init {
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = 10 * 1000.toLong()
locationRequest.fastestInterval = 2 * 1000.toLong()
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
mLocationSettingsRequest = builder.build()
builder.setAlwaysShow(true) //this is the key ingredient
}
// method for turn on GPS
fun turnGPSOn(gpsDialogCallback: ActivityResultLauncher<IntentSenderRequest>) {
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
gpsStatus.invoke(true)
} else {
mSettingsClient
.checkLocationSettings(mLocationSettingsRequest)
.addOnSuccessListener(
(context as Activity)
) {
// GPS is already enable, callback GPS status through listener
gpsStatus.invoke(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().
if (e is ResolvableApiException) {
try {
val intentSenderRequest = IntentSenderRequest.Builder(e.resolution).build()
gpsDialogCallback.launch(intentSenderRequest)
} catch (throwable: Throwable) {
// Ignore the error.
}
}
} catch (sie: IntentSender.SendIntentException) {
// Ignore the error.
Timber.i("PendingIntent unable to execute request.")
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
// Location settings are not satisfied. However, we have no way to fix the
// settings so we won't show the dialog.
val errorMessage =
"Location settings are inadequate, and cannot be fixed here. Fix in Settings."
Timber.e(errorMessage)
}
LocationSettingsStatusCodes.CANCELED -> {
val errorMessage =
"Location settings are inadequate, and cannot be fixed here. Fix in Settings."
Timber.e(errorMessage)
}
LocationSettingsStatusCodes.SUCCESS -> {
// All location settings are satisfied. The client can initialize location
// requests here.
val errorMessage =
"Location settings are inadequate, and cannot be fixed here. Fix in Settings."
Timber.e(errorMessage)
}
}
}
}
}
}
For checking location I have created one more class :
class LocationUtils(
context: Context,
private val latLng: (location: Location) -> Unit) {
private var fusedLocationClient: FusedLocationProviderClient? = null
private val locationRequest = LocationRequest.create()?.apply {
interval = 20 * 1000.toLong()
fastestInterval = 2 * 1000.toLong()
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
init {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
}
/**
* call when location permission is allowed and you want to fetch the last location of the user
*/
fun getLastLocation() {
fusedLocationClient?.lastLocation?.addOnSuccessListener { location ->
location?.let {
latLng.invoke(location)
stopLocationUpdates()
}
}
}
/**
* Requested location callback
*/
private val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations) {
location?.let {
latLng.invoke(it)
stopLocationUpdates()
}
}
super.onLocationResult(locationResult)
}
}
/**
* call when location permission is already given to user and you want to receive continues location updates
*/
fun startLocationUpdates() {
fusedLocationClient?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
/**
* call when you want to stop location updates
*/
fun stopLocationUpdates() {
fusedLocationClient?.removeLocationUpdates(locationCallback)?.addOnCompleteListener { }
}
}
I have created one worker class to get the address from latlng:
class FetchAddressWorker(
context: Context,
workerParameters: WorkerParameters
) :
Worker(context, workerParameters) {
companion object {
const val TAG = "FetchAddressWorker"
}
override fun doWork(): Result {
val geoCoder = Geocoder(applicationContext, Locale.getDefault())
var errorMessage = ""
val lat: Double = inputData.getDouble(CPKConstants.LATITUDE, 0.0)
val lng: Double = inputData.getDouble(CPKConstants.LONGITUDE, 0.0)
var addresses: List<Address> = emptyList()
try {
addresses =
geoCoder.getFromLocation(lat, lng, 1)
} catch (ioException: IOException) {
// Catch network or other I/O problems.
errorMessage = "Service not available"
LogUtils.e(TAG, errorMessage, ioException)
} catch (illegalArgumentException: IllegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = "Invalid lat lng used"
LogUtils.e(
TAG,
"$errorMessage. Latitude = $lat , Longitude = $lng",
illegalArgumentException
)
}
// Handle case where no address was found.
if (addresses.isEmpty()) {
if (errorMessage.isEmpty()) {
errorMessage = "No Address Found"
LogUtils.e(TAG, errorMessage)
}
val data = Data.Builder()
.putString(
CPKConstants.FAILURE_RESULT,
errorMessage
)
.build()
return Result.failure(data)
} else {
val address: Address = addresses[0]
// Fetch the address lines using getAddressLine,
// join them, and send them to the thread.
val addressFragments = with(address) {
(0..maxAddressLineIndex).map { getAddressLine(it) }
}
LogUtils.i(TAG, "Address Found " + addressFragments.joinToString(separator = "\n"))
val data = Data.Builder()
.putString(
CPKConstants.SUCCESS_RESULT,
addressFragments.joinToString(separator = "\n")
)
.build()
// Indicate whether the work finished successfully with the Result
return Result.success(data)
}
}
}
Than use AddressResultReceiver in your fragment or activity:
internal inner class AddressResultReceiver(handler: Handler) : ResultReceiver(handler) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
// Display the address string
// or an error message sent from the intent service.
val addressOutput = resultData?.getString(AppConstants.RESULT_DATA_KEY).orEmpty()
//displayAddressOutput()
// Show a toast message if an address was found.
if (resultCode == AppConstants.SUCCESS_RESULT) {
Boast.showText(requireContext(), "Address found = $addressOutput")
txtContinueWith.text = addressOutput
}
}
}
You will need to initialize this in fragment or activity where you will use the above receiver to get the address:
private var resultReceiver = AddressResultReceiver(Handler())
These are some constants that you should use as it is.
//Location Constants
const val LOCATION_SERVICE = "LOCATION_SERVICE"
const val SUCCESS_RESULT = 0
const val FAILURE_RESULT = 1
const val PACKAGE_NAME = "com.google.android.gms.location.sample.locationaddress"
const val RECEIVER = "$PACKAGE_NAME.RECEIVER"
const val RESULT_DATA_KEY = "${PACKAGE_NAME}.RESULT_DATA_KEY"
const val LOCATION_DATA_EXTRA = "${PACKAGE_NAME}.LOCATION_DATA_EXTRA"
Start worker from view
private fun startAddressWorkManager(location: Location) {
val inputData = Data.Builder()
.putDouble(CPKConstants.LATITUDE, location.latitude)
.putDouble(CPKConstants.LONGITUDE, location.longitude)
.build()
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val fetchAddressWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<FetchAddressWorker>()
.setConstraints(constraints)
.setInputData(inputData)
.build()
WorkManager
.getInstance(this)
.enqueue(fetchAddressWorkRequest)
WorkManager.getInstance(this).getWorkInfoByIdLiveData(fetchAddressWorkRequest.id)
.observe(this,
{ workInfo ->
when (workInfo.state) {
WorkInfo.State.SUCCEEDED -> {
if (workInfo.state.isFinished) {
val addressData =
workInfo.outputData.getString(CPKConstants.SUCCESS_RESULT)
Timber.d("AddressData %s", addressData)
_binding?.searchEdt?.setText(addressData)
}
}
else -> {
Timber.d("workInfo %s", workInfo)
}
}
})
}