mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 23:05:26 +02:00
fix: Properly reset AudioRecorder
This commit is contained in:
parent
1269c6cb00
commit
9a43afbe3e
@ -11,9 +11,6 @@ import android.os.Handler
|
|||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.animation.core.Animatable
|
|
||||||
import androidx.compose.animation.core.LinearEasing
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
@ -35,7 +32,7 @@ import java.time.ZoneId
|
|||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID
|
||||||
|
|
||||||
const val AMPLITUDE_UPDATE_INTERVAL = 100L
|
const val AMPLITUDE_UPDATE_INTERVAL = 100L
|
||||||
|
|
||||||
@ -47,26 +44,20 @@ class RecorderService: Service() {
|
|||||||
|
|
||||||
private var mediaRecorder: MediaRecorder? = null
|
private var mediaRecorder: MediaRecorder? = null
|
||||||
private var onError: MediaRecorder.OnErrorListener? = null
|
private var onError: MediaRecorder.OnErrorListener? = null
|
||||||
private var onStateChange: (RecorderState) -> Unit = {}
|
|
||||||
private var onAmplitudeUpdate: () -> Unit = {}
|
private var onAmplitudeUpdate: () -> Unit = {}
|
||||||
|
|
||||||
private var counter = 0
|
private var counter = 0
|
||||||
|
|
||||||
lateinit var settings: Settings
|
lateinit var settings: Settings
|
||||||
|
|
||||||
var recordingStart = mutableStateOf<LocalDateTime?>(null)
|
|
||||||
private set
|
|
||||||
var fileFolder: String? = null
|
var fileFolder: String? = null
|
||||||
private set
|
private set
|
||||||
var recordingState: RecorderState = RecorderState.IDLE
|
val isRecording = mutableStateOf(false)
|
||||||
private set
|
|
||||||
val isRecording: Boolean
|
|
||||||
get() = recordingStart.value != null
|
|
||||||
|
|
||||||
val filePaths = mutableListOf<String>()
|
val filePaths = mutableListOf<String>()
|
||||||
val amplitudes = mutableStateListOf<Int>()
|
val amplitudes = mutableStateListOf<Int>()
|
||||||
|
|
||||||
var originalRecordingStart: LocalDateTime? = null
|
var recordingStart: LocalDateTime? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun onBind(p0: Intent?): IBinder = binder
|
override fun onBind(p0: Intent?): IBinder = binder
|
||||||
@ -90,21 +81,26 @@ class RecorderService: Service() {
|
|||||||
scope.cancel()
|
scope.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnErrorListener(onError: MediaRecorder.OnErrorListener) {
|
|
||||||
this.onError = onError
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setOnAmplitudeUpdateListener(onAmplitudeUpdate: () -> Unit) {
|
fun setOnAmplitudeUpdateListener(onAmplitudeUpdate: () -> Unit) {
|
||||||
this.onAmplitudeUpdate = onAmplitudeUpdate
|
this.onAmplitudeUpdate = onAmplitudeUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnStateChangeListener(onStateChange: (RecorderState) -> Unit) {
|
fun reset() {
|
||||||
this.onStateChange = onStateChange
|
recordingStart = null
|
||||||
|
counter = 0
|
||||||
|
amplitudes.clear()
|
||||||
|
isRecording.value = false
|
||||||
|
|
||||||
|
filePaths.forEach {
|
||||||
|
File(it).delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
filePaths.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun concatenateFiles(forceConcatenation: Boolean = false): File {
|
fun concatenateFiles(forceConcatenation: Boolean = false): File {
|
||||||
val paths = filePaths.joinToString("|")
|
val paths = filePaths.joinToString("|")
|
||||||
val outputFile = "$fileFolder/${originalRecordingStart!!.format(DateTimeFormatter.ISO_DATE_TIME)}.${settings.fileExtension}"
|
val outputFile = "$fileFolder/${recordingStart!!.format(DateTimeFormatter.ISO_DATE_TIME)}.${settings.fileExtension}"
|
||||||
|
|
||||||
if (File(outputFile).exists() && !forceConcatenation) {
|
if (File(outputFile).exists() && !forceConcatenation) {
|
||||||
return File(outputFile)
|
return File(outputFile)
|
||||||
@ -132,7 +128,7 @@ class RecorderService: Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateAmplitude() {
|
private fun updateAmplitude() {
|
||||||
if (!isRecording || mediaRecorder == null) {
|
if (!isRecording.value || mediaRecorder == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +140,7 @@ class RecorderService: Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startNewRecording() {
|
private fun startNewRecording() {
|
||||||
if (!isRecording) {
|
if (!isRecording.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,17 +201,15 @@ class RecorderService: Service() {
|
|||||||
|
|
||||||
|
|
||||||
private fun start() {
|
private fun start() {
|
||||||
amplitudes.clear()
|
reset()
|
||||||
filePaths.clear()
|
|
||||||
// Create folder
|
// Create folder
|
||||||
File(this.fileFolder!!).mkdirs()
|
File(this.fileFolder!!).mkdirs()
|
||||||
|
|
||||||
scope.launch {
|
scope.launch {
|
||||||
dataStore.data.collectLatest { preferenceSettings ->
|
dataStore.data.collectLatest { preferenceSettings ->
|
||||||
settings = Settings.from(preferenceSettings.audioRecorderSettings)
|
settings = Settings.from(preferenceSettings.audioRecorderSettings)
|
||||||
recordingState = RecorderState.RECORDING
|
recordingStart = LocalDateTime.now()
|
||||||
recordingStart.value = LocalDateTime.now()
|
isRecording.value = true
|
||||||
originalRecordingStart = recordingStart.value
|
|
||||||
|
|
||||||
showNotification()
|
showNotification()
|
||||||
startNewRecording()
|
startNewRecording()
|
||||||
@ -225,22 +219,20 @@ class RecorderService: Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun stop() {
|
private fun stop() {
|
||||||
recordingState = RecorderState.IDLE
|
|
||||||
|
|
||||||
mediaRecorder?.apply {
|
mediaRecorder?.apply {
|
||||||
runCatching {
|
runCatching {
|
||||||
stop()
|
stop()
|
||||||
release()
|
release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recordingStart.value = null
|
isRecording.value = false
|
||||||
|
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNotification() {
|
private fun showNotification() {
|
||||||
if (recordingStart.value == null) {
|
if (!isRecording.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +246,7 @@ class RecorderService: Service() {
|
|||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setUsesChronometer(true)
|
.setUsesChronometer(true)
|
||||||
.setChronometerCountDown(false)
|
.setChronometerCountDown(false)
|
||||||
.setWhen(Date.from(recordingStart.value!!.atZone(ZoneId.systemDefault()).toInstant()).time)
|
.setWhen(Date.from(recordingStart!!.atZone(ZoneId.systemDefault()).toInstant()).time)
|
||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@ -267,10 +259,10 @@ class RecorderService: Service() {
|
|||||||
|
|
||||||
// To avoid int overflow, we'll use the number of seconds since 2023-01-01 01:01:01
|
// To avoid int overflow, we'll use the number of seconds since 2023-01-01 01:01:01
|
||||||
private fun getNotificationId(): Int {
|
private fun getNotificationId(): Int {
|
||||||
val offset = ZoneId.of("UTC").rules.getOffset(recordingStart.value)
|
val offset = ZoneId.of("UTC").rules.getOffset(recordingStart!!)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
recordingStart.value!!.toEpochSecond(offset) -
|
recordingStart!!.toEpochSecond(offset) -
|
||||||
LocalDateTime.of(2023, 1, 1, 1, 1).toEpochSecond(offset)
|
LocalDateTime.of(2023, 1, 1, 1, 1).toEpochSecond(offset)
|
||||||
).toInt()
|
).toInt()
|
||||||
}
|
}
|
||||||
@ -286,12 +278,6 @@ class RecorderService: Service() {
|
|||||||
STOP,
|
STOP,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RecorderState {
|
|
||||||
IDLE,
|
|
||||||
RECORDING,
|
|
||||||
PAUSED,
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getRandomFileFolder(context: Context): String {
|
fun getRandomFileFolder(context: Context): String {
|
||||||
// uuid
|
// uuid
|
||||||
@ -302,7 +288,7 @@ class RecorderService: Service() {
|
|||||||
|
|
||||||
fun startService(context: Context, connection: ServiceConnection?) {
|
fun startService(context: Context, connection: ServiceConnection?) {
|
||||||
Intent(context, RecorderService::class.java).also { intent ->
|
Intent(context, RecorderService::class.java).also { intent ->
|
||||||
intent.action = RecorderService.Actions.START.toString()
|
intent.action = Actions.START.toString()
|
||||||
|
|
||||||
ContextCompat.startForegroundService(context, intent)
|
ContextCompat.startForegroundService(context, intent)
|
||||||
|
|
||||||
@ -314,7 +300,7 @@ class RecorderService: Service() {
|
|||||||
|
|
||||||
fun stopService(context: Context) {
|
fun stopService(context: Context) {
|
||||||
Intent(context, RecorderService::class.java).also { intent ->
|
Intent(context, RecorderService::class.java).also { intent ->
|
||||||
intent.action = RecorderService.Actions.STOP.toString()
|
intent.action = Actions.STOP.toString()
|
||||||
|
|
||||||
context.startService(intent)
|
context.startService(intent)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Cancel
|
import androidx.compose.material.icons.filled.Cancel
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Save
|
import androidx.compose.material.icons.filled.Save
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@ -55,7 +57,7 @@ fun RecordingStatus(
|
|||||||
|
|
||||||
var now by remember { mutableStateOf(LocalDateTime.now()) }
|
var now by remember { mutableStateOf(LocalDateTime.now()) }
|
||||||
|
|
||||||
val start = service.recordingStart.value!!
|
val start = service.recordingStart!!
|
||||||
val duration = now.toEpochSecond(ZoneId.systemDefault().rules.getOffset(now)) - start.toEpochSecond(ZoneId.systemDefault().rules.getOffset(start))
|
val duration = now.toEpochSecond(ZoneId.systemDefault().rules.getOffset(now)) - start.toEpochSecond(ZoneId.systemDefault().rules.getOffset(start))
|
||||||
val progress = duration / (service.settings.maxDuration / 1000f)
|
val progress = duration / (service.settings.maxDuration / 1000f)
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ fun RecordingStatus(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
val distance = Duration.between(service.recordingStart.value, now).toMillis()
|
val distance = Duration.between(service.recordingStart, now).toMillis()
|
||||||
|
|
||||||
Pulsating {
|
Pulsating {
|
||||||
Box(
|
Box(
|
||||||
@ -104,18 +106,81 @@ fun RecordingStatus(
|
|||||||
.width(300.dp)
|
.width(300.dp)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
|
var showDeleteDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
if (showDeleteDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
showDeleteDialog = false
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text("Delete Recording?")
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text("Are you sure you want to delete this recording?")
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.semantics {
|
||||||
|
contentDescription = "Confirm Recording Deletion"
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
showDeleteDialog = false
|
||||||
|
RecorderService.stopService(context)
|
||||||
|
service.reset()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||||
|
Text("Delete")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.semantics {
|
||||||
|
contentDescription = "Cancel Recording Deletion"
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
showDeleteDialog = false
|
||||||
|
},
|
||||||
|
colors = ButtonDefaults.textButtonColors(),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Cancel,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||||
|
Text("Cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.semantics {
|
.semantics {
|
||||||
contentDescription = "Delete Recording"
|
contentDescription = "Delete Recording"
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
RecorderService.stopService(context)
|
showDeleteDialog = true
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.textButtonColors(),
|
colors = ButtonDefaults.textButtonColors(),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Cancel,
|
Icons.Default.Delete,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package app.myzel394.locationtest.ui.components.AudioRecorder.atoms
|
package app.myzel394.locationtest.ui.components.AudioRecorder.atoms
|
||||||
|
|
||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import androidx.compose.foundation.Canvas
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -25,8 +23,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.geometry.Size
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
@ -83,7 +79,7 @@ fun StartRecording(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (service?.originalRecordingStart != null)
|
if (service?.recordingStart != null)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
@ -112,7 +108,7 @@ fun StartRecording(
|
|||||||
.size(ButtonDefaults.IconSize),
|
.size(ButtonDefaults.IconSize),
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||||
Text("Save Recording from ${service.originalRecordingStart!!.format(DateTimeFormatter.ISO_DATE_TIME)}")
|
Text("Save Recording from ${service.recordingStart!!.format(DateTimeFormatter.ISO_DATE_TIME)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -58,9 +58,6 @@ fun AudioRecorder(
|
|||||||
object : ServiceConnection {
|
object : ServiceConnection {
|
||||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
||||||
service = (binder as RecorderService.LocalBinder).getService().also { service ->
|
service = (binder as RecorderService.LocalBinder).getService().also { service ->
|
||||||
service.setOnStateChangeListener {
|
|
||||||
println("asd")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +66,7 @@ fun AudioRecorder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isRecording = service?.isRecording ?: false
|
val isRecording = service?.isRecording?.value ?: false
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
Intent(context, RecorderService::class.java).also { intent ->
|
Intent(context, RecorderService::class.java).also { intent ->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user