Managing Android Runtime Permissions with Permiso

November 19, 2015

Perhaps the biggest change in Android Marshmallow from a developer’s perspective is the addition of runtime permissions. Instead of requesting all of your permissions at install-time, you now request permissions as you need them. Users can also revoke permissions at any time, meaning that you always have to check if you have a permission before you can use it. As you can imagine, the code that manages all of this can get a bit hairy. If you haven’t already, you can check out the official docs.

The Problem

Just for giggles, let’s look at how you would normally have to handle requesting a runtime permission.

public class MyActivity extends Activity {

    private static final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 24601;

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Permission granted
                } else {
                    // Permission denied
                }
                return;
            }
        }
    }

    private void someMethod() {
        if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) {
                // Show explanation
            } else {
                ActivityCompat.requestPermissions(thisActivity new String[]{Manifest.permission.READ_CONTACTS, MY_PERMISSIONS_REQUEST_READ_CONTACTS);
            }
        }
    }
}

Sweet fancy Moses! Can you imagine having to do this every time you wanted to perform a task that requires a permission?

via GIPHY

In all seriousness, let’s look at some of the pain points we have with this code:

  • There’s a lot of branches to account for. Do we have the permission? Do we need to show a rationale? Did they accept it? Did they deny it?
  • The process is spread over multiple methods. You need the permission in one place, but you’re getting your response back someplace else entirely. You’ll likely have to introduce some state variables to your Activity to keep track of what’s going on, and you know that’s going to turn into a mess.
  • You need to perform and handle this request in an Activity or Fragment. What if you have some contact manager or file-writing class that operates outside of an Activity? Tough luck. You can’t request the permission there.
  • You probably want to show the user the rationale for your permission in a pop-up. Now you have to create a DialogFragment, handle rotation, make sure that you make the permission request after the user dismisses it, yada yada yada. So much boilerplate.

The Solution

To solve these pains, I developed a library called Permiso. Permiso provides a much easier way to request permissions. Specifically, it does the following:

  • Localizes permission requests so you can handle everything using a simple callback mechanism.
  • Can easily make permission requests outside of the context of an Activity.
  • Simplifies showing the user your rationale for requesting a permission.
  • Can request multiple permissions at once.
  • Merges simultaneous requests for the same permission into a single request.

Let’s check it out in action. This is how you’d make a request for a single permission:

public class MyActivity extends PermisoActivity {

    private void someMethod() {
        Permiso.getInstance().requestPermissions(new Permiso.IOnPermissionResult() {
            @Override
            public void onPermissionResult(Permiso.ResultSet resultSet) {
                if (resultSet.areAllPermissionsGranted()) {
                    // Permission granted!
                } else {
                    // Permission denied.
                }
            }

            @Override
            public void onRationaleRequested(Permiso.IOnRationaleProvided callback, String... permissions) {
                Permiso.getInstance().showRationaleInDialog("Title", "Message", null, callback);
            }
        }, Manifest.permission.READ_EXTERNAL_STORAGE);
    }
}

Ah, much better!

via GIPHY

So, how did Permiso help?

  • Everything is now localized in one place. No more managing what happens across multiple methods.
  • We’re not limited to doing everything in an Activity. Notice how the call to Permiso.getInstance().requestPermissions() doesn’t require an Activity? Permiso keeps track of the current Activity so you don’t have to.
  • We have an easy way to show our rationale. Permiso gives has a built-in method to show a DialogFragment. All rotation-related problems are taken care of.
  • Permiso handles all of that request-code nonsense you’d have to deal with normally.

For more details, check out Permiso on Github.

Enjoy!

comments powered by Disqus