Android Location Permission Guide
A straightforward guide for location permission in Android.
Welcome to this straightforward guide on managing location permissions in Android. Throughout this guide, our primary focus will be on the technical aspects of handling location permissions, setting aside specific use cases for now.
Before delving into the code, let's categorize location permission requests into two main parts:
- Foreground Location
- Background Location
Declare Permissions in the Manifest
The permissions required by your app should be declared in the manifest. While we already handle these declarations in our SDK, it's still worth mentioning.
<manifest ... >
<!-- Approximate location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Precise location access. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Background location access. -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
Requesting Foreground Permission
Foreground location access can be either Approximate or Precise. For most scenarios, you should include both permissions in the request.
Check if the permission is granted before making the request:
private fun checkLocationPermissionStatus(): Boolean {
val permission = Manifest.permission.ACCESS_FINE_LOCATION
return ContextCompat.checkSelfPermission(
requireContext(),
permission
) == PackageManager.PERMISSION_GRANTED
}
Request the permission:
// First, declare your permission launcher
private val foregroundLocationPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val granted = permissions.any { it.value }
if (granted) {
// Permission is granted
}
}
// Request the permission
private fun ensureForegroundLocationPermission() {
foregroundLocationPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
This is a very basic example, and we are not handling a particular scenario - when we should show permission rationale. It is not mandatory, but highly recommended, as this may be the key to obtain the permission. Usually, we should show the rationale if the permission has been denied previously.
In the next example, I will combine permission check, request and rationale in one, so it is much easier to maintain and understand.
private fun ensureForegroundLocationPermission(): Boolean {
val permission = Manifest.permission.ACCESS_FINE_LOCATION
val granted = ContextCompat.checkSelfPermission(
requireContext(),
permission
) == PackageManager.PERMISSION_GRANTED
// Permission has been granted
if (granted) return true
if (shouldShowRequestPermissionRationale(permission)) {
AlertDialog.Builder(requireContext())
.setTitle("Your title")
.setMessage("Your message")
.setCancelable(false)
.setPositiveButton("Ok") { _, _ ->
// Requesting foreground location permission
foregroundLocationPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
.setNegativeButton("Cancel") { _, _ ->
// Foreground location permission rationale cancelled
}
.show()
return false
}
// Requesting foreground location permission
foregroundLocationPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
return false
}
Requesting Background Permission
This step is relevant only if foreground permission has been previously requested and granted.
You might wonder what if only approximate foreground location is granted?
Even if only approximate foreground location is granted, you can still request background permission. Whether precise or approximate foreground location was granted earlier, background location updates will align accordingly, maintaining either precision level.
To request background permission, we first declare our launcher:
private val backgroundLocationPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) {
// Permisison granted
}
}
Checking, handling rationale and requesting the permission:
private fun ensureBackgroundLocationPermission(): Boolean {
val permission = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Manifest.permission.ACCESS_BACKGROUND_LOCATION
else -> Manifest.permission.ACCESS_FINE_LOCATION
}
val granted = ContextCompat.checkSelfPermission(
requireContext(),
permission
) == PackageManager.PERMISSION_GRANTED
if (granted) return true
if (shouldShowRequestPermissionRationale(permission)) {
AlertDialog.Builder(requireContext())
.setTitle("Title")
.setMessage("Message")
.setCancelable(false)
.setPositiveButton("Ok") { _, _ ->
// Requesting background location permission
backgroundLocationPermissionLauncher.launch(permission)
}
.setNegativeButton("Cancel") { _, _ ->
// Background location permission rationale cancelled
}
.show()
return false
}
// Requesting background location permission
backgroundLocationPermissionLauncher.launch(permission)
return false
}
Conclusion
I aimed to make this guide as straightforward as possible, and for the most part, it should be sufficient. However, there's more to explore, including requesting only approximate foreground permission, transitioning from foreground approximate location to precise, dealing with permanently denied permission, and more.
Although, for the most use cases, you don't have to worry about those aspects, it's always beneficial to have a comprehensive understanding of how location services work, isn't it?
Stay tuned for my upcoming blog post, where I will explore these topics and more!
As always, you can find us available for any question you might have via our Support Channel.