Skip to content

Commit acb15d9

Browse files
committed
Enhance startRecording function to handle edge cases
where there could be multiple calls to the function
1 parent e2a6b41 commit acb15d9

File tree

1 file changed

+86
-80
lines changed

1 file changed

+86
-80
lines changed

app/src/main/java/app/grapheneos/camera/capturer/VideoCapturer.kt

+86-80
Original file line numberDiff line numberDiff line change
@@ -147,115 +147,121 @@ class VideoCapturer(private val mActivity: MainActivity) {
147147
return null
148148
}
149149

150+
val startRecordingLock = Any()
151+
150152
fun startRecording() {
151-
if (camConfig.camera == null) return
152-
val recorder = camConfig.videoCapture?.output ?: return
153+
synchronized(startRecordingLock) {
154+
if (camConfig.camera == null) return
155+
val recorder = camConfig.videoCapture?.output ?: return
156+
if (isRecording) return
157+
isRecording = true
153158

154-
val dateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
155-
val fileName = VIDEO_NAME_PREFIX + dateString + videoFileFormat
159+
val dateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
160+
val fileName = VIDEO_NAME_PREFIX + dateString + videoFileFormat
156161

157-
includeAudio = false
162+
includeAudio = false
158163

159-
val ctx = mActivity
164+
val ctx = mActivity
160165

161-
if (ctx.settingsDialog.includeAudioToggle.isChecked) {
162-
if (ctx.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PERMISSION_GRANTED) {
163-
includeAudio = true
164-
} else {
165-
ctx.restartRecordingWithMicPermission()
166-
return
166+
if (ctx.settingsDialog.includeAudioToggle.isChecked) {
167+
if (ctx.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PERMISSION_GRANTED) {
168+
includeAudio = true
169+
} else {
170+
ctx.restartRecordingWithMicPermission()
171+
isRecording = false
172+
return
173+
}
167174
}
168-
}
169175

170-
val recordingCtx = try {
171-
createRecordingContext(recorder, fileName)!!
172-
} catch (exception: Exception) {
173-
val foreignUri = ctx is VideoCaptureActivity && ctx.isOutputUriAvailable()
174-
if (!foreignUri) {
175-
camConfig.onStorageLocationNotFound()
176+
val recordingCtx = try {
177+
createRecordingContext(recorder, fileName)!!
178+
} catch (exception: Exception) {
179+
val foreignUri = ctx is VideoCaptureActivity && ctx.isOutputUriAvailable()
180+
if (!foreignUri) {
181+
camConfig.onStorageLocationNotFound()
182+
}
183+
ctx.showMessage(R.string.unable_to_access_output_file)
184+
isRecording = false
185+
return
176186
}
177-
ctx.showMessage(R.string.unable_to_access_output_file)
178-
return
179-
}
180187

181-
val pendingRecording = recordingCtx.pendingRecording
182-
183-
if (includeAudio) {
184-
pendingRecording.withAudioEnabled()
185-
}
188+
val pendingRecording = recordingCtx.pendingRecording
186189

187-
beforeRecordingStarts()
188-
189-
isRecording = true
190+
if (includeAudio) {
191+
pendingRecording.withAudioEnabled()
192+
}
190193

191-
camConfig.mPlayer.playVRStartSound(handler) {
194+
beforeRecordingStarts()
192195

193-
recording = pendingRecording.start(ctx.mainExecutor) { event ->
196+
camConfig.mPlayer.playVRStartSound(handler) {
194197

195-
if (event is VideoRecordEvent.Start) {
196-
onRecordingStart()
197-
}
198+
recording = pendingRecording.start(ctx.mainExecutor) { event ->
198199

199-
if (event is VideoRecordEvent.Status) {
200-
updateTimerTime(event.recordingStats.recordedDurationNanos)
201-
}
202-
203-
if (event is VideoRecordEvent.Finalize) {
204-
afterRecordingStops()
200+
if (event is VideoRecordEvent.Start) {
201+
onRecordingStart()
202+
}
205203

206-
camConfig.mPlayer.playVRStopSound()
204+
if (event is VideoRecordEvent.Status) {
205+
updateTimerTime(event.recordingStats.recordedDurationNanos)
206+
}
207207

208-
if (event.hasError()) {
209-
when (event.error) {
210-
VideoRecordEvent.Finalize.ERROR_NO_VALID_DATA -> {
211-
ctx.showMessage(R.string.recording_too_short_to_be_saved)
212-
return@start
213-
}
214-
VideoRecordEvent.Finalize.ERROR_ENCODING_FAILED,
215-
VideoRecordEvent.Finalize.ERROR_RECORDER_ERROR,
216-
VideoRecordEvent.Finalize.ERROR_UNKNOWN -> {
217-
ctx.showMessage(ctx.getString(R.string.unable_to_save_video_verbose, event.error))
218-
return@start
219-
}
220-
else -> {
221-
ctx.showMessage(ctx.getString(R.string.error_during_recording, event.error))
208+
if (event is VideoRecordEvent.Finalize) {
209+
afterRecordingStops()
210+
211+
camConfig.mPlayer.playVRStopSound()
212+
213+
if (event.hasError()) {
214+
when (event.error) {
215+
VideoRecordEvent.Finalize.ERROR_NO_VALID_DATA -> {
216+
ctx.showMessage(R.string.recording_too_short_to_be_saved)
217+
return@start
218+
}
219+
VideoRecordEvent.Finalize.ERROR_ENCODING_FAILED,
220+
VideoRecordEvent.Finalize.ERROR_RECORDER_ERROR,
221+
VideoRecordEvent.Finalize.ERROR_UNKNOWN -> {
222+
ctx.showMessage(ctx.getString(R.string.unable_to_save_video_verbose, event.error))
223+
return@start
224+
}
225+
else -> {
226+
ctx.showMessage(ctx.getString(R.string.error_during_recording, event.error))
227+
}
222228
}
223229
}
224-
}
225230

226-
val uri = recordingCtx.uri
231+
val uri = recordingCtx.uri
227232

228-
if (recordingCtx.isPendingMediaStoreUri) {
229-
try {
230-
removePendingFlagFromUri(ctx.contentResolver, uri)
231-
} catch (e: Exception) {
232-
ctx.showMessage(R.string.unable_to_save_video)
233+
if (recordingCtx.isPendingMediaStoreUri) {
234+
try {
235+
removePendingFlagFromUri(ctx.contentResolver, uri)
236+
} catch (e: Exception) {
237+
ctx.showMessage(R.string.unable_to_save_video)
238+
}
233239
}
234-
}
235240

236-
if (recordingCtx.shouldAddToGallery) {
237-
val item = CapturedItem(ITEM_TYPE_VIDEO, dateString, uri)
238-
camConfig.updateLastCapturedItem(item)
241+
if (recordingCtx.shouldAddToGallery) {
242+
val item = CapturedItem(ITEM_TYPE_VIDEO, dateString, uri)
243+
camConfig.updateLastCapturedItem(item)
239244

240-
ctx.updateThumbnail()
245+
ctx.updateThumbnail()
241246

242-
if (ctx is SecureMainActivity) {
243-
ctx.capturedItems.add(item)
247+
if (ctx is SecureMainActivity) {
248+
ctx.capturedItems.add(item)
249+
}
244250
}
245-
}
246251

247-
if (ctx is VideoCaptureActivity) {
248-
ctx.afterRecording(uri)
252+
if (ctx is VideoCaptureActivity) {
253+
ctx.afterRecording(uri)
254+
}
249255
}
250256
}
251-
}
252257

253-
try {
254-
// FileDescriptorOutputOptions doc says that the file descriptor should be closed by the
255-
// caller, and that it's safe to do so as soon as pendingRecording.start() returns
256-
recordingCtx.fileDescriptor.close()
257-
} catch (e: Exception) {
258-
e.printStackTrace()
258+
try {
259+
// FileDescriptorOutputOptions doc says that the file descriptor should be closed by the
260+
// caller, and that it's safe to do so as soon as pendingRecording.start() returns
261+
recordingCtx.fileDescriptor.close()
262+
} catch (e: Exception) {
263+
e.printStackTrace()
264+
}
259265
}
260266
}
261267
}

0 commit comments

Comments
 (0)