diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 558f605..b59c132 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -40,6 +40,10 @@ data class AppSettings( return copy(audioRecorderSettings = audioRecorderSettings) } + fun setVideoRecorderSettings(videoRecorderSettings: VideoRecorderSettings): AppSettings { + return copy(videoRecorderSettings = videoRecorderSettings) + } + fun setNotificationSettings(notificationSettings: NotificationSettings?): AppSettings { return copy(notificationSettings = notificationSettings) } @@ -401,6 +405,20 @@ data class VideoRecorderSettings( "FHD" to Quality.FHD, "UHD" to Quality.UHD, ) + + val EXAMPLE_BITRATE_VALUES = listOf( + null, + 500 * 1000, + // 1 Mbps + 1 * 1000 * 1000, + 2 * 1000 * 1000, + 4 * 1000 * 1000, + 8 * 1000 * 1000, + 16 * 1000 * 1000, + 32 * 1000 * 1000, + 50 * 1000 * 1000, + 100 * 1000 * 1000, + ) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt new file mode 100644 index 0000000..a8a842e --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt @@ -0,0 +1,145 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +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.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.db.AudioRecorderSettings +import app.myzel394.alibi.db.VideoRecorderSettings +import app.myzel394.alibi.ui.components.atoms.ExampleListRoulette +import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.utils.IconResource +import com.maxkeppeker.sheets.core.models.base.Header +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 VideoRecorderBitrateTile( + settings: AppSettings, +) { + val scope = rememberCoroutineScope() + val showDialog = rememberUseCaseState() + val dataStore = LocalContext.current.dataStore + + fun updateValue(bitRate: Int?) { + scope.launch { + dataStore.updateData { + it.setVideoRecorderSettings( + it.videoRecorderSettings.setTargetedVideoBitRate(bitRate) + ) + } + } + } + + val notNumberLabel = stringResource(R.string.form_error_type_notNumber) + InputDialog( + state = showDialog, + header = Header.Default( + title = stringResource(R.string.ui_settings_option_videoTargetedBitrate_title), + icon = IconSource( + painter = IconResource.fromImageVector(Icons.Default.Tune).asPainterResource(), + contentDescription = null, + ) + ), + selection = InputSelection( + input = listOf( + InputTextField( + header = InputHeader( + title = stringResource(id = R.string.ui_settings_option_videoTargetedBitrate_explanation), + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + type = InputTextFieldType.OUTLINED, + text = if (settings.videoRecorderSettings.targetedVideoBitRate == null) "" else (settings.videoRecorderSettings.targetedVideoBitRate / 1000).toString(), + validationListener = { text -> + val bitRate = text?.toIntOrNull() + + if (bitRate == null) { + return@InputTextField ValidationResult.Invalid(notNumberLabel) + } + + ValidationResult.Valid + }, + key = "bitrate", + ) + ), + ) { result -> + val bitRate = result.getString("bitrate")?.toIntOrNull() ?: return@InputSelection + + updateValue(bitRate * 1000) + } + ) + SettingsTile( + title = stringResource(R.string.ui_settings_option_videoTargetedBitrate_title), + description = stringResource(R.string.ui_settings_option_bitrate_description), + leading = { + Icon( + Icons.Default.Tune, + contentDescription = null, + ) + }, + trailing = { + Button( + onClick = showDialog::show, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + ), + shape = MaterialTheme.shapes.medium, + ) { + Text(formatBitrate(settings.videoRecorderSettings.targetedVideoBitRate)) + } + }, + extra = { + ExampleListRoulette( + items = VideoRecorderSettings.EXAMPLE_BITRATE_VALUES, + onItemSelected = ::updateValue, + ) { bitRate -> + Text(formatBitrate(bitRate)) + } + } + ) +} + +@Composable +fun formatBitrate(bitrate: Int?): String { + return if (bitrate == null) + stringResource(R.string.ui_settings_value_auto_label) + else if (bitrate >= 1000 * 1000 && bitrate % (1000 * 1000) == 0) + stringResource( + R.string.format_mbps, + bitrate / 1000 / 1000, + ) + else if (bitrate >= 1000 && bitrate % 1000 == 0) + stringResource( + R.string.format_kbps, + bitrate / 1000, + ) + else + stringResource( + R.string.format_bps, + bitrate, + ) +} + diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 400c542..a208a48 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -1,13 +1,11 @@ package app.myzel394.alibi.ui.screens import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -40,7 +38,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile -import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderBitrateTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle @@ -164,15 +162,20 @@ fun SettingsScreen( title = stringResource(R.string.ui_settings_sections_audio_title), description = stringResource(R.string.ui_settings_sections_audio_description), ) - AudioRecorderShowAllMicrophonesTile(settings = settings) - AudioRecorderBitrateTile(settings = settings) + VideoRecorderBitrateTile(settings = settings) AudioRecorderSamplingRateTile(settings = settings) AudioRecorderEncoderTile( snackbarHostState = snackbarHostState, settings = settings ) AudioRecorderOutputFormatTile(settings = settings) + + DividerTitle( + title = stringResource(R.string.ui_settings_sections_video_title), + description = stringResource(R.string.ui_settings_sections_video_description), + ) + VideoRecorderBitrateTile(settings = settings) } Divider( modifier = Modifier diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d67172c..ee6bbe6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -122,4 +122,10 @@ Open Audio Recording Only applies to audio recordings + Video Recording + Only applies to video recordings + Targeted Bitrate + Bitrate for the video recording. Only applies to the video itself, not the audio. The actual bitrate may be different depending on what you will be recording. + %s MB/s + %s B/s \ No newline at end of file