Monday, 21 December 2020

Why onRequestPermissionsResult is not triggered in the Fragment after process death?

I am facing a strange issue with onRequestPermissionsResult in the fragment. Basically fragment asks for camera permission inside of onCreate:

override fun onCreate(savedInstanceState: Bundle?) {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
    super.onCreate(savedInstanceState)
    if(!permissionsGranted){
        requestPermissions(arrayOf(Manifest.permission.CAMERA),
                PermissionsDelegateUtil.REQUEST_PERMISSIONS_CAMERA
        )
    }
}

Then I handle permissions in the onRequestPermissionsResult:

 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    val resultResponse = permissionsDelegateUtil.resultGranted(requestCode=requestCode, permissions = permissions, grantResults = grantResults)
    when(resultResponse){
        PermissionResult.PermissionGranted -> {
            setupCameraX()
        }
        PermissionResult.PermissionNotGrantedRetryAuto -> {
            //retrying again
            requestPermissions(arrayOf(Manifest.permission.CAMERA),
                PermissionsDelegateUtil.REQUEST_PERMISSIONS_CAMERA
        )
        }
        PermissionResult.PermissionNotGrantedCantRetry -> {
                //show a screen that user must enabled permission
        }
       PermissionResult.PermissionNotGrantedDontAsk -> {
           //Don't do anything, because you can't retry here, otherwise it will cause infinite loop.
        }
    }
}

Then permission delegate class handles permission logic:

 fun resultGranted(requestCode: Int,
                  permissions: Array<out String>,
                  grantResults: IntArray): PermissionResult {
    if (requestCode != REQUEST_PERMISSIONS_CAMERA) {
        return PermissionResult.PermissionNotGrantedDontAsk
    }
    if (grantResults.isEmpty()) {
        return PermissionResult.PermissionNotGrantedDontAsk
    }
    if(permissions[0] != Manifest.permission.CAMERA){
        return PermissionResult.PermissionNotGrantedDontAsk
    }
    return if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        PermissionResult.PermissionGranted

    } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (fragment.shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                PermissionResult.PermissionNotGrantedRetryAuto
            } else{
                ///User selected don't show again checkbox.
                PermissionResult.PermissionNotGrantedCantRetry
            }
        } else{
            PermissionResult.PermissionNotGrantedRetryAuto
        }
    }
}

This code works perfectly fine until I force process death (I select don't keep activities checkbox in developer tools). After that, I return from background and the permission dialog is still present (because onCreate is triggered again and permission check is executed again).

The problem is that, after I press any button in the permission dialog, the onRequestPermissionsResult method is not being triggered in the fragment. I looked at logcat and found that permission result should be delivered, because of these logs:

2020-12-17 18:48:17.816 22496-22496/? V/GrantPermissionsActivity: Logged buttons presented and clicked permissionGroupName=android.permission-group.CAMERA uid=10135 package=com.test.app  presentedButtons=25 clickedButton=8
2020-12-17 18:48:17.821 22496-22496/? V/GrantPermissionsActivity: Permission grant result requestId=-2584100455717644829 callingUid=10135 callingPackage=com.test.app permission=android.permission.CAMERA isImplicit=false result=6

This is really strange. Just out of curiosity I tried to request permissions from the activity (fragment calls activity.requestPermissions and activity override onRequestPermissionsResult). It works perfectly fine in the activity.

I also tried to retry calling permissions after PermissionResult.PermissionNotGrantedDontAsk is returned. It would work, but it causes an infinite loop of permission requests and response triggering and as a result, it crashes the app.

EDIT

I add a fragment without using backstack:

fun addCameraSessionFragment(supportFragmentManager: FragmentManager) {
    val fragment = supportFragmentManager.findFragmentByTag(CAMERA_SESSION_FRAGMENT_TAG)
    if (fragment == null) {
        val cameraSessionFragment =
                CameraSessionFragment()
        val fragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.replace(getTranscationRootId(), cameraSessionFragment, CAMERA_SESSION_FRAGMENT_TAG).commitNow()
    }
}

The thing is that this addition logic is triggered by ViewModel as LiveData Event. After process death occurs, addition actually occurs two times. The first is because it is the last fragment and it is restored after process death. The second is triggered by ViewModel action.



from Why onRequestPermissionsResult is not triggered in the Fragment after process death?

No comments:

Post a Comment