permissionsBuilder .sendSuspend() not completing first time
GuilhE opened this issue ยท 31 comments
Description
Using version 3.2.0, after given Location Permissions, sendSuspend()
doesn't return, in other words, my app freezes the UI (it doesn't get rendered because it needs location permission and since sendSuspend()
doesn't complete I can't query result.allGranted()
and proceed with logic.
If I navigate back and then forward it works (the permission wore granted and no pop-up is shown, I can query result.allGranted()
happily and carry on).
If I downgrade to 3.1.3 it works as expected.
KPermissions version:
3.2.0
API level:
SDK 30, Google Pixel 3a Android 11
How to reproduce it:
Clear app cache and run it so that permissions are prompt again.
Sample code:
lifecycleScope.launchWhenResumed {
val result =
permissionsBuilder(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION).build().sendSuspend()
if (result.allGranted()) { ... }
}
@GuilhE I'll try to reproduce it, thanks.
In the meanwhile, can you try to call in your Application
the method useLegacyRuntimePermissionHandler()
using the version 3.2.0 and tell me if it fixes your problem?
As another try, can you try adding the following class to your project using the version 3.2.0?
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.fondesa.kpermissions.PermissionStatus
import com.fondesa.kpermissions.allGranted
import com.fondesa.kpermissions.extension.isPermissionGranted
import com.fondesa.kpermissions.request.runtime.RuntimePermissionHandler
import com.fondesa.kpermissions.request.runtime.RuntimePermissionHandlerProvider
internal class CustomRuntimePermissionHandlerProvider(private val manager: FragmentManager) : RuntimePermissionHandlerProvider {
@RequiresApi(23)
override fun provideHandler(): RuntimePermissionHandler {
var fragment = manager.findFragmentByTag(FRAGMENT_TAG) as? RuntimePermissionHandler
if (fragment == null) {
fragment = CustomRuntimePermissionsHandler()
manager.beginTransaction()
.add(fragment, FRAGMENT_TAG)
.commitAllowingStateLoss()
}
return fragment
}
@RequiresApi(23)
class CustomRuntimePermissionsHandler : Fragment(), RuntimePermissionHandler {
private val resultLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions(),
::onPermissionsResult
)
private val listeners = mutableMapOf<Set<String>, RuntimePermissionHandler.Listener>()
private var pendingHandleRuntimePermissions: (() -> Unit)? = null
private var pendingPermissions: Array<out String>? = null
override fun onAttach(context: Context) {
super.onAttach(context)
pendingHandleRuntimePermissions?.invoke()
pendingHandleRuntimePermissions = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (pendingPermissions == null) {
pendingPermissions = savedInstanceState?.getStringArray(KEY_PENDING_PERMISSIONS)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putStringArray(KEY_PENDING_PERMISSIONS, pendingPermissions)
}
override fun attachListener(permissions: Array<out String>, listener: RuntimePermissionHandler.Listener) {
listeners[permissions.toSet()] = listener
}
override fun handleRuntimePermissions(permissions: Array<out String>) {
if (isAdded) {
handleRuntimePermissionsWhenAdded(permissions)
} else {
pendingHandleRuntimePermissions = { handleRuntimePermissionsWhenAdded(permissions) }
}
}
@Suppress("OverridingDeprecatedMember")
override fun requestRuntimePermissions(permissions: Array<out String>) {
pendingPermissions = permissions
Log.d(TAG, "requesting permissions: ${permissions.joinToString()}")
resultLauncher.launch(permissions)
}
private fun handleRuntimePermissionsWhenAdded(permissions: Array<out String>) {
val listener = listeners[permissions.toSet()] ?: return
val activity = requireActivity()
val currentStatus = activity.checkRuntimePermissionsStatus(permissions.toList())
val areAllGranted = currentStatus.allGranted()
if (!areAllGranted) {
if (pendingPermissions != null) return
@Suppress("DEPRECATION")
requestRuntimePermissions(permissions)
} else {
listener.onPermissionsResult(currentStatus)
}
}
private fun onPermissionsResult(permissionsResult: Map<String, Boolean>) {
val pendingPermissions = pendingPermissions ?: return
this.pendingPermissions = null
val listener = listeners[pendingPermissions.toSet()] ?: return
val result = permissionsResult.map { (permission, isGranted) ->
when {
isGranted -> PermissionStatus.Granted(permission)
shouldShowRequestPermissionRationale(permission) -> PermissionStatus.Denied.ShouldShowRationale(permission)
else -> PermissionStatus.Denied.Permanently(permission)
}
}
listener.onPermissionsResult(result)
}
private fun Activity.checkRuntimePermissionsStatus(permissions: List<String>): List<PermissionStatus> =
permissions.map { permission ->
if (isPermissionGranted(permission)) {
return@map PermissionStatus.Granted(permission)
}
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
PermissionStatus.Denied.ShouldShowRationale(permission)
} else {
PermissionStatus.RequestRequired(permission)
}
}
companion object {
private val TAG = CustomRuntimePermissionHandlerProvider::class.java.simpleName
private const val KEY_PENDING_PERMISSIONS = "pending_permissions"
}
}
companion object {
private const val FRAGMENT_TAG = "CustomPermissionsFragment"
}
}
After doing that, you should integrate it with:
lifecycleScope.launchWhenResumed {
val result = permissionsBuilder(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)
// Use supportFragmentManager in an Activity or requireActivity().supportFragmentManager in a Fragment.
.runtimeHandlerProvider(CustomRuntimePermissionHandlerProvider(supportFragmentManager))
.build()
.sendSuspend()
if (result.allGranted()) { ... }
}
@GuilhE I'll try to reproduce it, thanks.
In the meanwhile, can you try to call in your
Application
the methoduseLegacyRuntimePermissionHandler()
using the version 3.2.0 and tell me if it fixes your problem?
Yup adding that line solves the problem
@GuilhE Perfect, when you have time, can you do the other try I suggested? If that second try works, I'll release a fix soon
@GuilhE Perfect, when you have time, can you do the other try I suggested? If that second try works, I'll release a fix soon
Already doing it ๐
Btw, I have other permissions (RECORD_AUDIO and CAMERA) and I don't have this problem with them ๐ค
Using CustomRuntimePermissionHandlerProvider
doesn't solve.
Btw, I have other permissions (for Camera and Gallery) and I don't have this problem with them, maybe because they open other activities? ๐ค
Are you using launchWithResumed
also to send those requests or you are sending them after an event (e.g. a user click)?
Maybe that's the difference between them.
Using CustomRuntimePermissionHandlerProvider doesn't solve.
This is strange, I expected that it would have solved the problem.
I've tried to reproduce your same behavior in the sample of this library and I hadn't any problem with launchWhenResumed
but I had it with launchWhenCreated
and using the class I pasted above should have solved the problem.
I'll investigate more about it.
In case I won't be able to reproduce it, would you be able to provide a sample in which the same error happens? (even using the sample provided by this library if you want)
Until this problem is not solved, you can use useLegacyRuntimePermissionHandler()
which opts-in the behavior of the version 3.1.3.
Are you using launchWithResumed also to send those requests or you are sending them after an event (e.g. a user click)?
Maybe that's the difference between them.
Yes I forgot, the use case is quite different, for CAMERA and RECORD_AUDIO is upon user interaction (click). For the location it's "onCreate".
Unfortunately I'm not able to provide access to the code but it's quite simple I'll explain the use case. I've an Activity
with a ViewPager
. Only 2 pages are being created (Fragments
) the first is a List of locations and the second a Map with markers.
MapFragment:
override fun onMapReady(map: GoogleMap) {
with(map) {
...
lifecycleScope.launchWhenResumed {
val result = permissionsBuilder(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)
.runtimeHandlerProvider(CustomRuntimePermissionHandlerProvider(requireActivity().supportFragmentManager))
.build()
.sendSuspend()
if (result.allGranted()) {
map.isMyLocationEnabled = true
...
}
...
}
}
}
ListFragment:
override fun initViews(binding: FragmentListBinding, viewModel: ViewModel) {
...
lifecycleScope.launchWhenResumed {
val result = permissionsBuilder(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)
.runtimeHandlerProvider(CustomRuntimePermissionHandlerProvider(requireActivity().supportFragmentManager))
.build()
.sendSuspend()
if (result.allGranted()) {
...
}
}
}
I'll investigate more about it.
Feel free to ask for any help with testing ๐
I'll try to reproduce a similar scenario, in case I'll ask you for some help on this one, thanks!
I tried with a similar configuration as yours but unfortunately I couldn't reproduce the problem yet.
Can you add the following logs and tell me how and if they are printed when you grant the permissions?
Right before lifecycleScope.launchWhenResumed
in the map fragment:
Log.d("KPermissions", "before launchWhenResumed - map")
Right before lifecycleScope.launchWhenResumed
in the list fragment:
Log.d("KPermissions", "before launchWhenResumed - list")
After sendSuspend
in the map fragment:
Log.d("KPermissions", "after sendSuspend - map")
After sendSuspend
in the list fragment:
Log.d("KPermissions", "after sendSuspend - list")
This because I see few problems here:
KPermissions
allows max 1 permission request at the same time, in the sample you have provided, you are sending two different requests at the same time, so one of them shouldn't receive the result- You are using
lifecycleScope
instead ofviewLifecycleOwner.lifecycleOwner
in a fragment
This is what's printed when I use useLegacyRuntimePermissionHandler()
regardless of my choice:
2021-03-18 09:24:48.419 D/KPermissions: before launchWhenResumed - list
2021-03-18 09:24:48.428 D/KPermissions: after sendSuspend - list
2021-03-18 09:24:49.084 D/KPermissions: before lifecycleScope.launchWhenResumed - map
2021-03-18 09:24:57.239 D/KPermissions: after sendSuspend - map
note: after sendSuspend - map
is fired only when I switch page, and in this case I granted the permissions in the first page.
note 2: the log only gets printed after I made a choice ๐ค
When I don't use useLegacyRuntimePermissionHandler()
nothing gets printed until I go back and forth (like expalined above). Except when I deny permissions (forever). Then it will never reach if (result.allGranted())
until I go to app settings / permissions and change it.
KPermissions allows max 1 permission request at the same time, in the sample you have provided, you are sending two different requests at the same time, so one of them shouldn't receive the result
When you say "1 permission request at the same time" you mean I can't call .sendSuspend()
simultaneous right?
Well it worked without any problem so far because as you can see by the log, .sendSuspend()
aren't called simultaneous.
You are using lifecycleScope instead of viewLifecycleOwner.lifecycleOwner in a fragment
Nice catch! My observables are using viewLifecycleOwner.lifecycleOwner
, I missed this one. Changed it but the behaviour didn't change.
Both of these sound strange.
note 2: the log only gets printed after I made a choice ๐ค
When I don't use useLegacyRuntimePermissionHandler() nothing gets printed until I go back and forth (like expalined above).
Just to be sure I explained it right before, I expect the logs to be in this position:
override fun initViews(binding: FragmentListBinding, viewModel: ViewModel) {
Log.d("KPermissions", "before launchWhenResumed - list")
lifecycleScope.launchWhenResumed {
val result = permissionsBuilder(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)
.runtimeHandlerProvider(CustomRuntimePermissionHandlerProvider(requireActivity().supportFragmentManager))
.build()
.sendSuspend()
Log.d("KPermissions", "after sendSuspend - list")
if (result.allGranted()) {
...
}
}
}
When you say "1 permission request at the same time" you mean I can't call .sendSuspend() simultaneous right?
Well it worked without any problem so far because as you can see by the log, .sendSuspend() aren't called simultaneous.
You are right, only one of the two fragments is being resumed so the requests aren't simultaneous, so both the requests should deliver a result when launched.
Yup, a strange behaviour indeed.
Yes the logs were positioned as you asked but I had the CustomRuntimePermissionHandlerProvider
removed. Should I try again with it?
This library doesn't change anything in the container (activity or fragment) before the first call to permissionsBuilder()
is invoked.
Are you creating a permission request before your calls to launchWhenResumed
? Or you are maybe creating the request outside it and sending it inside it?
Yes the logs were positioned as you asked but I had the CustomRuntimePermissionHandlerProvider removed. Should I try again with it?
It shouldn't make any difference.
I'm creating and calling inside viewLifecycleOwner.lifecycleScope.launchWhenResume
as follow:
override fun initViews(binding: FragmentListBinding, viewModel: ViewModel) {
with(binding.myRecyclerView) {
...
}
viewModel.someData.observe(viewLifecycleOwner, {
...
})
viewLifecycleOwner.lifecycleScope.launchWhenResumed {
val result = permissionsBuilder(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)
.build()
.sendSuspend()
if (result.allGranted()) {
//fetch data and populate UI
} else showWarning()
}
}
There should be something else which impacts your code, because this library doesn't do absolutely anything before your first call to permissionsBuilder()
.
I guess we have two solutions here since I can't reproduce it:
- you can try to provide a sample in which you can reproduce the bug
- we need to "debug" it together since there's something strange happening
For the second solution, we can start trying to put a breakpoint inside the extensions permissionsBuilder()
of this library. You can see where is it called from. Is it called from the list fragment, from the map fragment or from a 3rd caller we don't know yet?
Well I didn't change this piece of code for a long time and it was working, only thing I was doing was updating some libs (yours inclusive). Can it be a combination with a google lib that's causing this?
const val activity_ktx: String = "1.2.0" > "1.2.1"
const val fragment_ktx: String = "1.3.0" > "1.3.1"
const val kpermissions: String = "3.1.3" > "3.2.0"
Last time I've updated kpermission
was in ~20/11/2020 since then a few updates from activity_ktx
and fragment_ktx
were released ๐ค
I'll try to downgrade and test.
First, we can ensure KPermissions
is involved with the debug try I wrote above.
I expect that method to be called.
If it's not called, something else is happening.
I expect this to be a bug in my lib since using useLegacyRuntimePermissionHandler()
fixes the problem.
I think it could be called somewhere else before your the snippets you posted from your list and map fragments.
There should be something else which impacts your code, because this library doesn't do absolutely anything before your first call to
permissionsBuilder()
.I guess we have two solutions here since I can't reproduce it:
1. you can try to provide a sample in which you can reproduce the bug 2. we need to "debug" it together since there's something strange happening
For the second solution, we can start trying to put a breakpoint inside the extensions
permissionsBuilder()
of this library. You can see where is it called from. Is it called from the list fragment, from the map fragment or from a 3rd caller we don't know yet?
Ok so here it goes:
- ListFragment calls
.sendSuspend()
- permission builder uses
ResultLauncherRuntimePermissionHandlerProvider(supportFragmentManager)
ResultLauncherRuntimePermissionHandlerProvider
creates Fragment delegated to handle permissionsSuspendExtensions
callsPermissionRequest.sendSuspend()
ResultLauncherRuntimePermissionHandler.handleRuntimePermissions
callspendingHandleRuntimePermissions
becauseisAdded = false
- Permission dialog is shown and I choose Allow while using app.
ResultLauncherRuntimePermissionHandler.onPermissionsResult
it's called but the breakpoint at line 98 and 105 doesn't get called.- I go back and forth, everything runs the same but now
PermissionRequest.Listener.onPermissionsResult
it's called with result - And
(result.allGranted())
is reached
So I guess this is the line to blame: val pendingPermissions = pendingPermissions ?: return
@VisibleForTesting
fun onPermissionsResult(permissionsResult: Map<String, Boolean>) {
val pendingPermissions = pendingPermissions ?: return
// Now the Fragment is not processing the permissions anymore.
this.pendingPermissions = null
shouldn't you be querying permissionsResult
? ๐ค
Second time works because of this:
private fun handleRuntimePermissionsWhenAdded(permissions: Array<out String>) {
// Get the listener for this set of permissions.
// If it's null, the permissions can't be notified.
val listener = listeners[permissions.toSet()] ?: return
val activity = requireActivity()
val currentStatus = activity.checkRuntimePermissionsStatus(permissions.toList()) <-------
val areAllGranted = currentStatus.allGranted() <-------
if (!areAllGranted) {
if (pendingPermissions != null) {
// The Fragment can process only one request at the same time.
return
}
// Request the permissions.
@Suppress("DEPRECATION")
requestRuntimePermissions(permissions)
} else {
listener.onPermissionsResult(currentStatus) <-------
}
}
Mystery solved ๐
Thanks for the deep debugging, this indeed helps.
shouldn't you be querying permissionsResult? ๐ค
I do after, but pendingPermissions
was needed because there was a bug in AndroidX Fragment 1.3.0 for which some permissions weren't contained in the result.
So, I guess that pendingPermissions
is null
. Can you verify when that class makes again pendingPermissions
as null
? I guess it could be in the method onCreate()
.
In the meanwhile, I'll try to see if the bug is completely solved with the new version of AndroidX Fragment 1.3.1.
So, I guess that
pendingPermissions
isnull
. Can you verify when that class makes againpendingPermissions
asnull
? I guess it could be in the methodonCreate()
.
Exactly ๐
Strange it wasn't solved with the provider I added above, because that was the bug I fixed.
Can you try with the following one?
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.fondesa.kpermissions.PermissionStatus
import com.fondesa.kpermissions.allGranted
import com.fondesa.kpermissions.extension.isPermissionGranted
import com.fondesa.kpermissions.request.runtime.RuntimePermissionHandler
import com.fondesa.kpermissions.request.runtime.RuntimePermissionHandlerProvider
internal class CustomRuntimePermissionHandlerProvider(private val manager: FragmentManager) : RuntimePermissionHandlerProvider {
@RequiresApi(23)
override fun provideHandler(): RuntimePermissionHandler {
var fragment = manager.findFragmentByTag(FRAGMENT_TAG) as? RuntimePermissionHandler
if (fragment == null) {
fragment = CustomRuntimePermissionsHandler()
manager.beginTransaction()
.add(fragment, FRAGMENT_TAG)
.commitAllowingStateLoss()
}
return fragment
}
@RequiresApi(23)
class CustomRuntimePermissionsHandler : Fragment(), RuntimePermissionHandler {
private val resultLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions(),
::onPermissionsResult
)
private val listeners = mutableMapOf<Set<String>, RuntimePermissionHandler.Listener>()
private var pendingHandleRuntimePermissions: (() -> Unit)? = null
private var isProcessingPermissions = false
override fun onAttach(context: Context) {
super.onAttach(context)
pendingHandleRuntimePermissions?.invoke()
pendingHandleRuntimePermissions = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
isProcessingPermissions = savedInstanceState?.getBoolean(KEY_IS_PROCESSING_PERMISSIONS) ?: false
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(KEY_IS_PROCESSING_PERMISSIONS, isProcessingPermissions)
}
override fun attachListener(permissions: Array<out String>, listener: RuntimePermissionHandler.Listener) {
listeners[permissions.toSet()] = listener
}
override fun handleRuntimePermissions(permissions: Array<out String>) {
if (isAdded) {
handleRuntimePermissionsWhenAdded(permissions)
} else {
pendingHandleRuntimePermissions = { handleRuntimePermissionsWhenAdded(permissions) }
}
}
@Suppress("OverridingDeprecatedMember")
override fun requestRuntimePermissions(permissions: Array<out String>) {
isProcessingPermissions = true
Log.d(TAG, "requesting permissions: ${permissions.joinToString()}")
resultLauncher.launch(permissions)
}
private fun handleRuntimePermissionsWhenAdded(permissions: Array<out String>) {
val listener = listeners[permissions.toSet()] ?: return
val activity = requireActivity()
val currentStatus = activity.checkRuntimePermissionsStatus(permissions.toList())
val areAllGranted = currentStatus.allGranted()
if (!areAllGranted) {
if (isProcessingPermissions) return
@Suppress("DEPRECATION")
requestRuntimePermissions(permissions)
} else {
listener.onPermissionsResult(currentStatus)
}
}
private fun onPermissionsResult(permissionsResult: Map<String, Boolean>) {
isProcessingPermissions = false
val listener = listeners[permissionsResult.keys] ?: return
val result = permissionsResult.map { (permission, isGranted) ->
when {
isGranted -> PermissionStatus.Granted(permission)
shouldShowRequestPermissionRationale(permission) -> PermissionStatus.Denied.ShouldShowRationale(permission)
else -> PermissionStatus.Denied.Permanently(permission)
}
}
listener.onPermissionsResult(result)
}
private fun Activity.checkRuntimePermissionsStatus(permissions: List<String>): List<PermissionStatus> =
permissions.map { permission ->
if (isPermissionGranted(permission)) {
return@map PermissionStatus.Granted(permission)
}
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
PermissionStatus.Denied.ShouldShowRationale(permission)
} else {
PermissionStatus.RequestRequired(permission)
}
}
companion object {
private val TAG = CustomRuntimePermissionHandlerProvider::class.java.simpleName
private const val KEY_IS_PROCESSING_PERMISSIONS = "is_processing_permissions"
}
}
companion object {
private const val FRAGMENT_TAG = "CustomPermissionsFragment"
}
}
It works!
Perfect!! Thanks!
Can you still try using the first CustomRuntimePermissionHandlerProvider
telling me what happens there? Because that should have solved the bug of pendingPermissions
reset in onCreate()
It also works.
But now I realise we had a false positive in the first time because while I was testing I moved the permission to the Activity that hosts both fragments and then I forgot to remove the code from the Activity. Note that I wasn't calling permission twice because the fragments weren't being added since (result.allGranted())
in the Activity wasn't being called either.
So, If I leave the code in the Activity both CustomRuntimePermissionHandlerProvider
won't work, but if move that logic to the fragments initViews, it works as expected (with both).
Note that if I downgrade to 3.1.3 or use useLegacyRuntimePermissionHandler()
it works with the code also in the Activity.
but if move that logic to the fragments initViews, it works as expected (with both).
Perfect, thanks. I'm fixing this and release a new version after.
So, If I leave the code in the Activity both CustomRuntimePermissionHandlerProvider won't work
So, now you have fragment list, fragment map and their activity.
- Where did you sent the permission request? Only in the activity or in all the 3 places?
- Did you use the
CustomRuntimePermissionHandlerProvider
in all these 3 places? - How do you send the request in the activity? With
lifecycleScope.launch { }
inonCreate()
or how?
Sorry if this issue is becoming a bit painful but I don't want to ship a partially-working version
So, If I leave the code in the Activity both CustomRuntimePermissionHandlerProvider won't work
So, now you have fragment list, fragment map and their activity.
1. Where did you sent the permission request? Only in the activity or in all the 3 places?
Only in the Activity, because the Permission Dialog shows up and the ListFragment wouldn't reach onResume
state so launchWhenResumed
wouldn't launch.
2. Did you use the `CustomRuntimePermissionHandlerProvider` in all these 3 places?
Yes except in the Activity (because I forgot to remove the code).
But now that you asked I've added .runtimeHandlerProvider(CustomRuntimePermissionHandlerProvider(supportFragmentManager))
in the Activity and it worked.
3. How do you send the request in the activity? With `lifecycleScope.launch { }` in `onCreate()` or how?
lifecycleScope.launchWhenResumed same logic as in Fragments
Sorry if this issue is becoming a bit painful but I don't want to ship a partially-working version
Of course, no problem ๐
Your CustomRuntimePermissionHandlerProvider
solves this problem.
Perfect, thanks for your great help for this issue, I wouldn't be able to spot and fix this issue alone.
I'll release a fix soon. Until that moment I'm leaving this issue open also for the other users.
I'm the one who thanks all your effort to provide us with this awesome lib. The least I can do is to help :)
It's fixed!