The easiest way to handle Android Runtime Permissions in Kotlin.
Android runtime permissions is pain.
Android runtime permissions was introduced in the Marshmallow (v 6.0) version of Android. It asks for permissions to user when they are running the app instead of asking for all the permissions while installing the app. It gives more control to users as they can give the permissions they want and deny to those who they do not fill comfortable with.
With this, it has increased the pain in the neck for the developer, to enable/disable features based on what permissions user has granted or denied. The model asking for permissions was design to function asynchronously, which increased the complexity of an app largely.
To make it with that, google has created it's own library easypermissions. (I didn't find it easy, but that's what they said). Still, you have to do handful of things and it's asynchronous way of handling things make it hard to manage. There are many other libraries as well to help developers easily handle it. But, all libraries has to manage it with proxy classes or managing and passing callbacks and all.
So, to solve this issue QuickPermissions was created. But, it turns out that, it doesn't work with instant-run and lots of developers can not afford to disable instant run, because it dramatically increased the build time. So, if you are working with Kotlin, you are lucky. This library is created to solve that problem in Kotlin. No gradle plugin and no long running build times. Asking for permissions synchronous way (It looks like that, but won't block the main thread, don't worry, no ANRs, promise!). And after the permissions are granted, it will continue executing the method block which was on hold. As simple as that.
In your settings.gradle
file (or wherever you've set up repositories):
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io/" } // <-- THIS MUST BE ADDED
}
}
As this library is using jitpack to publish this, you need to add jitpack url. If you already have, do not add that again.
In your app's build.gradle
file, add the following dependency:
dependencies {
implementation 'com.github.cyb3rko:quickpermissions-kotlin:VERSION'
}
The following call has to be made in an Activity
or Fragment
context:
Use runWithPermissions
block, and you are all done. Library will manage everything, will ask for permissions, will show rationale dialog if denied and also ask to user to allow permissions from settings if user has permanently denied some permission(s) required by the method.
fun methodWithPermissions() = runWithPermissions(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) {
Toast.makeText(this, "Camera and audio recording permissions granted", Toast.LENGTH_SHORT).show()
// Do the stuff with permissions safely
}
That's it! You are good to go.
Your inner code will not be executed until the permission mentioned will not be granted. If it already has the permissions, it will simply be executed right away. If not, it will ask for permissions and executes it after it was granted.
NOTE: The one thing is to make sure, your function should not return anything (return type should be Unit), as the asking permissions is asynchronous behavior, that can not be handled in a true synchronous way.-
You can optionally pass on the options
object to that method:
Things you can do with it:
handleRationale
: Boolean indicating whether the library should handle rationale dialog or notrationaleMessage
: Custom rational message overriding default value if specifiedhandlePermanentlyDenied
: Boolean indicating whether the library should handle permissions permanently denied dialog or notpermanentlyDeniedMessage
: Custom permanently denied message overriding default value if specifiedpreRationaleAction
: Function to be called when permissions are missing and before starting flowrationaleMethod
: Function to handle rational callback yourself. It will prvoide aQuickPermissionsRequest
instance on which you can callproceed()
to continue asking permissions again, or callcancel()
to cancel the flowpermanentDeniedMethod
: Function to handle permissions permanently denied callback yourself. It will prvoide aQuickPermissionsRequest
instance on which you can callopenAppSettings()
to continue orcancel()
to cancel the flowpermissionsDeniedMethod
: Function to handle cases when some/all permissions are denied at the end of the flow. It will prvoide aQuickPermissionsRequest
instance from which you can retrieve the permissions which were denied.
val quickPermissionsOption = QuickPermissionsOptions(
handleRationale = false
rationaleMessage = "Custom rational message",
permanentlyDeniedMessage = "Custom permanently denied message",
preRationaleAction = { showToast() },
rationaleMethod = { req -> rationaleCallback(req) },
permanentDeniedMethod = { req -> permissionsPermanentlyDenied(req) }
)
private fun methodRequiresPermissions() = runWithPermissions(Manifest.permission.WRITE_CALENDAR, Manifest.permission.RECORD_AUDIO, options = quickPermissionsOption) {
Toast.makeText(this, "Cal and microphone permissions granted", Toast.LENGTH_LONG).show()
}
There is an sample app
available in this repo. Just clone it and run app
to check it out.
Have any questions or any trouble? Create an issue.