Getting Began with CameraX in Jetpack Compose | by Jolanda Verhoef | Android Builders | Jan, 2025

Getting Began with CameraX in Jetpack Compose | by Jolanda Verhoef | Android Builders | Jan, 2025


We’ve heard from you that you just love the ability that each the CameraX and Jetpack Compose libraries offer you, however that you just’d like idiomatic Compose APIs for constructing Digicam UIs. This 12 months, our engineering groups labored on two new Compose artifacts, the low-level viewfinder-compose and the high-level camera-compose. Each at the moment are out there as alpha releases 🚀🚀🚀.

On this weblog submit collection, we’ll present you easy methods to combine the camera-compose APIs in your app. However extra excitingly, we’ll present you among the ✨ pleasant UI experiences that integration with Compose unlocks. All of the superb Compose options, like adaptive APIs and animation help, combine seamlessly with the digital camera preview!

Right here’s a brief abstract of what every submit will include:

  • 🧱 Half 1 (this submit): Constructing a fundamental digital camera preview utilizing the brand new camera-compose artifact. We’ll cowl permission dealing with and fundamental integration.
  • 👆 Half 2: Utilizing the Compose gesture system, graphics, and coroutines to implement a visible tap-to-focus.
  • 🔎 Half 3: Exploring easy methods to overlay Compose UI parts on prime of your digital camera preview for a richer consumer expertise.
  • 📂 Half 4: Utilizing adaptive APIs and the Compose animation framework to easily animate to and from tabletop mode on foldable telephones.

With all of those in motion, our last app will look as follows:

As well as, it should easily transfer to and from tabletop mode:

By the top of this primary submit, you’ll have a practical digital camera viewfinder able to be expanded upon within the subsequent elements of the collection. Do please code alongside, it’s one of the best ways to study!

I’m assuming that you have already got Compose arrange in your app. If you wish to comply with alongside, merely create a brand new app in Android Studio. I sometimes use the newest Canary model, as a result of it has the newest Compose templates (and since I like dwelling on the sting 😀).

Add the next to your libs.variations.toml file:

[versions]
..
camerax = "1.5.0-alpha03"
accompanist = "0.36.0" # or no matter matches along with your Compose model

[libraries]
..
# Comprises the fundamental digital camera performance reminiscent of SurfaceRequest
androidx-camera-core = { module = "androidx.digital camera:camera-core", model.ref = "camerax" }
# Comprises the CameraXViewfinder composable
androidx-camera-compose = { module = "androidx.digital camera:camera-compose", model.ref = "camerax" }
# Permits us to bind the digital camera preview to our UI lifecycle
androidx-camera-lifecycle = { group = "androidx.digital camera", identify = "camera-lifecycle", model.ref = "camerax" }
# The particular digital camera implementation that renders the preview
androidx-camera-camera2 = { module = "androidx.digital camera:camera-camera2", model.ref = "camerax" }
# The helper library to grant the digital camera permission
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", model.ref = "accompanist" }

Subsequent, add these to your module construct.gradle.kts dependencies block:

dependencies {
..
implementation(libs.androidx.digital camera.core)
implementation(libs.androidx.digital camera.compose)
implementation(libs.androidx.digital camera.lifecycle)
implementation(libs.androidx.digital camera.camera2)
implementation(libs.accompanist.permissions)
}

We added all dependencies in order that we are able to grant the digital camera permission after which really show the digital camera preview. Subsequent, let’s have a look at granting the fitting permission.

The Accompanist permissions library permits us to simply grant the fitting digital camera permission. First, we have to arrange the AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:identify="android.{hardware}.digital camera" android:required="true" />
<uses-permission android:identify="android.permission.CAMERA" />

..

</manifest>

Now, we are able to merely comply with the library’s directions to grant the fitting permission:

class MainActivity : ComponentActivity() {
override enjoyable onCreate(savedInstanceState: Bundle?) {
tremendous.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplicationTheme {
CameraPreviewScreen()
}
}
}
}

