Skip to content

Fix crash on rapid double-clicking video record button - #541

Open
davidjiagoogle wants to merge 6 commits into
mainfrom
david/doubleClilckRecordingFix
Open

Fix crash on rapid double-clicking video record button#541
davidjiagoogle wants to merge 6 commits into
mainfrom
david/doubleClilckRecordingFix

Conversation

@davidjiagoogle

@davidjiagoogle davidjiagoogle commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes an issue (b/535645067) where double-clicking or rapidly tapping the video recording button in quick succession crashed the app with IllegalStateException: A recording is already in progress.

Changes Made

  1. Added CaptureButtonUiState.Enabled.Recording.Pending: Created a new UI state representing the initialization window when video recording is starting.
  2. Mapped VideoRecordingState.Starting: Mapped VideoRecordingState.Starting to CaptureButtonUiState.Enabled.Recording.Pending instead of Idle in CaptureButtonUiStateAdapter.kt.
  3. Updated CaptureButtonComponents.kt: Added Recording.Pending to the ignored/no-op states during click handling (onKeyUp), preventing rapid consecutive clicks from sending duplicate StartRecordingEvents.
  4. Defensive Error Handling in CameraSession.kt: Replaced throwing an unhandled IllegalStateException with logging a warning when receiving a duplicate StartRecordingEvent during active recording.
  5. Unit Tests: Updated CaptureButtonUiStateAdapterTest.kt to assert the new Pending state.

Add CaptureButtonUiState.Enabled.Recording.Pending state to represent the window when video recording is starting. Map VideoRecordingState.Starting to Pending instead of Idle, preventing rapid double-clicks from sending duplicate StartRecordingEvents.

Additionally, update CameraSession to log a warning instead of throwing an unhandled IllegalStateException when receiving duplicate StartRecordingEvents during active recording.

Bug: 535645067
Test: ./gradlew :ui:uistateadapter:capture:test

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Pending state to CaptureButtonUiState.Enabled.Recording to represent when a video recording request has been sent but is still initializing. This state is mapped from VideoRecordingState.Starting in the adapter and handled in the capture button components and tests. Additionally, CameraSession now logs a warning instead of throwing an exception if a start recording event is received while a recording is already in progress. Feedback was provided regarding a potential bug where releasing the capture button during the Pending state would ignore the key-up event, causing the recording to continue indefinitely; a code suggestion was provided to handle this state in onKeyUp.

@davidjiagoogle
davidjiagoogle requested a review from temcguir July 21, 2026 23:38

@temcguir temcguir left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be awesome to add a quick unit test to PreviewViewModelTest.kt to ensure this specific fast-release cancellation doesn't regress.

First, add a diagnostic counter to FakeCameraSystem.kt:

    var numVideoRecordingStarts = 0
    
    override suspend fun startVideoRecording(
        saveLocation: SaveLocation,
        onVideoRecord: (OnVideoRecordEvent) -> Unit
    ) {
        if (!useCasesBinded) {
            throw IllegalStateException("Usecases not bound")
        }
        numVideoRecordingStarts++
        recordingInProgress = true
    }

Then we can add the following test directly inside PreviewViewModelTest.kt to verify that rapidly firing "stop" immediately cancels the "start" job:

    @Test
    fun fastStartAndStopVideoRecording_cancelsRecording() = runTest(StandardTestDispatcher()) {
        startCameraUntilRunning()
        
        // Start and stop immediately without advancing the dispatcher
        previewViewModel.captureController.startVideoRecording()
        previewViewModel.captureController.stopVideoRecording()
        
        // Let the coroutines execute
        advanceUntilIdle()
        
        // Verify that because we cancelled the job synchronously, the start implementation
        // was bypassed completely to protect us from the channel race condition!
        assertThat(cameraSystem.numVideoRecordingStarts).isEqualTo(0)
    }

davidjiagoogle and others added 2 commits July 29, 2026 21:57
- Rename Pending state to Starting in CaptureButtonUiState
- Handle key-up release during Starting state in CaptureButtonComponents
- Synchronously cancel recordingJob in CaptureControllerImpl.stopVideoRecording()
- Add fastStartAndStopVideoRecording_cancelsRecording unit test
@davidjiagoogle
davidjiagoogle requested a review from temcguir July 29, 2026 23:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants