Android Runtime Permissions Tutorial With Example Programmatically

runtime permissions with dexter, android runtime permissions, horizontal listview android, android horizontal recyclerview

Android Runtime Permissions Tutorial With Example Programmatically is today’s topic.

We will learn to request runtime permissions with easy and simple example source code.

In this post, there three examples as per the following index.

1. Android Runtime Permissions Example.

2. Multiple Runtime Permission In Android Tutorial.

3. Dexter Runtime Permission Example.

From Android Version M (marshmallow), developer needs to ask for runtime permissions to the user.

The main purpose of runtime permission is to enhance the privacy of the user.





1. Single Runtime Permission Example

First let us understand some brief information about runtime permissions, the lifecycle, and then we will develop an example in the android studio.

Output of this app

LifeCycle Of Runtime Permissions

This is the key part of the tutorial. I suggest you to read this below steps carefully. You can create more user friendly runtime structure if you know it’s entire lifecycle deeply.

For example let’s assume, we want to request camera permission from user.

Step 1 -> First of all, we will ask it with pop up dialog. If user grants permissions then you can implement camera feature.

Step 2 -> Other than granting the permission, user have other option to deny it. When user denies the permission we have to repeat the step 1 and this process continues.

Step 3 -> Last case is that user first checks the “Never Ask Again” checkbox and then denies the permission. Here you can not repeat the step 1. In this scenario, you need to create custom pop up dialog which will educate user about the importance of the permission. In this dialog, give user a button which will led him to the app settings, from where he can grant the camera or other permissions.

Developer needs to use this feature carefully, otherwise it may cause app crashes or it can lead to bad user experience.

You can read google’s official documentation for proper usage of runtime permissions.

Now let us create an example in android studio to learn how to request runtime permissions.

Step 1. Adding in Manifest

First, create a fresh new project in the android studio. Choose empty activity while you are opening a new project.

Now, add the camera permission in the AndoridManifest.xml file using below line

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

Step 2. Changing Main Files

Copy the following source code in activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn"
        android:textSize="30sp"
        android:text="Ask for Camera Permission"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

I have taken only one button in the above source code.

Write down the following coding lines in MainActivity.java file

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
  
 private Button btn;
   private int CAMREA_CODE = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (permissionAlreadyGranted()) {
                    Toast.makeText(MainActivity.this, "Permission is already granted!", Toast.LENGTH_SHORT).show();
                    return;
                }
                requestPermission();
            }
        });
    }
    private boolean permissionAlreadyGranted() {
        int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (result == PackageManager.PERMISSION_GRANTED)
            return true;
        return false;
    }
    private void requestPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
        }
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMREA_CODE);
    }
    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == CAMREA_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Permission granted successfully", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Permission is denied!", Toast.LENGTH_SHORT).show();
                boolean showRationale = shouldShowRequestPermissionRationale( Manifest.permission.CAMERA );
                if (! showRationale) {
                    openSettingsDialog();
                }
            }
        }
    }
    private void openSettingsDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("Required Permissions");
        builder.setMessage("This app require permission to use awesome feature. Grant them in app settings.");
        builder.setPositiveButton("Take Me To SETTINGS", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Uri uri = Uri.fromParts("package", getPackageName(), null);
                intent.setData(uri);
                startActivityForResult(intent, 101);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.show();
    }
}

After adding above code, our example is over. Now let us see how compiler will execute this coding lines.

Diving In above code snippet

Consider the following snippet

btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
                 if (permissionAlreadyGranted()) {
                  Toast.makeText(MainActivity.this, "Permission is already granted!",Toast.LENGTH_SHORT).show();
                       return;
                  }
 
                  requestPermission();
               }
           });

Compiler will execute the above code when the user clicks the button.

Compiler will first check if the camera permission is already granted by the user or not.

If user has granted the permission, then a toast will be displayed. You can write your code for the app feature that is using camera here.

For checking if the user has granted the camera permissions or not, compiler will use permissionAlreadyGranted() method.

Code for permissionAlreadyGranted() method is as following

 private boolean permissionAlreadyGranted() {
        int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (result == PackageManager.PERMISSION_GRANTED)
            return true;
        return false;
 }

permissionAlreadyGranted() method will return true if permission is already allowed otherwise it will return false.

Now if the user have not granted camera permission yet, then compiler will request for runtime permission by using the requestPermission() method.

Below is the code for requestPermission() method

 private void requestPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
        }
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMREA_CODE);
}

When the compiler will execute the requestPermission() method, a pop up dialog will be shown to the user with allow and deny options.

When the user selects any of the option, compiler will execute the onRequestPermissionsResult() method.

onRequestPermissionsResult() method is written as below

 @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == CAMREA_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Permission granted successfully", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Permission is denied!", Toast.LENGTH_SHORT).show();
                boolean showRationale = shouldShowRequestPermissionRationale( Manifest.permission.CAMERA );
                if (! showRationale) {
                    openSettingsDialog();
                }
            }
        }
    }

In this method, compiler will decide whether user have clicked allow button or deny button.

If the user have clicked allow button then it will create a toast saying that “Permission granted successfully.”

Otherwise it will make toast saying “permission is denied!”. After this, compiler will check if the user have checked mark the checkbox saying “Never Ask Again” or not.

If no, then again compiler will show the pop up dialog to request camera permission.

If yes, then compiler will not be able to generate a pop up dialog again.

Here, we will call the openSettingsDialo() method.

openSettingsDialo() method is as following

private void openSettingsDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("Required Permissions");
        builder.setMessage("This app require permission to use awesome feature. Grant them in app settings.");
        builder.setPositiveButton("Take Me To SETTINGS", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Uri uri = Uri.fromParts("package", getPackageName(), null);
                intent.setData(uri);
                startActivityForResult(intent, 101);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.show();
    }

We will create one alert dialog with message that tells the user about the importance of the permission.

This alert dialog has two options : TAKE ME TO SETTINGS and CANCEL

When user clicks the TAKE ME TO SETTINGS button, it will take him to the app settings where he can turn on the camera permission.

Thus, we have successfully request the runtime permissions. If you have any query then ask them in comments.

I will try my best to help you out in every possible way.





2. Multiple Runtime Permission in Android

Hi, Guys. Welcome to check android multiple runtime permissions example.

In this tutorial, We will learn how to programmatically request multiple marshmallow runtime permissions in android studio.

You will learn how to request multiple runtime permissions at once or single request.

From android’s Marshmallow version, the developer needs to programmatically check and request multiple runtime permissions to the user.

Using this example, you can ask for multiple or single runtime permissions Android as per your requirements.

Following is the output:

Following are the required code snippets.

Step 1: Creating WelcomeActivity.java class:

Create new Activity named WelcomeActivity.java and add below code

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class WelcomeActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);
    }
}

User will only enter in this activity when he has allowed all the runtime permissions. Otherwise compiler will close the app.

Update activity_welcome.xml as below

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.exampledemo.parsaniahardik.marshmallowpermission.WelcomeActivity">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Welcome"
        android:textSize="40sp"/>
</RelativeLayout>

Step 2: Creating MainActivity.java class:

