Learn-Jetpack-Compose-By-Example
Learn-Jetpack-Compose-By-Example copied to clipboard
Add Example that requests permissions from a composable
It is apparent how we could request permissions from the MainActivity. However what if we are several layers deep in our app, how would we request runtime permission (such as location)?
Thanks for the request! This would be a very useful example so I'll add this soon. In the meantime, if you need something sooner, here's a related discussion - https://kotlinlang.slack.com/archives/CJLTWPH7S/p1590597865122200
Hi @vinaygaba ,
I did something like this:
class PermissionHandler(
private val activity: AppCompatActivity
) {
fun requestPermission(
permission: String,
onResultReceived: (PermissionResult) -> Unit,
) {
val requestPermissionLauncher = activity.registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) { granted ->
onResultReceived(
if (granted)
PermissionResult.Granted
else
PermissionResult.Denied
)
}
when {
ContextCompat.checkSelfPermission(
activity,
permission
) == PackageManager.PERMISSION_GRANTED -> {
onResultReceived(PermissionResult.Granted)
}
ActivityCompat.shouldShowRequestPermissionRationale(
activity,
permission
) -> {
onResultReceived(PermissionResult.ShowRationale)
}
else -> requestPermissionLauncher.launch(permission)
}
}
}
@Composable
fun requestPermission(
permission: String,
): State<PermissionResult> {
val permissionHandler = AmbientPermissionHandler.current
val permissionResult = remember(permission) {
mutableStateOf<PermissionResult>(PermissionResult.Requesting)
}
remember(permission) {
permissionHandler.requestPermission(permission) {
permissionResult.value = it
}
}
return permissionResult
}
sealed class PermissionResult {
object Requesting : PermissionResult()
object ShowRationale : PermissionResult()
object Denied : PermissionResult()
object Granted : PermissionResult()
}
val AmbientPermissionHandler: ProvidableAmbient<PermissionHandler> =
staticAmbientOf { throw IllegalStateException("permission handler is not initialized") }
and then provide PermissionHandler as an ambient like so:
Providers(AmbientPermissionHandler provides permissionHandler) {
....
}
My idea was to be able to use it like:
val permissionResult by requestPermission(permission = Manifest.permission.CAMERA)
when (missionResult) {
PermissionResult.Requesting -> Text("Requesting")
PermissionResult.ShowRationale -> Text("Show Rationale")
PermissionResult.Denied -> Text("Denied")
PermissionResult.Granted -> FragmentContainer(fragment = SelfieFragment())
}
The problem is when I get to the Composable where the code above is located, I get the error:
LifecycleOwner ... is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
at androidx.activity.result.ActivityResultRegistry.register
...
Not really sure how to solve the issue. It seems to me that at the point of a @Composable the lifecycle is already at LifecycleState.RESUMED and so registerForActivityResult(...) throws the error.
I was wondering whether you've already implemented permissions and maybe have some idea on how to solve this or do it a different way?
@dri94 I am aware of this gist by one of the developers on Compose that shows how they are thinking about permission handling - https://gist.github.com/objcode/775fe45127fd40f17932f672ee203f72#file-permissions-kt-L78
I think you tagged the wrong person but Thanks! It was the right call to ask about it here. Thank you very much.
Oh my bad 🙈
Update: You should probably be using Accompanist for permission support in Compose - https://github.com/google/accompanist/tree/main/permissions
I'd like to work on this issue, please assign it to me. @vinaygaba