mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 23:05:26 +02:00
feat: Add SaveFolderTile
This commit is contained in:
parent
6605f44eec
commit
aa8fd2a37f
@ -173,10 +173,6 @@ data class AudioRecorderSettings(
|
||||
|
||||
runCatching {
|
||||
return File(saveFolder!!).apply {
|
||||
if (!AudioRecorderExporter.canFolderBeUsed(this)) {
|
||||
throw SecurityException("Can't write to folder")
|
||||
}
|
||||
|
||||
if (!exists()) {
|
||||
mkdirs()
|
||||
}
|
||||
|
@ -114,9 +114,5 @@ data class AudioRecorderExporter(
|
||||
|
||||
fun hasRecordingsAvailable(context: Context) =
|
||||
getFolder(context).listFiles()?.isNotEmpty() ?: false
|
||||
|
||||
// Write required for saving the audio files
|
||||
// Read required for concatenating the audio files
|
||||
fun canFolderBeUsed(file: File) = file.canRead() && file.canWrite()
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
package app.myzel394.alibi.ui.components.SettingsScreen.atoms
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AudioFile
|
||||
import androidx.compose.material.icons.filled.Cancel
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toFile
|
||||
import app.myzel394.alibi.R
|
||||
import app.myzel394.alibi.dataStore
|
||||
import app.myzel394.alibi.db.AppSettings
|
||||
import app.myzel394.alibi.helpers.AudioRecorderExporter
|
||||
import app.myzel394.alibi.ui.components.atoms.SettingsTile
|
||||
import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog
|
||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
@Composable
|
||||
fun SaveFolderTile(
|
||||
settings: AppSettings,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val dataStore = context.dataStore
|
||||
|
||||
fun updateValue(path: String?) {
|
||||
scope.launch {
|
||||
dataStore.updateData {
|
||||
it.setAudioRecorderSettings(
|
||||
it.audioRecorderSettings.setSaveFolder(path)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val selectFolder = rememberFolderSelectorDialog { folder ->
|
||||
if (folder == null) {
|
||||
return@rememberFolderSelectorDialog
|
||||
}
|
||||
|
||||
updateValue(folder.path)
|
||||
}
|
||||
|
||||
var showWarning by remember { mutableStateOf(false) }
|
||||
|
||||
if (showWarning) {
|
||||
val title = stringResource(R.string.ui_settings_option_saveFolder_warning_title)
|
||||
val text = stringResource(R.string.ui_settings_option_saveFolder_warning_text)
|
||||
|
||||
AlertDialog(
|
||||
icon = {
|
||||
Icon(
|
||||
Icons.Default.Warning,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
onDismissRequest = {
|
||||
showWarning = false
|
||||
},
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
text = {
|
||||
Text(text = text)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
showWarning = false
|
||||
selectFolder()
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.ui_settings_option_saveFolder_warning_action_confirm),
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
showWarning = false
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Cancel,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
|
||||
Text(stringResource(R.string.dialog_close_cancel_label))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SettingsTile(
|
||||
title = stringResource(R.string.ui_settings_option_saveFolder_title),
|
||||
description = stringResource(R.string.ui_settings_option_saveFolder_explanation),
|
||||
leading = {
|
||||
Icon(
|
||||
Icons.Default.AudioFile,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
Button(
|
||||
onClick = {
|
||||
showWarning = true
|
||||
},
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Folder,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier.size(ButtonDefaults.IconSpacing)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.ui_settings_option_saveFolder_action_select_label),
|
||||
)
|
||||
}
|
||||
},
|
||||
extra = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
if (settings.audioRecorderSettings.saveFolder != null) {
|
||||
Button(
|
||||
colors = ButtonDefaults.filledTonalButtonColors(),
|
||||
onClick = {
|
||||
updateValue(null)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Lock,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier.size(ButtonDefaults.IconSpacing)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.ui_settings_option_saveFolder_action_default_label),
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.form_value_selected,
|
||||
settings.audioRecorderSettings.saveFolder
|
||||
?: stringResource(R.string.ui_settings_option_saveFolder_defaultValue)
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -51,6 +51,7 @@ import app.myzel394.alibi.ui.components.SettingsScreen.atoms.IntervalDurationTil
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.MaxDurationTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.OutputFormatTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SamplingRateTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SaveFolderTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ShowAllMicrophonesTile
|
||||
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector
|
||||
import app.myzel394.alibi.ui.components.atoms.GlobalSwitch
|
||||
@ -161,6 +162,7 @@ fun SettingsScreen(
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 32.dp)
|
||||
)
|
||||
SaveFolderTile(settings = settings)
|
||||
ShowAllMicrophonesTile(settings = settings)
|
||||
BitrateTile(settings = settings)
|
||||
SamplingRateTile(settings = settings)
|
||||
|
@ -56,3 +56,18 @@ fun rememberFileSelectorDialog(
|
||||
launcher.launch(arrayOf(mimeType))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberFolderSelectorDialog(
|
||||
callback: (Uri?) -> Unit
|
||||
): (() -> Unit) {
|
||||
val launcher =
|
||||
rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.OpenDocumentTree(),
|
||||
callback,
|
||||
)
|
||||
|
||||
return {
|
||||
launcher.launch(null)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
<string name="form_error_type_notNumber">Please enter a valid number</string>
|
||||
<string name="form_error_value_notInRange">Please enter a number between <xliff:g name="min">%s</xliff:g> and <xliff:g name="max">%s</xliff:g></string>
|
||||
<string name="form_error_value_mustBeGreaterThan">Please enter a number greater than <xliff:g name="min">%s</xliff:g></string>
|
||||
<string name="form_value_selected">Selected: %s</string>
|
||||
|
||||
<string name="notificationChannels_recorder_name">Recorder</string>
|
||||
<string name="notificationChannels_recorder_description">Shows the current recording status</string>
|
||||
@ -109,4 +110,12 @@
|
||||
<string name="ui_about_contribute_donation_githubSponsors">Become a GitHub Sponsor</string>
|
||||
<string name="ui_settings_option_deleteRecordingsImmediately_title">Delete Recordings Immediately</string>
|
||||
<string name="ui_settings_option_deleteRecordingsImmediately_description">If enabled, Alibi will immediately delete recordings after you have saved the file.</string>
|
||||
<string name="ui_settings_option_saveFolder_title">Batches folder</string>
|
||||
<string name="ui_settings_option_saveFolder_explanation">Where Alibi should store the temporary batches of your recordings.</string>
|
||||
<string name="ui_settings_option_saveFolder_action_select_label">Select</string>
|
||||
<string name="ui_settings_option_saveFolder_defaultValue">Encrypted Internal Storage</string>
|
||||
<string name="ui_settings_option_saveFolder_warning_title">Are you sure you want to change the folder?</string>
|
||||
<string name="ui_settings_option_saveFolder_warning_text">By default, Alibi will save the recording batches into its private, encrypted file storage. You can change this and specify an external, unencrypted folder. This will allow you to access the batches manually. ONLY DO THIS IF YOU KNOW WHAT YOU ARE DOING!</string>
|
||||
<string name="ui_settings_option_saveFolder_warning_action_confirm">Yes, change folder</string>
|
||||
<string name="ui_settings_option_saveFolder_action_default_label">Use private, encrypted storage</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user