@OptIn(ExperimentalPermissionsApi::class)
@Composable
enjoyable CameraPreviewScreen(modifier: Modifier = Modifier) {
val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
if (cameraPermissionState.standing.isGranted) {
CameraPreviewContent(modifier)
} else {
Column(
modifier = modifier.fillMaxSize().wrapContentSize().widthIn(max = 480.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val textToShow = if (cameraPermissionState.standing.shouldShowRationale) {
// If the consumer has denied the permission however the rationale may be proven,
// then gently clarify why the app requires this permission
"Whoops! Appears like we'd like your digital camera to work our magic!" +
"Don't fret, we simply wanna see your fairly face (and perhaps some cats). " +
"Grant us permission and let's get this occasion began!"
} else {
// If it is the primary time the consumer lands on this characteristic, or the consumer
// would not wish to be requested once more for this permission, clarify that the
// permission is required
"Hello there! We want your digital camera to work our magic! ✨n" +
"Grant us permission and let's get this occasion began! uD83CuDF89"
}
Textual content(textToShow, textAlign = TextAlign.Heart)
Spacer(Modifier.peak(16.dp))
Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
Textual content("Unleash the Digicam!")
}
}
}
}

@Composable
personal enjoyable CameraPreviewContent(modifier: Modifier = Modifier) {
// TODO: Implement
}

With this, we get a pleasant UI that enables the consumer to grant the digital camera permission earlier than exhibiting the digital camera preview:

It’s good observe to separate our enterprise logic from our UI. We are able to do that by making a view mannequin for our display screen. This view mannequin units up the CameraX Preview use case. Please word that use instances in CameraX characterize configurations of assorted workflows one can implement with the library, i.e. previewing, capturing, recording, and analyzing. The view mannequin additionally binds the UI to the digital camera supplier:

class CameraPreviewViewModel : ViewModel() {
// Used to arrange a hyperlink between the Digicam and your UI.
personal val _surfaceRequest = MutableStateFlow<SurfaceRequest?>(null)
val surfaceRequest: StateFlow<SurfaceRequest?> = _surfaceRequest

personal val cameraPreviewUseCase = Preview.Builder().construct().apply {
setSurfaceProvider { newSurfaceRequest ->
_surfaceRequest.replace { newSurfaceRequest }
}
}

droop enjoyable bindToCamera(appContext: Context, lifecycleOwner: LifecycleOwner) {
val processCameraProvider = ProcessCameraProvider.awaitInstance(appContext)
processCameraProvider.bindToLifecycle(
lifecycleOwner, DEFAULT_FRONT_CAMERA, cameraPreviewUseCase
)

// Cancellation indicators we're executed with the digital camera
strive { awaitCancellation() } lastly { processCameraProvider.unbindAll() }
}
}

There’s fairly a bit happening right here! The code defines a CameraPreviewViewModel class, accountable for managing the digital camera preview. It makes use of the CameraX Preview builder to configure how the preview ought to be sure to the UI. The bindToCamera perform initializes the digital camera, binds to the offered LifecycleOwner in order that the digital camera solely runs when the lifecycle is at the very least began, and begins the preview stream.

The digital camera, which is a part of the internals of the digital camera libraries, must render to the floor that’s offered by the UI. So the library must have a option to request a floor. That’s precisely what the SurfaceRequest is for! So each time the digital camera signifies it wants a floor, a surfaceRequest is triggered. You then ahead that request to the UI, the place it may possibly move the floor to the request object.

Lastly, we wait till the UI is finished binding to the digital camera, and ensure that we launch the digital camera assets to keep away from leaks.

Now that now we have a view mannequin, we are able to implement our CameraPreviewContent composable. It reads the floor request from the view mannequin, binds to the digital camera whereas the composable is within the composition tree, and calls the CameraXViewfinder from the library:

@Composable
enjoyable CameraPreviewContent(
viewModel: CameraPreviewViewModel,
modifier: Modifier = Modifier,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.present
) {
val surfaceRequest by viewModel.surfaceRequest.collectAsStateWithLifecycle()
val context = LocalContext.present
LaunchedEffect(lifecycleOwner) {
viewModel.bindToCamera(context.applicationContext, lifecycleOwner)
}

surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
modifier = modifier
)
}
}

As talked about within the earlier part, the surfaceRequest permits the digital camera library to request a floor when it wants one to render to. On this piece of code, we acquire these surfaceRequest cases and ahead them to the CameraXViewfinder, which is a part of the camera-compose artifact.

And with that, now we have a working full display screen viewfinder! You will discover the total code snippet right here. Within the subsequent weblog submit, we’ll add a easy tabletop mode by listening to the gadget’s show options, and through the use of Compose animations to go to and from tabletop mode. Keep tuned!

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.