import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
    public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1;
    private static int SPLASH_TIME_OUT = 2000;
    private String TAG = "tag";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(checkAndRequestPermissions()) {
            // carry on the normal flow, as the case of  permissions  granted.
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    // This method will be executed once the timer is over
                    // Start your app main activity
                    Intent i = new Intent(MainActivity.this, WelcomeActivity.class);
                    startActivity(i);
                    // close this activity
                    finish();
                }
            }, SPLASH_TIME_OUT);
        }
    }
    private  boolean checkAndRequestPermissions() {
        int camerapermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        int writepermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int permissionLocation = ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION);
        int permissionRecordAudio = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);
        List<String> listPermissionsNeeded = new ArrayList<>();
        if (camerapermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.CAMERA);
        }
        if (writepermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (permissionLocation != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (permissionRecordAudio != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.RECORD_AUDIO);
        }
        if (!listPermissionsNeeded.isEmpty()) {
            ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), REQUEST_ID_MULTIPLE_PERMISSIONS);
            return false;
        }
        return true;
    }
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        Log.d(TAG, "Permission callback called-------");
        switch (requestCode) {
            case REQUEST_ID_MULTIPLE_PERMISSIONS: {
                Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
                // Fill with actual results from user
                if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);
                    // Check for both permissions
                    if (perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED 
&& perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED 
&& perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
                        Log.d(TAG, "sms & location services permission granted");
                        // process the normal flow
                        Intent i = new Intent(MainActivity.this, WelcomeActivity.class);
                        startActivity(i);
                        finish();
                        //else any one or both the permissions are not granted
                    } else {
                        Log.d(TAG, "Some permissions are not granted ask again ");
                        //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
//                        // shouldShowRequestPermissionRationale will return true
                        //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
                        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) 
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) 
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
 || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
                            showDialogOK("Service Permissions are required for this app",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                                case DialogInterface.BUTTON_POSITIVE:
                                                    checkAndRequestPermissions();
                                                    break;
                                                case DialogInterface.BUTTON_NEGATIVE:
                                                    // proceed with logic by disabling the related features or quit the app.
                                                    finish();
                                                    break;
                                            }
                                        }
                                    });
                        }
                        //permission is denied (and never ask again is  checked)
                        //shouldShowRequestPermissionRationale will return false
                        else {
                            explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
                            //                            //proceed with logic by disabling the related features or quit the app.
                        }
                    }
                }
            }
        }
    }
    private void showDialogOK(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", okListener)
                .create()
                .show();
    }
    private void explain(String msg){
        final android.support.v7.app.AlertDialog.Builder dialog = new android.support.v7.app.AlertDialog.Builder(this);
        dialog.setMessage(msg)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        //  permissionsclass.requestPermission(type,code);
                        startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        finish();
                    }
                });
        dialog.show();
    }
}

Step 3: Description of MainActivity

Defining permission codes

In checkAndRequestPermissions() method, create permission code for each permission like below.

int camerapermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        int writepermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int permissionLocation = ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION);
        int permissionRecordAudio = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);

These integer variables will represent the specific type of permissions in the whole example.

Preparing ArrayList

Add each permission in ArrayList and ask user by below source code

  List<String> listPermissionsNeeded = new ArrayList<>();
        if (camerapermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.CAMERA);
        }
        if (writepermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (permissionLocation != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (permissionRecordAudio != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.RECORD_AUDIO);
        }
        if (!listPermissionsNeeded.isEmpty()) {
            ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), REQUEST_ID_MULTIPLE_PERMISSIONS);
            return false;
        }

listPermissionsNeeded will contain all the permissions which we are going to ask from the user.

Hashmap Creation

In onRequestPermissionResult() method, put all the permission in one Hashmap.

 Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);

Now check if user have granted all the required permissions or not.

If user have granted all the required permissions then open WelcomeActivity or continue to the normal flow of your app.

If not granted, then ask again with your message.

 if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);
                    // Check for both permissions
                    if (perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
                        Log.d(TAG, "sms & location services permission granted");
                        // process the normal flow
                        Intent i = new Intent(MainActivity.this, WelcomeActivity.class);
                        startActivity(i);
                        finish();
                        //else any one or both the permissions are not granted
                    } else {
                        Log.d(TAG, "Some permissions are not granted ask again ");
                        //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
//                        // shouldShowRequestPermissionRationale will return true
                        //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
                        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
                            showDialogOK("Service Permissions are required for this app",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                                case DialogInterface.BUTTON_POSITIVE:
                                                    checkAndRequestPermissions();
                                                    break;
                                                case DialogInterface.BUTTON_NEGATIVE:
                                                    // proceed with logic by disabling the related features or quit the app.
                                                    finish();
                                                    break;
                                            }
                                        }
                                    });
                        }
                        //permission is denied (and never ask again is  checked)
                        //shouldShowRequestPermissionRationale will return false
                        else {
                            explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
                            //                            //proceed with logic by disabling the related features or quit the app.
                  }

