Tasneem
Tasneem

Reputation: 803

Activity Result APIs is not returning callback for RequestPermission Contract

I am not getting permission result callback on Activity Result api as mentioned in the doc.

Getting a result from an activity

I am using implementation "androidx.activity:activity-ktx:1.2.0-alpha02"

Here is the api code :

/**
 * An {@link ActivityResultContract} to {@link Activity#requestPermissions request a permission}
 */
public static class RequestPermission extends ActivityResultContract<String, Boolean> {

    @NonNull
    @Override
    public Intent createIntent(@NonNull String input) {
        return new Intent(ACTION_REQUEST_PERMISSIONS)
                .putExtra(EXTRA_PERMISSIONS, new String[] { input });
    }

    @NonNull
    @Override
    public Boolean parseResult(int resultCode, @Nullable Intent intent) {
        if (resultCode != Activity.RESULT_OK) return false;
        if (intent == null) return false;
        int[] grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS);
        if (grantResults == null) return false;
        return grantResults[0] == PackageManager.PERMISSION_GRANTED;
    }
}

Here is my implementation :

private val permission = prepareCall(object : ActivityResultContracts.RequestPermission() {
    override fun createIntent(input: String): Intent {
        if (shouldShowRequestPermissionRationale(input)) {
            Toast.makeText(
                this@MainActivity,
                "shouldShowRequestPermissionRationale - true",
                Toast.LENGTH_SHORT
            ).show()
            // case 2
        } else {
            Toast.makeText(
                this@MainActivity,
                "shouldShowRequestPermissionRationale - false",
                Toast.LENGTH_SHORT
            ).show()
            // case 1 and 3
        }
        return super.createIntent(input)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
        val t = super.parseResult(resultCode, intent)
        Toast.makeText(this@MainActivity, "parseResult $t", Toast.LENGTH_SHORT).show()
        return t
    }
}) {
    Log.d("permission granted?", "$it")
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    button.setOnClickListener {
        permission(android.Manifest.permission.CAMERA)
    }
}

As per doc, I should be getting callback inside ActivityResultCallback but after backtracking I figure in my case, it even does not call ParseResult function. Though I am still receiving callback inside onRequestPermissionsResult function.

So, am I doing something wrong ?

Please refer Getting a result from an activity to understand this api.

Update

I was able to get the callback after extending my activity with ComponentActivity but again, if the permission is already given then app wont receive callback, is there any way to directly handle here? I don't wanna check self permission.

Sample demo code - https://github.com/tasneembohra/ActivityResultApiDemo

Upvotes: 4

Views: 4241

Answers (3)

LLL
LLL

Reputation: 111

You can easily handle your permissions callback by creating a support class extending DefaultLifecycleObserver

class PermissionHandler(
    private val context: Context,
    private val registry: ActivityResultRegistry
    ) : DefaultLifecycleObserver {

    private val permissionsKey = "permissionsKey"

    private lateinit var requestPermissions: ActivityResultLauncher<Array<String>>

    private var onAllowed: (() -> Unit)? = null
    private var onDenied: (() -> Unit)? = null

    private val readExternalStorageRequest = arrayOf(
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.CAMERA
    )

    override fun onCreate(owner: LifecycleOwner) {
        requestPermissions = registry.register(permissionsKey, owner, ActivityResultContracts.RequestMultiplePermissions()) { result ->

            Log.d("PERMISSIONS", "RESULT: $result")

            if (result.values.isNullOrEmpty() || result.values.contains(false)) {
                Log.d("PERMISSIONS", "DENIED")
                onDenied?.invoke()
            }
            else {
                Log.d("PERMISSIONS", "ALLOWED")
                onAllowed?.invoke()
            }
        }
    }

    fun checkForPermissions(permissions: Array<String> = readExternalStorageRequest, onAllowed: (() -> Unit)?, onDenied: (() -> Unit)?) {
        this.onAllowed = onAllowed
        this.onDenied = onDenied

        val remainingPermissions = mutableListOf<String>()
        remainingPermissions.clear()

        for (permission in permissions) {
            if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                remainingPermissions.add(permission)
            }
        }

        Log.d("PERMISSIONS", "REMAINING: $remainingPermissions")

        if (remainingPermissions.isNotEmpty()) {
            requestPermissions.launch(remainingPermissions.toTypedArray())
        }
        else {
            onAllowed?.invoke()
        }
    }

}
then use it in your activity or fragment
    private lateinit var permissionHandler: PermissionHandler

    override fun onCreate(savedInstanceState: Bundle?) {
        
        permissionHandler = PermissionHandler(this, activityResultRegistry)
        lifecycle.addObserver(permissionHandler)
    }

    fun foo() {
        permissionHandler.checkForPermissions(
                onAllowed = {
                    //on allowed stuffs...
                },
                onDenied = {
                    //on denied stuffs...
                }
            )
    }
for fragment
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        permissionHandler = PermissionHandler(requireContext(), requireActivity().activityResultRegistry)
        lifecycle.addObserver(permissionHandler)
    }

Upvotes: 0

TimB
TimB

Reputation: 591

Well I ran in a lot of circles trying to solve the same problem, but my solution was to simply remove the override onActivityResult from my activity!

Upvotes: 0

Misha Akopov
Misha Akopov

Reputation: 13027

Here is my implementation, hope it helps:

      val cameraPermission = prepareCall(ActivityResultContracts.RequestPermission()){
            if(it){
                Log.e("TAG", "permnission granted")
            }else{
                Log.e("TAG", "No permnission")
            }
        }

        cameraPermission(Manifest.permission.CAMERA)

It enters Permission Granted condition, if you granted permission earlier and just open app, or grant it now.

P.S. My Activity extends AppCompatActivity

Upvotes: 1

Related Questions