From 7a9df2156909cee3789efc7593758eedef04024a Mon Sep 17 00:00:00 2001 From: Jacob Ras Date: Thu, 29 Feb 2024 13:41:54 +0100 Subject: [PATCH 1/2] Show `PermissionState` in sample app --- .../src/main/java/com/icerockdev/MainActivity.kt | 14 ++++++++++++-- .../com/icerockdev/library/SampleViewModel.kt | 15 +++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sample/compose-android-app/src/main/java/com/icerockdev/MainActivity.kt b/sample/compose-android-app/src/main/java/com/icerockdev/MainActivity.kt index e42c1c8..d10d7d3 100644 --- a/sample/compose-android-app/src/main/java/com/icerockdev/MainActivity.kt +++ b/sample/compose-android-app/src/main/java/com/icerockdev/MainActivity.kt @@ -4,7 +4,9 @@ import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.Button @@ -17,6 +19,9 @@ import androidx.compose.material.Text import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -29,6 +34,7 @@ import dev.icerock.moko.mvvm.getViewModel import dev.icerock.moko.permissions.DeniedAlwaysException import dev.icerock.moko.permissions.DeniedException import dev.icerock.moko.permissions.Permission +import dev.icerock.moko.permissions.PermissionState import dev.icerock.moko.permissions.PermissionsController import dev.icerock.moko.permissions.compose.BindEffect import kotlinx.coroutines.launch @@ -100,16 +106,20 @@ fun TestScreen(viewModel: SampleViewModel) { LaunchedEffect(true) { viewModel.eventsDispatcher.bind(lifecycleOwner, eventsListener) } + val permissionState by viewModel.permissionState.collectAsState() BindEffect(viewModel.permissionsController) Scaffold(scaffoldState = scaffoldState) { - Box( + Column( modifier = Modifier .fillMaxSize() .padding(it), - contentAlignment = Alignment.Center + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center ) { + Text("Permission state: $permissionState") + Button( onClick = viewModel::onRequestPermissionButtonPressed, content = { Text("Request permission") } diff --git a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/SampleViewModel.kt b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/SampleViewModel.kt index e561af0..433bea5 100755 --- a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/SampleViewModel.kt +++ b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/SampleViewModel.kt @@ -6,7 +6,10 @@ import dev.icerock.moko.mvvm.viewmodel.ViewModel import dev.icerock.moko.permissions.DeniedAlwaysException import dev.icerock.moko.permissions.DeniedException import dev.icerock.moko.permissions.Permission +import dev.icerock.moko.permissions.PermissionState import dev.icerock.moko.permissions.PermissionsController +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch class SampleViewModel( @@ -15,10 +18,12 @@ class SampleViewModel( private val permissionType: Permission ) : ViewModel(), EventsDispatcherOwner { + val permissionState = MutableStateFlow(PermissionState.NotDetermined) + init { viewModelScope.launch { - val startState = permissionsController.getPermissionState(permissionType) - println(startState) + permissionState.update { permissionsController.getPermissionState(permissionType) } + println(permissionState) } } @@ -45,8 +50,10 @@ class SampleViewModel( } catch (deniedException: DeniedException) { eventsDispatcher.dispatchEvent { onDenied(deniedException) } } finally { - permissionsController.getPermissionState(permission) - .also { println("post provide $it") } + permissionState.update { + permissionsController.getPermissionState(permission) + .also { println("post provide $it") } + } } } } From 1ed21083a3d90c9ded92f01f6d52c2024acf42cc Mon Sep 17 00:00:00 2001 From: Jacob Ras Date: Thu, 29 Feb 2024 13:45:31 +0100 Subject: [PATCH 2/2] Add `NotGranted` item to `PermissionState` This fixes the issue with [deny and don't ask again] returning an incorrect state. Unfortunately, it's not possible on Android to determine the difference. One has to keep track of whether a permission has been requested in the app already, so that's up to consumers. --- .../permissions/PermissionsControllerImpl.kt | 6 +++--- .../moko/permissions/PermissionState.kt | 19 +++++++++++++++++++ .../moko/permissions/PermissionsController.kt | 7 ++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/permissions/src/androidMain/kotlin/dev/icerock/moko/permissions/PermissionsControllerImpl.kt b/permissions/src/androidMain/kotlin/dev/icerock/moko/permissions/PermissionsControllerImpl.kt index d8e6562..4b35db3 100755 --- a/permissions/src/androidMain/kotlin/dev/icerock/moko/permissions/PermissionsControllerImpl.kt +++ b/permissions/src/androidMain/kotlin/dev/icerock/moko/permissions/PermissionsControllerImpl.kt @@ -91,10 +91,10 @@ class PermissionsControllerImpl( val resolverFragment: ResolverFragment = getOrCreateResolverFragment(fragmentManager) val isAllRequestRationale: Boolean = permissions.all { - !resolverFragment.shouldShowRequestPermissionRationale(it) + resolverFragment.shouldShowRequestPermissionRationale(it) } - return if (isAllRequestRationale) PermissionState.NotDetermined - else PermissionState.Denied + return if (isAllRequestRationale) PermissionState.Denied + else PermissionState.NotGranted } override fun openAppSettings() { diff --git a/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionState.kt b/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionState.kt index 075241f..414f9b6 100644 --- a/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionState.kt +++ b/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionState.kt @@ -5,8 +5,27 @@ package dev.icerock.moko.permissions enum class PermissionState { + + /** + * Starting state for each permission. + */ NotDetermined, + + /** + * Android-only. This could mean [NotDetermined] or [DeniedAlways], but the OS doesn't + * expose which of the two it is in all scenarios. + */ + NotGranted, + Granted, + + /** + * Android-only. + */ Denied, + + /** + * On Android only applicable to Push Notifications. + */ DeniedAlways } diff --git a/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionsController.kt b/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionsController.kt index 1ac8381..6505e01 100755 --- a/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionsController.kt +++ b/permissions/src/commonMain/kotlin/dev/icerock/moko/permissions/PermissionsController.kt @@ -24,12 +24,13 @@ expect interface PermissionsController { /** * Returns current state of permission. Can be suspended because on - * android detection of Denied/NotDetermined case require binded FragmentManager. + * Android detection of `Denied`/`NotDetermined` requires a bound FragmentManager. * * @param permission state of what permission we want * - * @return current state. On Android can't be DeniedAlways (except push notifications). - * On iOS can't be Denied. + * @return current state. On Android can't be `DeniedAlways` (except push notifications). + * On iOS can't be `Denied`. + * @see PermissionState for a detailed description. */ suspend fun getPermissionState(permission: Permission): PermissionState