feat: Add i18n

This commit is contained in:
Myzel394 2023-08-06 14:40:52 +02:00
parent 46f74e8a8b
commit a8cb592632
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
16 changed files with 154 additions and 65 deletions

View File

@ -13,8 +13,10 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import app.myzel394.locationtest.R
import app.myzel394.locationtest.services.RecorderService import app.myzel394.locationtest.services.RecorderService
@Composable @Composable
@ -27,10 +29,10 @@ fun ConfirmDeletionDialog(
onDismiss() onDismiss()
}, },
title = { title = {
Text("Delete Recording?") Text(stringResource(R.string.ui_audioRecorder_action_delete_confirm_title))
}, },
text = { text = {
Text("Are you sure you want to delete this recording?") Text(stringResource(R.string.ui_audioRecorder_action_delete_confirm_message))
}, },
icon = { icon = {
Icon( Icon(
@ -39,10 +41,11 @@ fun ConfirmDeletionDialog(
) )
}, },
confirmButton = { confirmButton = {
val label = stringResource(R.string.ui_audioRecorder_action_delete_label)
Button( Button(
modifier = Modifier modifier = Modifier
.semantics { .semantics {
contentDescription = "Confirm Recording Deletion" contentDescription = label
}, },
onClick = { onClick = {
onConfirm() onConfirm()
@ -54,14 +57,15 @@ fun ConfirmDeletionDialog(
modifier = Modifier.size(ButtonDefaults.IconSize), modifier = Modifier.size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Delete") Text(label)
} }
}, },
dismissButton = { dismissButton = {
val label = stringResource(R.string.dialog_close_cancel_label)
Button( Button(
modifier = Modifier modifier = Modifier
.semantics { .semantics {
contentDescription = "Cancel Recording Deletion" contentDescription = label
}, },
onClick = { onClick = {
onDismiss() onDismiss()
@ -74,7 +78,7 @@ fun ConfirmDeletionDialog(
modifier = Modifier.size(ButtonDefaults.IconSize), modifier = Modifier.size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Cancel") Text(label)
} }
} }
) )

View File

@ -33,9 +33,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.services.RecorderService import app.myzel394.locationtest.services.RecorderService
import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE
import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.ConfirmDeletionDialog import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.ConfirmDeletionDialog
@ -124,10 +126,11 @@ fun RecordingStatus(
}, },
) )
} }
val label = stringResource(R.string.ui_audioRecorder_action_delete_label)
Button( Button(
modifier = Modifier modifier = Modifier
.semantics { .semantics {
contentDescription = "Delete Recording" contentDescription = label
}, },
onClick = { onClick = {
showDeleteDialog = true showDeleteDialog = true
@ -140,16 +143,17 @@ fun RecordingStatus(
modifier = Modifier.size(ButtonDefaults.IconSize), modifier = Modifier.size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Delete") Text(label)
} }
} }
val label = stringResource(R.string.ui_audioRecorder_action_save_label)
Button( Button(
modifier = Modifier modifier = Modifier
.padding(16.dp) .padding(16.dp)
.fillMaxWidth() .fillMaxWidth()
.height(BIG_PRIMARY_BUTTON_SIZE) .height(BIG_PRIMARY_BUTTON_SIZE)
.semantics { .semantics {
contentDescription = "Save Recording" contentDescription = label
}, },
onClick = { onClick = {
RecorderService.stopService(context) RecorderService.stopService(context)
@ -163,7 +167,7 @@ fun RecordingStatus(
modifier = Modifier.size(ButtonDefaults.IconSize), modifier = Modifier.size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Save Recording") Text(label)
} }
} }
} }

View File

