mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 23:05:26 +02:00
feat: Add recording error handling
This commit is contained in:
parent
7620a065b4
commit
66b1d8a88d
@ -336,6 +336,9 @@ data class AudioRecorderSettings(
|
||||
7 to "OPUS",
|
||||
)
|
||||
val ENCODER_SUPPORTED_OUTPUT_FORMATS_MAP: Map<Int, Array<Int>> = mutableMapOf(
|
||||
MediaRecorder.AudioEncoder.DEFAULT to arrayOf(
|
||||
MediaRecorder.OutputFormat.DEFAULT,
|
||||
),
|
||||
MediaRecorder.AudioEncoder.AAC to arrayOf(
|
||||
MediaRecorder.OutputFormat.THREE_GPP,
|
||||
MediaRecorder.OutputFormat.MPEG_4,
|
||||
|
@ -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() {
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
},
|
||||
) {
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -60,4 +60,6 @@
|
||||
<string name="ui_settings_value_auto_label">Auto</string>
|
||||
<string name="ui_audioRecorder_state_paused_title">Recording paused</string>
|
||||
<string name="ui_audioRecorder_state_paused_description">Audio Recording has been paused</string>
|
||||
<string name="ui_audioRecorder_error_recording_title">An error occured</string>
|
||||
<string name="ui_audioRecorder_error_recording_description">Alibi encountered an error during recording. Would you like to try saving the recording?</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user