diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index e4e6640..89df173 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -336,6 +336,9 @@ data class AudioRecorderSettings( 7 to "OPUS", ) val ENCODER_SUPPORTED_OUTPUT_FORMATS_MAP: Map> = mutableMapOf( + MediaRecorder.AudioEncoder.DEFAULT to arrayOf( + MediaRecorder.OutputFormat.DEFAULT, + ), MediaRecorder.AudioEncoder.AAC to arrayOf( MediaRecorder.OutputFormat.THREE_GPP, MediaRecorder.OutputFormat.MPEG_4, diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index b54f5b7..bca33ec 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -1,13 +1,16 @@ package app.myzel394.alibi.services import android.media.MediaRecorder +import android.media.MediaRecorder.OnErrorListener import android.os.Build +import java.lang.IllegalStateException class AudioRecorderService: IntervalRecorderService() { var amplitudesAmount = 1000 var recorder: MediaRecorder? = null private set + var onError: () -> Unit = {} val filePath: String get() = "$folder/$counter.${settings!!.fileExtension}" @@ -24,6 +27,9 @@ class AudioRecorderService: IntervalRecorderService() { setAudioEncoder(settings!!.encoder) setAudioEncodingBitRate(settings!!.bitRate) setAudioSamplingRate(settings!!.samplingRate) + setOnErrorListener(OnErrorListener { _, _, _ -> + onError() + }) } } @@ -45,8 +51,12 @@ class AudioRecorderService: IntervalRecorderService() { resetRecorder() - newRecorder.start() - recorder = newRecorder + try { + recorder = newRecorder + newRecorder.start() + } catch (error: RuntimeException) { + onError() + } } override fun pause() { diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 9e91a5a..939f933 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -131,6 +131,7 @@ abstract class RecorderService: Service() { onStateChange?.invoke(newState) } + // Must be immediately called after creating the service! fun startRecording() { recordingStart = LocalDateTime.now() @@ -141,12 +142,6 @@ abstract class RecorderService: Service() { changeState(RecorderState.RECORDING) } - override fun onCreate() { - super.onCreate() - - startRecording() - } - override fun onDestroy() { super.onDestroy() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/RecordingStatus.kt index 300450b..f1d832b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/RecordingStatus.kt @@ -33,6 +33,7 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -200,7 +201,9 @@ fun RecordingStatus( contentDescription = label }, onClick = { - audioRecorder.stopRecording(context) + runCatching { + audioRecorder.stopRecording(context) + } audioRecorder.onRecordingSave() }, ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 23dabc9..e0abd86 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -4,6 +4,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection +import android.media.MediaRecorder import android.os.IBinder import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -44,6 +45,7 @@ class AudioRecorderModel: ViewModel() { private set var onRecordingSave: () -> Unit = {} + var onError: () -> Unit = {} private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { @@ -58,10 +60,17 @@ class AudioRecorderModel: ViewModel() { amplitudes = amps onAmplitudeChange() } + recorder.onError = { + recorderService!!.createLastRecording() + onError() + } + }.also { + it.startRecording() + + recorderState = it.state + recordingTime = it.recordingTime + amplitudes = it.amplitudes } - recorderState = recorderService!!.state - recordingTime = recorderService!!.recordingTime - amplitudes = recorderService!!.amplitudes } override fun onServiceDisconnected(arg0: ComponentName) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt index 9d856cb..0db7196 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt @@ -10,7 +10,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Memory import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -45,13 +48,15 @@ fun AudioRecorder( navController: NavController, audioRecorder: AudioRecorderModel, ) { + val context = LocalContext.current val settings = rememberSettings() val saveFile = rememberFileSaverDialog(settings.audioRecorderSettings.getMimeType()) val scope = rememberCoroutineScope() var isProcessingAudio by remember { mutableStateOf(false) } + var showRecorderError by remember { mutableStateOf(false) } - LaunchedEffect(Unit) { + DisposableEffect(Unit) { audioRecorder.onRecordingSave = { scope.launch { isProcessingAudio = true @@ -67,6 +72,16 @@ fun AudioRecorder( } } } + audioRecorder.onError = { + // No need to save last recording as it's done automatically on error + audioRecorder.stopRecording(context, saveAsLastRecording = false) + showRecorderError = true + } + + onDispose { + audioRecorder.onRecordingSave = {} + audioRecorder.onError = {} + } } if (isProcessingAudio) @@ -96,6 +111,40 @@ fun AudioRecorder( }, confirmButton = {} ) + if (showRecorderError) + AlertDialog( + onDismissRequest = { showRecorderError = false }, + icon = { + Icon( + Icons.Default.Warning, + contentDescription = null, + ) + }, + title = { + Text(stringResource(R.string.ui_audioRecorder_error_recording_title)) + }, + text = { + Text(stringResource(R.string.ui_audioRecorder_error_recording_description)) + }, + dismissButton = { + Button( + onClick = { showRecorderError = false }, + colors = ButtonDefaults.textButtonColors(), + ) { + Text(stringResource(R.string.dialog_close_cancel_label)) + } + }, + confirmButton = { + Button( + onClick = { + audioRecorder.onRecordingSave() + }, + colors = ButtonDefaults.textButtonColors(), + ) { + Text(stringResource(R.string.ui_audioRecorder_action_save_label)) + } + } + ) Scaffold( topBar = { TopAppBar( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ae619fc..303afad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,4 +60,6 @@ Auto Recording paused Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? \ No newline at end of file