Above source code will check if user gave granted all the permissions or not.

If any of the permissions is missing then again this code will request for runtime permissions.

Update Package name

When the user have denied any permission and he has also checked the “”Never ask again,” checkbox then the compiler will call explain() method.

In explain() method you need to update package name in below line

 startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));

Define required permissions in AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.exampledemo.parsaniahardik.marshmallowpermission">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".WelcomeActivity"></activity>
    </application>
</manifest>

Other Scenario to handle

In the above code, we ask check multiple marshmallow runtime permissions in splash screen.

But sometimes, you want to ask for specific permission in specific activity.

You may want to ask for a different number of permissions in different activities.

To handle this situation you just need to change a number of permissions in following snippets.

  1. When creating permissions codes
 int camerapermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        int writepermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int permissionLocation = ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION);
        int permissionRecordAudio = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);

2. Create ArrayList of permissions with below one

 List<String> listPermissionsNeeded = new ArrayList<>();
        if (camerapermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.CAMERA);
        }
        if (writepermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (permissionLocation != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (permissionRecordAudio != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.RECORD_AUDIO);
        }
        if (!listPermissionsNeeded.isEmpty()) {
            ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), REQUEST_ID_MULTIPLE_PERMISSIONS);
            return false;
        }

3. While creating HashMap

Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);

4. In following two if conditions

 if (perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED 
&& perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED 
&& perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) 

5. and

if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) 
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) 
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
 || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO))

That’s all for check request multiple marshmallow runtime permissions android studio tutorial.

If you have any question about implementing check request multiple marshmallow runtime permissions android, feel free to ask in the comment section.





3. Dexter Runtime Permission Tutorial

Today’s topic is about easy runtime permissions with dexter library example tutorial.

Asking for runtime permissions to user is a little bit of complex task to handle.

Developer need to take care of user experience while implementing feature carefully because user does not like to deal with dialogs very often while using the app.

Also, developer have to write plenty of coding lines for getting single permission. So, think about scenario where multiple permissions are required.

Dexter is a github third party library which simplifies the process of requesting runtime permissions.

Dexter have predefined classes which can work with very few lines of coding. You can ask for single as well as multiple permissions using dexter with only one method.

We will see how to request permissions with simple technique and in easy manner with example.

First of all, check the final output of this tutorial by watching the below video.

Step 1. Integrating Dexter Library

You need to add the following line your build.gradle(Module: app) file.

implementation 'com.karumi:dexter:5.0.0'

Above line will fetch all the necessary classes from the github dexter library.

So that we can use them with less number of coding lines.

Step 2. Android Manifest

Now simply add required permissions in your AndroidManifext.xml file.

In this tutorial, I will add below five permissions.

 <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

You can change them as per your app’s requirements.

Step 3. Creating Main Layout

Now let us create a main layout of our app.

Write down the following coding lines in the activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:textSize="25sp"
        android:text="Single Permission"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:id="@+id/btnCamera"
        android:text="Camera"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:textSize="25sp"
        android:text="Multiple Permission"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:id="@+id/btnMultiple"
        android:text="Contact, Storage, Audio"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:id="@+id/btn"
        android:text="Snackbar Example"/>
</LinearLayout>

Here, I have added three buttons.

One button will ask for single permission (only Camera permission).

Second button will ask for multiple permissions (Contact, Storage and Audio Permissions).

