With Release of Android M, Google has divided the app permissions into three protection levels : normal, signature, and dangerous permissions. The protection level affects whether runtime permission requests are required.

Normal category : Permissions with little risk to the user’s privacy comes under this category.

Signature permissions : The system grants these app permissions at install time, but only when the app that attempts to use a permission is signed by the same certificate as the app that defines the permission

Dangerous permissions : Permissions which involves accessing resources/data that involves users’ private information falls under this category. For example the ability to access to device storage using READ_EXTERNAL_STORAGE permission. 

Apps that require such permissions must prompt the user to grant permission at runtime. We will cover handling dangerous permissions in this article.

Runtime Permissions

App has to follow three simple steps to get grant of Dangerous permissions from the user:

Declare permission in AndroidManifest.xml:



Request permission :

private static final int RC_CALL_PERMISSIONS = 786;

private void requestCallPermissions(){

   final String[] permissions = new String[]{Manifest.permission.CALL_PHONE};

   requestPermissions(permissions, RC_CALL_PERMISSIONS);

}

Handle permission request results :
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

   super.onRequestPermissionsResult(requestCode, permissions, grantResults);

   if(grantResults[0]==PackageManager.PERMISSION_GRANTED){

       //Permissions granted Tadaaa..

   }

}

Using the above approach, handling permissions looks quite simple, unfortunately that's not true for most cases, because you have to check for permissions before using resources which requires dangerous permissions, because user can revoke access to such permission from app settings any time. So you add checks and request permissions for each feature of your app. 

For example if you need to make a phone call, without any kind of permission request and check, Your code might look like this:

private void makePhoneCall(@NonNull Context context, @NonNull String phoneNumber){

   final Intent callIntent = new Intent(Intent.ACTION_CALL);

   callIntent.setData(Uri.parse("tel:" + phoneNumber));

   context.startActivity(callIntent);

}

After adding permission check and request, that single method with 3 lines of code will become:

private static final int RC_CALL_PERMISSIONS = 786;

private void checkPermissionsAndMakePhoneCall(@NonNull Context context, @NonNull String phoneNumber){

   if (ContextCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE)==PackageManager.PERMISSION_GRANTED) {

       makePhoneCall(context,phoneNumber);

   }else {

       requestCallPermissions();

   }

}



@RequiresPermission(allOf = {Manifest.permission.CALL_PHONE})

private void makePhoneCall(@NonNull Context context, @NonNull String phoneNumber){

   final Intent callIntent = new Intent(Intent.ACTION_CALL);

   callIntent.setData(Uri.parse("tel:" + phoneNumber));

   context.startActivity(callIntent);

}



private void requestCallPermissions(){

   final String[] permissions = new String[]{Manifest.permission.CALL_PHONE};

   requestPermissions(permissions, RC_CALL_PERMISSIONS);

}



@Override

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

   super.onRequestPermissionsResult(requestCode, permissions, grantResults);

   if(grantResults[0]==PackageManager.PERMISSION_GRANTED){

       //Permissions granted Tadaaa..

   }else {

       //permissions denied

       Toast.makeText(this,"Permission denied!",Toast.LENGTH_SHORT).show();

   }

}

As you can see you have to write too much code to implement a simple phone call feature,

Just imagine the amount of code you have to write if your application has features like read storage, image capture, access GPS Location etc. adding those permission checks and handling requests, permission results will going to be a lot more difficult.

Also, you might have noticed that, the parameters (context and phoneNumber) aren’t available in result of permission request, hence you won’t be able to call the makePhoneCall method, so you have to store those variables in some global variable as well, which will break the abstraction and requires extra effort to handle such variables as well.

To solve such issue many developers implement these permission checks in Launching/Landing activity of application, and they believe that user might not change the permission access later from app settings and most of the users won’t bother changing permission access of application, but some advanced users might change the permission access of your application later on, and as a result, your app will start acting weird because features which require such permissions won’t work or throw exception on run-time resulting bad user experience.

I was also facing such issues, and really was suffering with the hassle to add this much code and checks for simple features, so I developed a PermissionHelper which helped me in handling all that with ease.

PermissionHelper.java

import android.app.Activity;

import android.content.Context;

import android.content.pm.PackageManager;

import androidx.annotation.NonNull;

import androidx.core.app.ActivityCompat;

import androidx.core.content.ContextCompat;

import androidx.fragment.app.Fragment;

import java.util.Map;

import java.util.Random;

import java.util.TreeMap;

/**

* Created by Harsh at Intellisense Technology.

*/

public class PermissionHelper {

   @NonNull

   private final Map permissionReqMap = new TreeMap<>();

   //Generating initial (random) number to create unique request for each instance of Permission helper

   private final int initialNumber = new Random().nextInt(786 - 101) + 101;

   //Callbacks for Permission request

   public interface PermissionReq {

       void onGranted();

       void onDenied();

   }

  

