Run Time Permissions on Android M and Above Simplified Using Easypermissions Library
What is the most annoying and time-consuming task you have done in Android? For me, it is the handling of run time permissions. Remember the good all days? We will mention the permissions we want in the manifest file, and everything works fine as expected? Even if I want to go back to that old methodology, thinking about user privacy, it is not a reliable way to handle permissions. That’s why if the Android version is 6, or higher, we need to ask for these permissions as run-time permissions. I’m not going deep into the different types of run time permissions because I hope you all know that better than me. It is painstaking to implement these permissions. We have to ask the user for the required permissions. Then we have to show them an additional description dialog to tell them this is why we are taking your permissions. If the user declines it, we have to make sure the app will ask for the permissions again until it gets access, then the users can check on the ‘never ask again’ checkbox for not showing that permission dialog in the future. In that case, we will have to alert the user that certain features of the app will not work until you give this permission and redirect them to the app settings page. And again, handle all the permission accept and denied callbacks. Whenever the Android permission module comes up, I was reluctant to work on it due to these tedious activities. Until recently, I heard about a library to simplify this run time permission implementation on Android M and above.
Easy Permissions
As the official documentation of easypermissions says, EasyPermissions is a wrapper library to simplify basic system permissions logic when targeting Android M or higher.
Implementation
Let us now try to create a new project using the easypermissions library and see how to implement it correctly. We are going to take three types of permissions here.
- Access Fine Location
- Access Coarse Location
- Access Background Location
Add the above permissions in the Android manifest file.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Please note that, if the Android version is above 10, the background location permission is also run-time permission. So we will have to add logic for that as well.
We will create the project completely in Kotlin and AndroidX.
Let us add the dependency first.
dependencies {
// For developers using AndroidX in their applications
implementation 'pub.devrel:easypermissions:3.0.0'
// For developers using the Android Support Library
implementation 'pub.devrel:easypermissions:2.0.1'
}
Now, let us create a new utility class. It will help us verify if the user has already granted the above permissions. I’m going to make it an object class since we only need a single instance of it.
object TrackingUtility {
fun hasLocationPermissions(context: Context) =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
EasyPermissions.hasPermissions(
context,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
} else {
EasyPermissions.hasPermissions(
context,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
}
}
We have created a new function called hasLocationPermissions that takes a single parameter, Context. We are checking the device’s Android version. If the android version is greater than 10 ( Android Q ), we will have to ask for the ACCESS_BACKGROUND_LOCATION permission as well. Make sure you are importing the correct Manifest package. That is, android.Manifest.
Now, let us go to our Activity/Fragment and write a new function that will request our required permissions.
private fun requestPermissions() { if (TrackingUtility.hasLocationPermissions(this)) {
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
EasyPermissions.requestPermissions(
this,
"You need to accept location permissions to use this app",
REQUEST_CODE_LOCATION_PERMISSION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION
)
} else {
EasyPermissions.requestPermissions(
this,
"You need to accept location permissions to use this app",
REQUEST_CODE_LOCATION_PERMISSION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
}
}
This function will first check if the user has given permissions already using the Utility class we created earlier. If not, we will ask for the permissions using EasyPermissions, requestPermissions function. When we compare this function with the hasLocationPermissions, they are almost similar, except we are taking two more parameters called the rationale, which is a message explaining why the application needs this set of permissions and a constant called the REQUEST_CODE_LOCATION_PERMISSION. The rationale parameter is a helpful feature because we might have to explain to the user this is the precise reason which we are taking these permissions.
Now, we will have to implement the EasyPermissions PermissionCallBacks. These callbacks will help us identify if the permissions are granted or denied.
class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks
It will implement/override two functions/callbacks. onPermissionGranted and onPermissionDenied.
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
TODO("Not yet implemented")
}
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
TODO("Not yet implemented")
}
You must have understood by now that if the Android version is greater than 6 ( Marshmallow ), the user can disable the permission requests forever. Even if the user accidentally clicks on it, the permission dialogue will not show up again. If such a scenario occurs, we should redirect the user to the settings screen to manually give access to the permanently disabled permission. It will be helpful to the user if we show an additional alert message as well. Saying, “Some features of the app might not work if you don’t give this permission”. Let us write that logic inside the onPermissionsDenied function.
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
AppSettingsDialog.Builder(this).build().show()
} else {
requestPermissions()
}
}
We are checking if the permissions are denied permanently or not, using the function, somePermissionPermanentlyDenied. If it is permanently denied, we will show an alert box, which will redirect the user to the app settings screen. If it is not, that is, they just ignored it the first time, then we will request permission again when the user starts to use the app.
Finally, we will override onRequestPermissionResult, which will tell us the result of our request permissions. We will pass the to Easypermissions, onRequestPermissionResult function.
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}
Don’t forget to call the requestPermissions() function in the onCreate()/onViewCreated(). Else, the permission checks will not happen.
I loved this library. It is easy to implement and simple to understand. Try it out and tell me your feedback about the library. You can check the complete code in this repository. I learned about this library when I watched this tutorial from Philipp Lackner. He has tons of Android and Kotlin related tutorials. Please share it with your friend and colleagues if you find this article useful. I wish you all a great Christmas season ahead. Stay safe.
Article posted originally in clintpauldev.com
Screenshots of the app