'How request permissions with Jetpack Compose?

How should be implemented requesting permission from Jetpack Compose View? I'm trying implement application accessing Camera with Jetpack Compose. I tried example from How to get Current state or context in Jetpack Compose Unfortunately example is no longer working with dev06.

        fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
        }
    }
}


Solution 1:[1]

Check out Google Accompanist's Jetpack Compose Permissions.

Bear in mind that, at the time of writing, the API is still considered experimental and will require the @ExperimentalPermissionsApi annotation when used.

Solution 2:[2]

as compose_version = '1.0.0-beta04' and

implementation 'androidx.activity:activity-compose:1.3.0-alpha06'

you can do request permission as simple as this:

@Composable
fun ExampleScreen() {
    val launcher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            // Permission Accepted: Do something
            Log.d("ExampleScreen","PERMISSION GRANTED")

        } else {
            // Permission Denied: Do something
            Log.d("ExampleScreen","PERMISSION DENIED")
        }
    }
    val context = LocalContext.current

    Button(
        onClick = {
            // Check permission
            when (PackageManager.PERMISSION_GRANTED) {
                ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.READ_EXTERNAL_STORAGE
                ) -> {
                    // Some works that require permission
                    Log.d("ExampleScreen","Code requires permission")
                }
                else -> {
                    // Asking for permission
                    launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
                }
            }
        }
    ) {
        Text(text = "Check and Request Permission")
    }
}

Solution 3:[3]

Google has a library called "Accompanist". It has many help libraries and one of them is the Permission Library.

Check: Library: https://github.com/google/accompanist/

Documentation: https://google.github.io/accompanist/permissions/

Example:

Setup in build.gradle file:

repositories {
    mavenCentral()
}

dependencies {

implementation "com.google.accompanist:accompanist-permissions:<latest_version>"

}

Implementation in Code

@Composable
private fun FeatureThatRequiresCameraPermission() {

    // Camera permission state
    val cameraPermissionState = rememberPermissionState(
        android.Manifest.permission.CAMERA
    )

    when (cameraPermissionState.status) {
        // If the camera permission is granted, then show screen with the feature enabled
        PermissionStatus.Granted -> {
            Text("Camera permission Granted")
        }
        is PermissionStatus.Denied -> {
            Column {
                val textToShow = if (cameraPermissionState.status.shouldShowRationale) {
                    // If the user has denied the permission but the rationale can be shown,
                    // then gently explain why the app requires this permission
                    "The camera is important for this app. Please grant the permission."
                } else {
                    // If it's the first time the user lands on this feature, or the user
                    // doesn't want to be asked again for this permission, explain that the
                    // permission is required
                    "Camera permission required for this feature to be available. " +
                        "Please grant the permission"
                }
                Text(textToShow)
                Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
                    Text("Request permission")
                }
            }
        }
    }
}

Solution 4:[4]

/**
 * Composable helper for permission checking
 *
 * onDenied contains lambda for request permission
 *
 * @param permission permission for request
 * @param onGranted composable for [PackageManager.PERMISSION_GRANTED]
 * @param onDenied composable for [PackageManager.PERMISSION_DENIED]
 */
@Composable
fun ComposablePermission(
    permission: String,
    onDenied: @Composable (requester: () -> Unit) -> Unit,
    onGranted: @Composable () -> Unit
) {
    val ctx = LocalContext.current

    // check initial state of permission, it may be already granted
    var grantState by remember {
        mutableStateOf(
            ContextCompat.checkSelfPermission(
                ctx,
                permission
            ) == PackageManager.PERMISSION_GRANTED
        )
    }
    if (grantState) {
        onGranted()
    } else {
        val launcher: ManagedActivityResultLauncher<String, Boolean> =
            rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) {
                grantState = it
            }
        onDenied { launcher.launch(permission) }
    }
}

Solution 5:[5]

A little late but this might help as I had the problem today:

With ContextAmbient.current it is not guaranteed that you have an activity or fragment thus I created my own ambient for handling permissions.

val AmbientPermissionHandler: ProvidableAmbient<PermissionHandler> =
    ambientOf { throw IllegalStateException("permission handler is not initialized") }

