Permissions

Permissions – Part 2

With Marshmallow a new permissions model was added to Android which requires developers to take a somewhat different approach to permissions on Android. In this series we’ll take a look at ways to handle requesting permissions both from a technical perspective, and in term of how to provide a smooth user experience.

Icon_no_permissionPreviously we looked at how we could check whether we had already been granted our required permissions, but we had no mechanism in place to request any missing permissions. In this article we’ll look at how we can include the necessary permissions checking and requesting in all of our Activities without having large amounts of duplicate code.. Please bear in mind that everything that follows is specific to Marshmallow and later (earlier OS levels have permissions granted implicitly from the Manifest declarations) but that you’ll need to implement this kind of checking if you are specifying targetSdkVersion=23 or higher in your builds.

So the first thing we need to understand is how the permissions request model works. As we have already discussed, normal permissions will be granted implicitly but dangerous permissions will require the user to explicitly grant that permission. Things are easy enough when the user grants us the required permission but we need to defend against instances where the user denies us permission. For the app we’re going to eventually be developing it may not be obvious to the user why we require the RECORD_AUDIO permission, so we need to make some provision to inform the user of why this will be needed.

From the users’ perspective, the way this works is that the user will be asked for the require permission the first time they run the app:

Part2-first-run

If they allow the permission then everything is fine and we can carry on. However, if they deny the permission then we can repeatedly ask them for the required permission:

Part2-second-run

But note that if the user has previously denied the permission being requested they will be given the option to never be asked for the permission again. If the user selects this option then any further attempts by our code to request the permission will automatically be denied without prompting the user. Clearly this can be problematic for us as developers, so we need to make allowance for it.

This can further be compounded because the user can, at any time, go in to the Settings page for our app and grant or deny any permissions required by our app. This is why it is important to check that required permissions have been granted not only at app startup, but also in each Activity as the permissions could potentially change at any time.

So the pattern that we’re going to use to manage this is to have a separate Activity which is responsible for requesting permissions, and all of the other Activities within our app will need to check that they have the permissions they require, and pass control back to the PermissionsActivity if they have not been granted.

So let’s update our MainActivity slightly:

public class MainActivity extends AppCompatActivity {

    static final String[] PERMISSIONS = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS};
    private PermissionsChecker checker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        checker = new PermissionsChecker(this);
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (checker.lacksPermissions(PERMISSIONS)) {
            startPermissionsActivity();
        }
    }

    private void startPermissionsActivity() {
        PermissionsActivity.startActivity(this, PERMISSIONS);
    }
}

We’ve moved the actual permissions check to onResume(). This is to cover the case where the user might pause our app, switch to Settings, deny a permission, and then resume our app. OK this is something of an edge case but it’s worth defending against crashes which could occur from this.

So the basic pattern that we’re implementing here is that whenever our Activity is resumed we confirm that we have the required permissions for that Activity to operate. If we don’t then we pass control to the PermissionsActivity which is responsible for obtaining the required permissions. Although this feels like a really defensive approach I feel that it really is a sensible approach which doesn’t actually require an awful lot of code. All of the checking logic in encapsulated within PermissionsChecker, and the request logic is handled in PermissionsActivity.

Having the permissions check as a relatively lightweight component is important because we can check for required permissions relatively cheaply and only make the rather more expensive Activity change to request missing permissions where absolutely necessary.

In the next article we’ll take a look at PermissionsActivity to see how we actually handle the permission requests and explore how to further inform the user why a permission is required if they deny a permission that we’re requested.

The source code for this article is available here. There’s a placeholder PermissionsActivity in the code which we’ll expand upon in the next article, so it’s not fully functional code, I’m afraid.

© 2016, Mark Allison. All rights reserved.

Copyright © 2016 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

6 Comments

  1. Hi Mark, for my app i choose a similar approach (a stand-alone Activity) given thta it is problematic to insert this new stuff in legacy code. I’ve a doubt regarding your snippet: is it really necessary the permission check in the onResume()? Shouldn’t the process be killed after a permission change at runtime? I’ve inserted the check inside the onCreate ()…

    1. You’re quite correct that the Activity should be killed if permissions change outside of the app so a check in onCreate() should work. Perhaps I’m overly cautious, but I felt that performing the check in onResume() guards against future changes to this behaviour or OEMs changing it (which some OEMs, mentioning no names, are notorious for doing).

    2. I think OS itself will kill the app once any permission is changed in settings. Tested with my Nexus 5 running marshmallow.

      1. As I have already stated in another comment: The Activity *should* be killed if the permissions change, but I’m being cautious about potential changes to this behaviour, or OEMs screwing it up. Doing the check in onResume() instead on onCreate() is no big deal, IMO because it’s a pretty lightweight check.

  2. Hey, thanks for the article!
    Quick question, though:
    Checking permissions in every activitiy and handling denying of every single permission seems like it could potentially bloat up the code of especially complex apps with many permissions very fast. I’m not saying it’s wrong though. Users should definitely have that kind of control. However, I’m wondering how this works on iOS from the developer’s perspective. From what I understand, they’ve had runtime permission checks from the very beginning. Do you happen to know whether permissions have to be checked on each of their activity equivalent (whatever that would be called) or how else it works on iOS?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.