Third button will also ask for single permission, but we will show a snackbar when the user denies the permission in this scenario.

Step 4. Java Coding For Main Activity

At last, write down the following code in the MainActivity.java file

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.DexterError;
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.PermissionRequestErrorListener;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import com.karumi.dexter.listener.single.PermissionListener;
import com.karumi.dexter.listener.single.SnackbarOnDeniedPermissionListener;
import java.util.List;
public class MainActivity extends AppCompatActivity {
    private Button btnSingle, btnMultiple, btn;
    private PermissionListener snackbarPermissionListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View parentLayout = findViewById(android.R.id.content);
        snackbarPermissionListener =
                SnackbarOnDeniedPermissionListener.Builder
                        .with(parentLayout, "Call Phone access is needed to call your dog")
                        .withOpenSettingsButton("Settings")
                        .withCallback(new Snackbar.Callback() {
                            @Override
                            public void onShown(Snackbar snackbar) {
                                // Event handler for when the given Snackbar is visible
                            }
                            @Override
                            public void onDismissed(Snackbar snackbar, int event) {
                                // Event handler for when the given Snackbar has been dismissed
                            }
                        }).build();
        btnSingle = findViewById(R.id.btnCamera);
        btnMultiple = findViewById(R.id.btnMultiple);
        btn = findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               requestSnackbarPermission();
            }
        });
        btnSingle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                requestSinglePermission();
            }
        });
        btnMultiple.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                requestMultiplePermissions();
            }
        });
    }
    private void  requestMultiplePermissions(){
        Dexter.withActivity(this)
                .withPermissions(
                        Manifest.permission.READ_CONTACTS,
                        Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.READ_EXTERNAL_STORAGE)
                .withListener(new MultiplePermissionsListener() {
                    @Override
                    public void onPermissionsChecked(MultiplePermissionsReport report) {
                        // check if all permissions are granted
                        if (report.areAllPermissionsGranted()) {
                            Toast.makeText(getApplicationContext(), "All permissions are granted by user!", Toast.LENGTH_SHORT).show();
                        }
                        // check for permanent denial of any permission
                        if (report.isAnyPermissionPermanentlyDenied()) {
                            // show alert dialog navigating to Settings
                            openSettingsDialog();
                        }
                    }
                    @Override
                    public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).
                withErrorListener(new PermissionRequestErrorListener() {
                    @Override
                    public void onError(DexterError error) {
                        Toast.makeText(getApplicationContext(), "Some Error! ", Toast.LENGTH_SHORT).show();
                    }
                })
                .onSameThread()
                .check();
    }
    private void requestSinglePermission(){
        Dexter.withActivity(this)
                .withPermission(Manifest.permission.CAMERA)
                .withListener(new PermissionListener() {
                    @Override
                    public void onPermissionGranted(PermissionGrantedResponse response) {
                        //Single Permission is granted
                        Toast.makeText(MainActivity.this, "Single permission is granted!", Toast.LENGTH_SHORT).show();
                    }
                    @Override
                    public void onPermissionDenied(PermissionDeniedResponse response) {
                        // check for permanent denial of permission
                        if (response.isPermanentlyDenied()) {
                            openSettingsDialog();
                        }
                    }
                    @Override
                    public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).check();
    }
    private void openSettingsDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("Required Permissions");
        builder.setMessage("This app require permission to use awesome feature. Grant them in app settings.");
        builder.setPositiveButton("Take Me To SETTINGS", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Uri uri = Uri.fromParts("package", getPackageName(), null);
                intent.setData(uri);
                startActivityForResult(intent, 101);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.show();
    }
    private void requestSnackbarPermission() {
        Dexter.withActivity(this)
                .withPermission(Manifest.permission.CALL_PHONE)
                .withListener(snackbarPermissionListener)
                .check();
    }
}

Above is the main heart of the whole tutorial example.

Let us fully read and understand what above code is exactly doing.

Consider the below code

 private Button btnSingle, btnMultiple, btn;
 private PermissionListener snackbarPermissionListener;

I have declared three buttons and one object of the PermissionListener class from the dexter library.

Below is the declaration of the view object

View parentLayout = findViewById(android.R.id.content);

Above line is defining a view which contains the main layout. We have used this view object (parentLayout) in the declaration of the snackbarPermissionListener.

Following is the declaration of the snackbarPermissionListener.

snackbarPermissionListener =
                SnackbarOnDeniedPermissionListener.Builder
                        .with(parentLayout, "Call Phone access is needed to call your dog")
                        .withOpenSettingsButton("Settings")
                        .withCallback(new Snackbar.Callback() {
                            @Override
                            public void onShown(Snackbar snackbar) {
                                // Event handler for when the given Snackbar is visible
                            }
                            @Override
                            public void onDismissed(Snackbar snackbar, int event) {
                                // Event handler for when the given Snackbar has been dismissed
                            }
                        }).build();

We will use this snackbarPermissionListener to pop up the snack bar when the user deny any permissions.

We have used this snackbar to tell user about the importance of granting the required permission.

Now let us see the button clicks.

btnSingle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                requestSinglePermission();
            }
        });