// Activity:
private val permissionHandler = PermissionHandler(this)
// onCreate:
setContent {
    Providers(
        AmbientPermissionHandler provides permissionHandler
    ) {/* Composable Contnent */}

Usage:

@Composable
fun PermissionHandler(
    permissions: Array<out String>,
    requestCode: Int,
    granted: @Composable() () -> Unit,
    denied: @Composable() () -> Unit,
    deniedPermanently: (@Composable() () -> Unit)? = null,
    rational: (@Composable() () -> Unit)? = null,
    awaitResult: (@Composable() () -> Unit)? = null,
) {
    val permissionHandler = AmbientPermissionHandler.current
    val (permissionResult, setPermissionResult) = remember(permissions) { mutableStateOf<PermissionResult?>(null) }
    LaunchedEffect(Unit) {
        setPermissionResult(permissionHandler.requestPermissionsSuspend(requestCode, permissions))
    }
    when (permissionResult) {
        is PermissionResult.PermissionGranted -> granted()
        is PermissionResult.PermissionDenied -> denied()
        is PermissionResult.PermissionDeniedPermanently -> deniedPermanently?.invoke()
        is PermissionResult.ShowRational -> rational?.invoke()
        null -> awaitResult?.invoke()
    }
}

Implementation of PermissionHandler with dependency https://github.com/sagar-viradiya/eazypermissions

class PermissionHandler(
    private val actualHandler: AppCompatActivity,
) {
    suspend fun requestPermissionsSuspend(requestCode: Int, permissions: Array<out String>): PermissionResult {
        return PermissionManager.requestPermissions(actualHandler, requestCode, *permissions)
    }

    fun requestPermissionsWithCallback(requestCode: Int, permissions: Array<out String>, onResult: (PermissionResult) -> Unit) {
        actualHandler.lifecycleScope.launch {
            onResult.invoke(PermissionManager.requestPermissions(actualHandler, requestCode, *permissions))
        }
    }
}

If you prefer a callback the second function works also.

Solution 6:[6]

Don't forget to add

<uses-permission android:name="android.permission.CAMERA"/>

(when requesting camera permission) to your AndroidManifest.xml, otherwise it might end up with the permission denied state, when using the solutions others provided.

Solution 7:[7]

private const val PERMISSIONS_REQUEST_CODE = 10
private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)

@Composable
fun PermissionButton() {

    val context = ContextAmbient.current

    Button(onClick = {
        if (!hasPermissions(context)) {
            requestPermissions(
                context as Activity,
                PERMISSIONS_REQUIRED,
                PERMISSIONS_REQUEST_CODE
            )
        }
    }
    ) {}
}

fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
    ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}

Solution 8:[8]

You can request multiples permissions.

class MainActivity : ComponentActivity() {

    private val neededPermissions = arrayOf(
        Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_FINE_LOCATION
    )

    @OptIn(ExperimentalMaterialApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MfmTheme {
                val launcher = rememberLauncherForActivityResult(
                    contract = ActivityResultContracts.RequestMultiplePermissions()
                ) { maps ->
                    val granted = maps.values.reduce { acc, next -> (acc && next) }
                    if (granted) {
                        // all permission granted
                    } else {
                        // Permission Denied: Do something
                    }
                    // You can check one by one
                    maps.forEach { entry ->
                        Log.i("Permission = ${entry.key}", "Enabled ${entry.value}")
                    }
                }
                val context = LocalContext.current
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background,
                    onClick = {
                        when {
                            hasPermissions(context, *neededPermissions) -> {
                                // All permissions granted
                               
                            }
                            else -> {
                                // Request permissions
                                launcher.launch(neededPermissions)
                            }
                        }
                    }
                ) {
                    Greeting("Android")
                }
            }
        }
    }

    private fun hasPermissions(context: Context, vararg permissions: String): Boolean =
        permissions.all {
            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
        }

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MfmTheme {
        Greeting("Android")
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mr. Gauss
Solution 2 Putra Hardjono
Solution 3
Solution 4 ????? ????????????
Solution 5 Alexander
Solution 6
Solution 7 Mateusz Krawczuk
Solution 8 ben khedher mahmoud