Adding runtime permissions to your app in Kotlin

Introduction

App permissions are used to provide access to data and functionality beyond that which is provided by the app's sandbox. They can enhance the functionality of your app by allowing it access to the internet, device location, camera, and more.

To use these capabilities, you will need to ask for permissions from the user. This codelab takes you through the steps necessary to add runtime permissions and to check if they are granted or not.

What you'll need

What you should already know

What you'll learn

  • How to add permissions to the Android manifest.
  • How to request permissions.
  • How to handle when permissions are accepted and when they are rejected.
  • How to check if a permission was granted.

What you'll build

You will make an app that requests a camera permission. You will implement the permission portion of the app, but not the camera part.

  1. Start a new Android Studio project.
  2. Choose Empty Activity.

70e5de28256e3dcb.png

  1. Name the project Permissions Codelab and set the language to Kotlin.

f2948b584ca99c47.png

  1. Add the latest versions of the following dependencies into your app-level build.gradle file. These allow you to use the Activity library that is covered later in the codelab.
implementation "androidx.activity:activity-ktx:1.2.2"
implementation "androidx.fragment:fragment-ktx:1.3.2"
  1. Set the viewBinding build option to true in the android block to enable ViewBinding.
android {
   ...
   buildFeatures {
       viewBinding true
   }
}
  1. Then press the green hammer button to build. This will generate a binding class called ActivityMainBinding that you will use later for ViewBinding.

d4064454e5c50111.png

  1. Navigate to the activity_main.xml file and replace the code with this.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/main_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Request Permissions"
       android:onClick="onClickRequestPermission"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. Add these strings to the strings.xml file.
<string name="app_name">Permissions Codelab</string>
<string name="permission_required">Camera access is required to display the camera preview.</string>
<string name="ok">OK</string>
<string name="permission_granted">Permission is granted. You can use the camera now.</string>
  1. In MainActivity.kt, above the onCreate() method, define variables for the layout and the ViewBinding.
private lateinit var layout: View
private lateinit var binding: ActivityMainBinding
  1. Replace the code in the onCreate() method with the code below. This code initializes the binding, creates a val to represent the view and sets it to the binding's root, and sets the layout to binding's mainLayout.
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   binding = ActivityMainBinding.inflate(layoutInflater)
   val view = binding.root
   layout = binding.mainLayout
   setContentView(view)
}
  1. At the bottom of the file, under the MainActivity class add an extension function to show snackbars throughout the codelab.
fun View.showSnackbar(
   view: View,
   msg: String,
   length: Int,
   actionMessage: CharSequence?,
   action: (View) -> Unit
) {
   val snackbar = Snackbar.make(view, msg, length)
   if (actionMessage != null) {
       snackbar.setAction(actionMessage) {
           action(this)
       }.show()
   } else {
       snackbar.show()
   }
}

The first thing you will need to do is declare that your permission will be used in the Android manifest using the <uses-permission> tag.

Sometimes the permission you are asking for will also have other requirements. In this case, you cannot use a camera app unless the device has a camera. Because of this, you will also add the <uses-feature> tag to the manifest.

  1. In the AndroidManifest.xml file, add the permission for camera above the <application> tag.
<uses-permission android:name="android.permission.CAMERA" />

In MainActivity.kt, create a val called requestPermissionLauncher and copy this code into it. The next few bullet points will break down what is in here.

private val requestPermissionLauncher =
   registerForActivityResult(
       ActivityResultContracts.RequestPermission()
   ) { isGranted: Boolean ->
       if (isGranted) {
           Log.i("Permission: ", "Granted")
       } else {
           Log.i("Permission: ", "Denied")
       }
   }
private val requestPermissionLauncher =
   registerForActivityResult(
       ActivityResultContracts.RequestPermission())
  • Add a callback to handle the case where it is granted or not. In this case we are logging the result.
{ isGranted: Boolean ->
       if (isGranted) {
           Log.i("Permission: ", "Granted")
       } else {
           Log.i("Permission: ", "Denied")
       }
}
  1. Create a function called onClickRequestPermission(view: View) and copy this code into it. In the next few bullet points we will break down what is happening inside.
fun onClickRequestPermission(view: View) {
   when {
       ContextCompat.checkSelfPermission(
           this,
           Manifest.permission.CAMERA
       ) == PackageManager.PERMISSION_GRANTED -> {
           layout.showSnackbar(
               view,
               getString(R.string.permission_granted),
               Snackbar.LENGTH_INDEFINITE,
               null
           ) {}
       }

       ActivityCompat.shouldShowRequestPermissionRationale(
           this,
           Manifest.permission.CAMERA
       ) -> {
           layout.showSnackbar(
               view,
               getString(R.string.permission_required),
               Snackbar.LENGTH_INDEFINITE,
               getString(R.string.ok)
           ) {
               requestPermissionLauncher.launch(
                   Manifest.permission.CAMERA
               )
           }
       }

       else -> {
           requestPermissionLauncher.launch(
               Manifest.permission.CAMERA
           )
       }
   }
}
  • Set up a when statement to cover the three cases: if permission is already granted, if the app deems that they should show the request permission rationale, and if it hasn't been asked yet.
when {
   ContextCompat.checkSelfPermission(
       this,
       Manifest.permission.CAMERA
   ) == PackageManager.PERMISSION_GRANTED -> {

   }

   ActivityCompat.shouldShowRequestPermissionRationale(
       this,
       Manifest.permission.CAMERA
   ) -> {

   }

   else -> {

   }
}
  • In the case that the permission is granted, display a snackbar that announces that.
ContextCompat.checkSelfPermission(
   this,
   Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
   layout.showSnackbar(
       view,
       getString(R.string.permission_granted),
       Snackbar.LENGTH_INDEFINITE,
       null
   ) {}
}
  • If shouldShowRequestPermissionRationale() returns true, show a UI that describes in more detail why the feature needs that particular permission and also gives the user a way to accept/deny the permission from the UI.

a25d8c6c1d5051d.png

ActivityCompat.shouldShowRequestPermissionRationale(
   this,
   Manifest.permission.CAMERA
) -> {
   layout.showSnackbar(
       view,
       getString(R.string.permission_required),
       Snackbar.LENGTH_INDEFINITE,
       getString(R.string.ok)
   ) {
       requestPermissionLauncher.launch(
           Manifest.permission.CAMERA
       )
   }
}
  • Else, request the permission.
else -> {
   requestPermissionLauncher.launch(
       Manifest.permission.CAMERA
   )
}

That is it! This codelab shows you the way to add in runtime permissions and can be used for permissions regarding location, network, media, and more!

You can check out the final code and read more in the documentation.