When the user clicks on the Camera button, the compiler will execute the above code.

Asking for Single Permission

It will call requestSinglePermission() method.

Below is the coding for requestSinglePermission() method.

private void requestSinglePermission(){
        Dexter.withActivity(this)
                .withPermission(Manifest.permission.CAMERA)
                .withListener(new PermissionListener() {
                    @Override
                    public void onPermissionGranted(PermissionGrantedResponse response) {
                        //Single Permission is granted
                        Toast.makeText(MainActivity.this, "Single permission is granted!", Toast.LENGTH_SHORT).show();
                    }
                    @Override
                    public void onPermissionDenied(PermissionDeniedResponse response) {
                        // check for permanent denial of permission
                        if (response.isPermanentlyDenied()) {
                            openSettingsDialog();
                        }
                    }
                    @Override
                    public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).check();
    }

As you can see in the above code, Dexter.withActivity is used to ask the camera permission.

You need to pass the permission in below line

.withPermission(Manifest.permission.CAMERA)

When the user grants the permission, onPermissionGranted() method is called.

You can write the code of the feature which uses this permission in this method.

When the user denies the permission, compiler will call the following code

 @Override
                    public void onPermissionDenied(PermissionDeniedResponse response) {
                        // check for permanent denial of permission
                        if (response.isPermanentlyDenied()) {
                            openSettingsDialog();
                        }
                    }

I will open a new dialog when the user denies the permission. This dialog is opened by using the openSettingsDialog() method.

Code for openSettingsDialog() method is as following

 private void openSettingsDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("Required Permissions");
        builder.setMessage("This app require permission to use awesome feature. Grant them in app settings.");
        builder.setPositiveButton("Take Me To SETTINGS", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Uri uri = Uri.fromParts("package", getPackageName(), null);
                intent.setData(uri);
                startActivityForResult(intent, 101);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.show();
    }

builder.setMessage() will contain a message that tell user why we need runtime permission.

Following code will open app settings when the user clicks on “Take Me To Settings” button.

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, 101);

Asking for Multiple Permissions

When the user clicks on the Contact,Storage,Audio button, compiler will call the requestMultiplePermissions() method.

