eazypermissions

Additional

Language
Kotlin
Version
v2.0.0 (Sep 15, 2019)
Created
Apr 19, 2019
Updated
Oct 5, 2019
Owner
Sagar Viradiya (sagar-viradiya)
Contributor
Sagar Viradiya (sagar-viradiya)
1
Activity
Badge
Generate
Download
Source code
APK file

Blurb

Eazy Runtime Permission

A lightweight Android library which wraps boilerplate code of runtime permission and allows you to request permissions

  1. from coroutines. (No callbacks yay 🎉)
  2. request and observe permissions through LiveData.
  3. through clean and concise Kotlin DSL.

From release 2.0.0 onwards library is migrated to AndroidX. If you are still using support library and haven't migrated to AndroidX then check out non-androidX version of the library.

Including in your project

Eazy permissions is available in the Jcenter and divided into three modules so that based on your need you can include either coroutines or livedata or Kotlin DSL support in your project

//For coroutines
implementation 'com.sagar:coroutinespermission:[latest_version]'

//For LiveData
implementation 'com.sagar:livedatapermission:[latest_version]'

//For Kotlin DSL
implementation 'com.sagar:dslpermission:[latest_version]'
  • latest_version for coroutines -
  • latest_version for livedata -
  • latest_version for Kotlin DSL

Coroutines support

Requesting permission is just a simple function call to suspending function requestPermissions of PermissionManager from your coroutines or other suspending function which will return PermissionResult. It takes 3 parameters.

  1. An instance of AppCompactActivity or Fragment depending on from where you are requesting permission.
  2. Request id.
  3. varargs of permission you want to request.

This is how you would request for permission within coroutines and get result sequentially.

.
.
.
launch {
    //CoroutineScope

    val permissionResult = PermissionManager.requestPermissions(           //Suspends the coroutine
                            this@Fragment,                                  
                            REQUEST_ID,
                            Manifest.permission.ACCESS_FINE_LOCATION,
                            Manifest.permission.READ_CONTACTS,
                            Manifest.permission.CAMERA
                        )
                        
    //Resume coroutine once result is ready
    when(permissionResult) {
        is PermissionResult.PermissionGranted -> {
            //Add your logic here after user grants permission(s)
        }
        is PermissionResult.PermissionDenied -> {
            //Add your logic to handle permission denial
        }
        is PermissionResult.PermissionDeniedPermanently -> {
            //Add your logic here if user denied permission(s) permanently.
            //Ideally you should ask user to manually go to settings and enable permission(s)
        }
        is PermissionResult.ShowRational -> {
            //If user denied permission frequently then she/he is not clear about why you are asking this permission.
            //This is your chance to explain them why you need permission.
        }
    }

}

You can request permission from coroutine launched using any dispatcher(IO/Default/Main).

Library exposes PermissionResult as result of permission request which is nothing but simple sealed class which wraps all possible outcomes.

sealed class PermissionResult(val requestCode: Int) {
    class PermissionGranted(requestCode: Int) : PermissionResult(requestCode)
    class PermissionDenied(
        requestCode: Int,
        val deniedPermissions: List<String>
    ) : PermissionResult(requestCode)

    class ShowRational(requestCode: Int) : PermissionResult(requestCode)
    class PermissionDeniedPermanently(
        requestCode: Int,
        val permanentlyDeniedPermissions: List<String>
    ) : PermissionResult(requestCode)
}

Notice PermissionDenied and PermissionDeniedPermanently are also exposing list of denied permissions and permanently denied permissions respectively so that you can decide your flow based on denied permissions if you want to.

LiveData support

Just in case of coroutine we saw above requesting permission is just a simple method call to PermissionManager from your Activity/Fragment. It takes 3 parameters.

  1. An instance of AppCompactActivity or Fragment depending from where you are requesting permission.
  2. Request id.
  3. varargs of permission you want to request.

This is how you request permissions from your Activity/Fragment.

PermissionManager.requestPermissions(
                this,
                REQUEST_ID,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.READ_CONTACTS
            )

Observing permission request result

With just one simple step(implementing an interface) you are ready to observe the result of request. Your Activity/Fragment must implement setupObserver method of PermissionObserver interface which expose LiveData<PermissionResult>. Here is the definition of PermissionObserver

