-
-
Save adamp/26043788aee2a1b936691e917cd96ef2 to your computer and use it in GitHub Desktop.
Toying with camerax + compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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