@ -25,17 +25,18 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.services.RecorderService import app.myzel394.locationtest.services.RecorderService
import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE
import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.AudioVisualizer import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.AudioVisualizer
import app.myzel394.locationtest.ui.components.atoms.PermissionRequester import app.myzel394.locationtest.ui.components.atoms.PermissionRequester
import app.myzel394.locationtest.ui.utils.rememberFileSaverDialog import app.myzel394.locationtest.ui.utils.rememberFileSaverDialog
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
val VISUALIZER_HEIGHT = 200.dp
@Composable @Composable
fun StartRecording( fun StartRecording(
@ -66,13 +67,14 @@ fun StartRecording(
RecorderService.startService(context, connection) RecorderService.startService(context, connection)
}, },
) { trigger -> ) { trigger ->
val label = stringResource(R.string.ui_audioRecorder_action_start_label)
Button( Button(
onClick = { onClick = {
trigger() trigger()
}, },
modifier = Modifier modifier = Modifier
.semantics { .semantics {
contentDescription = "Start recording" contentDescription = label
} }
.size(200.dp) .size(200.dp)
.clip(shape = CircleShape), .clip(shape = CircleShape),
@ -89,7 +91,7 @@ fun StartRecording(
) )
Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing))
Text( Text(
"Start Recording", label,
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
) )
} }
@ -124,7 +126,12 @@ fun StartRecording(
.size(ButtonDefaults.IconSize), .size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Save Recording from ${service.recordingStart!!.format(DateTimeFormatter.ISO_DATE_TIME)}") Text(
stringResource(
R.string.ui_audioRecorder_action_saveOldRecording_label,
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(service.recordingStart!!),
),
)
} }
} }
else else

View File

