Make your android app Marshmallow(6.X) ready

Hello friends,

In this series, I am going to explain about making our android app Marshmallow ready.

This is an amazing new feature for android users, but it’s important to note that runtime permissions only impact devices running Android Marshmallow and above versions, and your app will still function the same on older versions of Android. Additionally, runtime permissions are only required if you change your targetSdkVersion in your  build.gradle to API 23 or higher.

defaultConfig {
    minSdkVersion 14
    targetSdkVersion 23
}

This means if you aren’t ready to implement the necessary changes for runtime permissions in your app, or you don’t need to take advantage of any new features in Android Marshmallow, then you can keep compiling and targeting against API 22 until you are ready.

How to enable RUNTIME PERMISSION ?


  • First of all we need to change the targetSdkVersion to 23
  • Need to request all the necessary permissions before using permission specific api’s
  • Override onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) inside inherited AppCompatActivity.
  • Check for the permission approval by the user and call respective api if permission approved.

    Example

I have created a common utility class for this

public class PermissionGranter {

    private static PermissionGranter instanceObject;
    private Activity activity;
    private AppController app;
    public List<String> permissionsList = new ArrayList<String>();
    public List<String> permissionsNeeded = new ArrayList<String>();
    private int requestedCode = -1;


    public static PermissionGranter getInstance(AppController app, Activity activity) {
        if (null == instanceObject) {
            instanceObject = new PermissionGranter(app, activity);
        }
        instanceObject.activity = activity;
        return instanceObject;
    }

    private void resetPermissionLists() {
        instanceObject.permissionsList.clear();
        instanceObject.permissionsNeeded.clear();
    }

    private PermissionGranter(AppController app, Activity activity) {
        this.app = app;
        this.activity = activity;
    }

    public boolean requestPermission(int requestCode, String[] permissions) {
        requestedCode = requestCode;
        resetPermissionLists();
        for (String permission : permissions) {
            if (addPermission(permissionsList, permission))
                permissionsNeeded.add(permission);
        }

        if (permissionsList.size() > 0) {
            if (permissionsNeeded.size() > 0) {
                // Need Rationale
                showPermissionRational(requestCode);
                return true;
            }

            ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
                    requestCode);
            return true;
        }
        return false;
    }

    public boolean isPermissionsGranted(int requestCode, String[] permissions, int[] grantResults) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
            if (requestCode == requestedCode) {
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    return true;
                } else {
                    CustomToast.getInstance(app).show("Oops we need all these permissions.Please grant them next time!");
                    return false;
                }
            }
        } else {
            return true;
        }
        return false;
    }

    private boolean addPermission(List<String> permissionsList, String permission) {
        if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
            permissionsList.add(permission);
            // Check for Rationale Option
            return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
        }
        return false;
    }

    private void showPermissionRational(final int requestCode) {
        // Need Rationale
        String message = "You need to grant access to " + getPermissionDisplayName(permissionsNeeded.get(0));
        for (int i = 1; i < permissionsNeeded.size(); i++)
            message = message + ", " + getPermissionDisplayName(permissionsNeeded.get(i));

        showMessageOKCancel(message,
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
                                requestCode);
                    }
                }, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int which) {
                        CustomToast.getInstance(app).show("Oops we need all these permissions.Please grant them next time!");
                        activity.finish();
                    }
                });

    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener) {
        new AlertDialog.Builder(activity)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", cancelListener)
                .create()
                .show();
    }

    private String getPermissionDisplayName(String permission) {
        String name = "";
        switch (permission) {
            case Manifest.permission.INTERNET:
                name = "Internet";
                break;
            case Manifest.permission.ACCESS_FINE_LOCATION:
            case Manifest.permission.ACCESS_COARSE_LOCATION:
                name = "GPS";
                break;
            case Manifest.permission.WRITE_EXTERNAL_STORAGE:
                name = "write data";
                break;
            case Manifest.permission.READ_EXTERNAL_STORAGE:
                name = "read data";
                break;
            case Manifest.permission.VIBRATE:
                name = "vibration to notify";
                break;
            case Manifest.permission.CAMERA:
                name = "camera";
                break;
            case Manifest.permission.CALL_PHONE:
                name = "phone call";
                break;
            default:
                name = "better experience";
                break;
        }
        return name;
    }
}

 

Now our utility class is ready what next ?

We have done most of the work. Our next step is to request for the permissions we need and this class is able to handle multiple permission requests at a time because sometimes we need it.

//Class member
final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 123;

doPermissionCheck(){
      permissionGranter = PermissionGranter.getInstance(app, this);
     String[] permissions = {Manifest.permission.INTERNET, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE};
      if(!permissionGranter.requestPermission(REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS, permissions)) {
           // do api call if permission is alre ady granted
        }
}
}

This doPermissionCheck() method call will show a dialog to ask for permissions grant with Never ask again option. Once the user will accept or deny , activity’s onRequestPermissionResult method get called. Now we have to override this as follows –

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (permissionGranter.isPermissionsGranted(requestCode, permissions, grantResults)) {
// to do api call
        } else {
            finish();
        }
    }

In all the places where we use permission centric api calls we have to do the same set of steps and then your app is ready with runtime permission check.

Note –

onRequestPermissionResult will not get called if you use

noHistory ="true"

within <activity> inside the AndroidManifest.xml.

For more details check this https://developer.android.com/training/permissions/requesting.html

Happy coding!!!

 

 

 

Leave a Reply

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