CameraX with Jetpack compose

Malik Saif
3 min readJul 27, 2024

--

Photo by Julius Drost on Unsplash

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!

--

--

Malik Saif
0 Followers

Android Developer @ DMS. I delve in to anything which seems interesting and try to do my best to write about it.