Home > Enterprise >  How to make the argument of registerForActivityResult composable?
How to make the argument of registerForActivityResult composable?

Time:10-28

The following code is from the Android Developer Docs. It explains how to request permissions.

// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher. You can use either a val, as shown in this snippet,
// or a lateinit var in your onAttach() or onCreate() method.
val requestPermissionLauncher =
    registerForActivityResult(RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            // Permission is granted. Continue the action or workflow in your
            // app.
        } else {
            // Explain to the user that the feature is unavailable because the
            // features requires a permission that the user has denied. At the
            // same time, respect the user's decision. Don't link to system
            // settings in an effort to convince the user to change their
            // decision.
        }
    }

I tried to use the code in my application. When isGranted is true I tried render my main ui. But it does not work, because I get the following error:

@Composable invocations can only happen from the context of a @Composable function

I am wondering why this happens, because I call the launcher from a composable context. Is it necessary to mark every function in the call stack as @Composable? And if so, how to make the closure passed to registerForActivityResult composable?

CodePudding user response:

You should define a state that is updated once the result is returned. That remembered state can within your statefull composable or coming as parameter. Updating the remembered state will trigger recomposition.

Lambda passed in the registerForActivity is callback and it is passed multiple times in recomposition but only the last one that has been passed is called back.

You can do something like this:

@Composable
fun OnPermissionGranted(permission : String, launch : Boolean, onGranted : @Composable () -> Unit ){
    val context = LocalContext.current
    var granted by remember { mutableStateOf(checkIfGranted(context) ) }
    val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()){
        if(it){
            granted = true
        }
    }

    if(!granted && launch){
        launcher.launch(permission)
    }
    if(granted){
        onGranted()
    }
}

CodePudding user response:

It seems to work somehow.

package com.example.konretsca

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.app.ActivityCompat
import com.example.konretsca.ui.theme.KonretscaTheme

fun debug (msg: String) {
    Log.d ("konretsca", msg)
}

class MainActivity : ComponentActivity() {

    override fun onCreate (savedInstanceState: Bundle?) {
        super.onCreate (savedInstanceState)
        debug ("onCreate")

        setContent {
            val permission =
                ActivityCompat.checkSelfPermission (
                    LocalContext.current,
                    Manifest.permission.CAMERA
                ) == PackageManager.PERMISSION_GRANTED
            var granted by remember { mutableStateOf (permission) }
            val launcher = rememberLauncherForActivityResult (ActivityResultContracts.RequestPermission()) {
                granted = it
            }

            if(!granted){
                SideEffect {
                    launcher.launch(Manifest.permission.CAMERA)
                }
            }
            else {
                KonretscaTheme {
                    Surface(
                        modifier = Modifier.fillMaxSize(),
                        color = MaterialTheme.colorScheme.background
                    ) {
                        Main()
                    }
                }
            }
        }
    }
}

@Composable
fun Main () {
    Text (text = "Main")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview () {
    KonretscaTheme {
        Main ()
    }
}
  • Related