Symptoms: upon first start my app crashes with java.lang.SecurityException: Lacking privileges to access camera service
. I am getting "Unfortunately your app has crashed" dialog, clicking "OK", under this dialog there are two dialogs asking for the necessary permissions. I say "OK" to both and from now on my app works. Next starts are without the crash.
Causes: After some time of reading and debugging I came to understanding that my app problem is that it wants to do some Camera related logic before it obtains the required permissions (onSurfaceTextureAvailable
callback in my CameraHandler class) or before camera surface view comes to foreground.
There's a lot of questions regarding similar errors on SO and Github, but still it's hard for me to figure out.
I tried to go through this answer, but my setup is a bit different, i.e. I have my Camera logic inside a different class that isn't an Activity, and I would really like to keep it that way not to clutter my CameraActivity class. Is there a good way to deal with this?
How to make sure that when onSurfaceTextureAvailable in my CameraHandler class is fired, the permissions are already granted, so that I am not getting java.lang.SecurityException: Lacking privileges to access camera service
on the first run?
This is my SurfaceTextureListener located in CameraHandler class:
private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
openCamera(width, height) //this line here makes my app crash
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
configureTransform(width, height)
}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
return true
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
}
}
My CameraActivity onCreate, onResume() and onPause():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!canAccessCamera() || !canRecordAudio()) {
requestPermissions(INITIAL_PERMISSIONS, INITIAL_REQUEST)
}
}
}
override fun onResume() {
super.onResume()
cameraHandler.startHandler()
}
override fun onPause() {
cameraHandler.stopHandler()
super.onPause()
}
permission checking inside CameraActivity
@RequiresApi(api = Build.VERSION_CODES.M)
private fun canAccessCamera() : Boolean {
return (hasPermission(android.Manifest.permission.CAMERA))
}
@RequiresApi(api = Build.VERSION_CODES.M)
private fun canRecordAudio() : Boolean {
return (hasPermission(android.Manifest.permission.RECORD_AUDIO))
}
@RequiresApi(api = Build.VERSION_CODES.M)
private fun hasPermission(perm : String) : Boolean{
return (PackageManager.PERMISSION_GRANTED == checkSelfPermission(perm))
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == INITIAL_REQUEST) {
if (canAccessCamera() && canRecordAudio()) {
recordButton2.setOnClickListener {
if (isRecording) {
cameraHandler.endRecording()
} else {
currentFileName = generateTimestampName()
createCSVFile(currentFileName)
cameraHandler.startStopRecording()
}
isRecording = !isRecording
}
} else {
Toast.makeText(this, "We need it to perform magic", Toast.LENGTH_SHORT).show()
}
}
}