mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-19 07:15:25 +02:00
feat: Adding BatchesFolder
This commit is contained in:
parent
7722127796
commit
6adff096d2
@ -4,8 +4,6 @@ import android.content.Context
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContentProviderCompat.requireContext
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import app.myzel394.alibi.db.RecordingInformation
|
import app.myzel394.alibi.db.RecordingInformation
|
||||||
import app.myzel394.alibi.ui.RECORDER_SUBFOLDER_NAME
|
import app.myzel394.alibi.ui.RECORDER_SUBFOLDER_NAME
|
||||||
@ -18,67 +16,32 @@ import java.time.format.DateTimeFormatter
|
|||||||
data class AudioRecorderExporter(
|
data class AudioRecorderExporter(
|
||||||
val recording: RecordingInformation,
|
val recording: RecordingInformation,
|
||||||
) {
|
) {
|
||||||
private fun getFilePaths(context: Context): List<File> =
|
private fun getInternalFilePaths(context: Context): List<File> =
|
||||||
getFolder(context).listFiles()?.filter {
|
getFolder(context)
|
||||||
|
.listFiles()
|
||||||
|
?.filter {
|
||||||
val name = it.nameWithoutExtension
|
val name = it.nameWithoutExtension
|
||||||
|
|
||||||
name.toIntOrNull() != null
|
name.toIntOrNull() != null
|
||||||
}?.toList() ?: emptyList()
|
|
||||||
|
|
||||||
private fun stripConcatenatedFileToExactDuration(
|
|
||||||
outputFile: File
|
|
||||||
) {
|
|
||||||
// Move the concatenated file to a temporary file
|
|
||||||
val rawFile =
|
|
||||||
File("${recording.folderPath}/${outputFile.nameWithoutExtension}-raw.${recording.fileExtension}")
|
|
||||||
outputFile.renameTo(rawFile)
|
|
||||||
|
|
||||||
val command = "-sseof ${recording.maxDuration / -1000} -i $rawFile -y $outputFile"
|
|
||||||
|
|
||||||
val session = FFmpegKit.execute(command)
|
|
||||||
|
|
||||||
if (!ReturnCode.isSuccess(session.returnCode)) {
|
|
||||||
Log.d(
|
|
||||||
"Audio Concatenation",
|
|
||||||
String.format(
|
|
||||||
"Command failed with state %s and rc %s.%s",
|
|
||||||
session.state,
|
|
||||||
session.returnCode,
|
|
||||||
session.failStackTrace,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
throw Exception("Failed to strip concatenated audio")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
?.toList()
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
suspend fun concatenateFiles(
|
suspend fun concatenateFiles(
|
||||||
context: Context,
|
context: Context,
|
||||||
uri: Uri,
|
batchesFolder: BatchesFolder,
|
||||||
folder: DocumentFile,
|
|
||||||
forceConcatenation: Boolean = false,
|
forceConcatenation: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val filePaths = getFilePaths(context)
|
val filePaths = batchesFolder.getBatchesForFFmpeg().joinToString("|")
|
||||||
val paths = filePaths.joinToString("|") {
|
val outputFile =
|
||||||
it.path
|
batchesFolder.getOutputFileForFFmpeg(recording.recordingStart, recording.fileExtension)
|
||||||
}
|
|
||||||
val filePath = FFmpegKitConfig.getSafParameter(context, uri, "rw")
|
|
||||||
val fileName = recording.recordingStart
|
|
||||||
.format(DateTimeFormatter.ISO_DATE_TIME)
|
|
||||||
.toString()
|
|
||||||
.replace(":", "-")
|
|
||||||
.replace(".", "_")
|
|
||||||
val outputFile = FFmpegKitConfig.getSafParameterForWrite(
|
|
||||||
context,
|
|
||||||
folder.createFile("audio/aac", "${fileName}.aac")!!.uri,
|
|
||||||
)
|
|
||||||
|
|
||||||
val command = "-protocol_whitelist saf,concat,content,file,subfile" +
|
val command =
|
||||||
" -i 'concat:${filePath}' -y" +
|
"-protocol_whitelist saf,concat,content,file,subfile" +
|
||||||
|
" -i 'concat:${filePaths}' -y" +
|
||||||
" -acodec copy" +
|
" -acodec copy" +
|
||||||
" -metadata title='$fileName'" +
|
|
||||||
" -metadata date='${recording.recordingStart.format(DateTimeFormatter.ISO_DATE_TIME)}'" +
|
" -metadata date='${recording.recordingStart.format(DateTimeFormatter.ISO_DATE_TIME)}'" +
|
||||||
" -metadata batch_count='${filePaths.size}'" +
|
" -metadata batch_count='${filePaths.length}'" +
|
||||||
" -metadata batch_duration='${recording.intervalDuration}'" +
|
" -metadata batch_duration='${recording.intervalDuration}'" +
|
||||||
" -metadata max_duration='${recording.maxDuration}'" +
|
" -metadata max_duration='${recording.maxDuration}'" +
|
||||||
" $outputFile"
|
" $outputFile"
|
||||||
@ -101,7 +64,6 @@ data class AudioRecorderExporter(
|
|||||||
|
|
||||||
val minRequiredForPossibleInExactMaxDuration =
|
val minRequiredForPossibleInExactMaxDuration =
|
||||||
recording.maxDuration / recording.intervalDuration
|
recording.maxDuration / recording.intervalDuration
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -115,7 +77,8 @@ data class AudioRecorderExporter(
|
|||||||
getFolder(context).listFiles()?.isNotEmpty() ?: false
|
getFolder(context).listFiles()?.isNotEmpty() ?: false
|
||||||
|
|
||||||
fun linkBatches(context: Context, batchesFolder: Uri, destinationFolder: File) {
|
fun linkBatches(context: Context, batchesFolder: Uri, destinationFolder: File) {
|
||||||
val folder = DocumentFile.fromTreeUri(
|
val folder =
|
||||||
|
DocumentFile.fromTreeUri(
|
||||||
context,
|
context,
|
||||||
batchesFolder,
|
batchesFolder,
|
||||||
)!!
|
)!!
|
||||||
@ -127,7 +90,6 @@ data class AudioRecorderExporter(
|
|||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Os.symlink(
|
Os.symlink(
|
||||||
"${folder.uri}/${it.name}",
|
"${folder.uri}/${it.name}",
|
||||||
"${destinationFolder.absolutePath}/${it.name}",
|
"${destinationFolder.absolutePath}/${it.name}",
|
||||||
@ -136,3 +98,4 @@ data class AudioRecorderExporter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
164
app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt
Normal file
164
app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package app.myzel394.alibi.helpers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import app.myzel394.alibi.ui.RECORDER_SUBFOLDER_NAME
|
||||||
|
import java.io.File
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import java.io.FileDescriptor
|
||||||
|
|
||||||
|
data class BatchesFolder(
|
||||||
|
val context: Context,
|
||||||
|
val type: BatchType,
|
||||||
|
val customFolder: DocumentFile? = null,
|
||||||
|
val subfolderName: String = ".recordings",
|
||||||
|
) {
|
||||||
|
private var customFileFileDescriptor: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
|
fun initFolders() {
|
||||||
|
when (type) {
|
||||||
|
BatchType.INTERNAL -> getFolder(context).mkdirs()
|
||||||
|
BatchType.CUSTOM -> customFolder?.createDirectory(subfolderName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanup() {
|
||||||
|
customFileFileDescriptor?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInternalFolder(): File {
|
||||||
|
return getFolder(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCustomDefinedFolder(): DocumentFile {
|
||||||
|
return customFolder!!.findFile(subfolderName)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBatchesForFFmpeg(): List<String> {
|
||||||
|
return when (type) {
|
||||||
|
BatchType.INTERNAL ->
|
||||||
|
(getInternalFolder()
|
||||||
|
.listFiles()
|
||||||
|
?.filter {
|
||||||
|
it.nameWithoutExtension.toIntOrNull() != null
|
||||||
|
}
|
||||||
|
?.toList()
|
||||||
|
?: emptyList())
|
||||||
|
.map { it.absolutePath }
|
||||||
|
|
||||||
|
BatchType.CUSTOM -> getCustomDefinedFolder()
|
||||||
|
.listFiles()
|
||||||
|
.filter {
|
||||||
|
it.name?.substringBeforeLast(".")?.toIntOrNull() != null
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
FFmpegKitConfig.getSafParameterForRead(
|
||||||
|
context,
|
||||||
|
customFolder!!.findFile(it.name!!)!!.uri
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOutputFileForFFmpeg(
|
||||||
|
date: LocalDateTime,
|
||||||
|
extension: String,
|
||||||
|
): String {
|
||||||
|
val name = date
|
||||||
|
.format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
.toString()
|
||||||
|
.replace(":", "-")
|
||||||
|
.replace(".", "_")
|
||||||
|
|
||||||
|
return when (type) {
|
||||||
|
BatchType.INTERNAL -> File(getInternalFolder(), "$name.$extension").absolutePath
|
||||||
|
BatchType.CUSTOM -> FFmpegKitConfig.getSafParameterForWrite(
|
||||||
|
context,
|
||||||
|
customFolder!!.createFile("audio/${extension}", "${name}.${extension}")!!.uri
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exportFolderForSettings(): String {
|
||||||
|
return when (type) {
|
||||||
|
BatchType.INTERNAL -> "_'internal"
|
||||||
|
BatchType.CUSTOM -> customFolder!!.uri.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteRecordings() {
|
||||||
|
when (type) {
|
||||||
|
BatchType.INTERNAL -> getInternalFolder().deleteRecursively()
|
||||||
|
BatchType.CUSTOM -> getCustomDefinedFolder().delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteOldRecordings(earliestCounter: Long) {
|
||||||
|
when (type) {
|
||||||
|
BatchType.INTERNAL -> getInternalFolder().listFiles()?.forEach {
|
||||||
|
val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
|
||||||
|
|
||||||
|
if (fileCounter < earliestCounter) {
|
||||||
|
it.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchType.CUSTOM -> getCustomDefinedFolder().listFiles().forEach {
|
||||||
|
val fileCounter = it.name?.substringBeforeLast(".")?.toIntOrNull() ?: return@forEach
|
||||||
|
|
||||||
|
if (fileCounter < earliestCounter) {
|
||||||
|
it.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkIfFolderIsAccessible(): Boolean {
|
||||||
|
return when (type) {
|
||||||
|
BatchType.INTERNAL -> true
|
||||||
|
BatchType.CUSTOM -> customFolder!!.canWrite() && customFolder.canRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asInternalGetOutputPath(counter: Long, fileExtension: String): String {
|
||||||
|
return getInternalFolder().absolutePath + "/$counter.$fileExtension"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asCustomGetFileDescriptor(
|
||||||
|
counter: Long,
|
||||||
|
fileExtension: String,
|
||||||
|
): FileDescriptor {
|
||||||
|
val file = customFolder!!.createFile("audio/$fileExtension", "$counter.$fileExtension")!!
|
||||||
|
|
||||||
|
customFileFileDescriptor = context.contentResolver.openFileDescriptor(file.uri, "w")!!
|
||||||
|
|
||||||
|
return customFileFileDescriptor!!.fileDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BatchType {
|
||||||
|
INTERNAL,
|
||||||
|
CUSTOM,
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun viaInternalFolder(context: Context): BatchesFolder {
|
||||||
|
return BatchesFolder(context, BatchType.INTERNAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder {
|
||||||
|
return BatchesFolder(context, BatchType.CUSTOM, folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFolder(context: Context) = File(context.filesDir, RECORDER_SUBFOLDER_NAME)
|
||||||
|
|
||||||
|
fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) {
|
||||||
|
"_'internal" -> viaInternalFolder(context)
|
||||||
|
else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import android.os.Handler
|
|||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import app.myzel394.alibi.enums.RecorderState
|
import app.myzel394.alibi.enums.RecorderState
|
||||||
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
import app.myzel394.alibi.ui.utils.MicrophoneInfo
|
import app.myzel394.alibi.ui.utils.MicrophoneInfo
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
@ -65,19 +66,17 @@ class AudioRecorderService : IntervalRecorderService() {
|
|||||||
// - DEFAULT: Uses the bottom microphone of the phone (17)
|
// - DEFAULT: Uses the bottom microphone of the phone (17)
|
||||||
setAudioSource(MediaRecorder.AudioSource.MIC)
|
setAudioSource(MediaRecorder.AudioSource.MIC)
|
||||||
|
|
||||||
// Setting file path
|
when (batchesFolder.type) {
|
||||||
if (customOutputFolder == null) {
|
BatchesFolder.BatchType.INTERNAL -> {
|
||||||
val newFilePath = "${defaultOutputFolder}/$counter.${settings!!.fileExtension}"
|
setOutputFile(
|
||||||
|
batchesFolder.asInternalGetOutputPath(counter, settings!!.fileExtension)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
setOutputFile(newFilePath)
|
BatchesFolder.BatchType.CUSTOM -> {
|
||||||
} else {
|
setOutputFile(
|
||||||
customOutputFolder!!.createFile(
|
batchesFolder.asCustomGetFileDescriptor(counter, settings!!.fileExtension)
|
||||||
"audio/${settings!!.fileExtension}",
|
)
|
||||||
"${counter}.${settings!!.fileExtension}"
|
|
||||||
)!!.let {
|
|
||||||
val fileDescriptor =
|
|
||||||
contentResolver.openFileDescriptor(it.uri, "w")!!.fileDescriptor
|
|
||||||
setOutputFile(fileDescriptor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +98,7 @@ class AudioRecorderService : IntervalRecorderService() {
|
|||||||
it.release()
|
it.release()
|
||||||
}
|
}
|
||||||
clearAudioDevice()
|
clearAudioDevice()
|
||||||
|
batchesFolder.cleanup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import app.myzel394.alibi.dataStore
|
|||||||
import app.myzel394.alibi.db.AudioRecorderSettings
|
import app.myzel394.alibi.db.AudioRecorderSettings
|
||||||
import app.myzel394.alibi.db.RecordingInformation
|
import app.myzel394.alibi.db.RecordingInformation
|
||||||
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
||||||
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@ -22,7 +23,7 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() {
|
|||||||
private var job = SupervisorJob()
|
private var job = SupervisorJob()
|
||||||
private var scope = CoroutineScope(Dispatchers.IO + job)
|
private var scope = CoroutineScope(Dispatchers.IO + job)
|
||||||
|
|
||||||
protected var counter = 0
|
protected var counter = 0L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var settings: Settings? = null
|
var settings: Settings? = null
|
||||||
@ -33,12 +34,12 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() {
|
|||||||
protected val defaultOutputFolder: File
|
protected val defaultOutputFolder: File
|
||||||
get() = AudioRecorderExporter.getFolder(this)
|
get() = AudioRecorderExporter.getFolder(this)
|
||||||
|
|
||||||
var customOutputFolder: DocumentFile? = null
|
var batchesFolder: BatchesFolder = BatchesFolder.viaInternalFolder(this)
|
||||||
|
|
||||||
var onCustomOutputFolderNotAccessible: () -> Unit = {}
|
var onCustomOutputFolderNotAccessible: () -> Unit = {}
|
||||||
|
|
||||||
fun getRecordingInformation(): RecordingInformation = RecordingInformation(
|
fun getRecordingInformation(): RecordingInformation = RecordingInformation(
|
||||||
folderPath = customOutputFolder?.uri?.toString() ?: defaultOutputFolder.absolutePath,
|
folderPath = batchesFolder.exportFolderForSettings(),
|
||||||
recordingStart = recordingStart,
|
recordingStart = recordingStart,
|
||||||
maxDuration = settings!!.maxDuration,
|
maxDuration = settings!!.maxDuration,
|
||||||
fileExtension = settings!!.fileExtension,
|
fileExtension = settings!!.fileExtension,
|
||||||
@ -68,33 +69,16 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() {
|
|||||||
override fun start() {
|
override fun start() {
|
||||||
super.start()
|
super.start()
|
||||||
|
|
||||||
scope.launch {
|
if (!batchesFolder.checkIfFolderIsAccessible()) {
|
||||||
dataStore.data.collectLatest { preferenceSettings ->
|
batchesFolder =
|
||||||
if (settings == null) {
|
BatchesFolder.viaInternalFolder(this@IntervalRecorderService)
|
||||||
settings = Settings.from(preferenceSettings.audioRecorderSettings)
|
|
||||||
|
|
||||||
if (settings!!.folder != null) {
|
|
||||||
customOutputFolder = DocumentFile.fromTreeUri(
|
|
||||||
this@IntervalRecorderService,
|
|
||||||
Uri.parse(settings!!.folder)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!customOutputFolder!!.canRead() || !customOutputFolder!!.canWrite()) {
|
|
||||||
customOutputFolder = null
|
|
||||||
onCustomOutputFolderNotAccessible()
|
onCustomOutputFolderNotAccessible()
|
||||||
}
|
}
|
||||||
}
|
batchesFolder.initFolders()
|
||||||
|
|
||||||
createTimer()
|
createTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customOutputFolder == null) {
|
|
||||||
defaultOutputFolder.mkdirs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pause() {
|
override fun pause() {
|
||||||
cycleTimer.shutdown()
|
cycleTimer.shutdown()
|
||||||
}
|
}
|
||||||
@ -112,38 +96,14 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearAllRecordings() {
|
fun clearAllRecordings() {
|
||||||
if (customOutputFolder != null) {
|
batchesFolder.deleteRecordings()
|
||||||
customOutputFolder!!.listFiles().forEach {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
defaultOutputFolder.listFiles()?.forEach {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteOldRecordings() {
|
private fun deleteOldRecordings() {
|
||||||
val timeMultiplier = settings!!.maxDuration / settings!!.intervalDuration
|
val timeMultiplier = settings!!.maxDuration / settings!!.intervalDuration
|
||||||
val earliestCounter = counter - timeMultiplier
|
val earliestCounter = counter - timeMultiplier
|
||||||
|
|
||||||
if (customOutputFolder != null) {
|
batchesFolder.deleteOldRecordings(earliestCounter)
|
||||||
customOutputFolder!!.listFiles().forEach {
|
|
||||||
val fileCounter = it.name?.substringBeforeLast(".")?.toIntOrNull() ?: return@forEach
|
|
||||||
|
|
||||||
if (fileCounter < earliestCounter) {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
defaultOutputFolder.listFiles()?.forEach {
|
|
||||||
val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return
|
|
||||||
|
|
||||||
if (fileCounter < earliestCounter) {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Settings(
|
data class Settings(
|
||||||
|
@ -46,6 +46,7 @@ import app.myzel394.alibi.dataStore
|
|||||||
import app.myzel394.alibi.db.AppSettings
|
import app.myzel394.alibi.db.AppSettings
|
||||||
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
||||||
import app.myzel394.alibi.helpers.AudioRecorderExporter.Companion.clearAllRecordings
|
import app.myzel394.alibi.helpers.AudioRecorderExporter.Companion.clearAllRecordings
|
||||||
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
import app.myzel394.alibi.services.RecorderNotificationHelper
|
import app.myzel394.alibi.services.RecorderNotificationHelper
|
||||||
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
|
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
|
||||||
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
|
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
|
||||||
@ -86,12 +87,16 @@ fun StartRecording(
|
|||||||
it
|
it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
recorder.customOutputFolder = appSettings.audioRecorderSettings.saveFolder.let {
|
recorder.batchesFolder = if (appSettings.audioRecorderSettings.saveFolder == null)
|
||||||
if (it == null)
|
BatchesFolder.viaInternalFolder(context)
|
||||||
null
|
|
||||||
else
|
else
|
||||||
DocumentFile.fromTreeUri(context, Uri.parse(it))
|
BatchesFolder.viaCustomFolder(
|
||||||
}
|
context,
|
||||||
|
DocumentFile.fromTreeUri(
|
||||||
|
context,
|
||||||
|
Uri.parse(appSettings.audioRecorderSettings.saveFolder)
|
||||||
|
)!!
|
||||||
|
)
|
||||||
|
|
||||||
recorder.startRecording(context)
|
recorder.startRecording(context)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import app.myzel394.alibi.db.RecordingInformation
|
import app.myzel394.alibi.db.RecordingInformation
|
||||||
import app.myzel394.alibi.enums.RecorderState
|
import app.myzel394.alibi.enums.RecorderState
|
||||||
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
||||||
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
import app.myzel394.alibi.services.AudioRecorderService
|
import app.myzel394.alibi.services.AudioRecorderService
|
||||||
import app.myzel394.alibi.services.RecorderNotificationHelper
|
import app.myzel394.alibi.services.RecorderNotificationHelper
|
||||||
import app.myzel394.alibi.services.RecorderService
|
import app.myzel394.alibi.services.RecorderService
|
||||||
@ -47,7 +48,7 @@ class AudioRecorderModel : ViewModel() {
|
|||||||
var onRecordingSave: () -> Unit = {}
|
var onRecordingSave: () -> Unit = {}
|
||||||
var onError: () -> Unit = {}
|
var onError: () -> Unit = {}
|
||||||
var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null
|
var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null
|
||||||
var customOutputFolder: DocumentFile? = null
|
var batchesFolder: BatchesFolder? = null
|
||||||
|
|
||||||
var microphoneStatus: MicrophoneConnectivityStatus = MicrophoneConnectivityStatus.CONNECTED
|
var microphoneStatus: MicrophoneConnectivityStatus = MicrophoneConnectivityStatus.CONNECTED
|
||||||
private set
|
private set
|
||||||
@ -61,8 +62,6 @@ class AudioRecorderModel : ViewModel() {
|
|||||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||||
recorderService =
|
recorderService =
|
||||||
((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder ->
|
((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder ->
|
||||||
recorder.clearAllRecordings()
|
|
||||||
|
|
||||||
// Update UI when the service changes
|
// Update UI when the service changes
|
||||||
recorder.onStateChange = { state ->
|
recorder.onStateChange = { state ->
|
||||||
recorderState = state
|
recorderState = state
|
||||||
@ -86,7 +85,7 @@ class AudioRecorderModel : ViewModel() {
|
|||||||
recorder.onMicrophoneReconnected = {
|
recorder.onMicrophoneReconnected = {
|
||||||
microphoneStatus = MicrophoneConnectivityStatus.CONNECTED
|
microphoneStatus = MicrophoneConnectivityStatus.CONNECTED
|
||||||
}
|
}
|
||||||
recorder.customOutputFolder = customOutputFolder
|
recorder.batchesFolder = batchesFolder ?: recorder.batchesFolder
|
||||||
}.also {
|
}.also {
|
||||||
// Init UI from the service
|
// Init UI from the service
|
||||||
it.startRecording()
|
it.startRecording()
|
||||||
|
@ -40,6 +40,7 @@ import app.myzel394.alibi.dataStore
|
|||||||
import app.myzel394.alibi.db.AppSettings
|
import app.myzel394.alibi.db.AppSettings
|
||||||
import app.myzel394.alibi.db.RecordingInformation
|
import app.myzel394.alibi.db.RecordingInformation
|
||||||
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
||||||
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
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 kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@ -96,20 +97,13 @@ fun AudioRecorderScreen(
|
|||||||
delay(100)
|
delay(100)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val file = AudioRecorderExporter(
|
AudioRecorderExporter(
|
||||||
audioRecorder.recorderService?.getRecordingInformation()
|
audioRecorder.recorderService?.getRecordingInformation()
|
||||||
?: settings.lastRecording
|
?: settings.lastRecording
|
||||||
?: throw Exception("No recording information available"),
|
?: throw Exception("No recording information available"),
|
||||||
).concatenateFiles(
|
).concatenateFiles(
|
||||||
context,
|
context,
|
||||||
DocumentFile.fromTreeUri(
|
audioRecorder.recorderService!!.batchesFolder
|
||||||
context,
|
|
||||||
settings.audioRecorderSettings.saveFolder!!.toUri(),
|
|
||||||
)!!.findFile("1.aac")!!.uri,
|
|
||||||
DocumentFile.fromTreeUri(
|
|
||||||
context,
|
|
||||||
settings.audioRecorderSettings.saveFolder!!.toUri(),
|
|
||||||
)!!
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// saveFile(file, file.name)
|
// saveFile(file, file.name)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user