@ -17,8 +17,10 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -55,13 +57,15 @@ fun BitrateTile() {
} }
} }
val notNumberLabel = stringResource(R.string.form_error_type_notNumber)
val notInRangeLabel = stringResource(R.string.form_error_value_notInRange, 1, 320)
InputDialog( InputDialog(
state = showDialog, state = showDialog,
selection = InputSelection( selection = InputSelection(
input = listOf( input = listOf(
InputTextField( InputTextField(
header = InputHeader( header = InputHeader(
title = "Set the bitrate for the audio recording", title = stringResource(id = R.string.ui_settings_option_bitrate_explanation),
icon = IconSource(Icons.Default.Tune), icon = IconSource(Icons.Default.Tune),
), ),
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
@ -73,14 +77,14 @@ fun BitrateTile() {
val bitRate = text?.toIntOrNull() val bitRate = text?.toIntOrNull()
if (bitRate == null) { if (bitRate == null) {
ValidationResult.Invalid("Please enter a valid number") ValidationResult.Invalid(notNumberLabel)
} }
if (bitRate in 1..320) { if (bitRate !in 1..320) {
ValidationResult.Valid ValidationResult.Invalid(notInRangeLabel)
} else {
ValidationResult.Invalid("Please enter a number between 1 and 320")
} }
ValidationResult.Valid
}, },
key = "bitrate", key = "bitrate",
) )
@ -92,8 +96,8 @@ fun BitrateTile() {
} }
) )
SettingsTile( SettingsTile(
title = "Bitrate", title = stringResource(R.string.ui_settings_option_bitrate_title),
description = "A higher bitrate means better quality but also larger file size", description = stringResource(R.string.ui_settings_option_bitrate_description),
leading = { leading = {
Icon( Icon(
Icons.Default.Tune, Icons.Default.Tune,
@ -109,7 +113,10 @@ fun BitrateTile() {
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
) { ) {
Text( Text(
text = "${settings.audioRecorderSettings.bitRate / 1000} KB/s", stringResource(
R.string.format_kbps,
settings.audioRecorderSettings.bitRate / 1000,
),
) )
} }
}, },
@ -119,7 +126,10 @@ fun BitrateTile() {
onItemSelected = ::updateValue, onItemSelected = ::updateValue,
) {bitRate -> ) {bitRate ->
Text( Text(
text = "${bitRate / 1000} KB/s", stringResource(
R.string.format_kbps,
bitRate / 1000,
),
) )
} }
} }

View File

@ -13,6 +13,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -60,7 +62,7 @@ fun EncoderTile() {
}, },
) )
SettingsTile( SettingsTile(
title = "Encoder", title = stringResource(R.string.ui_settings_option_encoder_title),
leading = { leading = {
Icon( Icon(
Icons.Default.Memory, Icons.Default.Memory,
@ -77,7 +79,7 @@ fun EncoderTile() {
) { ) {
Text( Text(
text = if (settings.audioRecorderSettings.encoder == null) { text = if (settings.audioRecorderSettings.encoder == null) {
"Auto" stringResource(R.string.ui_settings_value_auto_label)
} else { } else {
AudioRecorderSettings.ENCODER_INDEX_TEXT_MAP[settings.audioRecorderSettings.encoder]!! AudioRecorderSettings.ENCODER_INDEX_TEXT_MAP[settings.audioRecorderSettings.encoder]!!
} }
@ -89,9 +91,7 @@ fun EncoderTile() {
items = listOf(null), items = listOf(null),
onItemSelected = ::updateValue, onItemSelected = ::updateValue,
) { ) {
Text( Text(stringResource(R.string.ui_settings_value_auto_label))
text = "Auto"
)
} }
} }
) )

View File

@ -11,6 +11,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -42,8 +44,8 @@ fun ForceExactMaxDurationTile() {
SettingsTile( SettingsTile(
title = "Force exact duration", title = stringResource(R.string.ui_settings_option_forceExactDuration_title),
description = "Force to strip the output file to be the exactly specified duration. If this is disabled, the output file may be a bit longer due to batches of audio samples being encoded together.", description = stringResource(R.string.ui_settings_option_forceExactDuration_description),
leading = { leading = {
Icon( Icon(
Icons.Default.GraphicEq, Icons.Default.GraphicEq,

View File

@ -17,7 +17,9 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -66,8 +68,8 @@ fun IntervalDurationTile() {
) )
) )
SettingsTile( SettingsTile(
title = "Batch duration", title = stringResource(R.string.ui_settings_option_intervalDuration_title),
description = "Record a single batch for this duration. Alibi records multiple batches and deletes the oldest one. When exporting the audio, all batches will be merged together", description = stringResource(R.string.ui_settings_option_intervalDuration_description),
leading = { leading = {
Icon( Icon(
Icons.Default.Mic, Icons.Default.Mic,

View File

@ -18,8 +18,10 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -74,8 +76,8 @@ fun MaxDurationTile() {
) )
) )
SettingsTile( SettingsTile(
title = "Max duration", title = stringResource(R.string.ui_settings_option_maxDuration_title),
description = "Set the maximum duration of the recording", description = stringResource(R.string.ui_settings_option_maxDuration_description),
leading = { leading = {
Icon( Icon(
Icons.Default.Timer, Icons.Default.Timer,

View File

@ -15,7 +15,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -70,7 +72,7 @@ fun OutputFormatTile() {
}, },
) )
SettingsTile( SettingsTile(
title = "Output Format", title = stringResource(R.string.ui_settings_option_outputFormat_title),
leading = { leading = {
Icon( Icon(
Icons.Default.AudioFile, Icons.Default.AudioFile,
@ -87,7 +89,7 @@ fun OutputFormatTile() {
) { ) {
Text( Text(
text = if (settings.audioRecorderSettings.outputFormat == null) { text = if (settings.audioRecorderSettings.outputFormat == null) {
"Auto" stringResource(R.string.ui_settings_value_auto_label)
} else { } else {
AudioRecorderSettings.OUTPUT_FORMAT_INDEX_TEXT_MAP[settings.audioRecorderSettings.outputFormat]!! AudioRecorderSettings.OUTPUT_FORMAT_INDEX_TEXT_MAP[settings.audioRecorderSettings.outputFormat]!!
} }
@ -99,9 +101,7 @@ fun OutputFormatTile() {
items = listOf(null), items = listOf(null),
onItemSelected = ::updateValue, onItemSelected = ::updateValue,
) { ) {
Text( Text(stringResource(R.string.ui_settings_value_auto_label))
text = "Auto"
)
} }
} }
) )

View File

@ -13,7 +13,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -50,13 +52,15 @@ fun SamplingRateTile() {
} }
} }
val notNumberLabel = stringResource(R.string.form_error_type_notNumber)
val mustBeGreaterThanLabel = stringResource(R.string.form_error_value_mustBeGreaterThan, 1000)
InputDialog( InputDialog(
state = showDialog, state = showDialog,
selection = InputSelection( selection = InputSelection(
input = listOf( input = listOf(
InputTextField( InputTextField(
header = InputHeader( header = InputHeader(
title = "Set the sampling rate", title = stringResource(R.string.ui_settings_option_samplingRate_explanation),
icon = IconSource(Icons.Default.RadioButtonChecked), icon = IconSource(Icons.Default.RadioButtonChecked),
), ),
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
@ -68,11 +72,11 @@ fun SamplingRateTile() {
val samplingRate = text?.toIntOrNull() val samplingRate = text?.toIntOrNull()
if (samplingRate == null) { if (samplingRate == null) {
ValidationResult.Invalid("Please enter a valid number") ValidationResult.Invalid(notNumberLabel)
} }
if (samplingRate!! <= 1000) { if (samplingRate!! <= 1000) {
ValidationResult.Invalid("Sampling rate must be greater than 1000") ValidationResult.Invalid(mustBeGreaterThanLabel)
} }
ValidationResult.Valid ValidationResult.Valid
@ -87,8 +91,8 @@ fun SamplingRateTile() {
} }
) )
SettingsTile( SettingsTile(
title = "Sampling rate", title = stringResource(R.string.ui_settings_option_samplingRate_title),
description = "Define how many samples per second are taken from the audio signal", description = stringResource(R.string.ui_settings_option_samplingRate_description),
leading = { leading = {
Icon( Icon(
Icons.Default.RadioButtonChecked, Icons.Default.RadioButtonChecked,
@ -104,7 +108,7 @@ fun SamplingRateTile() {
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
) { ) {
Text( Text(
text = (settings.audioRecorderSettings.samplingRate ?: "Auto").toString() (settings.audioRecorderSettings.samplingRate ?: stringResource(R.string.ui_settings_value_auto_label)).toString()
) )
} }
}, },
@ -114,7 +118,7 @@ fun SamplingRateTile() {
onItemSelected = ::updateValue, onItemSelected = ::updateValue,
) {samplingRate -> ) {samplingRate ->
Text( Text(
text = (samplingRate ?: "Auto").toString() (samplingRate ?: stringResource(R.string.ui_settings_value_auto_label)).toString()
) )
} }
} }

View File

@ -23,7 +23,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE
@Composable @Composable
@ -50,12 +52,12 @@ fun ExplanationPage(
) )
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
Text( Text(
"Welcome to Alibi!", stringResource(R.string.ui_welcome_explanation_title),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text( Text(
"Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it.", stringResource(R.string.ui_welcome_explanation_message),
) )
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
@ -72,7 +74,7 @@ fun ExplanationPage(
modifier = Modifier.size(ButtonDefaults.IconSize) modifier = Modifier.size(ButtonDefaults.IconSize)
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Continue") Text(stringResource(R.string.continue_label))
} }
} }
} }

View File

@ -21,7 +21,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.myzel394.locationtest.R
import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE
@Composable @Composable
@ -48,12 +50,12 @@ fun ResponsibilityPage(
) )
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
Text( Text(
"You are solely responsible for the use of this app", stringResource(R.string.ui_welcome_responsibility_title),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text( Text(
"Alibi does not take any responsibility for the use of this app. You are solely responsible. Use it at your own risk.", stringResource(R.string.ui_welcome_responsibility_message),
) )
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
@ -70,7 +72,7 @@ fun ResponsibilityPage(
modifier = Modifier.size(ButtonDefaults.IconSize) modifier = Modifier.size(ButtonDefaults.IconSize)
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Start Alibi") Text(stringResource(R.string.ui_welcome_start_label))
} }
} }
} }

View File

@ -9,7 +9,6 @@ import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -19,6 +18,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.myzel394.locationtest.R
import app.myzel394.locationtest.ui.utils.PermissionHelper import app.myzel394.locationtest.ui.utils.PermissionHelper
import app.myzel394.locationtest.ui.utils.openAppSystemSettings import app.myzel394.locationtest.ui.utils.openAppSystemSettings
@ -51,10 +52,10 @@ fun PermissionRequester(
}, },
icon = icon, icon = icon,
title = { title = {
Text("Permission denied") Text(stringResource(R.string.ui_permissions_request_title))
}, },
text = { text = {
Text("Please grant the permission to continue. You will be redirected to the app settings to grant the permission there.") Text(stringResource(R.string.ui_permissions_request_message))
}, },
confirmButton = { confirmButton = {
Button( Button(
@ -69,7 +70,7 @@ fun PermissionRequester(
modifier = Modifier.size(ButtonDefaults.IconSize), modifier = Modifier.size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("OK") Text(stringResource(R.string.dialog_close_neutral_label))
} }
}, },
dismissButton = { dismissButton = {
@ -85,7 +86,7 @@ fun PermissionRequester(
modifier = Modifier.size(ButtonDefaults.IconSize), modifier = Modifier.size(ButtonDefaults.IconSize),
) )
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text("Cancel") Text(stringResource(R.string.dialog_close_cancel_label))
} }
} }
) )

View File

@ -14,12 +14,14 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController import androidx.navigation.NavController
import app.myzel394.locationtest.services.bindToRecorderService import app.myzel394.locationtest.services.bindToRecorderService
import app.myzel394.locationtest.ui.components.AudioRecorder.molecules.RecordingStatus import app.myzel394.locationtest.ui.components.AudioRecorder.molecules.RecordingStatus
import app.myzel394.locationtest.ui.components.AudioRecorder.molecules.StartRecording import app.myzel394.locationtest.ui.components.AudioRecorder.molecules.StartRecording
import app.myzel394.locationtest.ui.enums.Screen import app.myzel394.locationtest.ui.enums.Screen
import app.myzel394.locationtest.ui.utils.rememberFileSaverDialog import app.myzel394.locationtest.ui.utils.rememberFileSaverDialog
import app.myzel394.locationtest.R
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -34,7 +36,7 @@ fun AudioRecorder(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
Text("Alibi") Text(stringResource(R.string.app_name))
}, },
actions = { actions = {
IconButton( IconButton(

View File

@ -35,8 +35,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import app.myzel394.locationtest.R
import app.myzel394.locationtest.dataStore import app.myzel394.locationtest.dataStore
import app.myzel394.locationtest.db.AppSettings import app.myzel394.locationtest.db.AppSettings
import app.myzel394.locationtest.db.AudioRecorderSettings import app.myzel394.locationtest.db.AudioRecorderSettings
@ -75,7 +77,7 @@ fun SettingsScreen(
topBar = { topBar = {
LargeTopAppBar( LargeTopAppBar(
title = { title = {
Text(text = "Settings") Text(stringResource(R.string.ui_settings_title))
}, },
navigationIcon = { navigationIcon = {
IconButton(onClick = navController::popBackStack) { IconButton(onClick = navController::popBackStack) {
@ -113,12 +115,12 @@ fun SettingsScreen(
) { ) {
MessageBox( MessageBox(
type = MessageType.WARNING, type = MessageType.WARNING,
title = "You are recording", title = stringResource(R.string.ui_settings_hint_recordingActive_title),
message = "Your changes will be applied the next time you start recording", message = stringResource(R.string.ui_settings_hint_recordingActive_message),
) )
} }
GlobalSwitch( GlobalSwitch(
label = "Advanced Settings", label = stringResource(R.string.ui_settings_advancedSettings_label),
checked = settings.showAdvancedSettings, checked = settings.showAdvancedSettings,
onCheckedChange = { onCheckedChange = {
scope.launch { scope.launch {

View File

@ -1,3 +1,48 @@
<resources> <resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">LocationTest</string> <string name="app_name">LocationTest</string>
<string name="dialog_close_cancel_label">Cancel</string>
<string name="dialog_close_neutral_label">OK</string>
<string name="continue_label">Continue</string>
<string name="format_kbps"><xliff:g name="value">%s</xliff:g> KB/s</string>
<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="ui_permissions_request_title">Permission denied</string>
<string name="ui_permissions_request_message">Please grant the permission to continue. You will be redirected to the app settings to grant the permission there.</string>
<string name="ui_audioRecorder_action_start_label">Start Recording</string>
<string name="ui_audioRecorder_action_saveOldRecording_label">Save Recording from <xliff:g name="date">%s</xliff:g></string>
<string name="ui_audioRecorder_action_delete_label">Delete</string>
<string name="ui_audioRecorder_action_delete_confirm_title">Delete Recording?</string>
<string name="ui_audioRecorder_action_delete_confirm_message">Are you sure you want to delete this recording?</string>
<string name="ui_audioRecorder_action_save_label">Save Recording</string>
<string name="ui_welcome_explanation_title">Welcome to Alibi!</string>
<string name="ui_welcome_explanation_message">Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it.</string>
<string name="ui_welcome_responsibility_title">You are solely responsible for the use of this app</string>
<string name="ui_welcome_responsibility_message">Alibi does not take any responsibility for the use of this app. You are solely responsible. Use it at your own risk.</string>
<string name="ui_welcome_start_label">Start Alibi</string>
<string name="ui_settings_title">Settings</string>
<string name="ui_settings_advancedSettings_label">Advanced Settings</string>
<string name="ui_settings_hint_recordingActive_title">You are recording</string>
<string name="ui_settings_hint_recordingActive_message">Your changes will be applied the next time you start recording</string>
<string name="ui_settings_option_maxDuration_title">Max duration</string>
<string name="ui_settings_option_maxDuration_description">Set the maximum duration of the recording</string>
<string name="ui_settings_option_intervalDuration_title">Batch duration</string>
<string name="ui_settings_option_intervalDuration_description">Record a single batch for this duration. Alibi records multiple batches and deletes the oldest one. When exporting the audio, all batches will be merged together</string>
<string name="ui_settings_option_forceExactDuration_title">Force exact duration</string>
<string name="ui_settings_option_forceExactDuration_description">Force to strip the output file to be the exactly specified duration. If this is disabled, the output file may be a bit longer due to batches of audio samples being encoded together.</string>
<string name="ui_settings_option_bitrate_title">Bitrate</string>
<string name="ui_settings_option_bitrate_description">A higher bitrate means better quality but also larger file size</string>
<string name="ui_settings_option_bitrate_explanation">Set the bitrate for the audio recording</string>
<string name="ui_settings_option_outputFormat_title">Output Format</string>
<string name="ui_settings_value_auto_label">Auto</string>
<string name="ui_settings_option_samplingRate_title">Sampling rate</string>
<string name="ui_settings_option_samplingRate_description">Define how many samples per second are taken from the audio signal</string>
<string name="ui_settings_option_samplingRate_explanation">Set the sampling rate</string>
<string name="ui_settings_option_encoder_title">Encoder</string>
</resources> </resources>