Skip to content
Giorgio Antonioli edited this page Mar 3, 2021 · 21 revisions

This page will help you to discover all the currently supported features of this library. The main sections are the following:

Configuration

The main method to start the creation of a request is the extension function permissionsBuilder() that can be used from an Activity or a Fragment. The only necessary configuration that must be provided to create a new PermissionRequest is the permission's set that must be requested. Depending on your needs, you can provide one or more permissions for each request.

Example:

// This is the most basic request.
val request = permissionsBuilder(Manifest.permission.CAMERA).build()

You can define also a custom RuntimePermissionHandlerProvider. If not specified, its default is used (ResultLauncherRuntimePermissionHandlerProvider).

Example:

val request = permissionsBuilder(Manifest.permission.CAMERA, Manifest.permission.SEND_SMS)
        // Specifies a custom RuntimePermissionHandlerProvider.
        .runtimeHandlerProvider(MyRuntimePermissionHandlerProvider())
        // Build the request
        .build()

To find more information about RuntimePermissionHandlerProvider, go here.

Permission request

The result received from a permissions request contains a list of PermissionStatus (List<PermissionStatus>), one for each requested permission. The result list has the same size and the same order of the list of the requested permissions. The PermissionStatus contains the permission which is related to. The possible statuses which can be received in a callback are:

  • PermissionStatus.Granted -> the requested permission is granted
  • PermissionStatus.Denied.Permanently -> the requested permission is denied and the user checked "never ask again" in the permissions dialog OR the request is being executed under API 23 (where runtime permissions don't exist)
  • PermissionStatus.Denied.ShouldShowRationale -> the requested permission is denied, but not permanently. This status can't be returned under API 23

So, under API 23 only PermissionStatus.Granted and PermissionStatus.Denied.Permanently can be returned depending on their presence in the manifest.

There are several ways to receive the result of a permissions request.

Basic

permissionsBuilder(Manifest.permission.CAMERA).build().send { result ->
    // Handle the result.
}

You can also add a listener to the request before sending it in one of the following way:

// If you have a centralized management of the events.
request.addListener(this)
// If you want to receive the callback in-line.
request.addListener { result ->
    // Handle the result.
}
request.send()

Multiple listeners can be attached to the same request and they can be removed with the following methods:

// Remove a specific listener.
request.removeListener(specificListener)
// Removes all the listeners.
request.removeAllListeners()

LiveData

val request = permissionsBuilder(Manifest.permission.CAMERA).build()
request.liveData().observe(this) { result ->
    // Handle the result.
}
request.send()

Coroutines suspend function

You need to add the artifact com.github.fondesa:kpermissions-coroutines.

launch {
    val result = permissionsBuilder(Manifest.permission.CAMERA).build().sendSuspend()
    // Handle the result.
}

Coroutines Flow

You need to add the artifact com.github.fondesa:kpermissions-coroutines and opt-in the experimental API of Flow.

val request = permissionsBuilder(Manifest.permission.CAMERA).build()
launch {
    request.flow().collect { result ->
        // Handle the result. 
    }
}
request.send()

RxJava 2/3

You need to add the artifact com.github.fondesa:kpermissions-coroutines-rx2 if you are using RxJava 2 or the artifact com.github.fondesa:kpermissions-rx3 if you are using RxJava 3.

val request = permissionsBuilder(Manifest.permission.CAMERA).build()
request.observe().subscribe { result ->
    // Handle the result.
}
request.send()

Check current status

To check the current permissions status without sending a request, you have two possibilities which return the same result:

// Extension on Activity.
checkPermissionsStatus(Manifest.permission.SEND_SMS, Manifest.permission.ACCESS_FINE_LOCATION)
// OR
request.checkStatus()

They will return a list of PermissionStatus (List<PermissionStatus>), one for each requested permission. The result list has the same size and the same order of the list of the requested permissions. The possible statuses which can be received from these APIs are:

  • PermissionStatus.Granted -> since API 23 the requested permission is already granted. Since API 23 the permission isn't listed in the manifest
  • PermissionStatus.Denied.Permanently -> under API 23 the permission isn't listed in the manifest. This status can't be returned since API 23
  • PermissionStatus.Denied.ShouldShowRationale -> the requested permission is denied, but not permanently, this status can't be returned under API 23
  • PermissionStatus.RequestRequired -> the requested permission requires a runtime request to establish its status (it can be either permanently denied or never asked). This status can't be returned under API 23

This API returns the same result of sending a permission request under API 23.

Runtime handler

To manage the runtime permissions since Android M there are two main components:

  • RuntimePermissionHandlerProvider
  • RuntimePermissionHandler

RuntimePermissionHandlerProvider

This provider is used to provide an instance of RuntimePermissionHandler when the request is built. The provided instance of RuntimePermissionHandler must be available instantly. You can cache the instance of RuntimePermissionHandler to use it for all the requests, but you need to manage its lifecycle manually.

RuntimePermissionHandler

Used to handle the runtime permissions since Android M. This component must persist across configuration changes or save its state because the permissions request is partially handled by the OS. The entire lifecycle of the runtime permissions is specified inside this component, so, in order to implement a custom lifecycle, you need to specify a custom RuntimePermissionHandlerProvider that will provide your custom RuntimePermissionHandler.

Injection

This library is built also to easily inject the PermissionRequest instances inside the components which uses them.

Example

If you use Dagger, you can provide the implementation of a PermissionRequest and use that request inside of a presenter without relying on the Android framework. In this way, the permissions management can be easily mocked and tested.

Save instance state

This library has the ability to automatically save the instance state of your permissions request and to re-attach the listeners/observers to the pending request after the state has been restored.

This can be useful, for example, in the following case:

  • your Activity doesn't require any permissions by default
  • the user performs an action which requires some permissions
  • the permissions dialog is shown to the user
  • the user puts the app in the background
  • after some time the process is killed
  • the user puts the app in the foreground
  • the permissions dialog is still visible and the user accepts the permissions
  • the action should be executed instantly after the user accepted the permissions

The only limitation to this is that you should add the listeners/observers to your PermissionRequest in a method always invoked after the state has been restored (e.g. Activity.onCreate()).

Your request can still be sent without any limitation in any moment you require and the attached listeners will be automatically notified when the permissions status changes.

e.g.

override fun onCreate(savedInstanceState: Bundle?) {
    [...]
    val request = permissionsBuilder(Manifest.permission.CAMERA).build()
    request.addListener { result ->
        // Handle the result.  
    }
    button.setOnClickListener {
        request.send()
    }
}

Under the hoods

By default this library uses ContextCompat APIs to check the permissions below Android M and a Fragment to check the runtime permissions since Android M. The Fragment internally uses the ActivityResultLauncher API to request the permissions and to receive the result.

The usage of a Fragment has not a real performance impact because:

  • it's light-weight
  • it hasn't no UI at all
  • it's shared between permissions requests