I need to request POST_NOTIFICATIONS, and READ_PHONE_STATE
This:
@Composable
fun canPushNotifications () {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission()
) {"""on permission granted"""}
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(lifecycleOwner) {
lifecycleOwner.lifecycle.withStarted {
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
}
}
@Composable
fun canReadPhoneState (): Boolean {
val context = LocalContext.current
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(context as Activity, arrayOf(Manifest.permission.READ_PHONE_STATE), 1)
}
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_PHONE_STATE
) == PackageManager.PERMISSION_GRANTED
}
and this:
@Composable
fun canPushNotifications(): Boolean {
val context = LocalContext.current
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(context as Activity, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
}
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
} else {
return true
}
}
@Composable
fun canReadPhoneState (): Boolean {
val context = LocalContext.current
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(context as Activity, arrayOf(Manifest.permission.READ_PHONE_STATE), 1)
}
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_PHONE_STATE
) == PackageManager.PERMISSION_GRANTED
}
In both these sets of functions, permission 2 shows up only when a: permission 1 has been granted and b: the app has been restarted. a always happens first and b, last.
Version 1: the two permission request functions are structurally different. canPushNotifications has a callback, and a "Wait for lifecycle"
Function call order: POST_NOTIFICATIONS, then READ_PHONE_STATE
behavior: READ_PHONE_STATE first, app restart, then POST_NOTIFICATIONS Them being reversed makes sense, because of the LifeCycleOwner part. what doesn't make sense is the part where I have to re-launch the app to see the second permission dialogue
Version 2: both permission request functions are structurally identical.
Function call order: same
behavior: POST_NOTIFICATIONS first, app restart, then READ_PHONE_STATE. I still have to restart the app to see the second permission request dialogue, but at least they're in order.
In both versions of MainActivity.kt (the file this code is from), the order in which these two functions are called does not change.
canPushNotification()
canReadPhoneState()
why do I need to restart the app to view the second permission request?
How do I fix this?
I am available to respond 7 days a week
You can try like the following options
@Composable
fun RequestAllPermissions(onComplete: (Map<String, Boolean>) -> Unit) {
val requiredPermissions = buildList {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
add(Manifest.permission.POST_NOTIFICATIONS)
}
add(Manifest.permission.READ_PHONE_STATE)
}
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
// This callback fires once with results for ALL permissions
// permissions is a Map<String, Boolean> like:
// {"POST_NOTIFICATIONS" -> true, "READ_PHONE_STATE" -> false}
onComplete(permissions)
}
LaunchedEffect(Unit) {
launcher.launch(requiredPermissions.toTypedArray())
}
}
// Usage in your MainActivity Composable:
@Composable
fun MainActivityContent() {
var permissionsChecked by remember { mutableStateOf(false) }
var notificationsGranted by remember { mutableStateOf(false) }
var phoneStateGranted by remember { mutableStateOf(false) }
if (!permissionsChecked) {
RequestAllPermissions { permissionResults ->
notificationsGranted = permissionResults[Manifest.permission.POST_NOTIFICATIONS] ?: true
phoneStateGranted = permissionResults[Manifest.permission.READ_PHONE_STATE] ?: true
permissionsChecked = true
}
} else {
// All permissions processed, show your app content
AppContent(
notificationsEnabled = notificationsGranted,
phoneStateEnabled = phoneStateGranted
)
}
}
@Composable
fun SequentialPermissionRequester(
onAllPermissionsComplete: (Boolean) -> Unit
) {
val context = LocalContext.current as Activity
var currentStep by remember { mutableStateOf(0) } // 0 = notifications, 1 = phone, 2 = done
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
when (currentStep) {
0 -> {
// POST_NOTIFICATIONS result received
currentStep = 1
// Request next permission
launcher.launch(Manifest.permission.READ_PHONE_STATE)
}
1 -> {
// READ_PHONE_STATE result received
currentStep = 2
onAllPermissionsComplete(true)
}
}
}
LaunchedEffect(Unit) {
// Start with first permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
// Skip to phone state on older versions
currentStep = 1
launcher.launch(Manifest.permission.READ_PHONE_STATE)
}
}
}
// Usage:
@Composable
fun MainActivityContent() {
var permissionsComplete by remember { mutableStateOf(false) }
if (!permissionsComplete) {
SequentialPermissionRequester { allGranted ->
permissionsComplete = true
// Handle result (allGranted tells you if the flow completed)
}
} else {
AppContent()
}
}
### Option 3: State-Based Approach with ViewModel (Best for Complex Apps)
class PermissionViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
data class PermissionState(
val notificationsGranted: Boolean = false,
val phoneStateGranted: Boolean = false,
val allChecked: Boolean = false
)
private val _state = MutableStateFlow(PermissionState())
val state: StateFlow<PermissionState> = _state.asStateFlow()
fun setNotificationsGranted(granted: Boolean) {
_state.update { it.copy(notificationsGranted = granted) }
}
fun setPhoneStateGranted(granted: Boolean) {
_state.update { it.copy(phoneStateGranted = granted, allChecked = true) }
}
}
@Composable
fun PermissionHandler(viewModel: PermissionViewModel = hiltViewModel()) {
val state by viewModel.state.collectAsState()
val context = LocalContext.current as Activity
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (!state.notificationsGranted) {
// First permission (POST_NOTIFICATIONS) just completed
viewModel.setNotificationsGranted(isGranted)
// Now request second permission
launcher.launch(Manifest.permission.READ_PHONE_STATE)
} else {
// Second permission (READ_PHONE_STATE) just completed
viewModel.setPhoneStateGranted(isGranted)
}
}
LaunchedEffect(state.allChecked) {
if (!state.allChecked) {
// Start with first permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
viewModel.setNotificationsGranted(true)
launcher.launch(Manifest.permission.READ_PHONE_STATE)
}
}
}
if (state.allChecked) {
// All permissions processed
AppContent(
notificationsEnabled = state.notificationsGranted,
phoneStateEnabled = state.phoneStateGranted
)
}
// Otherwise show loading or nothing
}
Mohimenol Islam Fahim