mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-19 07:15:25 +02:00
refactor: Improving Recorder events handling
This commit is contained in:
parent
ce50ed1d68
commit
3cba3382f3
@ -253,9 +253,7 @@ class VideoRecorderService :
|
|||||||
targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate,
|
targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate,
|
||||||
targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate,
|
targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate,
|
||||||
quality = appSettings.videoRecorderSettings.getQualitySelector()
|
quality = appSettings.videoRecorderSettings.getQualitySelector()
|
||||||
?: QualitySelector.from(
|
?: QualitySelector.from(Quality.HIGHEST),
|
||||||
Quality.HIGHEST
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package app.myzel394.alibi.ui.components.RecorderScreen.atoms
|
||||||
|
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
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.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import app.myzel394.alibi.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RecorderErrorDialog(
|
||||||
|
onClose: () -> Unit,
|
||||||
|
onSave: () -> Unit,
|
||||||
|
) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onClose,
|
||||||
|
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 = onClose,
|
||||||
|
colors = ButtonDefaults.textButtonColors(),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.dialog_close_cancel_label))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = onSave,
|
||||||
|
colors = ButtonDefaults.textButtonColors(),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.ui_audioRecorder_action_save_label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,213 @@
|
|||||||
|
package app.myzel394.alibi.ui.components.RecorderScreen.atoms
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.material3.SnackbarDuration
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.SnackbarResult
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import app.myzel394.alibi.R
|
||||||
|
import app.myzel394.alibi.dataStore
|
||||||
|
import app.myzel394.alibi.db.AppSettings
|
||||||
|
import app.myzel394.alibi.db.RecordingInformation
|
||||||
|
import app.myzel394.alibi.helpers.AudioBatchesFolder
|
||||||
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
|
import app.myzel394.alibi.services.IntervalRecorderService
|
||||||
|
import app.myzel394.alibi.ui.models.AudioRecorderModel
|
||||||
|
import app.myzel394.alibi.ui.models.BaseRecorderModel
|
||||||
|
import app.myzel394.alibi.ui.models.VideoRecorderModel
|
||||||
|
import app.myzel394.alibi.ui.utils.rememberFileSaverDialog
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
typealias RecorderModel = BaseRecorderModel<
|
||||||
|
IntervalRecorderService.Settings,
|
||||||
|
RecordingInformation,
|
||||||
|
IntervalRecorderService<IntervalRecorderService.Settings, RecordingInformation>,
|
||||||
|
BatchesFolder?
|
||||||
|
>
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RecorderEventsHandler(
|
||||||
|
settings: AppSettings,
|
||||||
|
snackbarHostState: SnackbarHostState,
|
||||||
|
audioRecorder: AudioRecorderModel,
|
||||||
|
videoRecorder: VideoRecorderModel,
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
val dataStore = context.dataStore
|
||||||
|
|
||||||
|
var isProcessing by remember { mutableStateOf(false) }
|
||||||
|
var showRecorderError by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val saveFile = rememberFileSaverDialog(
|
||||||
|
settings.audioRecorderSettings.getMimeType()
|
||||||
|
) {
|
||||||
|
if (settings.deleteRecordingsImmediately) {
|
||||||
|
audioRecorder.batchesFolder!!.deleteRecordings()
|
||||||
|
videoRecorder.batchesFolder!!.deleteRecordings()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!audioRecorder.batchesFolder!!.hasRecordingsAvailable()
|
||||||
|
|| !videoRecorder.batchesFolder!!.hasRecordingsAvailable()
|
||||||
|
) {
|
||||||
|
scope.launch {
|
||||||
|
dataStore.updateData {
|
||||||
|
it.setLastRecording(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveAsLastRecording(
|
||||||
|
recorder: RecorderModel
|
||||||
|
) {
|
||||||
|
if (!settings.deleteRecordingsImmediately) {
|
||||||
|
scope.launch {
|
||||||
|
dataStore.updateData {
|
||||||
|
it.setLastRecording(
|
||||||
|
recorder.recorderService!!.getRecordingInformation()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val successMessage = stringResource(R.string.ui_audioRecorder_action_save_success)
|
||||||
|
val openMessage = stringResource(R.string.ui_audioRecorder_action_save_openFolder)
|
||||||
|
|
||||||
|
fun openFolder(uri: Uri) {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSnackbar(uri: Uri) {
|
||||||
|
scope.launch {
|
||||||
|
val result = snackbarHostState.showSnackbar(
|
||||||
|
message = successMessage,
|
||||||
|
actionLabel = openMessage,
|
||||||
|
duration = SnackbarDuration.Short,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result == SnackbarResult.ActionPerformed) {
|
||||||
|
openFolder(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveRecording(recorder: RecorderModel) {
|
||||||
|
scope.launch {
|
||||||
|
isProcessing = true
|
||||||
|
|
||||||
|
// Give the user some time to see the processing dialog
|
||||||
|
delay(100)
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
val recording =
|
||||||
|
// When new recording created
|
||||||
|
recorder.recorderService?.getRecordingInformation()
|
||||||
|
// When recording is loaded from lastRecording
|
||||||
|
?: settings.lastRecording
|
||||||
|
?: throw Exception("No recording information available")
|
||||||
|
val batchesFolder =
|
||||||
|
AudioBatchesFolder.importFromFolder(recording.folderPath, context)
|
||||||
|
|
||||||
|
batchesFolder.concatenate(
|
||||||
|
recording.recordingStart,
|
||||||
|
recording.fileExtension,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Save file
|
||||||
|
val name = batchesFolder.getName(
|
||||||
|
recording.recordingStart,
|
||||||
|
recording.fileExtension,
|
||||||
|
)
|
||||||
|
|
||||||
|
when (batchesFolder.type) {
|
||||||
|
BatchesFolder.BatchType.INTERNAL -> {
|
||||||
|
saveFile(
|
||||||
|
batchesFolder.asInternalGetOutputFile(
|
||||||
|
recording.recordingStart,
|
||||||
|
recording.fileExtension,
|
||||||
|
), name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchesFolder.BatchType.CUSTOM -> {
|
||||||
|
showSnackbar(batchesFolder.customFolder!!.uri)
|
||||||
|
|
||||||
|
if (settings.deleteRecordingsImmediately) {
|
||||||
|
batchesFolder.deleteRecordings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error: Exception) {
|
||||||
|
Log.getStackTraceString(error)
|
||||||
|
} finally {
|
||||||
|
isProcessing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register audio recorder events
|
||||||
|
DisposableEffect(key1 = audioRecorder, key2 = settings) {
|
||||||
|
audioRecorder.onRecordingSave = {
|
||||||
|
saveAsLastRecording(audioRecorder as RecorderModel)
|
||||||
|
|
||||||
|
saveRecording(audioRecorder)
|
||||||
|
}
|
||||||
|
audioRecorder.onError = {
|
||||||
|
saveAsLastRecording(audioRecorder as RecorderModel)
|
||||||
|
|
||||||
|
showRecorderError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
audioRecorder.onRecordingSave = {}
|
||||||
|
audioRecorder.onError = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register video recorder events
|
||||||
|
DisposableEffect(key1 = videoRecorder, key2 = settings) {
|
||||||
|
videoRecorder.onRecordingSave = {
|
||||||
|
saveAsLastRecording(videoRecorder as RecorderModel)
|
||||||
|
|
||||||
|
saveRecording(videoRecorder)
|
||||||
|
}
|
||||||
|
videoRecorder.onError = {
|
||||||
|
saveAsLastRecording(videoRecorder as RecorderModel)
|
||||||
|
|
||||||
|
showRecorderError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
videoRecorder.onRecordingSave = {}
|
||||||
|
videoRecorder.onError = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProcessing)
|
||||||
|
RecorderProcessingDialog()
|
||||||
|
|
||||||
|
if (showRecorderError)
|
||||||
|
RecorderErrorDialog(
|
||||||
|
onClose = {
|
||||||
|
showRecorderError = false
|
||||||
|
},
|
||||||
|
onSave = {
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package app.myzel394.alibi.ui.components.RecorderScreen.atoms
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Memory
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import app.myzel394.alibi.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RecorderProcessingDialog() {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { },
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Memory,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_title),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_description),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
LinearProgressIndicator()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {}
|
||||||
|
)
|
||||||
|
}
|
@ -75,7 +75,9 @@ fun AudioRecordingStatus(
|
|||||||
isPaused = audioRecorder.isPaused,
|
isPaused = audioRecorder.isPaused,
|
||||||
onDelete = {
|
onDelete = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
audioRecorder.stopRecording(context)
|
audioRecorder.stopRecording(context)
|
||||||
|
}
|
||||||
audioRecorder.batchesFolder!!.deleteRecordings()
|
audioRecorder.batchesFolder!!.deleteRecordings()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -88,9 +90,7 @@ fun AudioRecordingStatus(
|
|||||||
},
|
},
|
||||||
onSave = {
|
onSave = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
runCatching {
|
|
||||||
audioRecorder.stopRecording(context)
|
audioRecorder.stopRecording(context)
|
||||||
}
|
|
||||||
audioRecorder.onRecordingSave()
|
audioRecorder.onRecordingSave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,6 @@ fun VideoRecordingStatus(
|
|||||||
modifier = Modifier.padding(bottom = 32.dp),
|
modifier = Modifier.padding(bottom = 32.dp),
|
||||||
) {
|
) {
|
||||||
val cameraControl = videoRecorder.recorderService!!.cameraControl!!
|
val cameraControl = videoRecorder.recorderService!!.cameraControl!!
|
||||||
println("cameraControl.hasTorchAvailable(): ${cameraControl.hasTorchAvailable()}")
|
|
||||||
println("videoRecorder: ${videoRecorder.recorderService?.cameraControl}")
|
|
||||||
if (cameraControl.hasTorchAvailable()) {
|
if (cameraControl.hasTorchAvailable()) {
|
||||||
val isTorchEnabled = cameraControl.isTorchEnabled()
|
val isTorchEnabled = cameraControl.isTorchEnabled()
|
||||||
|
|
||||||
@ -101,7 +99,9 @@ fun VideoRecordingStatus(
|
|||||||
isPaused = videoRecorder.isPaused,
|
isPaused = videoRecorder.isPaused,
|
||||||
onDelete = {
|
onDelete = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
runCatching {
|
||||||
videoRecorder.stopRecording(context)
|
videoRecorder.stopRecording(context)
|
||||||
|
}
|
||||||
videoRecorder.batchesFolder!!.deleteRecordings()
|
videoRecorder.batchesFolder!!.deleteRecordings()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -114,9 +114,7 @@ fun VideoRecordingStatus(
|
|||||||
},
|
},
|
||||||
onSave = {
|
onSave = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
runCatching {
|
|
||||||
videoRecorder.stopRecording(context)
|
videoRecorder.stopRecording(context)
|
||||||
}
|
|
||||||
videoRecorder.onRecordingSave()
|
videoRecorder.onRecordingSave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,4 @@ class VideoRecorderModel :
|
|||||||
putExtra("cameraID", cameraID)
|
putExtra("cameraID", cameraID)
|
||||||
putExtra("enableAudio", enableAudio)
|
putExtra("enableAudio", enableAudio)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun startRecording(context: Context, settings: AppSettings) {
|
|
||||||
super.startRecording(context, settings)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,57 +1,37 @@
|
|||||||
package app.myzel394.alibi.ui.screens
|
package app.myzel394.alibi.ui.screens
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
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.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.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Snackbar
|
import androidx.compose.material3.Snackbar
|
||||||
import androidx.compose.material3.SnackbarDuration
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import app.myzel394.alibi.ui.components.RecorderScreen.organisms.AudioRecordingStatus
|
import app.myzel394.alibi.ui.components.RecorderScreen.organisms.AudioRecordingStatus
|
||||||
import app.myzel394.alibi.ui.components.RecorderScreen.organisms.StartRecording
|
import app.myzel394.alibi.ui.components.RecorderScreen.organisms.StartRecording
|
||||||
import app.myzel394.alibi.ui.enums.Screen
|
import app.myzel394.alibi.ui.enums.Screen
|
||||||
import app.myzel394.alibi.ui.utils.rememberFileSaverDialog
|
|
||||||
import app.myzel394.alibi.R
|
import app.myzel394.alibi.R
|
||||||
import app.myzel394.alibi.dataStore
|
import app.myzel394.alibi.dataStore
|
||||||
import app.myzel394.alibi.db.AppSettings
|
import app.myzel394.alibi.db.AppSettings
|
||||||
import app.myzel394.alibi.helpers.AudioBatchesFolder
|
import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderEventsHandler
|
||||||
import app.myzel394.alibi.helpers.BatchesFolder
|
|
||||||
import app.myzel394.alibi.ui.components.RecorderScreen.organisms.VideoRecordingStatus
|
import app.myzel394.alibi.ui.components.RecorderScreen.organisms.VideoRecordingStatus
|
||||||
import app.myzel394.alibi.ui.effects.rememberSettings
|
import app.myzel394.alibi.ui.effects.rememberSettings
|
||||||
import app.myzel394.alibi.ui.models.AudioRecorderModel
|
import app.myzel394.alibi.ui.models.AudioRecorderModel
|
||||||
import app.myzel394.alibi.ui.models.VideoRecorderModel
|
import app.myzel394.alibi.ui.models.VideoRecorderModel
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -63,197 +43,14 @@ fun RecorderScreen(
|
|||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val dataStore = context.dataStore
|
|
||||||
val settings = rememberSettings()
|
val settings = rememberSettings()
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val saveFile = rememberFileSaverDialog(
|
RecorderEventsHandler(
|
||||||
settings.audioRecorderSettings.getMimeType()
|
settings = settings,
|
||||||
) {
|
snackbarHostState = snackbarHostState,
|
||||||
if (settings.deleteRecordingsImmediately) {
|
audioRecorder = audioRecorder,
|
||||||
audioRecorder.batchesFolder!!.deleteRecordings()
|
videoRecorder = videoRecorder,
|
||||||
}
|
|
||||||
|
|
||||||
if (!audioRecorder.batchesFolder!!.hasRecordingsAvailable()) {
|
|
||||||
scope.launch {
|
|
||||||
dataStore.updateData {
|
|
||||||
it.setLastRecording(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isProcessing by remember { mutableStateOf(false) }
|
|
||||||
var showRecorderError by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
fun saveAsLastRecording() {
|
|
||||||
if (!settings.deleteRecordingsImmediately) {
|
|
||||||
scope.launch {
|
|
||||||
dataStore.updateData {
|
|
||||||
it.setLastRecording(
|
|
||||||
audioRecorder.recorderService!!.getRecordingInformation()
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val successMessage = stringResource(R.string.ui_audioRecorder_action_save_success)
|
|
||||||
val openMessage = stringResource(R.string.ui_audioRecorder_action_save_openFolder)
|
|
||||||
|
|
||||||
fun openFolder(uri: Uri) {
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
|
||||||
|
|
||||||
context.startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showSnackbar(uri: Uri) {
|
|
||||||
scope.launch {
|
|
||||||
val result = snackbarHostState.showSnackbar(
|
|
||||||
message = successMessage,
|
|
||||||
actionLabel = openMessage,
|
|
||||||
duration = SnackbarDuration.Short,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (result == SnackbarResult.ActionPerformed) {
|
|
||||||
openFolder(uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveRecording() {
|
|
||||||
scope.launch {
|
|
||||||
isProcessing = true
|
|
||||||
|
|
||||||
// Give the user some time to see the processing dialog
|
|
||||||
delay(100)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val recording = audioRecorder.recorderService?.getRecordingInformation()
|
|
||||||
?: settings.lastRecording
|
|
||||||
?: throw Exception("No recording information available")
|
|
||||||
val batchesFolder =
|
|
||||||
AudioBatchesFolder.importFromFolder(recording.folderPath, context)
|
|
||||||
|
|
||||||
batchesFolder.concatenate(
|
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Save file
|
|
||||||
val name = batchesFolder.getName(
|
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension,
|
|
||||||
)
|
|
||||||
|
|
||||||
when (batchesFolder.type) {
|
|
||||||
BatchesFolder.BatchType.INTERNAL -> {
|
|
||||||
saveFile(
|
|
||||||
batchesFolder.asInternalGetOutputFile(
|
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension,
|
|
||||||
), name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BatchesFolder.BatchType.CUSTOM -> {
|
|
||||||
showSnackbar(batchesFolder.customFolder!!.uri)
|
|
||||||
|
|
||||||
if (settings.deleteRecordingsImmediately) {
|
|
||||||
batchesFolder.deleteRecordings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error: Exception) {
|
|
||||||
Log.getStackTraceString(error)
|
|
||||||
} finally {
|
|
||||||
isProcessing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposableEffect(key1 = audioRecorder, key2 = settings) {
|
|
||||||
audioRecorder.onRecordingSave = onRecordingSave@{
|
|
||||||
saveAsLastRecording()
|
|
||||||
|
|
||||||
saveRecording()
|
|
||||||
}
|
|
||||||
audioRecorder.onError = {
|
|
||||||
saveAsLastRecording()
|
|
||||||
|
|
||||||
showRecorderError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onDispose {
|
|
||||||
audioRecorder.onRecordingSave = {}
|
|
||||||
audioRecorder.onError = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isProcessing)
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = { },
|
|
||||||
icon = {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Memory,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_title),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_description),
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
|
||||||
LinearProgressIndicator()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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 = {
|
|
||||||
showRecorderError = false
|
|
||||||
|
|
||||||
saveRecording()
|
|
||||||
},
|
|
||||||
colors = ButtonDefaults.textButtonColors(),
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.ui_audioRecorder_action_save_label))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// TopAppBar and AudioRecordingStart should be hidden when
|
// TopAppBar and AudioRecordingStart should be hidden when
|
||||||
// the video preview is visible.
|
// the video preview is visible.
|
||||||
@ -315,7 +112,8 @@ fun RecorderScreen(
|
|||||||
audioRecorder = audioRecorder,
|
audioRecorder = audioRecorder,
|
||||||
videoRecorder = videoRecorder,
|
videoRecorder = videoRecorder,
|
||||||
appSettings = appSettings,
|
appSettings = appSettings,
|
||||||
onSaveLastRecording = ::saveRecording,
|
onSaveLastRecording = {
|
||||||
|
},
|
||||||
showAudioRecorder = topBarVisible,
|
showAudioRecorder = topBarVisible,
|
||||||
onHideTopBar = {
|
onHideTopBar = {
|
||||||
topBarVisible = false
|
topBarVisible = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user