Android Marshmallow: How to allow Runtime Permissions programatically?

2k views Asked by At

I'm developing an app that require some system permissions, however these are no longer granted automatically at installation time on Android Marshmallow.

I would like to request these permissions at runtime and run some kind of automation to grant them without needing a user to tap the 'allow' button when the System permissions Dialog appears.

How can I achieve this? Is there any way to do so in Marshmallow and later versions?

2

There are 2 answers

9
Nathan Dunn On BEST ANSWER

For Marshmallow or later permissions are not granted at install time and must be requested when required at runtime (if not granted previously.)

To do this, you need to run ActivityCompat.requestPermissions() to pop up the systems permissions dialog in your Activity, at the time when the user is undertaking an action that requires additional system permissions.

An example of this for the WRITE_EXTERNAL_STORAGE permission would be:

ActivityCompat.requestPermissions(
    this, 
    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
    WRITE_EXTERNAL_STORAGE_REQUEST_CODE
);

Note: WRITE_EXTERNAL_STORAGE_REQUEST_CODE is an arbitrary integer constant you should define elsewhere.

The permissions you request should also be declared in your AndroidManifest.xml. In this example the declaration would be:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

In order to handle the system permissions dialog response you will also need to implement onRequestPermissionsResult() in your Activity. For this example the code would be similar to

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
        return; //permission not granted, could also optionally log an error
    }
    if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
        //Do whatever you needed the write permissions for            
    }
}

If you are automating your app through Espresso, UIAutomator and/or some other UI testing framework you will need to anticipate and click the system dialog during your test, which can be accomplished with the following test code:

private void allowPermissionsIfNeeded()  {
    if (Build.VERSION.SDK_INT >= 23) {
        UiObject allowPermissions = mDevice.findObject(new UiSelector().text("Allow"));
        if (allowPermissions.exists()) {
            try {
                allowPermissions.click();
            } catch (UiObjectNotFoundException e) {
                Timber.e(e, "There is no permissions dialog to interact with ");
            }
        }
    }
}

A more comprehensive explanation of testing System UI Permissions is available here.

1
Joao Neto On

I found out that an simpler way to automate the permission acceptance without using UIAutomator or espresso in a CI scenario is to simply, pre-installing the apk via adb using:

adb install -g {my_apk_file}

The -g flag automatically grants all manifest permissions to the app. Afterwards if you launch your espresso test suite, the ui won't ask you again to grant them.