EmergeTools logo
🙏 Supported by Emerge Tools
The platform for all your mobile performance needs

CameraX + Compose

Author: Adam Powell

Sample implementation of CameraX integration in a Composable

suspend fun <R> withChildLifecycleOwner(
parentLifecycle: Lifecycle,
block: suspend CoroutineScope.(LifecycleOwner) -> R
): R {
val childOwner = object : LifecycleOwner {
val lifecycle = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle = lifecycle
}
val registry = childOwner.lifecycle
return coroutineScope {
val myJob = coroutineContext.job
val parentObserver = LifecycleEventObserver { _, event ->
registry.currentState = event.targetState
if (event == Lifecycle.Event.ON_DESTROY) {
myJob.cancel("parent lifecycle was destroyed")
}
}
parentLifecycle.addObserver(parentObserver)
try {
block(childOwner)
} finally {
parentLifecycle.removeObserver(parentObserver)
registry.currentState = Lifecycle.State.DESTROYED
}
}
}
class CameraState {
private val bindingMutex = MutatorMutex()
var camera: Camera? by mutableStateOf(null)
private set
suspend fun <R> bind(
context: Context,
lifecycleOwner: LifecycleOwner,
selector: CameraSelector,
vararg useCases: UseCase,
block: suspend (Camera) -> R
): R {
val provider = ProcessCameraProvider.getInstance(context).await()
return bindingMutex.mutate {
withChildLifecycleOwner(lifecycleOwner.lifecycle) { childLifecycleOwner ->
try {
block(
provider.bindToLifecycle(childLifecycleOwner, selector, *useCases)
.also { this@CameraState.camera = it }
)
} finally {
camera = null
}
}
}
}
}
@Composable
fun CameraBinding(
state: CameraState,
vararg useCases: UseCase,
selectorBuilder: CameraSelector.Builder.() -> CameraSelector.Builder = { this }
) {
// Make selectorBuilder observe snapshot changes and only rebuild the selector
// when the builder changes. We do this since selectors don't implement equals
// and this makes the API a little nicer since the caller doesn't need
// to remember to remember {}.
val currentSelectorBuilder by rememberUpdatedState(selectorBuilder)
val selector by remember {
derivedStateOf {
CameraSelector.Builder().run(currentSelectorBuilder).build()
}
}
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
// useCases.toList() since arrays don't implement structural equality
LaunchedEffect(state, selector, lifecycleOwner, useCases.toList()) {
state.bind(context, lifecycleOwner, selector, *useCases) {
awaitCancellation()
}
}
}
view raw Camera.kt hosted with ❤ by GitHub

Have a project you'd like to submit? Fill this form, will ya!

If you like this snippet, you might also like:

Maker OS is an all-in-one productivity system for developers

I built Maker OS to track, manage & organize my life. Now you can do it too!