Following is the source code snippet for requestMultiplePermissions() method.

 private void  requestMultiplePermissions(){
        Dexter.withActivity(this)
                .withPermissions(
              Manifest.permission.READ_CONTACTS,
                        Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.READ_EXTERNAL_STORAGE)
                .withListener(new MultiplePermissionsListener() {
                    @Override
                    public void onPermissionsChecked(MultiplePermissionsReport report) {
                        // check if all permissions are granted
                        if (report.areAllPermissionsGranted()) {
                            Toast.makeText(getApplicationContext(), "All permissions are granted by user!", Toast.LENGTH_SHORT).show();
                        }
                        // check for permanent denial of any permission
                        if (report.isAnyPermissionPermanentlyDenied()) {
                            // show alert dialog navigating to Settings
                            openSettingsDialog();
                        }
                    }
                    @Override
                    public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
                        token.continuePermissionRequest();
                    }
                }).
                withErrorListener(new PermissionRequestErrorListener() {
                    @Override
                    public void onError(DexterError error) {
                        Toast.makeText(getApplicationContext(), "Some Error! ", Toast.LENGTH_SHORT).show();
                    }
                })
                .onSameThread()
                .check();
    }

You need to pass the multiple permissions in the below line

.withPermissions(
           Manifest.permission.READ_CONTACTS,
           Manifest.permission.RECORD_AUDIO,
           Manifest.permission.READ_EXTERNAL_STORAGE)

After that, compiler will call a method onPermissionsChecked().

In onPermissionsChecked() method, compiler will execute following lines if use have granted all the permissions.

 // check if all permissions are granted
     if (report.areAllPermissionsGranted()) {
                 Toast.makeText(getApplicationContext(), "All permissions are granted by user!", Toast.LENGTH_SHORT).show();
     }

If the user have not granted all the permissions, system will run the below source code

 // check for permanent denial of any permission
    if (report.isAnyPermissionPermanentlyDenied()) {
        // show alert dialog navigating to Settings
         openSettingsDialog();
    }

openSettingsDialog() is the same method as we have created in the requestSinglePermission() method.

So it was all lines about this android runtime permissions with dexter tutorial example.

Feel free to ask any query in the comment section.

Below is some addition information.

Android Runtime Permissions Concept

Earlier user used to grant these permissions at the time of the installation of the app.

This concept was less secure because user did not pay enough attention to the security purpose at the installation time.

Thus, google introduced the concept of the runtime permissions.

Runtime permissions means that developer will ask for every single permission hence, it will enhance the privacy as user will have to play with pop up dialog for each permission.

Pop up dialog will have two options, allow or deny. There is a one checkbox (never ask again) which allows user to prevent app for asking any particular permission in future.

Developer need to educate and inform his users about the need for permission requests properly.

Inappropriate permission requests, those that the user doesn’t fully understand, can result in users failing to grant a permission.

As a result, your app may be unable to deliver the user the features intended.

In the worst case, poorly requested permissions could result in users losing trust in your app and uninstalling it.

Three Types

There are mainly two types of permissions regarding to it’s risk level.

  1. Normal Permissions
  2. Signature Permissions
  3. Dangerous Permissions

Normal Permissions

Normal permissions are considered as a low risky permissions. These permissions are less risky for user’s privacy or for other app’s operations.

For example INTERNET permission. Accessing internet is now a days very common concept. Almost every app need internet for smooth functioning.

Thus INTERNET is normal permission and developer do not need to ask it to the user at runtime. Just define it in Manifest file and you are good to go.

Some normal permissions :

  • ACCESS_NETWORK_STATE
  • ACCESS_WIFI_STATE
  • BLUETOOTH
  • CHANGE_NETWORK_STATE
  • SET_ALARM

Signature Permissions

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

For example, CLEAR_APP_CACHE permission.

Dangerous Permissions

These are the permissions for which you are reading this tutorial. These permissions are dangerous to user’s security, his device’s data and to the functioning of the other apps.

For example, CONTACT permission comes under the dangerous concept. With this permission, app can read the contacts of the user’s device and can also send them to server.

Here, you need to ask for runtime permissions, because accessing the sensitive information like contact require user’s attention.

Several Dangerous Permissions:

  • READ_CALENDAR
  • ACCESS_FINE_LOCATION
  • RECORD_AUDIO
  • USE_SIP
  • SEND_SMS