Android Easy Runtime Permissions With Dexter Library Example Tutorial

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

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, 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.

Also Read,

Android Runtime permission example tutorial for more details.

Android Multiple permissions at runtime with single request

Download the Source Code for Runtime Permissions With Dexter

Click the below link to download whole android studio source code

[sociallocker]Download Android Runtime Permission Dexter Example[/sociallocker]