Preserve the selected flash mode across camera rebinds.

This commit is contained in:
Alex Hart
2026-08-19 19:05:50 -04:00
committed by Cody Henthorne
parent fb0345ff48
commit 3c8aab9be1
2 changed files with 28 additions and 1 deletions
@@ -531,10 +531,15 @@ class CameraScreenViewModel : ViewModel() {
.build()
.also { it.surfaceProvider = event.surfaceProvider }
// Image capture with 16:9 aspect ratio (optimized for speed)
// Image capture with 16:9 aspect ratio (optimized for speed).
// The flash mode must be seeded from state: this builds a brand new ImageCapture, and a fresh one
// defaults to FLASH_MODE_OFF. Rebinds happen on every camera switch and every re-entry into the
// camera, so without this the HUD would keep showing the user's flash selection while the hardware
// flash silently stopped firing.
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setResolutionSelector(resolutionSelector)
.setFlashMode(_state.value.flashMode.cameraxMode)
.build()
val videoCapture: VideoCapture<Recorder>? = if (event.captureMode == CameraCaptureMode.ImageAndVideoSimultaneous && !FORCE_LIMITED_BINDING) {
@@ -11,6 +11,7 @@ import androidx.camera.core.CameraControl
import androidx.camera.core.CameraInfo
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
import androidx.camera.core.ZoomState
import androidx.camera.lifecycle.ProcessCameraProvider
@@ -135,6 +136,7 @@ class CameraScreenViewModelTest {
private fun List<Any?>.hasVideoCapture() = any { it is VideoCapture<*> }
private fun List<Any?>.hasImageAnalysis() = any { it is ImageAnalysis }
private fun List<Any?>.imageCapture() = filterIsInstance<ImageCapture>().firstOrNull()
private fun setupZoomState(minZoom: Float, maxZoom: Float) {
val mockZoomState: ZoomState = mockk()
@@ -340,6 +342,26 @@ class CameraScreenViewModelTest {
assertThat(viewModel.state.value.flashMode).isEqualTo(FlashMode.Off)
}
@Test
fun `rebinding after the flash mode was set carries that flash mode onto the new ImageCapture`() {
bindCamera()
viewModel.onEvent(CameraScreenEvents.SetFlashMode(FlashMode.On))
// A camera switch or a re-entry into the camera rebinds, building a brand new ImageCapture.
val attempts = captureBindingAttempts()
bindCamera()
assertThat(attempts.last().imageCapture()?.flashMode).isEqualTo(ImageCapture.FLASH_MODE_ON)
}
@Test
fun `binding with the default flash mode leaves the ImageCapture flash off`() {
val attempts = captureBindingAttempts()
bindCamera()
assertThat(attempts.last().imageCapture()?.flashMode).isEqualTo(ImageCapture.FLASH_MODE_OFF)
}
// ===========================================================================
// Camera switching
// ===========================================================================