Jonas
01/29/2024, 6:45 AMJonas
01/29/2024, 7:19 AMTimo Drick
01/29/2024, 2:29 PMval permissionState = remember { MutablePermissionState(ctx, permission, launcher) }
// observe permission
val permissionCheckerObserver = remember(permissionState.hasPermission) {
LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
permissionState.hasPermission = permission.checkPermission(ctx)
}
}
}
DisposableEffect(lifecycle, permissionCheckerObserver) {
lifecycle.addObserver(permissionCheckerObserver)
onDispose { lifecycle.removeObserver(permissionCheckerObserver) }
}
I think since compose 1.6.0 there is also a new api for lifecycle:
val ctx = LocalContext.current
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = {} // already observed by lifecycle observer
)
val permissionState = remember { MutablePermissionState(ctx, permission, launcher) }
LifecycleResumeEffect(Unit) {
permissionState.hasPermission = permission.checkPermission(ctx)
onPauseOrDispose {
// Maybe nothing todo?
}
}
Please let me know if this work in the split screen use case.Timo Drick
01/29/2024, 2:31 PMenum class ManifestPermission(val permission: String, val minApiLevel: Int) {
@TargetApi(33)
POST_NOTIFICATIONS(Manifest.permission.POST_NOTIFICATIONS, 33),
ACCESS_FINE_LOCATION(Manifest.permission.ACCESS_FINE_LOCATION, 1),
@TargetApi(29)
ACTIVITY_RECOGNITION(Manifest.permission.ACTIVITY_RECOGNITION, 29)
}
/**
* Checks if the permission is granted
*
* Because of PermissionRequired linter check this method must
* be named like check|enforce....Permission otherwise it will not be recognized as permission check.
* See: <https://android.googlesource.com/platform/tools/base/+/studio-master-dev/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionDetector.kt>
*/
fun ManifestPermission.checkPermission(ctx: Context) = if (Build.VERSION.SDK_INT >= minApiLevel) {
ContextCompat.checkSelfPermission(ctx, permission) == PermissionChecker.PERMISSION_GRANTED
} else {
true
}
@Composable
fun rememberPermissionState(permission: ManifestPermission): PermissionState {
val ctx = LocalContext.current
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = {} // already observed by lifecycle observer
)
val permissionState = remember { MutablePermissionState(ctx, permission, launcher) }
LifecycleResumeEffect(Unit) {
permissionState.hasPermission = permission.checkPermission(ctx)
onPauseOrDispose {
// Maybe nothing todo?
}
}
return permissionState
}
interface PermissionState {
val hasPermission: Boolean
fun launchPermissionRequest()
}
private class MutablePermissionState(
ctx: Context,
private val permission: ManifestPermission,
private val launcher: ManagedActivityResultLauncher<String, Boolean>
) : PermissionState {
override var hasPermission by mutableStateOf(permission.checkPermission(ctx))
override fun launchPermissionRequest() = launcher.launch(permission.permission)
}
Jonas
01/30/2024, 5:59 AMval isTopResumedActivity =
LocalContext.current.getActivity()?.topResumedActivityChangedFlow?.collectAsState(true)?.value
// We add a toggling fake permission (true/false) to force a refresh of the permission state.
// This way the user can grant a permission in split screen mode and the app gets the most recent state
// after the user selects the app again.
val permissionState =
rememberMultiplePermissionsState(permissions = getNeededAppPermissions() + isTopResumedActivity.toString())
This does work but does not feel like the best approach. 😉Timo Drick
01/31/2024, 10:39 AMTimo Drick
01/31/2024, 10:40 AMJonas
02/01/2024, 10:00 AMTimo Drick
02/02/2024, 9:23 AMJonas
02/02/2024, 10:14 AM