CameraX with Jetpack compose
In this blog post, we will implement CameraX with Jetpack Compose. We will cover:
- Setting up the project
- Permission Handling
- CameraX Composable
Let’s get started!
Setting up the Project
Start by creating a new project with an Empty Activity.
Name your project accordingly.
Since we are using Kotlin DSL, we need to add the following libraries in a file named libs.version.toml
:
cameraX = "1.3.4"
# Camera X Dependencies
androidx-camerax-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraX" }
androidx-camerax-camera = { group = "androidx.camera", name = "camera-camera2", version.ref = "cameraX" }
androidx-camerax-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraX" }
androidx-camerax-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraX" }
Once done sync now and we are almost done with the initial setup.
Permission Handling
In your AndroidManifest.xml
, add camera permissions:
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.CAMERA"/>
In your Activity add following code to request user for permission
private val cameraPermissionRequest =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// Camera permision granted
} else {
// Camera permission denied
}
}
We also have to check if the permission is already granted or not and launch the permission request accordingly
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) -> {
// Camera permission granted
}
else -> {
cameraPermissionRequest.launch(Manifest.permission.CAMERA)
}
}
Here’s how your final code should look like
class FaceScanActivity : ComponentActivity() {
private val cameraPermissionRequest =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// Camera permission granted
} else {
// Camera permission denied
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) -> {
// Camera permission granted
}
else -> {
cameraPermissionRequest.launch(Manifest.permission.CAMERA)
}
}
}
After successful run you should be able to see this Permission Request Dialog
Composable for Camera Preview
Create a new file named CameraPreview.kt
:
@Composable
fun CameraPreview() {
// Camera LENS to Use = LENS_FACING_BACK can also be used
val lensFacing = CameraSelector.LENS_FACING_FRONT
// Retrieves the current LifecycleOwner from the composable's context
val lifeCycleOwner = LocalLifecycleOwner.current
// Fetches the current context from the Jetpack Compose environment
val context = LocalContext.current
// Responsible for displaying the camera feed to the user
val preview = Preview.Builder().build()
val previewView = remember { PreviewView(context) }
// ...
// Set Camera Selector & Camera Provider
val cameraXSelector = CameraSelector
.Builder().requireLensFacing(lensFacing)
.build()
// Bind everything and set it up for Preview
LaunchedEffect(lensFacing) {
val cameraProvider = context.getCameraProvider()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(lifeCycleOwner, cameraXSelector, preview)
preview.setSurfaceProvider(previewView.surfaceProvider)
}
AndroidView(factory = { previewView }, modifier = Modifier.fillMaxSize())
}
// Leverage Kotlin coroutines to get the CameraProvider
suspend fun Context.getCameraProvider(): ProcessCameraProvider =
suspendCoroutine { continuation ->
ProcessCameraProvider.getInstance(this).also { cameraProvider ->
cameraProvider.addListener({
continuation.resume(cameraProvider.get())
}, ContextCompat.getMainExecutor(this))
}
}
Final Output 🥳
Your final code should look like this
class FaceScanActivity : ComponentActivity() {
private val cameraPermissionRequest =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// Camera permission granted
setCameraPreview()
} else {
// Camera permission denied
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) -> {
setCameraPreview()
}
else -> {
cameraPermissionRequest.launch(Manifest.permission.CAMERA)
}
}
}
private fun setFaceScanPreview() {
setContent {
EKYCTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
CameraPreview()
}
}
}
}
}
Complete example can be found on Github.
Thanks for reading!