Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmark/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ android {

defaultConfig {
//Our app has a minSDK of 21, but in order for the benchmark tool to function, it must be 23
minSdk = 23
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()

// allows the benchmark to be run on an emulator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,10 @@ private suspend fun runVideoRecording(
for (event in videoControlEvents) {
when (event) {
is VideoCaptureControlEvent.StartRecordingEvent ->
throw IllegalStateException("A recording is already in progress")
Log.w(
TAG,
"A recording is already in progress, ignoring extra StartRecordingEvent"
)

VideoCaptureControlEvent.StopRecordingEvent -> {
recordingSettingsUpdater.cancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,16 @@ class FakeCameraSystem(defaultCameraSettings: CameraAppSettings = CameraAppSetti
screenFlashEvents.trySend(event)
}

var numVideoRecordingStarts = 0

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ class PreviewViewModelTest {
assertThat(cameraSystem.recordingInProgress).isFalse()
}

@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)
}

@Test
fun setFlash() = runTest(StandardTestDispatcher()) {
previewViewModel.cameraController.startCamera()
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
compileSdk = "36"
desugar_jdk_libs = "2.1.5"
orchestrator = "1.6.1"
minSdk = "23"
minSdk = "24"
targetSdk = "35"

# Used below in dependency definitions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,12 @@ internal fun CaptureButton(
if (firstKeyPressed.value == captureSource) {
if (isLongPressing.value) {
if (!isLocked &&
currentUiState.value is
CaptureButtonUiState.Enabled.Recording.PressedRecording
(
currentUiState.value is
CaptureButtonUiState.Enabled.Recording.PressedRecording ||
currentUiState.value is
CaptureButtonUiState.Enabled.Recording.Starting
)
) {
Log.d(TAG, "Stopping recording")
onStopRecording()
Expand All @@ -272,6 +276,7 @@ internal fun CaptureButton(
}

CaptureButtonUiState.Enabled.Recording.LockedRecording -> onStopRecording()
CaptureButtonUiState.Enabled.Recording.Starting,
CaptureButtonUiState.Enabled.Recording.PressedRecording,
CaptureButtonUiState.Unavailable -> {
}
Expand Down Expand Up @@ -668,6 +673,7 @@ private fun CaptureButtonNucleus(
// inner circle fills white ring when locked
CaptureButtonUiState.Enabled.Recording.LockedRecording -> captureButtonSize.dp

CaptureButtonUiState.Enabled.Recording.Starting,
CaptureButtonUiState.Enabled.Recording.PressedRecording ->
(captureButtonSize * pressedVideoCaptureScale).dp

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,10 @@ class CaptureControllerImpl(

override fun stopVideoRecording() {
Log.d(TAG, "stopVideoRecording")
recordingJob?.cancel()
recordingJob = null
scope.launch {
cameraSystem.stopVideoRecording()
recordingJob?.cancel()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ sealed interface CaptureButtonUiState {
sealed interface Recording : Enabled {
override val isEnabled: Boolean get() = true

/**
* The video recording request has been sent and is starting.
*/
data object Starting : Recording

/**
* The user is actively pressing the button to record video (press-and-hold).
*/
Expand Down
2 changes: 1 addition & 1 deletion ui/uistate/postcapture/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ android {
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
minSdk = 23
minSdk = libs.versions.minSdk.get().toInt()

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ fun CaptureButtonUiState.Companion.from(
}

is VideoRecordingState.Starting ->
CaptureButtonUiState
.Enabled.Idle(captureMode = cameraAppSettings.captureMode)
CaptureButtonUiState.Enabled.Recording.Starting
}
} else {
CaptureButtonUiState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class CaptureButtonUiStateAdapterTest {
}

@Test
fun from_cameraRunning_recordingStarting_returnsIdleAndEnabled() {
fun from_cameraRunning_recordingStarting_returnsPendingRecording() {
val cameraState = defaultCameraState.copy(
videoRecordingState = VideoRecordingState.Starting(null)
)
Expand All @@ -105,7 +105,8 @@ class CaptureButtonUiStateAdapterTest {
lockedState = false
)

assertThat(uiState).isInstanceOf(CaptureButtonUiState.Enabled.Idle::class.java)
assertThat(uiState)
.isInstanceOf(CaptureButtonUiState.Enabled.Recording.Starting::class.java)
assertThat(uiState.isEnabled).isTrue()
}
}
2 changes: 1 addition & 1 deletion ui/uistateadapter/postcapture/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ android {
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
minSdk = 23
minSdk = libs.versions.minSdk.get().toInt()

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
Expand Down
Loading