Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a MultipleEventsCutter utility to prevent concurrent image capture and video recording events within CaptureControllerImpl. The review identifies several logic errors where the capture gate could remain locked if exceptions occur during the setup of capture operations, or unlock prematurely during non-terminal video recording events. Additionally, the reviewer noted that KDoc documentation is missing for the new utility's internal methods, which is a violation of the project's style guidelines.
...ller/impl/src/main/java/com/google/jetpackcamera/ui/controller/impl/CaptureControllerImpl.kt
Outdated
Show resolved
Hide resolved
...ller/impl/src/main/java/com/google/jetpackcamera/ui/controller/impl/CaptureControllerImpl.kt
Outdated
Show resolved
Hide resolved
...oller/impl/src/main/java/com/google/jetpackcamera/ui/controller/impl/MultipleEventsCutter.kt
Show resolved
Hide resolved
...oller/impl/src/main/java/com/google/jetpackcamera/ui/controller/impl/MultipleEventsCutter.kt
Show resolved
Hide resolved
temcguir
left a comment
There was a problem hiding this comment.
Moving MultipleEventsCutter into the Controller layer is going to reintroduce the same test flakiness that caused us to remove the MultipleEventsCutter from the UI layer in the first place.
Because MultipleEventsCutter silently swallows events, our automated tests will occasionally fire a performClick() that gets dropped. The test will then time out or fail (sometimes) because it expects an image/video to be captured but nothing happened.
Instead of an imperative debounce workaround, we should fix this reactively. We should tie the capture button's enabled state directly to the camera's processing pipeline:
- Emit an 'in-progress' state from
CameraSystem/CameraStatethe momenttakePictureorstartVideoRecordingis invoked. - Have the UI state adapter (
CaptureButtonUiStateAdapter) map this 'in-progress' state toCaptureButtonUiState.Unavailable(or the Disabled state introduced in PR #425). - PR #425 already updates the
CaptureButtoncomposable to be semantically disabled (usingModifier.semantics { disabled() }) when appropriate, and updates our tests to usewaitForCaptureButton()which explicitly checksisEnabled().
If we implement the reactive state in CameraState, it will integrate with the groundwork laid out in PR #425, preventing double-clicks naturally, giving the user proper visual feedback that a capture is occurring, and tying the button's interactability to the actual reality of the camera hardware without breaking our tests.
| if (isProcessing.compareAndSet(expect = false, update = true)) { | ||
| event() | ||
| } |
There was a problem hiding this comment.
Silently dropping events here is the root cause of the test flakiness. The testing framework assumes the click was successful, but the underlying system ignores it. We should remove this class entirely and rely on the UI being disabled while a capture is in progress.
| mediaRepository, | ||
| MediaDescriptor.Content.Image(it, null, true) | ||
| ) | ||
| multipleEventsCutter.processEvent { |
There was a problem hiding this comment.
Instead of wrapping this in processEvent, cameraSystem.takePicture() and cameraSystem.startVideoRecording() should internally update an 'in-progress' state on CameraState. This will flow down to the UI state, disable the capture button, and naturally prevent a second click.
Re-implement MultipleEventsCutter that blocks capture button clicks during the capture process (either video or image) instead of using a hardcoded time duration. This prevents breaking tests while ensuring the button cannot be double-clicked before the underlying camera system call finishes or errors out.