Working with permissions in Android

Or how we can easily create a special Permission Manager

Michael Spitsin
5 min readJul 25, 2018

Today we will talk about permissions in Android. This is very important and needed topic, since pretty long time ago (since Android 6.0) permission request system was introduced. The permission request system became very handy for users, but required little bit more work from programmers side.

But it turns out to be very annoying to make this work over and over again. And that’s why I wrote an easy and small solution to simplify working with permissions in our project.

The task

The typical snippet of code, that uses permissions, for example, to make a phone call looks like:

The central method is requestCall which called, for instance, when user pressed button “Make a phone call”. We need to check that user granted access to phone. If he did, then we just make an appropriate intent. If not, then we request phone permission. After that in the method onRequestPermissionResult we receive the result status of permission handling. And if user granted access, then we create an intent. If not, that we show a message about, how it is important to give access to the phone functionality to the application.

As we can see, there are just one important method, one semi important method and one needed constant:

Manifest.permission.CALL_PHONE //important constprivate fun makeCall() { //important success method
startActivity(Intent(Intent.ACTION_CALL, Uri.parse("tel:79999999999")))
}
private fun showErrorSnackbar() { //semi important error method
view?.let { Snackbar.make(it, "You denied calling permission request", Snackbar.LENGTH_SHORT).show() }
}
  • Constant is needed to define set of permissions we need to process;
  • Success method is important as initial intention, we wanted to make;
  • Error method, is not so important, as we can provide a default error message for most cases, but still we need to leave an opportunity for developer to specify custom error handling

So we can form the task as follows:

Eliminate remained boilerplate

Solution

Result handlers

Let’s create a special class PermissionManager that will wrap all this stuff in itself. To do that, we need to discuss important topic about type of requesting permissions. In common, as I think, there are two types of processing intention with permission request:

  1. We have significant permission, without which we can not do our intentions. For example, to make a phone call, we need to as a permission. Without granted access (permission is denied by user) we can not make a phone call, so we just showing an error to user.
  2. We have inessential permissions, without which we can do work, but we will not have full functionality. For example, with phone authentication we can grant an access to reading sms. With help of it, we will be able to automatically parse and substitute verification code into text form. But if permission will be denied, we still can navigate user to verification code screen, but without auto parsing feature. User will just manually write sms code.

Those thoughts lead us to having a 2 permission handling mechanisms:

//for required permissions
val denied = grantResults.indices.filter { grantResults[it] != PERMISSION_GRANTED }
if
(denied.isEmpty()) {
action()
} else {
failAction()
}

//for unnecessary permissions
val denied = grantResults.indices.filter { grantResults[it] != PERMISSION_GRANTED }
if
(denied.isNotEmpty()) {
failAction(denied.map { permissions[it] })
}
action()

action: () -> Unit — action that called, after success handling, failAction: () -> Unit — action that called, after any permission was denied. For unnecessary permissions failAction is lambda with parameter, because for the programmer it is important information, which permissions were declined by user.

Now wrap both algorithms to appropriate classes, and we have:

Interface and Implementation

Now let’s describe an interface for interaction with permission system:

interface PermissionManager {
fun requestAndRun(permissions: List<String>, failAction: (List<String>) -> Unit, action: () -> Unit)
fun requestThenRun(permissions: List<String>, failAction: () -> Unit, action: () -> Unit)
}

The naming of methods is a matter of taste. I tried to invent concise, but precise names. First method is for unnecessary permissions and second one for required permissions. Now let’s write an implementation:

As you can see, the class has two maps for keep and identify permission handlers for each request. When developer calls, suppose, requestAndRun and permission is not granted, then special id is generated by Math.abs(handler.hashCode().toShort()).toInt(). You can make another way of creating ids, because hashCode not guarantee to you uniq identifier, but for year of using that approach I had no problem with that, because permissions are requested from the UI thread and unlikely you will face with situation, when permission set is requested, while user decide does he need to grant access to another permission set or not.

