mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 14:55:26 +02:00
commit
193ecd15d2
@ -35,8 +35,8 @@ android {
|
|||||||
applicationId "app.myzel394.alibi"
|
applicationId "app.myzel394.alibi"
|
||||||
minSdk 24
|
minSdk 24
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 15
|
versionCode 16
|
||||||
versionName "0.5.2"
|
versionName "0.5.3"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
@ -97,7 +97,7 @@ dependencies {
|
|||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.4'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.4'
|
||||||
implementation 'androidx.activity:activity-compose:1.9.1'
|
implementation 'androidx.activity:activity-compose:1.9.1'
|
||||||
implementation 'androidx.activity:activity-ktx:1.9.1'
|
implementation 'androidx.activity:activity-ktx:1.9.1'
|
||||||
implementation platform('androidx.compose:compose-bom:2024.06.00')
|
implementation platform('androidx.compose:compose-bom:2024.09.00')
|
||||||
implementation 'androidx.compose.ui:ui'
|
implementation 'androidx.compose.ui:ui'
|
||||||
implementation 'androidx.compose.ui:ui-graphics'
|
implementation 'androidx.compose.ui:ui-graphics'
|
||||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||||
@ -110,7 +110,7 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||||
androidTestImplementation platform('androidx.compose:compose-bom:2024.06.00')
|
androidTestImplementation platform('androidx.compose:compose-bom:2024.09.00')
|
||||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
||||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||||
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||||
@ -121,7 +121,7 @@ dependencies {
|
|||||||
annotationProcessor 'com.google.dagger:hilt-compiler:2.49'
|
annotationProcessor 'com.google.dagger:hilt-compiler:2.49'
|
||||||
implementation "androidx.hilt:hilt-navigation-compose:1.2.0"
|
implementation "androidx.hilt:hilt-navigation-compose:1.2.0"
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.1'
|
||||||
|
|
||||||
implementation 'com.arthenica:ffmpeg-kit-full-gpl:5.1'
|
implementation 'com.arthenica:ffmpeg-kit-full-gpl:5.1'
|
||||||
|
|
||||||
|
@ -40,18 +40,17 @@ class AudioBatchesFolder(
|
|||||||
override fun getOutputFileForFFmpeg(
|
override fun getOutputFileForFFmpeg(
|
||||||
date: LocalDateTime,
|
date: LocalDateTime,
|
||||||
extension: String,
|
extension: String,
|
||||||
|
fileName: String,
|
||||||
): String {
|
): String {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath
|
BatchType.INTERNAL -> asInternalGetOutputFile(fileName).absolutePath
|
||||||
|
|
||||||
BatchType.CUSTOM -> {
|
BatchType.CUSTOM -> {
|
||||||
val name = getName(date, extension)
|
|
||||||
|
|
||||||
FFmpegKitConfig.getSafParameterForWrite(
|
FFmpegKitConfig.getSafParameterForWrite(
|
||||||
context,
|
context,
|
||||||
(customFolder!!.findFile(name) ?: customFolder.createFile(
|
(customFolder!!.findFile(fileName) ?: customFolder.createFile(
|
||||||
"audio/${extension}",
|
"audio/${extension}",
|
||||||
getName(date, extension),
|
fileName,
|
||||||
)!!).uri
|
)!!).uri
|
||||||
)!!
|
)!!
|
||||||
}
|
}
|
||||||
@ -59,7 +58,7 @@ class AudioBatchesFolder(
|
|||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val mediaUri = getOrCreateMediaFile(
|
val mediaUri = getOrCreateMediaFile(
|
||||||
name = getName(date, extension),
|
name = fileName,
|
||||||
mimeType = "audio/$extension",
|
mimeType = "audio/$extension",
|
||||||
relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME,
|
relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME,
|
||||||
)
|
)
|
||||||
@ -72,7 +71,7 @@ class AudioBatchesFolder(
|
|||||||
val path = arrayOf(
|
val path = arrayOf(
|
||||||
Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER),
|
Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER),
|
||||||
MEDIA_SUBFOLDER_NAME,
|
MEDIA_SUBFOLDER_NAME,
|
||||||
getName(date, extension)
|
fileName,
|
||||||
).joinToString("/")
|
).joinToString("/")
|
||||||
return File(path)
|
return File(path)
|
||||||
.apply {
|
.apply {
|
||||||
@ -143,7 +142,7 @@ class AudioBatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val BASE_LEGACY_STORAGE_FOLDER = Environment.DIRECTORY_PODCASTS
|
val BASE_LEGACY_STORAGE_FOLDER = Environment.DIRECTORY_PODCASTS
|
||||||
val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/audio_recordings"
|
val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/.audio_recordings"
|
||||||
val BASE_SCOPED_STORAGE_RELATIVE_PATH =
|
val BASE_SCOPED_STORAGE_RELATIVE_PATH =
|
||||||
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||||
Environment.DIRECTORY_RECORDINGS
|
Environment.DIRECTORY_RECORDINGS
|
||||||
|
@ -191,8 +191,8 @@ abstract class BatchesFolder(
|
|||||||
return "$name.$extension"
|
return "$name.$extension"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asInternalGetOutputFile(date: LocalDateTime, extension: String): File {
|
fun asInternalGetOutputFile(fileName: String): File {
|
||||||
return File(getInternalFolder(), getName(date, extension))
|
return File(getInternalFolder(), fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asMediaGetLegacyFile(name: String): File = File(
|
fun asMediaGetLegacyFile(name: String): File = File(
|
||||||
@ -203,16 +203,8 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkIfOutputAlreadyExists(
|
fun checkIfOutputAlreadyExists(
|
||||||
date: LocalDateTime,
|
fileName: String,
|
||||||
extension: String
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val stem = date
|
|
||||||
.format(DateTimeFormatter.ISO_DATE_TIME)
|
|
||||||
.toString()
|
|
||||||
.replace(":", "-")
|
|
||||||
.replace(".", "_")
|
|
||||||
val fileName = "$stem.$extension"
|
|
||||||
|
|
||||||
return when (type) {
|
return when (type) {
|
||||||
BatchType.INTERNAL -> File(getInternalFolder(), fileName).exists()
|
BatchType.INTERNAL -> File(getInternalFolder(), fileName).exists()
|
||||||
|
|
||||||
@ -245,6 +237,7 @@ abstract class BatchesFolder(
|
|||||||
abstract fun getOutputFileForFFmpeg(
|
abstract fun getOutputFileForFFmpeg(
|
||||||
date: LocalDateTime,
|
date: LocalDateTime,
|
||||||
extension: String,
|
extension: String,
|
||||||
|
fileName: String,
|
||||||
): String
|
): String
|
||||||
|
|
||||||
abstract fun cleanup()
|
abstract fun cleanup()
|
||||||
@ -255,18 +248,17 @@ abstract class BatchesFolder(
|
|||||||
disableCache: Boolean? = null,
|
disableCache: Boolean? = null,
|
||||||
onNextParameterTry: (String) -> Unit = {},
|
onNextParameterTry: (String) -> Unit = {},
|
||||||
onProgress: (Float?) -> Unit = {},
|
onProgress: (Float?) -> Unit = {},
|
||||||
|
fileName: String,
|
||||||
): String {
|
): String {
|
||||||
val disableCache = disableCache ?: (type != BatchType.INTERNAL)
|
val disableCache = disableCache ?: (type != BatchType.INTERNAL)
|
||||||
val date = recording.getStartDateForFilename(filenameFormat)
|
val date = recording.getStartDateForFilename(filenameFormat)
|
||||||
|
|
||||||
if (!disableCache && checkIfOutputAlreadyExists(
|
if (!disableCache && checkIfOutputAlreadyExists(fileName)
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return getOutputFileForFFmpeg(
|
return getOutputFileForFFmpeg(
|
||||||
date = recording.recordingStart,
|
date = recording.recordingStart,
|
||||||
extension = recording.fileExtension,
|
extension = recording.fileExtension,
|
||||||
|
fileName = fileName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +274,7 @@ abstract class BatchesFolder(
|
|||||||
val outputFile = getOutputFileForFFmpeg(
|
val outputFile = getOutputFileForFFmpeg(
|
||||||
date = date,
|
date = date,
|
||||||
extension = recording.fileExtension,
|
extension = recording.fileExtension,
|
||||||
|
fileName = fileName,
|
||||||
)
|
)
|
||||||
|
|
||||||
concatenationFunction(
|
concatenationFunction(
|
||||||
|
@ -39,18 +39,20 @@ class VideoBatchesFolder(
|
|||||||
|
|
||||||
private var customParcelFileDescriptor: ParcelFileDescriptor? = null
|
private var customParcelFileDescriptor: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String {
|
override fun getOutputFileForFFmpeg(
|
||||||
|
date: LocalDateTime,
|
||||||
|
extension: String,
|
||||||
|
fileName: String,
|
||||||
|
): String {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath
|
BatchType.INTERNAL -> asInternalGetOutputFile(fileName).absolutePath
|
||||||
|
|
||||||
BatchType.CUSTOM -> {
|
BatchType.CUSTOM -> {
|
||||||
val name = getName(date, extension)
|
|
||||||
|
|
||||||
FFmpegKitConfig.getSafParameterForWrite(
|
FFmpegKitConfig.getSafParameterForWrite(
|
||||||
context,
|
context,
|
||||||
(customFolder!!.findFile(name) ?: customFolder.createFile(
|
(customFolder!!.findFile(fileName) ?: customFolder.createFile(
|
||||||
"video/${extension}",
|
"video/${extension}",
|
||||||
getName(date, extension),
|
fileName,
|
||||||
)!!).uri
|
)!!).uri
|
||||||
)!!
|
)!!
|
||||||
}
|
}
|
||||||
@ -58,7 +60,7 @@ class VideoBatchesFolder(
|
|||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val mediaUri = getOrCreateMediaFile(
|
val mediaUri = getOrCreateMediaFile(
|
||||||
name = getName(date, extension),
|
name = fileName,
|
||||||
mimeType = "video/$extension",
|
mimeType = "video/$extension",
|
||||||
relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME,
|
relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME,
|
||||||
)
|
)
|
||||||
@ -71,7 +73,7 @@ class VideoBatchesFolder(
|
|||||||
val path = arrayOf(
|
val path = arrayOf(
|
||||||
Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER),
|
Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER),
|
||||||
MEDIA_SUBFOLDER_NAME,
|
MEDIA_SUBFOLDER_NAME,
|
||||||
getName(date, extension)
|
fileName,
|
||||||
).joinToString("/")
|
).joinToString("/")
|
||||||
return File(path)
|
return File(path)
|
||||||
.apply {
|
.apply {
|
||||||
@ -146,7 +148,7 @@ class VideoBatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val BASE_LEGACY_STORAGE_FOLDER = Environment.DIRECTORY_DCIM
|
val BASE_LEGACY_STORAGE_FOLDER = Environment.DIRECTORY_DCIM
|
||||||
val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/video_recordings"
|
val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/.video_recordings"
|
||||||
val BASE_SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM
|
val BASE_SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM
|
||||||
val SCOPED_STORAGE_RELATIVE_PATH =
|
val SCOPED_STORAGE_RELATIVE_PATH =
|
||||||
BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_RECORDINGS_SUBFOLDER
|
BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_RECORDINGS_SUBFOLDER
|
||||||
|
@ -11,11 +11,11 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -40,8 +40,8 @@ fun BigButton(
|
|||||||
val orientation = LocalConfiguration.current.orientation
|
val orientation = LocalConfiguration.current.orientation
|
||||||
|
|
||||||
BoxWithConstraints {
|
BoxWithConstraints {
|
||||||
val isLarge = if (isBig == null)
|
val isLarge = isBig
|
||||||
maxWidth > 250.dp && maxHeight > 600.dp && orientation == Configuration.ORIENTATION_PORTRAIT else isBig
|
?: (maxWidth > 250.dp && maxHeight > 600.dp && orientation == Configuration.ORIENTATION_PORTRAIT)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -52,7 +52,7 @@ fun BigButton(
|
|||||||
}
|
}
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
|
indication = ripple(color = MaterialTheme.colorScheme.primary),
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
),
|
),
|
||||||
|
@ -6,10 +6,10 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -39,7 +39,7 @@ fun SaveButton(
|
|||||||
}
|
}
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
|
indication = ripple(color = MaterialTheme.colorScheme.primary),
|
||||||
onClick = onSave,
|
onClick = onSave,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
)
|
)
|
||||||
|
@ -76,6 +76,8 @@ fun RecordingStatus(
|
|||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
progress = { progress },
|
progress = { progress },
|
||||||
modifier = progressModifier,
|
modifier = progressModifier,
|
||||||
|
drawStopIndicator = { },
|
||||||
|
gapSize = 0.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,38 +177,33 @@ fun RecorderEventsHandler(
|
|||||||
else -> throw Exception("Unknown recorder type")
|
else -> throw Exception("Unknown recorder type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val fileName = batchesFolder.getName(
|
||||||
|
recording.recordingStart,
|
||||||
|
recording.fileExtension,
|
||||||
|
)
|
||||||
|
|
||||||
batchesFolder.concatenate(
|
batchesFolder.concatenate(
|
||||||
recording,
|
recording,
|
||||||
filenameFormat = settings.filenameFormat,
|
filenameFormat = settings.filenameFormat,
|
||||||
|
fileName = fileName,
|
||||||
onProgress = { percentage ->
|
onProgress = { percentage ->
|
||||||
processingProgress = percentage
|
processingProgress = percentage
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
val name = batchesFolder.getName(
|
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension,
|
|
||||||
)
|
|
||||||
|
|
||||||
when (batchesFolder.type) {
|
when (batchesFolder.type) {
|
||||||
BatchesFolder.BatchType.INTERNAL -> {
|
BatchesFolder.BatchType.INTERNAL -> {
|
||||||
when (batchesFolder) {
|
when (batchesFolder) {
|
||||||
is AudioBatchesFolder -> {
|
is AudioBatchesFolder -> {
|
||||||
saveAudioFile(
|
saveAudioFile(
|
||||||
batchesFolder.asInternalGetOutputFile(
|
batchesFolder.asInternalGetOutputFile(fileName), fileName
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension,
|
|
||||||
), name
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is VideoBatchesFolder -> {
|
is VideoBatchesFolder -> {
|
||||||
saveVideoFile(
|
saveVideoFile(
|
||||||
batchesFolder.asInternalGetOutputFile(
|
batchesFolder.asInternalGetOutputFile(fileName), fileName
|
||||||
recording.recordingStart,
|
|
||||||
recording.fileExtension,
|
|
||||||
), name
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user