/**
 * Interface definition for a callback to get [LiveData] of [PermissionResult]
 *
 * Implement this interface to get [LiveData] for observing permission request result.
 */
interface PermissionObserver {
    fun setupObserver(permissionResultLiveData: LiveData<PermissionResult>)
}

The library will only call setupObserver method when you are requesting permission for the first time. All the successive call to requestPermissions method will use the same observer.

Just as you would observe other LiveData you can observe LiveData<PermissionResult> as follow

override fun setupObserver(permissionResultLiveData: LiveData<PermissionResult>) {
    permissionResultLiveData.observe(this, Observer<PermissionResult> {
        when (it) {
            is PermissionResult.PermissionGranted -> {
                if (it.requestId == REQUEST_ID) {
                    //Add your logic here after user grants permission(s)
                }
            }
            is PermissionResult.PermissionDenied -> {
                if (it.requestId == REQUEST_ID) {
                    //Add your logic to handle permission denial
                }
            }
            is PermissionResult.PermissionDeniedPermanently -> {
                if (it.requestId == REQUEST_ID) {
                    //Add your logic here if user denied permission(s) permanently.
                    //Ideally you should ask user to manually go to settings and enable permission(s)
                }
            }
            is PermissionResult.ShowRational -> {
                if (it.requestId == REQUEST_ID) {
                    //If user denied permission frequently then she/he is not clear about why you are asking this permission.
                    //This is your chance to explain them why you need permission.
                }
            }
        }
    })
}

It is mandatory to implement PermissionObserver from where you are requesting permission(either Activity or Fragment). If you don't then library will throw IllegalArgumentException stating that you have to implement PermissionObserver

Library will take care of Activity/Fragment recreation so even if user rotates screen or due to some other reason if your Activity/Fragment gets recreated it will call setupObserver method to register new observer of LiveData.

Kotlin DSL

You can request permissions from Activity/Fragment as shown below.

requestPermissions(
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.READ_CONTACTS,
    Manifest.permission.CAMERA
) {
    requestCode = 4
    resultCallback = {
        when(this) {
            is PermissionResult.PermissionGranted -> {
                //Add your logic here after user grants permission(s)
            }
            is PermissionResult.PermissionDenied -> {
                //Add your logic to handle permission denial
            }
            is PermissionResult.PermissionDeniedPermanently -> {
                //Add your logic here if user denied permission(s) permanently.
                //Ideally you should ask user to manually go to settings and enable permission(s)
            }
            is PermissionResult.ShowRational -> {
                //If user denied permission frequently then she/he is not clear about why you are asking this permission.
                //This is your chance to explain them why you need permission.
            }
        }
    }
}

requestPermissions is an extension function on Activity/Fragment. It takes two arguments

  1. vararg of permissions.
  2. Lambda with a receiver on PermissionRequest.

Within lambda you need to initialize requestCode and resultCallback. Library will invoke resultCallback upon permission result. resultCallback is lambda with a receiver on PermissionResult so you have direct access to PermissionResult as this within lambda. PermissionResult is simple sealed class which wraps all possible outcomes as shown in above code snippet.

If you are requesting permissions outside Activity/Fragment then use the following DSL.

PermissionManager.requestPermissions(
    fragment,   //Instance of Activity or Fragment
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.READ_CONTACTS,
    Manifest.permission.CAMERA
) {
    requestCode = 4
    resultCallback = {
        when(this) {
            is PermissionResult.PermissionGranted -> {
                //Add your logic here after user grants permission(s)
            }
            is PermissionResult.PermissionDenied -> {
                //Add your logic to handle permission denial
            }
            is PermissionResult.PermissionDeniedPermanently -> {
                //Add your logic here if user denied permission(s) permanently.
                //Ideally you should ask user to manually go to settings and enable permission(s)
            }
            is PermissionResult.ShowRational -> {
                //If user denied permission frequently then she/he is not clear about why you are asking this permission.
                //This is your chance to explain them why you need permission.
            }
        }
    }
}

The only difference here is you need to call PermissionManager.requestPermissions which takes an instance of Fragment/Activity as an additional parameter.

Contributing

Have suggestions for improvements and want to contribute? or Found any issues? Head over to Contribution guidelines to know more about contributing to this library.

Screenshots of sample



License

Copyright 2019 Sagar Viradiya

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.