From f045e6f61037fdc13611e56908c997918ba85e41 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 5 Aug 2023 00:09:19 +0200 Subject: [PATCH] feat: Add MaxDurationTile --- .../myzel394/locationtest/db/AppSettings.kt | 38 ++++-- .../SettingsScreen/atoms/MaxDurationTile.kt | 129 ++++++++++++++++++ 2 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/app/myzel394/locationtest/ui/components/SettingsScreen/atoms/MaxDurationTile.kt diff --git a/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt b/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt index 9d0ed5a..7716e2a 100644 --- a/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt @@ -27,6 +27,7 @@ data class AppSettings( @Serializable data class AudioRecorderSettings( + val maxDuration: Long = 30 * 60 * 1000L, // 60 seconds val intervalDuration: Long = 60 * 1000L, // 320 Kbps @@ -59,24 +60,15 @@ data class AudioRecorderSettings( else MediaRecorder.AudioEncoder.AMR_NB - fun getFileExtensions(): String = - when(getOutputFormat()) { - MediaRecorder.OutputFormat.AAC_ADTS -> "aac" - MediaRecorder.OutputFormat.THREE_GPP -> "3gp" - MediaRecorder.OutputFormat.MPEG_4 -> "mp4" - MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" - MediaRecorder.OutputFormat.WEBM -> "webm" - MediaRecorder.OutputFormat.AMR_NB -> "amr" - MediaRecorder.OutputFormat.AMR_WB -> "awb" - MediaRecorder.OutputFormat.OGG -> "ogg" - else -> "raw" - } - fun setIntervalDuration(duration: Long): AudioRecorderSettings { if (duration < 30 * 1000L || duration > 60 * 60 * 1000L) { throw Exception("Interval duration must be between 30 seconds and 1 hour") } + if (duration > maxDuration) { + throw Exception("Interval duration must be less than max duration") + } + return copy(intervalDuration = duration) } @@ -113,7 +105,27 @@ data class AudioRecorderSettings( return copy(encoder = encoder) } + fun setMaxDuration(duration: Long): AudioRecorderSettings { + if (duration < 60 * 1000L || duration > 60 * 60 * 1000L) { + throw Exception("Max duration must be between 1 minute and 1 hour") + } + + if (duration < intervalDuration) { + throw Exception("Max duration must be greater than interval duration") + } + + return copy(maxDuration = duration) + } + companion object { + fun getDefaultInstance(): AudioRecorderSettings = AudioRecorderSettings() + val EXAMPLE_MAX_DURATIONS = listOf( + 15 * 60 * 1000L, + 30 * 60 * 1000L, + 60 * 60 * 1000L, + 2 * 60 * 60 * 1000L, + 3 * 60 * 60 * 1000L, + ) val EXAMPLE_DURATION_TIMES = listOf( 60 * 1000L, 60 * 5 * 1000L, diff --git a/app/src/main/java/app/myzel394/locationtest/ui/components/SettingsScreen/atoms/MaxDurationTile.kt b/app/src/main/java/app/myzel394/locationtest/ui/components/SettingsScreen/atoms/MaxDurationTile.kt new file mode 100644 index 0000000..8191bf2 --- /dev/null +++ b/app/src/main/java/app/myzel394/locationtest/ui/components/SettingsScreen/atoms/MaxDurationTile.kt @@ -0,0 +1,129 @@ +package app.myzel394.locationtest.ui.components.SettingsScreen.atoms + +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic +import androidx.compose.material.icons.filled.Timer +import androidx.compose.material.icons.filled.Tune +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import app.myzel394.locationtest.dataStore +import app.myzel394.locationtest.db.AppSettings +import app.myzel394.locationtest.db.AudioRecorderSettings +import app.myzel394.locationtest.ui.components.atoms.ExampleListRoulette +import app.myzel394.locationtest.ui.components.atoms.SettingsTile +import app.myzel394.locationtest.ui.utils.formatDuration +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.input.InputDialog +import com.maxkeppeler.sheets.input.models.InputHeader +import com.maxkeppeler.sheets.input.models.InputSelection +import com.maxkeppeler.sheets.input.models.InputTextField +import com.maxkeppeler.sheets.input.models.InputTextFieldType +import com.maxkeppeler.sheets.input.models.ValidationResult +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MaxDurationTile() { + val scope = rememberCoroutineScope() + val showDialog = rememberUseCaseState() + val dataStore = LocalContext.current.dataStore + val settings = dataStore + .data + .collectAsState(initial = AppSettings.getDefaultInstance()) + .value + + fun updateValue(maxDuration: Long) { + scope.launch { + dataStore.updateData { + it.setAudioRecorderSettings( + it.audioRecorderSettings.setMaxDuration(maxDuration) + ) + } + } + } + + InputDialog( + state = showDialog, + selection = InputSelection( + input = listOf( + InputTextField( + header = InputHeader( + title = "Set the maximum duration", + icon = IconSource(Icons.Default.Timer), + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + type = InputTextFieldType.OUTLINED, + text = settings.audioRecorderSettings.maxDuration.toString(), + validationListener = { text -> + val maxDuration = text?.toLongOrNull() + + if (maxDuration == null) { + ValidationResult.Invalid("Please enter a valid number") + } + + if (maxDuration !in (60 * 1000L)..(60 * 60 * 1000L)) { + ValidationResult.Invalid("Please enter a number between 1 minute and 1 hour") + } + + ValidationResult.Valid + }, + key = "maxDuration", + ) + ), + ) { result -> + val maxDuration = result.getString("maxDuration")?.toLongOrNull() ?: throw IllegalStateException("Invalid maxDuration") + + updateValue(maxDuration) + } + ) + SettingsTile( + title = "Max duration", + description = "Set the maximum duration of the recording", + leading = { + Icon( + Icons.Default.Timer, + contentDescription = null, + ) + }, + trailing = { + Button( + onClick = showDialog::show, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + ), + shape = MaterialTheme.shapes.medium, + ) { + Text( + text = formatDuration(settings.audioRecorderSettings.maxDuration), + ) + } + }, + extra = { + ExampleListRoulette( + items = AudioRecorderSettings.EXAMPLE_MAX_DURATIONS, + onItemSelected = ::updateValue, + ) {maxDuration -> + Text( + text = formatDuration(maxDuration), + ) + } + } + ) +} \ No newline at end of file