North Border Software




Home Tutorials Hints & Tips About
Share on Facebook Share on Twitter

Granting Android Permissions in Runtime

From Android 6.0 (API 23 or Marshmallow), a user has to grant permission to each and every "dangerous" permission in runtime. Prior to API 23, permissions were dealt with at the time of installation.

If the device is running Android 5.1 or lower or your app's target SDK is 22 or lower, if you list a permission in your manifest, the user has to allow that permission when they install the app. If they do not grant the permission, the system does not install the app at all.

If the device is running Android 6.0 or higher and your app's target SDK is 23 or higher, the app still has to list all permissions in the manifest but it must prompt the user to grant permission for each dangerous permission. The user can grant or deny each permission and the app can continue to run with limited capabilities even if the user denies a permission request.

So what are "dangerous" permissions? Android defines a dangerous permission as one where the app wants data or resources that involve the user's private information, could potentially affect the user's stored data or the operation of other apps. Not all permissions are classed as dangerous. All permissions that do not fall into the dangerous category are classed as "normal" permissions. These are listed in the Android Manifest file as usual and the user will not be prompted to allow or deny the permission.

A list of dangerous permission is show below. Each permission is categorized into a permission group and the indented bullets list the actual permission.

We looked at using location based services in a previous tutorial. This project will use the code for that project as a foundation project. You can review the location based services project here and download to code if necessary here.

So let's get started!

There are essentially three steps needed to deal with the handling of the permission in runtime. First, we need show a dialog box prompting the user to allow or deny the permission. Second, when this dialog box is acted on, a call-back method is executed. This method is called onRequestPermissionsResult. Thirdly, we need to place appropriate controls into the code to bypass permission that are denied. You may also want to consider adding extra information to inform the user why the permission is needed, what it is used for and what the consequences are if it is denied.

The Permissions Dialog Box

The first step is to ensure that your code has a permissions check. If compiling against a target SDK of API 23 or higher, Android studio will generate an error if this code is not present and will allow you to auto correct the error with the appropriate code. Within our location based service example, this code is generating just prior to starting the location updates. The default code for our example is shown below:

if (ActivityCompat.checkSelfPermission(this,
   Manifest.permission.ACCESS_FINE_LOCATION) !=
   PackageManager.PERMISSION_GRANTED &&
   ActivityCompat.checkSelfPermission(this,
   Manifest.permission.ACCESS_COARSE_LOCATION) !=
   PackageManager.PERMISSION_GRANTED) {

// TODO: Consider calling
//    ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
//   public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.

    return;
}

Essentially, the code looking for dangerous permissions in the Android Manifest and asking the following: If permission for the fine location and coarse location has not been granted, then execute any code within the if statement.

Therefore, within this if statement we will first of all display the dialog box prompting the user to grant permissions. This is done by calling the requestPermissions method.

requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSION_REQUEST_FINE_LOCATION);

For the sake of simplicity, we are only asking for permission for the fine location. Granting permission for the fine location automatically allows permission to the coarse location. There may be situations where you have to ask for multiple permissions to be authorized, but this will be dealt with by means of another tutorial. Make sure that ActivityCompat is imported to ensure that devices running an API lower than 23 are supported. There two parameters passed to the method. The first is a string value for the actual permission and the second is an integer value referred to as the request code. The need for the request code is explained later on and its use will become clear. It is a user defined integer constant value and so the following declaration will be added to the code.

private static final int MY_PERMISSION_REQUEST_FINE_LOCATION = 101;

Calling the permissions check dialog box is only needed if the device is running API 23 or higher. Therefore, it is recommended to surround the requestPermissions method with code to check the API and only execute the request if the device is running API 23 or higher.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
   requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSION_REQUEST_FINE_LOCATION);
} else {
   //do something for API 23 or less
}

Handling Allow or Deny Conditions

You have to consider what to do should a user deny any particular permission. It is also good practice to inform a user why a particular permission is needed and what is does. This can easily be done in runtime with a toast, snackbar or dialog box message.

In our example we will simply bypass the parts of the code that make calls to the Google locations based services and pop up a toast if the permission is denied.

Therefore, I have defined a Boolean switch and pre-set this to false.

private boolean permissionIsGranted = false;

If the app is to run on a device running a release less then API 23, then we need to set this switch to true to ensure that the location based service commands are executed.

if (ActivityCompat.checkSelfPermission(this,
   Manifest.permission.ACCESS_FINE_LOCATION) !=
   PackageManager.PERMISSION_GRANTED &&
   ActivityCompat.checkSelfPermission(this,
   Manifest.permission.ACCESS_COARSE_LOCATION) !=
   PackageManager.PERMISSION_GRANTED) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
         requestPermissions(new
         String[]{Manifest.permission.ACCESS_FINE_LOCATION},
         MY_PERMISSION_REQUEST_FINE_LOCATION);
      } else {
         permissionIsGranted = true;
      }

Handling the User's Choice

The user will select either allow or deny for a particular permission. Irrespective of what is selected, the onRequestPermissionsResult callback method is executed. This must be included in your code.

Let's take a closer look at this callback method.

onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults).

There are several parameters passed to this callback method:

In our example we are going to use both the requestCode and grantResults to set the behavior of our example.

Now you can see the need for the user defined constant that is the request code. We can use a switch and case to identify each permission and deal with the allow or deny result. To help show this  I have set up another requestCode for the coarse location. This will not be acted upon, but helps to clarify what is happening.

private static final int MY_PERMISSION_REQUEST_COARSE_LOCATION = 102;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

   super.onRequestPermissionsResult(requestCode, permissions, grantResults);
   switch (requestCode) {
      case MY_PERMISSION_REQUEST_FINE_LOCATION:
         if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // permission granted
         } else {
            //permission denied
         }
      break;
      case MY_PERMISSION_REQUEST_COARSE_LOCATION:
         // do something for coarse location
      break;
}

It's really simply a case of dealing with each case in the if else statements. If the permission is allowed, we simply set the Boolean permissionIsGranted switch to true. If the permission is denied we set the Boolean permissionIsGranted to false, display a toast message and also set the text in the layout to indicate that permission has been denied.  

if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
   // permission granted
   permissionIsGranted = true;
} else {
   //permission denied
   permissionIsGranted = false;
   Toast.makeText(getApplicationContext(), "This app requires location permission         to be granted", Toast.LENGTH_SHORT).show();
    latitudeText.setText("Latitude : permission not granted");
    longitudeText.setText("Longitude : permission not granted");
}

It's a simple task to bypass elements of the code that call upon the location services by simply using the condition:

if (permissionIsGranted) { // execute code that needs permission to be granted}

Allowing users to change the Permission Status

Users may change their minds about whether they allow or deny a particular permission. This situation can be handled by means of a settings activity or similar. In our example, I have simply added "settings" button to the layout and the permissions dialog box is called within the onClick method.

settingButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
         requestPermissions(new
            String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            MY_PERMISSION_REQUEST_FINE_LOCATION);
      } else {
         permissionIsGranted = true;
      }
      latitudeText.setText("Latitude");
      longitudeText.setText("Longitude");
   }
});

This concludes tutorial for handling permissions. It is a fairly involved process, but one that needs to be mastered if you are developing current applications. We have focused on handling a single permission, but I will build on this to look at handling multiple permissions in the near future.

After this tutorial your MainActivity.java file should look similar to the one below:

Your activity_main.xml file should look similar to the one below:

Your Android Manifest file should look similar to the one below:


Download Download project files



North Border Software Logo

Android Apps and Training

© 2015 North Border Software All rights reserved
Privacy and Cookies Terms of Use Disclaimer Copyright