I have following permission check in Jetpack Compose :
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
if (permissions.values.first { true }) {
log.info("Permission is granted")
shouldDisplayQuestionDialog.value = true
} else {
log.info("Permission is not granted")
}
}
When I deny permission, Permission is not granted log called once.
Now I change the code to this :
val displayEmptyScreen = remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
if (permissions.values.first { true }) {
log.info("Permission is granted")
shouldDisplayQuestionDialog.value = true
} else {
log.info("Permission is not granted")
displayEmptyScreen.value = true
}
}
Now Permission is not granted log called twice after adding the mutableStateOf
. Why is that? How to make it call once?
ADDENDA
@Composable
private fun DisplayPermissionDialog(shouldDisplayQuestionDialog: MutableState<Boolean>) {
val displayEmptyScreen = remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
if (permissions.values.first { true }) {
log.info("Permission is granted")
shouldDisplayQuestionDialog.value = true
} else {
log.info("Permission is not granted")
displayEmptyScreen.value = true
}
}
DisplayQuestionAlertDialog(shouldDisplayQuestionDialog)
if (displayEmptyScreen.value) {
Box {
// This box works as background
Box(
modifier = Modifier
.matchParentSize()
.background(Color.White)
)
}
}
val context = LocalContext.current
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_EXTERNAL_STORAGE
) -> {
LaunchedEffect(Unit) {
log.info("Set State for mutableStateFlow")
viewModel.setState()
}
DisplayQuestionAlertDialog(shouldDisplayQuestionDialog)
}
else -> {
Handler(Looper.getMainLooper()).post {
// Asking for permission
launcher.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
)
}
}
}
}
CodePudding user response:
Your code guarantees that DisplayPermissionDialog()
will be called at least twice, if you do not hold the permission at the time that you called DisplayPermissionDialog()
initially.
The first time through, if you do not hold permission, you will go into the else
branch of your when()
, and you will use your launcher
to bring up a system permission dialog. That will eventually trigger your lambda on launcher
, which mutates state whether you were granted permission or not.
Since DisplayPermissionDialog()
reads that same state, DisplayPermissionDialog()
will be called a second time (the function is "recomposed"). And, on that second time through if you do not hold permission, you will go into the else
branch of your when()
, and you will use your launcher
to bring up a system permission dialog. That will eventually trigger your lambda on launcher
, which mutates state whether you were granted permission or not.
Frankly, I would expect an infinite chain of recompositions once you get to the point where Android stops showing the system permission dialog.
Either:
Do not mutate state that this function reads when the user rejects your permission request, or
Arrange it such that you do not call
launch()
on yourlauncher
on every call of this function when you do not hold permission
I cannot give you more specific advice, just because this is not my function and I do not fully understand what you're trying to do here.
For more, you might want to watch this screencast on runtime permissions in Compose UI. Or, try Accompanist's permission composables — see this Medium post for more on that library.