feat: Add VideoRecorderBitrateTile

This commit is contained in:
Myzel394 2023-12-02 17:40:11 +01:00
parent 4f265b23f8
commit 9598cd45fa
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
4 changed files with 177 additions and 5 deletions

View File

@ -40,6 +40,10 @@ data class AppSettings(
return copy(audioRecorderSettings = audioRecorderSettings) return copy(audioRecorderSettings = audioRecorderSettings)
} }
fun setVideoRecorderSettings(videoRecorderSettings: VideoRecorderSettings): AppSettings {
return copy(videoRecorderSettings = videoRecorderSettings)
}
fun setNotificationSettings(notificationSettings: NotificationSettings?): AppSettings { fun setNotificationSettings(notificationSettings: NotificationSettings?): AppSettings {
return copy(notificationSettings = notificationSettings) return copy(notificationSettings = notificationSettings)
} }
@ -401,6 +405,20 @@ data class VideoRecorderSettings(
"FHD" to Quality.FHD, "FHD" to Quality.FHD,
"UHD" to Quality.UHD, "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,
)
} }
} }

View File

@ -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,
)
}

View File

@ -1,13 +1,11 @@
package app.myzel394.alibi.ui.screens package app.myzel394.alibi.ui.screens
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@ -40,7 +38,7 @@ import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore import app.myzel394.alibi.dataStore
import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY 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.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.CustomNotificationTile
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle
@ -164,15 +162,20 @@ fun SettingsScreen(
title = stringResource(R.string.ui_settings_sections_audio_title), title = stringResource(R.string.ui_settings_sections_audio_title),
description = stringResource(R.string.ui_settings_sections_audio_description), description = stringResource(R.string.ui_settings_sections_audio_description),
) )
AudioRecorderShowAllMicrophonesTile(settings = settings) AudioRecorderShowAllMicrophonesTile(settings = settings)
AudioRecorderBitrateTile(settings = settings) VideoRecorderBitrateTile(settings = settings)
AudioRecorderSamplingRateTile(settings = settings) AudioRecorderSamplingRateTile(settings = settings)
AudioRecorderEncoderTile( AudioRecorderEncoderTile(
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
settings = settings settings = settings
) )
AudioRecorderOutputFormatTile(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( Divider(
modifier = Modifier modifier = Modifier

View File

@ -122,4 +122,10 @@
<string name="ui_audioRecorder_action_save_openFolder">Open</string> <string name="ui_audioRecorder_action_save_openFolder">Open</string>
<string name="ui_settings_sections_audio_title">Audio Recording</string> <string name="ui_settings_sections_audio_title">Audio Recording</string>
<string name="ui_settings_sections_audio_description">Only applies to audio recordings</string> <string name="ui_settings_sections_audio_description">Only applies to audio recordings</string>
<string name="ui_settings_sections_video_title">Video Recording</string>
<string name="ui_settings_sections_video_description">Only applies to video recordings</string>
<string name="ui_settings_option_videoTargetedBitrate_title">Targeted Bitrate</string>
<string name="ui_settings_option_videoTargetedBitrate_explanation">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.</string>
<string name="format_mbps">%s MB/s</string>
<string name="format_bps">%s B/s</string>
</resources> </resources>