Handling Runtime permissions in Android
In Marshmallow, android introduced runtime permissions. Before Marshmallow, to use certain features, we need to declare the permissions in manifest file only. But in Marshmallow and above, the permissions are divided into normal and dangerous permissions.
For dangerous permissions, we need to ask the permission at runtime. Fortunately, the android documentation provides an example to request runtime permission.
The above code is from developer documentation. Nice. It gives all the code we need to request runtime permission, right? Awesome. But wait. Let’s see what really is happening here.
First we check if the contact permission is granted or not. If it is granted, we can fetch contacts or whatever (which is not given in the above example). If it is not granted, we need to request permission. But in this case, we need to handle some cases.
- If the permission is never asked for this app (First time asking), we can request permission without any hassle.
- If the permission is already asked and the user denied it, we should probably explain the user about why the permission is required. (Because, the user denied the permission which is used for some feature in your app, but it is again needed which means the user is trying to use that feature again. So you need to show a dialog or something explaining why you are asking that permission so that this time the user won’t deny it.)
- If the permission is already asked but the user denied it with checking Never ask again option in the permission dialog
Among these, first two cases are handled in the above example but not the last one (which is why I’m writing this post). I’ll show you how to handle them.
In the example, the shouldShowRequestPermissionRationale method is used to determine whether we need to show explanation for the permission or not.
It returns true if the permission is already requested before but was denied (second case).
But it will return false in two cases.
- If the permission is requested first time.
- If the permission is disabled by some device policy or the permission is already requested but the user denied it with checking Never ask again option in the permission dialog (third case).
For the first case, we can check whether the permission is being asked first time or not by putting a flag on Shared Preferences.
In firstTimeAskingPermission, we put a boolean flag with the permission string as key and the boolean parameter as value.
In isFirstTimeAskingPermission, we get the value associated with the permission string and return it. Note that we set the default value as true. This will be useful because, if the key is not in the preference file or even if the file itself is not created, the android framework will create a file for us with the name we gave and will return the default value we set.
We will call this method every time when a permission request is required but after first call, we set the flag as false using firstTimeAskingPermission(context, permission, false).
So, the code would now be like this.
As you can see, if shouldShowRequestPermissionRationale returns false, there would be two cases to be handled and we did it perfectly. Now all the four cases I first mentioned is solved.
Now you need to use all this confusing code for every permission you are going to use in your app. Isn’t that frustrating and confusing? Obviously it is a lot of boilerplate. We can reduce these frustrations by putting all these logic in a method and use a callback to get which case we should handle now.
Nice! All the cases are handled in a nicely packed util file.
As you can see shouldAskPermission checks whether the SDK is M and above and if it is true, then only we process runtime permission logic, else onPermissionGranted would be called where we implement the feature which uses the permission we requested.
Still wondering how to use it? I can help you.
Note: REQUEST_EXTERNAL_STORAGE is a static final int with some value which would be used in onRequestPermissionResult where permission granted/denied callback would be available. In this method we can match the request code with this, check whether permission is granted or not and do stuff regarding that.
But if the permission really needed for the app (ex: camera permission for camera app), you can show a dialog saying something like “Camera permission disabled and app will not work. Please proceed and give permission to access camera” with a button which will open you app’s permission settings. The user may enable the permission manually and return to the app.
Now all we need to do is whenever some dangerous permission is needed, we can call this checkPermission method by passing a new PermissionAskListener. Then we will receive corresponding callbacks in the overriden methods based on the current state.
Happy coding :)