 //Execute permission request from activity

   public void executePermissionSafeRequest(@NonNull PermissionReq permissionReq,

                                            @NonNull Activity activity,

                                            @NonNull String... permissions) {

       if (hasPermission(activity, permissions)) {

           permissionReq.onGranted();

           return;

       }

       final int reqCode = permissionReqMap.size() + initialNumber; //Generating unique request code

       permissionReqMap.put(reqCode, permissionReq);

       requestPermissions(activity, reqCode, permissions);

   }



   //Execute permission request from fragment

   public void executePermissionSafeRequest(@NonNull PermissionReq permissionReq,

                                            @NonNull Fragment fragment,

                                            @NonNull String... permissions) {

       if (hasPermission(fragment.requireContext(), permissions)) {

           permissionReq.onGranted();

           return;

       }

       final int reqCode = permissionReqMap.size() + initialNumber; //Generating unique request code

       permissionReqMap.put(reqCode, permissionReq);

       requestPermissions(fragment, reqCode, permissions);

   }

   //Handling results of permission request

   public void onRequestPermissionsResult(int requestCode,

                                          @NonNull String[] permissions,

                                          @NonNull int[] grantResults) {

       final PermissionReq permissionReq = permissionReqMap.get(requestCode);

       if (permissionReq != null) {

           for (int result : grantResults) {

               if (result == PackageManager.PERMISSION_DENIED) {

                   permissionReq.onDenied();

                   permissionReqMap.remove(requestCode);

                   return;

               }

           }

           permissionReq.onGranted();

           permissionReqMap.remove(requestCode);

       }

   }

   private boolean hasPermission(@NonNull Context context, @NonNull String... permissions) {

       for (String permission : permissions) {

           if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) {

               return false;

           }

       }

       return true;

   }

   private void requestPermissions(@NonNull Activity activity, int reqCode, String... permissions) {

       ActivityCompat.requestPermissions(activity, permissions, reqCode);

   }

   private void requestPermissions(@NonNull Fragment fragment, int reqCode, String... permissions) {

       fragment.requestPermissions(permissions, reqCode);

   }

}

Handling Runtime Permissions using PermissionHelper

Although it is possible to use Permission helper directly in Activity or Fragment but i’ll encourage you to create a BaseFragment or BaseActivity class, extend your fragment/activity to this base class and then configure the permission helper in Base class to avoid code redundancy.

Configuring  PermissionHelper:

  1. Copy PermissionHelper to your project.
  2. Create a final Instance of PermissionHelper in your Base Fragment/Activity.
    @NonNull
    protected final PermissionHelper permissionHelper = new PermissionHelper();
    
    @Override
    
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
       permissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
    }
  1. Call permissionHelper.onRequestPermissionsResultinonRequestPermissionsResultmethod of your Base class

 

PermissionHelper in action
Its very easy to handle runtime permissions with PermissionHelper. You won’t have to write too long code to implement your desired feature that requires runtime permission checks.

All you have to do is call the permissionHelper.executePermissionSafeRequest and write your actual code inside ‘onGranted’ method.

Unlike legacy methods of handling runtime permissions, with the help of PermissionHelper you won’t have to store and handle your method parameters as well as you will be writing your actual logic inside the same method.

For example:

@SuppressLint("MissingPermission")

private void makePhoneCall(@NonNull final String phoneNumber) {

   permissionHelper.executePermissionSafeRequest(new PermissionHelper.PermissionReq() {

       @Override

       public void onGranted() {

           final Intent callIntent = new Intent(Intent.ACTION_CALL);

           callIntent.setData(Uri.parse("tel:" + phoneNumber));

           startActivity(callIntent);

       }

       @Override

       public void onDenied() {

           Toast.makeText(getContext(), "Permission denied!", Toast.LENGTH_SHORT).show();

       }

   }, this, Manifest.permission.CALL_PHONE);

}


Now you no longer have to add permission checks, permission request and handle grant results, Also there is no need to create multiple request code constants in class and increase LOC. Because with the help of PermissionHelper handling runtime permission became so much easy. That's why I named this article Handling runtime permissions in Android - The Easy Way.

Thank you for your time and patience for reading a XL sized article 😂. Hope this might help you in your development. Happy coding…


About the Author

  • Harsh Jatinder

    Android Developer

    Harsh is passionate android app & game developer. Having years of experience in software development, he loves to learn & develop codes for easiest solutions.

  • Plot No. B1/823/1A, Aman Nagar, Tanda Road, Nr. KMV College, Jalandhar.
  • +91-9815075800
  • harjitsingh575
  • info@intellisensetechnology.ca
  • 12+ YEARS

    Transforming business Since 2006

  • 1000+ PROJECTS

    Successfully Delivered

  • 5 STAR RATED

    Fully Digital Service

  • 45+ COUNTRIES

    Served

Chat with Us
 

Leave a message, we will contact you shortly

Country Code :