Peko
Peko copied to clipboard
Android Library for requesting Permissions with Kotlin Flow
PEKO
PErmissions with KOtlin
Android Permissions with Kotlin Coroutines or LiveData
No more callbacks, builders, listeners or verbose code for requesting Android permissions.
Get Permission Request Result asynchronously with one function call.
Thanks to Kotlin Coroutines, permissions requests are async and lightweight (no new threads are used/created).
Or if you don't use Coroutines, and don't want to manage Lifecycles ... receive Permission Results with LiveData.
Thanks to JetBrains
Supported by JetBrains Open Source
Installation
Hosted on Maven Central
implementation 'com.markodevcic:peko:2.2.0'
Example
In an Activity or a Fragment that implements CoroutineScope interface:
launch {
val result = Peko.requestPermissionsAsync(this, Manifest.permission.READ_CONTACTS)
if (result is PermissionResult.Granted) {
// we have contacts permission
} else {
// permission denied
}
}
Or use one of the extension functions on an Activity or a Fragment:
launch {
val result = requestPermissionsAsync(Manifest.permission.READ_CONTACTS)
if (result is PermissionResult.Granted) {
// we have contacts permission
} else {
// permission denied
}
}
Request multiple permissions:
launch {
val result = requestPermissionsAsync(Manifest.permission.READ_CONTACTS, Manifest.permission.CAMERA)
if (result is PermissionResult.Granted) {
// we have both permissions
} else if (result is PermissionResult.Denied) {
result.deniedPermissions.forEach { p ->
// this one was denied
}
}
}
Denied Result has three subtypes which can be checked to see if we need Permission Rationale or user Clicked Do Not Ask Again.
launch {
val result = requestPermissionsAsync(Manifest.permission.BLUETOOTH, Manifest.permission.CAMERA)
when (result) {
is PermissionResult.Granted -> { } // woohoo, all requested permissions granted
is PermissionResult.Denied.JustDenied -> { } // at least one permission was denied, maybe we forgot to register it in the AndroidManifest?
is PermissionResult.Denied.NeedsRationale -> { } // user clicked Deny, let's show a rationale
is PermissionResult.Denied.DeniedPermanently -> { } // Android System won't show Permission dialog anymore, let's tell the user we can't proceed
is PermissionResult.Cancelled -> { } // interaction was interrupted
}
}
If you want to know which permissions were denied, they are a property of Denied class.
class Denied(val deniedPermissions: Collection<String>)
Need to check if permission is granted? Yes, let's skip the horrible Android API. Single call, accepts multiple Strings as arguments, returns true if all are granted.
val granted = Peko.areGranted(activity, Manifest.permission.READ_CONTACTS)
LiveData
Hate Coroutines? No problem ... just create an instance of PermissionsLiveData and observe the results with your LifecycleOwner
In a ViewModel ... if you need to support orientation changes, or anywhere else if not (Presenter)
val permissionLiveData = PermissionsLiveData()
fun checkPermissions(vararg permissions: String) {
permissionLiveData.checkPermissions(*permissions)
}
In your LifecycleOwner, for example in an Activity
override fun onCreate(savedInstanceState: Bundle?) {
viewModel = ViewModelProviders.of(this).get(YourViewModel::class.java)
// observe has to be called before checkPermissions, so we can get the LifecycleOwner
viewModel.permissionLiveData.observe(this, Observer { r: PermissionResult ->
// do something with permission results
})
}
private fun askContactsPermissions() {
viewModel.checkPermissions(Manifest.permission.READ_CONTACTS)
}
Screen rotations
Library has support for screen rotations.
To avoid memory leaks, all Coroutines that have not completed yet, should be cancelled in the onDestroy function.
When you detect a orientation change, cancel the Job of a CoroutineScope with an instance of ActivityRotatingException. Internally, this will retain the current request that is in progress. The request is then resumed with calling resumeRequest method.
Example:
First:
// job that will be cancelled in onDestroy
private val job = Job()
private fun requestPermission(vararg permissions: String) {
launch {
val result = Peko.requestPermissionsAsync(this@MainActivity, *permissions)
// check granted permissions
}
}
Then in onDestroy of an Activity:
if (isChangingConfigurations) {
job.cancel(ActivityRotatingException()) // screen rotation, retain the results
} else {
job.cancel() // no rotation, just cancel the Coroutine
}
And when this Activity gets recreated in one of the Activity lifecycle functions, e.g.onCreate:
// check if we have a request already (or some other way you detect screen orientation)
if (Peko.isRequestInProgress()) {
launch {
// get the existing request and await the result
val result = Peko.resumeRequest()
// check granted permissions
}
}
LiveData and screen rotations
You don't have to do anything, this logic is already inside the PermissionsLiveData class.
You just have to call observe in the onCreate method and of course use androidx.lifecycle.ViewModel.
What is new
Peko Version 2 now uses Android X packages, Kotlin v1.5.30 and Coroutines 1.5.2.
Breaking changes from Peko Version 1.0
-
PermissionRequestResultis renamed toPermissionResultand is now a sealed class.PermissionResulthas a sealed class hierarchy of following types:PermissionResult.Granted-> returned when all requested permissions were grantedPermissionResult.Denied-> returned when at least one of the permissions was deniedPermissionResult.Denied.NeedsRationale-> subclass ofPermissionResult.Denied, returned when Android OS signals that at least one of the permissions needs to show a rationalePermissionResult.Denied.DeniedPermanently-> subclass ofPermissionResult.Denied, returned when no permissions need a Rationale and at least one of the permissions has a ticked Do Not Ask Again check boxPermissionResult.Denied.JustDenied-> subclass ofPermissionResult.Denied, returned when previous two cases are not the cause, for example if you forget to register the Permission in AndroidManifestPermissionResult.Cancelled-> returned when Android System cancels the request, ie returned -
PermissionRationaleinterface was removed. Library does not show Permission Rationales anymore. You can check now ifPermissionResultis of typePermissionResult.NeedsRationaleand implement the rationale yourself. -
Added support for requesting permissions with LiveData
Peko Version 1.0 uses AppCompat libraries and is here.
License
Copyright 2022 Marko Devcic
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.
