I have an android application that need device current location (latitude and longitude). I\'ve tried some tutorial on the net and specially some solutions from stack overfl
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()
}
}
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() {
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().
val rae = e as ResolvableApiException
rae.startResolutionForResult(
context,
AppConstants.GPS_REQUEST
)
} catch (sie: IntentSender.SendIntentException) {
// Ignore the error.
Timber.i(
ContentValues.TAG,
"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(ContentValues.TAG, errorMessage)
}
LocationSettingsStatusCodes.CANCELED -> {
val errorMessage =
"Location settings are inadequate, and cannot be fixed here. Fix in Settings."
Timber.e(ContentValues.TAG, 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(ContentValues.TAG, 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 { }
}
}
startIntentService method:
private fun startIntentService(location: Location?) {
val intent = Intent(requireActivity(), FetchAddressIntentService::class.java).apply {
putExtra(AppConstants.RECEIVER, resultReceiver)
putExtra(AppConstants.LOCATION_DATA_EXTRA, location)
}
requireActivity().startService(intent)
}
I have created one intent service to get the address from latlng:
class FetchAddressIntentService : IntentService(AppConstants.LOCATION_SERVICE) {
companion object {
const val TAG = "FetchAddressService"
}
private var receiver: ResultReceiver? = null
override fun onHandleIntent(intent: Intent?) {
val geoCoder = Geocoder(this, Locale.getDefault())
intent ?: return
var errorMessage = ""
// Get the location passed to this service through an extra.
val location = intent.getParcelableExtra(AppConstants.LOCATION_DATA_EXTRA) as Location
receiver = intent.getParcelableExtra(AppConstants.RECEIVER) as ResultReceiver
var addresses: List<Address> = emptyList()
try {
addresses = geoCoder.getFromLocation(location.latitude, location.longitude, 1)
} catch (ioException: IOException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available)
Log.e(TAG, errorMessage, ioException)
} catch (illegalArgumentException: IllegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used)
Log.e(
TAG,
"$errorMessage. Latitude = $location.latitude , Longitude = $location.longitude",
illegalArgumentException
)
}
// Handle case where no address was found.
if (addresses.isEmpty()) {
if (errorMessage.isEmpty()) {
errorMessage = getString(R.string.no_address_found)
Log.e(TAG, errorMessage)
}
deliverResultToReceiver(AppConstants.FAILURE_RESULT, errorMessage)
} else {
val 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) }
}
Log.i(TAG, getString(R.string.address_found))
deliverResultToReceiver(
AppConstants.SUCCESS_RESULT,
addressFragments.joinToString(separator = "\n")
)
}
}
private fun deliverResultToReceiver(resultCode: Int, message: String) {
val bundle = Bundle().apply { putString(AppConstants.RESULT_DATA_KEY, message) }
receiver?.send(resultCode, bundle)
}
}
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"
And don't forget to add service in manifest file and also add the internet permission:
<service
android:name=".ui.account.signin.service.FetchAddressIntentService"
android:exported="false" />