Also, permission result process is divided on two methods. In one onRequestPermissionsResult we just check results and place them in pendingResults map. And in ready method we execute all results that are waiting for handling. Last method will be executed in onResume method. This is done, because otherwise (when we will execute all permission result right in onRequestPermissionsResult method) there are situations on some devices, where it is required to wait until fragment will be ready before handle permissions result.

Attach to Lifecycle

Now we need to slightly change PermissionManagerImpl in order link it with lifecycle architecture component. To do that we need to:

  1. Remove fragment reference from constructor
  2. Add init methods for fragment and activity
  3. Add different ways of checking and requesting permissions for fragment and activity
  4. Add annotation for ready method

As the result we will have next version of PermissionManagerImpl

FragmentLifecycleObserver and ActivityLifecycleObserver are special extensions for LifecycleObserver interface. I created them in order to provide special extensions for Lifecycle class:

interface FragmentLifecycleObserver : LifecycleObserver {
fun initWith(owner: Fragment)
}

interface ActivityLifecycleObserver : LifecycleObserver {
fun initWith(owner: Activity)
}

fun Lifecycle.addObserver(observer: FragmentLifecycleObserver, fragment: Fragment) {
addObserver(observer as LifecycleObserver)
observer.initWith(fragment)
}

fun Lifecycle.addObserver(observer: ActivityLifecycleObserver, activity: Activity) {
addObserver(observer as LifecycleObserver)
observer.initWith(activity)
}

They are help us to write similar code as we did for ordinary LifecycleOwners without writing additional initWith calls.

Example

Now let’s see, how we can built in our PermissionManager to use it in needed Fragment or Activity. We will consider situation with fragmetns. For activities you can make similar things. First let’s take or create a BaseFragment class and write following snippet in it:

private val permissionManager = PermissionManagerImpl()

init {
lifecycle.addObserver(permissionManager, this)
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
permissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

fun requestAndRun(permissions: List<String>, failAction: (List<String>) -> Unit, action: () -> Unit) {
permissionManager.requestAndRun(permissions, failAction, action)
}

fun requestThenRun(permissions: List<String>, failAction: () -> Unit, action: () -> Unit) {
permissionManager.requestAndRun(permissions, failAction, action)
}

Now in the inheritor (first example I’ve shown) we can rewrite code and finally see:

...
private fun requestCall() {
requestThenRun(
listOf(Manifest.permission.CALL_PHONE),
this::makeCall,
this::showErrorSnackbar)
}

private fun makeCall() {
startActivity(Intent(Intent.ACTION_CALL, Uri.parse("tel:79999999999")))
}

private fun showErrorSnackbar() {
view?.let { Snackbar.make(it, "You denied calling permission request", Snackbar.LENGTH_SHORT).show() }
}
...

As for me it looks much nicer, than first one (14 lines against 37)

Enhancements

Now when we have finished structure and we know how to use it, we can build bunch of extensions to slightly enhance work with it

  1. We can add extensions to provide default error result:
fun PermissionManager.requestAndRun(permissions: List<String>, action: () -> Unit) {
requestAndRun(permissions, { it: List<String> -> getDefaultError(it)() }, action)
}

fun PermissionManager.requestThenRun(permissions: List<String>, action: () -> Unit) {
requestThenRun(permissions, getDefaultError(permissions), action)
}

private fun getDefaultError(permissions: List<String>): () -> Unit = {
//provide default error
//it could be toast, then you can place extensions whenever you want
//or it could be snackbar, thats why you will need to place it in base fragment
//(which is not so good, so you need be aware of that enhancement)
}

2. We can add extensions to provide handling single permissions:

Both enhancements are not required, if you have no problem with writing additional code each time, then you can just skip those ideas.

Afterwards

Now we can write simple, clean and concise permission-dependent code. :)

--

--

Michael Spitsin

Love being creative to solve some problems with an simple and elegant ways