Let me introduce you to Android Dexter Kotlin Request Runtime Permissions Example.
Every android app need to ask for the runtime permissions to the user when they want sensitive data of the user’s android device.
In this example, you will learn to ask for runtime permissions in kotlin using dexter library.
In general, it is difficult to use android’s built in code and methods implement runtime permissions.
But dexter library makes this process simple and faster for us.
Using the classes and methods of the dexter library, we will be able to write runtime permissions code in no time.
First of all, go through the below video to show the final result of this tutorial.
Step 1. Gradle Working
In your android studio, make a fresh new project for making this android dexter kotlin project.
Now in this new project, open gradle file : build.gradle(Module:app)
You need to below line in this build.gradle(Module:app)
implementation 'com.karumi:dexter:5.0.0'
Above file will fetch all the classes and methods of dexter library in our project.
So after this, we will be able to use this library and it’s source code directly into our project.
Step 2. Permissions in Manifest
Now open your AndroidManifest.xml file.
In this AndroidManifest.xml file, add the below lines
<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 see that above lines are defining various permissions like camera, read contact, call phone etc.
We will have to ask at runtime to the user for all the above permissions.
Step 3. Final Writings
Your project must have two files. One is activity_main.xml and another is MainActivity.kt
In your activity_main.xml file, add the below coding lines
<?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>
Above coding lines hold two text views and three buttons.
Text views are static so we will not change it’s text values in this project.
We will see what button click events are doing in the Kotlin file.
Now, open up your MainActivity.kt file and write down the below code snippet in it,
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 class MainActivity : AppCompatActivity() { private var btnSingle: Button? = null private var btnMultiple: Button? = null private var btn: Button? = null private var snackbarPermissionListener: PermissionListener? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val parentLayout = findViewById<View>(android.R.id.content) snackbarPermissionListener = SnackbarOnDeniedPermissionListener.Builder .with(parentLayout, "Call Phone access is needed to call your dog") .withOpenSettingsButton("Settings") .withCallback(object : Snackbar.Callback() { override fun onShown(snackbar: Snackbar?) { // Event handler for when the given Snackbar is visible } override fun onDismissed(snackbar: Snackbar?, event: Int) { // 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 { requestSnackbarPermission() } btnSingle!!.setOnClickListener { requestSinglePermission() } btnMultiple!!.setOnClickListener { requestMultiplePermissions() } } private fun requestMultiplePermissions() { Dexter.withActivity(this) .withPermissions( Manifest.permission.READ_CONTACTS, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE ) .withListener(object : MultiplePermissionsListener { override fun onPermissionsChecked(report: MultiplePermissionsReport) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { Toast.makeText(applicationContext, "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 fun onPermissionRationaleShouldBeShown( permissions: List<PermissionRequest>, token: PermissionToken ) { token.continuePermissionRequest() } }).withErrorListener { Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show() } .onSameThread() .check() } private fun requestSinglePermission() { Dexter.withActivity(this) .withPermission(Manifest.permission.CAMERA) .withListener(object : PermissionListener { override fun onPermissionGranted(response: PermissionGrantedResponse) { //Single Permission is granted Toast.makeText(this@MainActivity, "Single permission is granted!", Toast.LENGTH_SHORT).show() } override fun onPermissionDenied(response: PermissionDeniedResponse) { // check for permanent denial of permission if (response.isPermanentlyDenied) { openSettingsDialog() } } override fun onPermissionRationaleShouldBeShown(permission: PermissionRequest, token: PermissionToken) { token.continuePermissionRequest() } }).check() } private fun openSettingsDialog() { val builder = AlertDialog.Builder(this@MainActivity) 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") { dialog, which -> dialog.cancel() val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivityForResult(intent, 101) } builder.setNegativeButton( "Cancel" ) { dialog, which -> dialog.cancel() } builder.show() } private fun requestSnackbarPermission() { Dexter.withActivity(this) .withPermission(Manifest.permission.CALL_PHONE) .withListener(snackbarPermissionListener) .check() } }
More Reference For Above
First of all, read the below lines of code
private var btnSingle: Button? = null private var btnMultiple: Button? = null private var btn: Button? = null private var snackbarPermissionListener: PermissionListener? = null
First three lines are giving us the objects of the button class.
Last line is making the object of the PermissionListener class.
Now attend the below coding structure
btn!!.setOnClickListener { requestSnackbarPermission() }
When the user clicks the “SNACKBAR EXAMPLE”, Compiler will call requestSnackbarPermission() method.
Below are the coding lines for requestSnackbarPermission() method.
private fun requestSnackbarPermission() { Dexter.withActivity(this) .withPermission(Manifest.permission.CALL_PHONE) .withListener(snackbarPermissionListener) .check() }
This method will ask for the CALL_PHONE permission to the user.
If user allow permission then compiler will not do anything but if user denies the permission then it will show a snackbar with settings button.
We are displaying a snackbar at the bottom of the screen.
Snackbar is similar to the toast which shows some short messages to the user to inform him about certain important facts.
When the user clicks the settings button of the snackbar, compiler will take hm to the settings of the app where he can turn on or off various permissions.
Now see the following code part
btnSingle!!.setOnClickListener { requestSinglePermission() }
When the user clicks the btnSingle button, compiler will call requestSinglePermission() method,
Below is the code for requestSinglePermission() method
private fun requestSinglePermission() { Dexter.withActivity(this) .withPermission(Manifest.permission.CAMERA) .withListener(object : PermissionListener { override fun onPermissionGranted(response: PermissionGrantedResponse) { //Single Permission is granted Toast.makeText(this@MainActivity, "Single permission is granted!", Toast.LENGTH_SHORT).show() } override fun onPermissionDenied(response: PermissionDeniedResponse) { // check for permanent denial of permission if (response.isPermanentlyDenied) { openSettingsDialog() } } override fun onPermissionRationaleShouldBeShown(permission: PermissionRequest, token: PermissionToken) { token.continuePermissionRequest() } }).check() }
As per the name suggests, this method ask for single runtime permission to the user.
Compiler will use the various classes and methods of Dexter library to ask for CAMERA permission in the above code.
Above method will handle all three possible scenarios ,
1. User allows the permission : Here, compiler will show the Toast saying “Single Permission is granted !”
2. User Denies the permission : Compiler will not do anything in this case.
3. User clicks the “Don’t ask again checkbox” and then Denies the permission : In this case, compiler will call openSettingsDialog() method.
Below are the source line for openSettingsDialog() method,
private fun openSettingsDialog() { val builder = AlertDialog.Builder(this@MainActivity) 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") { dialog, which -> dialog.cancel() val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivityForResult(intent, 101) } builder.setNegativeButton( "Cancel" ) { dialog, which -> dialog.cancel() } builder.show() }
This dialog will have one message which informs user regarding the importance of the permission.
Other than the message, this dialog will also have two buttons : CANCEL and TAKE ME TO SETTINGS
On the click of CANCEL button, compiler will simply close the dialog.
Click event of other button will led the user to the settings of the app where he can manually turn on or off any permission.
Now read the following coding flow
private fun requestMultiplePermissions() { Dexter.withActivity(this) .withPermissions( Manifest.permission.READ_CONTACTS, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE ) .withListener(object : MultiplePermissionsListener { override fun onPermissionsChecked(report: MultiplePermissionsReport) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { Toast.makeText(applicationContext, "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 fun onPermissionRationaleShouldBeShown( permissions: List<PermissionRequest>, token: PermissionToken ) { token.continuePermissionRequest() } }).withErrorListener { Toast.makeText(applicationContext, "Some Error! ", Toast.LENGTH_SHORT).show() } .onSameThread() .check() }
Compiler will run the above code when the user clicks the “CONTACT,STORAGE,AUDIO” button.
Here, compiler will run requestMultiplePermissions() method.
This method is asking multiple permissions to the user. I have three permissions in this method, but you can add or delete permission as per your requirement.
If the user grants all the permissions then compiler will show a Toast like “All permissions are granted b user !!”
but the user makes permanent denial of any permission (user clicks on “Don’t ask again check and then denies permission”) then compiler will run openSettingsDialog() method.
More Tutorials
Android Runtime Permission with Dexter Example in JAVA
Multiple Runtime Permission without external Library