Permissions in Jetpack Compose
Permissions in Android just got a lot easier to manage thanks to Compose and the Google Accompanist library. Now we can create a composable function to wrap any other function that might need to access permissions easily like this:
AcceptPermissions {
MainScreen()
}
Add the following dependency in your build.gradle (module) file:
implementation "com.google.accompanist:accompanist-permissions:0.23.1"
We then set up a composable function to check our permissions and display a dialog to the user. If the user doesn't accept the permissions, we also create a composable to show if the user denied the permissions.
Permissions.kt
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun AcceptPermissions(content: @Composable () -> Unit) {
val permissions = mutableListOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
permissions.add(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
val permissionsState = rememberMultiplePermissionsState(
permissions = permissions
)
PermissionsRequired(
multiplePermissionsState = permissionsState,
permissionsNotGrantedContent = {
LaunchedEffect(permissionsState) {
permissionsState.launchMultiplePermissionRequest()
}
},
permissionsNotAvailableContent = {
PermissionsNotAvailableScreen(permissionsState = permissionsState)
}
) {
content()
}
}
Here, we declare our permissions inside of a list. In this case, we must declare an additional permission if we are on Android 10 (Q) or above, since we will now need a permission to access our background location on those versions. It is simple to check the version and add it as an additional permission in our list.
Next, we set our permissions state to pass to our PermissionsRequired() function. If you have only one permission to check, you can use rememberPermissionState() and PermissionRequired() instead of the functions for multiple permissions.
PermissionsRequired() takes three parameters here, plus the content parameter block:
multiplePermissionsState: The state variable we declared above containing our list of permissions.
permissionsNotGrantedContent: The content to show if permissions have yet to be granted by the user. Here, we use a LaunchedEffect with the key set as our permissionsState and launch our permission request with permissionsState.launchMultiplePermissionRequest(). This will prompt the user to accept or deny the permissions set in your permissions list. If you just have one permission using rememberPermissionState() instead, you can launch one permission request with permissionState.launchPermissionRequest().
permissionsNotAvailableContent: The content to show if the user has previously denied the permissions. Here, we just show a simple screen with a button that will launch your permissions request on click. Permission requests must always be launched inside of a lambda or coroutine and not inside your composable!
content: The content to show when the permissions are accepted. In this example, we show the composable function we passed into our AcceptPermissions() composable.
Permissions.kt
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun PermissionsNotAvailableScreen(permissionsState: MultiplePermissionsState) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
style = MaterialTheme.typography.bodyMedium,
text = stringResource(id = R.string.text_permissions),
textAlign = TextAlign.Center,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Button(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 16.dp),
onClick = { permissionsState.launchMultiplePermissionRequest() }
) {
Text(
style = MaterialTheme.typography.labelMedium,
text = stringResource(id = R.string.button_permissions),
fontSize = 20.sp
)
}
}
}
Now that everything is set up, we can call our AcceptPermissions() composable and wrap any content that needs permissions. Here, we just wrap the MainContent() composable of our app inside MainActivity. In a finished app you should ask for specific permissions right before they need to be granted at runtime as specified in Google's documentation.
https://developer.android.com/training/location/permissions
MainActivity.kt
AcceptPermissions {
MainScreen()
}
Finally, don't forget to add these permissions to your app's manifest.
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
That's it! Happy coding!
Comments
Post a Comment