mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-19 07:15:25 +02:00
feat: Add lock / unlock method for interval files
Signed-off-by: Myzel394 <50424412+Myzel394@users.noreply.github.com>
This commit is contained in:
parent
671c8da56a
commit
6627289666
@ -3,28 +3,26 @@ package app.myzel394.alibi.helpers
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.MediaStore.Video.Media
|
import android.provider.MediaStore.Video.Media
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import java.io.File
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX
|
||||||
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
|
||||||
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
||||||
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||||
import app.myzel394.alibi.ui.utils.PermissionHelper
|
import app.myzel394.alibi.ui.utils.PermissionHelper
|
||||||
import com.arthenica.ffmpegkit.FFprobeKit
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import java.io.File
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
import kotlin.reflect.KFunction4
|
import kotlin.reflect.KFunction4
|
||||||
|
|
||||||
abstract class BatchesFolder(
|
abstract class BatchesFolder(
|
||||||
@ -197,7 +195,6 @@ abstract class BatchesFolder(
|
|||||||
createNewFile()
|
createNewFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun checkIfOutputAlreadyExists(
|
fun checkIfOutputAlreadyExists(
|
||||||
date: LocalDateTime,
|
date: LocalDateTime,
|
||||||
extension: String
|
extension: String
|
||||||
@ -388,12 +385,12 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteOldRecordings(earliestCounter: Long) {
|
fun deleteRecordings(range: LongRange) {
|
||||||
when (type) {
|
when (type) {
|
||||||
BatchType.INTERNAL -> getInternalFolder().listFiles()?.forEach {
|
BatchType.INTERNAL -> getInternalFolder().listFiles()?.forEach {
|
||||||
val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
|
val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
|
||||||
|
|
||||||
if (fileCounter < earliestCounter) {
|
if (fileCounter in range) {
|
||||||
it.delete()
|
it.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,7 +398,7 @@ abstract class BatchesFolder(
|
|||||||
BatchType.CUSTOM -> getCustomDefinedFolder().listFiles().forEach {
|
BatchType.CUSTOM -> getCustomDefinedFolder().listFiles().forEach {
|
||||||
val fileCounter = it.name?.substringBeforeLast(".")?.toIntOrNull() ?: return@forEach
|
val fileCounter = it.name?.substringBeforeLast(".")?.toIntOrNull() ?: return@forEach
|
||||||
|
|
||||||
if (fileCounter < earliestCounter) {
|
if (fileCounter in range) {
|
||||||
it.delete()
|
it.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,7 +408,7 @@ abstract class BatchesFolder(
|
|||||||
val deletableNames = mutableListOf<String>()
|
val deletableNames = mutableListOf<String>()
|
||||||
|
|
||||||
queryMediaContent { rawName, counter, _, _ ->
|
queryMediaContent { rawName, counter, _, _ ->
|
||||||
if (counter < earliestCounter) {
|
if (counter in range) {
|
||||||
deletableNames.add(rawName)
|
deletableNames.add(rawName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,7 +425,7 @@ abstract class BatchesFolder(
|
|||||||
it.nameWithoutExtension.substring(mediaPrefix.length).toIntOrNull()
|
it.nameWithoutExtension.substring(mediaPrefix.length).toIntOrNull()
|
||||||
?: return@forEach
|
?: return@forEach
|
||||||
|
|
||||||
if (fileCounter < earliestCounter) {
|
if (fileCounter in range) {
|
||||||
it.delete()
|
it.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ abstract class IntervalRecorderService<I, B : BatchesFolder> :
|
|||||||
protected var counter = 0L
|
protected var counter = 0L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
// Tracks the index of the currently locked file
|
||||||
|
private var lockedIndex: Long? = null
|
||||||
|
|
||||||
lateinit var settings: AppSettings
|
lateinit var settings: AppSettings
|
||||||
|
|
||||||
private lateinit var cycleTimer: ScheduledExecutorService
|
private lateinit var cycleTimer: ScheduledExecutorService
|
||||||
@ -21,6 +24,23 @@ abstract class IntervalRecorderService<I, B : BatchesFolder> :
|
|||||||
|
|
||||||
abstract fun getRecordingInformation(): I
|
abstract fun getRecordingInformation(): I
|
||||||
|
|
||||||
|
// When saving the recording, the files should be locked.
|
||||||
|
// This prevents the service from deleting the currently available files, so that
|
||||||
|
// they can be safely used to save the recording.
|
||||||
|
// Once finished, make sure to unlock the files using `unlockFiles`.
|
||||||
|
fun lockFiles() {
|
||||||
|
lockedIndex = counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlocks and deletes the files that were locked using `lockFiles`.
|
||||||
|
fun unlockFiles(cleanupFiles: Boolean = false) {
|
||||||
|
if (cleanupFiles) {
|
||||||
|
batchesFolder.deleteRecordings(0..lockedIndex!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
lockedIndex = null
|
||||||
|
}
|
||||||
|
|
||||||
// Make overrideable
|
// Make overrideable
|
||||||
open fun startNewCycle() {
|
open fun startNewCycle() {
|
||||||
counter += 1
|
counter += 1
|
||||||
@ -72,12 +92,12 @@ abstract class IntervalRecorderService<I, B : BatchesFolder> :
|
|||||||
|
|
||||||
private fun deleteOldRecordings() {
|
private fun deleteOldRecordings() {
|
||||||
val timeMultiplier = settings.maxDuration / settings.intervalDuration
|
val timeMultiplier = settings.maxDuration / settings.intervalDuration
|
||||||
val earliestCounter = counter - timeMultiplier
|
val earliestCounter = Math.max(counter - timeMultiplier, lockedIndex ?: 0)
|
||||||
|
|
||||||
if (earliestCounter <= 0) {
|
if (earliestCounter <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
batchesFolder.deleteOldRecordings(earliestCounter)
|
batchesFolder.deleteRecordings(0..earliestCounter)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -139,7 +139,7 @@ class VideoRecorderService :
|
|||||||
if (_cameraAvailableListener.isCompleted) {
|
if (_cameraAvailableListener.isCompleted) {
|
||||||
action()
|
action()
|
||||||
} else {
|
} else {
|
||||||
// Race condition of `startNewCycle` being called before `invpkeOnCompletion`
|
// Race condition of `startNewCycle` being called before `invokeOnCompletion`
|
||||||
// has been called can be ignored, as the camera usually opens within 5 seconds
|
// has been called can be ignored, as the camera usually opens within 5 seconds
|
||||||
// and the interval can't be set shorter than 10 seconds.
|
// and the interval can't be set shorter than 10 seconds.
|
||||||
_cameraAvailableListener.invokeOnCompletion {
|
_cameraAvailableListener.invokeOnCompletion {
|
||||||
|
@ -129,15 +129,17 @@ fun RecorderEventsHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun saveRecording(recorder: RecorderModel) {
|
suspend fun saveRecording(recorder: RecorderModel): Thread {
|
||||||
isProcessing = true
|
isProcessing = true
|
||||||
|
|
||||||
// Give the user some time to see the processing dialog
|
// Give the user some time to see the processing dialog
|
||||||
delay(100)
|
delay(100)
|
||||||
|
|
||||||
thread {
|
return thread {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
try {
|
try {
|
||||||
|
recorder.recorderService?.lockFiles()
|
||||||
|
|
||||||
val recording =
|
val recording =
|
||||||
// When new recording created
|
// When new recording created
|
||||||
recorder.recorderService?.getRecordingInformation()
|
recorder.recorderService?.getRecordingInformation()
|
||||||
@ -215,6 +217,7 @@ fun RecorderEventsHandler(
|
|||||||
} catch (error: Exception) {
|
} catch (error: Exception) {
|
||||||
Log.getStackTraceString(error)
|
Log.getStackTraceString(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
recorder.recorderService?.unlockFiles()
|
||||||
isProcessing = false
|
isProcessing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,7 +233,7 @@ fun RecorderEventsHandler(
|
|||||||
// instead of hoping that the coroutine from where this will be called will be alive
|
// instead of hoping that the coroutine from where this will be called will be alive
|
||||||
// until the end of the saving process
|
// until the end of the saving process
|
||||||
scope.launch {
|
scope.launch {
|
||||||
saveRecording(audioRecorder as RecorderModel)
|
saveRecording(audioRecorder as RecorderModel).join()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audioRecorder.onRecordingStart = {
|
audioRecorder.onRecordingStart = {
|
||||||
@ -273,7 +276,7 @@ fun RecorderEventsHandler(
|
|||||||
// instead of hoping that the coroutine from where this will be called will be alive
|
// instead of hoping that the coroutine from where this will be called will be alive
|
||||||
// until the end of the saving process
|
// until the end of the saving process
|
||||||
scope.launch {
|
scope.launch {
|
||||||
saveRecording(videoRecorder as RecorderModel)
|
saveRecording(videoRecorder as RecorderModel).join()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
videoRecorder.onRecordingStart = {
|
videoRecorder.onRecordingStart = {
|
||||||
|
@ -227,7 +227,7 @@ fun _PrimitiveControls(videoRecorder: VideoRecorderModel) {
|
|||||||
it.saveLastRecording(videoRecorder as RecorderModel)
|
it.saveLastRecording(videoRecorder as RecorderModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
videoRecorder.onRecordingSave()
|
videoRecorder.onRecordingSave().join()
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
videoRecorder.destroyService(context)
|
videoRecorder.destroyService(context)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user