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]