Skip to content

Instantly share code, notes, and snippets.

@adamp

adamp/Camera.kt Secret

Created May 9, 2021 16:26
Show Gist options
  • Save adamp/26043788aee2a1b936691e917cd96ef2 to your computer and use it in GitHub Desktop.
Save adamp/26043788aee2a1b936691e917cd96ef2 to your computer and use it in GitHub Desktop.
Toying with camerax + compose
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()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment